Upstash を使ってみる

momento を Deno で使えないかと、試みたが失敗したので他のものを探していたら、次の記事を見つけました。

無料で使えるデータベース Upstash をご存知、ないのですか!?

公式サイトを見ると、キャッシュ以外に qStash というスケジュール形式のメッセージングソリューションのサービスをしているという記載があります。

Deno Deploy を間接的にスケジュール実行できそうな気がするのは魅力的に感じます。
というところでこの2つを試します。

参考

upstash redis

upstash redis を作成するところは、冒頭紹介したブログに記載が有るので、そちらを参考にします。

データベース名は test-db としました。

実装は以下の通り。(公式が Deno 向けに SDK を提供してくれているところは非常に最高。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import "https://deno.land/std@0.150.0/dotenv/load.ts";
import { Application, Router, Context } from "https://deno.land/x/oak/mod.ts";
import { Redis } from "https://deno.land/x/upstash_redis/mod.ts";

const redis = new Redis({
url: Deno.env.get("UPSTASH_URL"),
token: Deno.env.get("UPSTASH_TOKEN"),
});

const router = new Router();
router.get("/", async (context: Context) => {
const count = await redis.get("count");
context.response.body = count;
});

router.get("/update", async (context: Context) => {
await redis.incr("count");
context.response.body = "updated";
});

const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8080 });

以上の用意をして、サーバーを起動し curl で動作確認していくと次のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
$ curl http://localhost:8080/
1
$ curl http://localhost:8080/update
updated
$ curl http://localhost:8080/
2
$ curl http://localhost:8080/update
updated
$ curl http://localhost:8080/update
updated
$ curl http://localhost:8080/
4

書き込みと更新ができました。
(ざっと試したかったので update を get メソッドで呼んでいます。)

upstash qstash

先に作ったアプリは、/update にアクセスすると、値が増えるものです。
これを定期的に、実行してくれれば/ にアクセスするだけで内容が更新されます。
Deno Deploy 向けの説明 があったので、これを踏襲します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import "https://deno.land/std@0.150.0/dotenv/load.ts";
import { Application, Router, Context } from "https://deno.land/x/oak/mod.ts";
import { Redis } from "https://deno.land/x/upstash_redis/mod.ts";
import { Receiver } from "https://deno.land/x/upstash_qstash/mod.ts";

const redis = new Redis({
url: Deno.env.get("UPSTASH_URL"),
token: Deno.env.get("UPSTASH_TOKEN"),
})

const receiver = new Receiver({
currentSigningKey: Deno.env.get("QSTASH_CURRENT_SIGNING_KEY")!,
nextSigningKey: Deno.env.get("QSTASH_NEXT_SIGNING_KEY")!,
});

const router = new Router();
router.get("/", async(context:Context) => {
const count = await redis.get("count");
context.response.body = count;
});

router.post("/update", async(context:Context) => {
const isValid = await receiver.verify({
signature: context.request.headers.get("Upstash-Signature")!,
body: await context.request.body({ type: "text" }).value,
}).catch((err: Error) => {
console.error(err);
return false;
});
if (!isValid) {
return new Response("Invalid signature", { status: 401 });
}

await redis.incr("count");

console.log("updated");

return new Response("OK", { status: 200 });
});

const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8080 });

こちらを deno deploy で稼働させて、qStash の設定をします。

ドキュメントには、200 系のメッセージが、返されるまで繰り返す。
とあるのですが、200 を返していても、1 分に 3 回程度繰り返しメッセージが到達しているようで、ドキュメント通りなのかははっきりとしません。

本番運用するなら、排他制御するとかの方法もありそうですが、GUI にはないリトライ回数オプションが curl では制御できました。

次のように設定します。

1
2
3
4
5
$ curl -XPOST \
"https://qstash.upstash.io/v1/publish/https://hoge.deno.dev/update" \
-H "Authorization: Bearer hogehogehogehoge" \
-H "Upstash-Retries: 0" \ # <== ここ
-H "Upstash-Cron: * * * * *"

リトライ回数を 0 にすると、到達件数が 1 件だけになった(ように見えます)。
必ずメッセージを受け取りたいという要件が有った場合には、これはこれでまずい気がします。


ということで、upStash を触ってみました。
確認のため 1 分に 1 回 qStash を呼び出すと早々に、リクエスト回数上限に達してしまい、有償プランに切り替えてしまいました。

面白いので、次回何か作る際のインフラ候補としてぜひ採用するでしょう。

ではでは。