バックエンドチュートリアルの罠
チュートリアルでは、次のような単純なプロセスが示されます。 Webhookを受信する。 データベースを更新する。 200 OKを返す。
テストではコードは正常に動作します。それを本番環境にデプロイします。すると、データベースに重複したレコードが現れます。ユーザーに二重にクレジットが付与されたり、データエントリーが積み上がったりします。
チュートリアルは、ネットワーク障害という現実を無視しています。
問題:信頼性の低いネットワーク ネットワークは失敗します。サーバーの処理が遅れることもあります。DNSエラーによって、200 OKのレスポンスが送信元に届かないこともあります。
サービスが確認(confirmation)を受け取れない場合、リトライを行います。同じWebhookを再度送信します。もしコードがすべてのリクエストを受け入れてしまうと、重複が発生します。
解決策:べき等性(Idempotency) べき等性とは、複数の同一のリクエストを行っても、単一のリクエストと同じ結果になることを意味します。
エレベーターのボタンを想像してください。5階のボタンを1回押すと、エレベーターはその階へ向かいます。10回押したからといって、50階へ行くわけではありません。結果は同じです。
Webhookも、そのボタンのように動作する必要があります。
修正方法 安全なWebhookを構築するために、以下の手順に従ってください:
- イベントの一意なID(unique ID)を見つける。
- 何かを行う前に、そのIDがデータベースに存在するか確認する。
- IDが存在する場合、処理を停止する。送信元がリトライを停止するように、200 OKを返す。
- IDが新しい場合は、データを処理する。
- 直ちにイベントIDをデータベースに保存する。
Node.jsでのロジック例:
const eventId = req.body.event_id;
const existingEvent = await db.processedEvents.findUnique({
where: { id: eventId }
});
if (existingEvent) {
return res.status(200).send('Already processed');
}
await updateUserData(req.body.data);
await db.processedEvents.create({ data: { id: eventId } });
return res.status(200).send('Success');
完璧な条件下で動作するシステムを作るのは簡単です。失敗を想定してシステムを作るのが、真のエンジニアリングです。
リトライによるデータの重複に直面したことはありますか?べき等性をどのように扱っていますか?