QStash を使用して Redis 状態をデータベースとシームレスに同期する
前回の記事では、Redis を使用してユーザーのクォータの状態をフルスタック Next.js アプリに保存する方法について説明しました。 Redis は一般にデータベースよりもはるかに高速であるため、これを行うことでアプリケーションのパフォーマンスを向上させることができます。
Upstash Redis はフルマネージドの Redis データベースであり、HTTP 経由でアクセスできるため、このユースケースには特に最適です。これは、Vercel のようなエッジ ランタイムを使用して、最小限のレイテンシでユーザーの近くでコードを実行することもできることを意味します。
QStash とは何ですか?
QStash は、サーバーレスおよびエッジ ランタイム向けの HTTP ベースのメッセージングおよびスケジューリング ソリューションです。 。つまり、HTTP リクエストを送信して CRON ジョブを実行できます。 .
CRON ジョブは、特定の時間にタスクを実行するようにスケジュールを設定する優れた方法です。この CRON ジョブは、アプリケーションやエンドユーザーのパフォーマンスに影響を与えることなく、必要なだけ実行できます。
この記事の CRON ジョブの目標は、ユーザーのクォータの状態を Redis からデータベースに同期することです。このタスクを1 時間ごとに実行します。 .
QStash を使用した CRON ジョブ エンドポイントの作成
ここでは、Redis でユーザーのクォータの状態をどのように処理するかについて、前回の記事を簡単に要約します。
// Key stored in Redis. A key is created for each user, and made unique by their ID.
const quotaKey = `user:${userId}:quota`;
// Call the INCR command to increment the value of the key by 1.
const quota = await redis.incr(quotaKey);
// If the value of the key is 1, it means the key has been created.
// We can set an expiration date of 24 hours to this key by using the EXPIRE command.
if (quota === 1) {
await redis.expire(quotaKey, 60 * 60 * 24);
}
このコードを考慮すると、QStash を使用して CRON ジョブ エンドポイントを作成できるようになります。 CRON ジョブ エンドポイントは URL /api/cron/update-usage でトリガーされます。 .
Next.js エンドポイント用の新しいファイルを作成しましょう。
touch pages/api/cron/update-usage.ts
次に、QStash のみがこのエンドポイントをトリガーできることを確認します。これが @upstash/qstash です。 私たちのためにハンドルします。これを行わないと、 誰でも潜在的に CRON ジョブ エンドポイントをトリガーすると、セキュリティ上の問題が発生する可能性があります。
@upstash/qstash をインストールしましょう :
npm install @upstash/qstash ここで、CRON ジョブ エンドポイントのコードに集中できます。
これから行うことの簡単な要約は次のとおりです。
SCANを使用して Redis からすべてのユーザーのクォータ キーを取得します (パフォーマンスのカーソルベースのページネーション) .SCANから見つかった各キーについて 、usersを作成します。 ユーザーの ID と割り当てを含むオブジェクトの配列users内で各ユーザーのトランザクションを作成します。 配列を使用してデータベース内のクォータを更新します。これは Prisma を使用して行われます。verifySignatureを使用してエンドポイント関数をエクスポートします。@upstash/qstash/nextjsからの関数 QStash のみがエンドポイントをトリガーできるようにするため。
完全なコード エンドポイントはここにあります。コードのあらゆる側面に必ずコメントを付けました。
// /pages/api/cron/update-usage.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { verifySignature } from "@upstash/qstash/nextjs";
import prisma from "@/lib/prisma";
// Generate an Upstash Redis instance using environment variables.
// Make sure those are defined in your .env file.
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
// Required by `@upstash/qstash`.
export const config = { api: { bodyParser: false } };
// Update the usage of every user by pulling the data from the Redis database into the Prisma database.
async function handler(req: NextApiRequest, res: NextApiResponse) {
// Contains all the keys found during the `SCAN`.
const keys: string[] = [];
// Current position of the cursor, updated after each `SCAN`.
let cursor = 0;
// Execute the cursor-based pagination using the `SCAN` command.
// Redis return `0` when the cursor is at the end of the pagination.
do {
const [nextCursor, newKeys] = await redis.scan(cursor, {
// Match the pattern of the keys we want to find, where `*` is a wildcard.
match: "user:*:quota",
// Limit the number of keys returned by the `SCAN` command.
count: 10,
});
cursor = nextCursor;
keys.push(...newKeys);
} while (cursor !== 0);
// Array of objects containing the user's ID and their quota.
const users: { id: number; quota: number }[] = [];
// Get the usage of each user from Redis based on the keys found.
for (const key of keys) {
// A key should be in the format `user:${id}:quota`. Split the key to get the ID.
const id = key.split(":")[1];
// Get the current quota of the user using the `GET` command.
const quota = await redis.get(`user:${id}:quota`);
users.push({ id: parseInt(id, 10), quota });
}
// Create a transaction for each user to update their quota in the database.
const promises = users.map((user) =>
prisma.user.update({
where: { id: user.id },
data: { quota: user.quota },
}),
);
// Run all transactions.
await Promise.all(promises);
return res.status(200).json({ message: "OK" });
}
// Export the handler with the `verifySignature` function to make sure only
// QStash can authenticate to trigger the CRON job.
export default verifySignature(handler); 最も難しい部分は完了しました!
QStash のセットアップ
コードをプッシュする前に、QStash をセットアップする必要があります。 QStash は寛大な無料枠を提供しており、1 日あたり最大 500 件のリクエストを使用できます。 .
Upstash コンソール内にログインし、QStash をクリックします。 タブ。
次に、リクエスト ビルダーを使用して、新しいスケジュールされた CRON ジョブを作成しましょう。 。直感的な UI のおかげで、非常に簡単です。

[スケジュール] をクリックした後 ボタンをクリックして下にスクロールすると、スケジュールされたジョブに CRON ジョブが表示されます。 セクション:

最後のステップでは、2 つの環境変数を設定する必要があります。 Next.js アプリケーションで。これらの変数は、Next.js アプリケーションがホストされている場所 (この場合は Vercel) に追加されます。
リクエスト ビルダー内 セクションでは、コピーできる環境変数が 2 つあります:
QSTASH_CURRENT_SIGNING_KEYQSTASH_NEXT_SIGNING_KEY
これらは、受信メッセージを認証するために必要です。そうしないと、誰でもエンドポイントを呼び出すことができてしまいます。

これらの変数を取得したら、Vercel プロジェクトに追加できます。
これを行うには、Vercel プロジェクトに移動し、設定 をクリックします。 次に環境変数についてです。 :

2 つの環境変数を追加した後、コードをプッシュできるようになります。
QStash は CRON ジョブ エンドポイントを定期的にトリガーし、すべてのユーザーの使用状況を 1 時間ごとに更新します。
結論
Upstash はサーバーレス環境用のツールを多数提供しています。 QStash と Redis を組み合わせて使用すると、多くのユースケースに対応するパフォーマンスとスケーラブルなアプリケーションを作成できます。
実際に見てみたい場合は、同じシステムが fastsheet に実装されています。fastsheet は、スプレッドシートを API に変換するツールです。
-
redisのキーに有効期限を設定する方法– Redis EXPIRE | EXPIREAT
このチュートリアルでは、redis EXPIRE、PEXPIRE、EXPIREAT、およびPEXPIREATコマンドを使用して、キーに有効期限(タイムアウト)を設定する方法について学習します。 有効期限(秒単位) :- キーの有効期限を秒単位で設定するには、redis EXPIREを使用します redis-cliのコマンド。この秒数は、存続時間を表します。秒数がゼロまたは負の場合、キーはすぐに削除されます。 redis EXPIREの構文は次のとおりです:- 構文:- redis host:post> EXPIRE <key name> <seconds&g
-
Redis SUBSCRIBE – redis pub/subで複数のチャネルをサブスクライブする方法
このチュートリアルでは、redis-cliを使用してredisメッセージブローカーシステムで複数のチャネルをサブスクライブする方法について学習します。 SUBSCRIBEコマンド SUBSCRIBEコマンドは、redisメッセージブローカーシステムで指定された1つ以上のチャネルにクライアントをサブスクライブするために使用されます。クライアントがサブスクライブコマンドを実行すると、サブスクライブされたチャネルをリッスンするサブスクライブ状態になります。他のクライアントによって指定されたチャネルに公開されたメッセージは、redisによってサブスクライブされたすべてのクライアントにプッシュされま