バックエンドチュートリアルの罠

チュートリアルでは、次のような単純なプロセスが示されます。 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');

完璧な条件下で動作するシステムを作るのは簡単です。失敗を想定してシステムを作るのが、真のエンジニアリングです。

リトライによるデータの重複に直面したことはありますか?べき等性をどのように扱っていますか?

Source: https://dev.to/anubhavg23/the-hidden-trap-in-backend-tutorials-why-your-webhooks-are-creating-duplicate-data-and-how-to-fix-dba