この記事をもとに、ランキング機能のリアルタイム更新を題材として模擬面接を行いました。このページでは、元記事の面接観点、今回の設計議論、回答例、詰まったポイントをまとめています。

次回同じテーマで模擬面接をするときに、答えを暗記するのではなく「何を確認し、どう考えて、なぜその技術を選ぶのか」を思い出すための復習メモです。


まず意識したいこと

今回一番大きかった反省は、技術名から入るのではなく、要件 → 問題 → 設計判断 → 技術選択の順番で話すことです。

たとえば「リアルタイムランキング」という言葉を聞いて、すぐに WebSocket や Firestore を選ぶのではなく、先に次のような点を確認します。

  • そもそも、なぜリアルタイムにしたいのか
  • 誰がランキングを競うのか
  • ユーザーがギフトを送る動機は何か
  • 現在のランキング機能はどうなっているのか
  • どの画面にリアルタイム反映するのか
  • 何秒程度の遅延まで許容できるのか
  • 同時閲覧者数やギフト送信数はどれくらいか
  • 一時的な不整合は許容できるのか
  • 最終的な正しさはどの程度必要か

今回の前提では、ランキング画面の同時閲覧は数千〜1万人程度、ギフト送信はピークで毎秒数百件、数秒程度の遅延は許容され、一時的に古い順位でも最終的に正しくなればよい、という要件でした。

この要件が、その後の設計判断の基準になります。


元記事から学んだ面接の見られ方

元記事の面接では、単に知っている技術やアーキテクチャを答えられるかではなく、曖昧な要求を整理し、実装可能な要件へ落とし込み、技術設計を行い、障害時にも復旧可能な仕組みまで考えられるかを見ています。

特に重要なのは、次の流れです。

  1. 要求の背景を確認する
  2. 要件を具体化する
  3. 規模や整合性などの非機能要件を確認する
  4. 技術設計を行う
  5. 障害や競合、重複処理を考える
  6. 復旧可能性を考える
  7. リリース後の監視や原因調査まで考える

今回の模擬面接でも、途中から「正常系の設計」よりも「途中で失敗したらどうなるか」「同じイベントが2回来たらどうなるか」「複数サーバーでどう配信するか」といった質問が増えました。

設計面接では、正常系を一度描いたあとに、自分から失敗パターンを考えていく癖をつけたいです。


今回考えたランキングシステムの全体像

最終的には、次のような構成を考えました。

flowchart LR
    A["ギフト送信"] --> B["Gift API"]
    B --> C["RDB: gifts"]
    B --> D["RDB: outbox_events"]
    D --> E["Outbox Worker"]
    E --> F["Redis Sorted Set"]
    F --> G["Pub/Sub"]
    G --> H["WebSocket Servers"]
    H --> I["Frontend"]
    C --> J["ランキング再構築"]
    J --> F

基本的な考え方は、RDB を正、Redis を高速な派生データ、WebSocket を配信手段として扱うことです。


RDB と Redis の役割

今回、最初は「パフォーマンスが気になるので RDB ではなく Firestore を使う」と考えましたが、ここは設計判断が早すぎました。

Firestore はグラフ指向DBではなくドキュメント指向DBです。また、「負荷が高そうだから RDB ではないものを使う」という判断も適切ではありません。

まず、ランキングというデータ構造に何が必要なのかを考えます。

  • ポイントを加算したい
  • 上位 N 人を取得したい
  • 特定ユーザーの順位を取得したい

この用途には Redis の Sorted Set が適しています。

streamer_A -> 12500
streamer_B -> 10200
streamer_C -> 9800

Redis はランキングの読み書きを高速に行う用途に使い、正しいデータそのものは RDB に保存します。

面接では次のように答えられるようにしたいです。

ランキングではポイント加算と順位、上位N件の取得が頻繁に発生するため、Sorted Set を使える Redis が適しています。ただし Redis は派生データとして使い、正データは RDB に保持します。Redis に障害が起きても RDB からランキングを再構築できるようにします。

Transactional Outbox を使う理由

途中で重要だったのが、「RDBへの保存は成功したが、ランキング更新イベントの発行に失敗したらどうするか」という質問です。

単純に次のように処理すると問題があります。

1. RDB にギフト保存 -> 成功
2. メッセージキューへイベント送信 -> 失敗

これでは DB にはギフトが存在するのに、ランキング更新イベントが失われます。

そこで Transactional Outbox Pattern を使います。

同一トランザクション
- gifts にギフトを保存
- outbox_events にイベントを保存
- commit

その後、別の Worker が outbox_events を読み、Redis 更新処理へ流します。

イベント送信に失敗しても Outbox に未処理イベントが残るため、再実行できます。

今回、最初はイベントソーシングという言葉を使いましたが、今回の問題への直接的な解決策は Transactional Outbox です。

イベントソーシングは、状態ではなくイベント列を正としてシステムの状態を再構築する、より大きな設計パターンです。用語を混同しないようにしたいです。


冪等性と重複イベント

Outbox やメッセージキューでは、同じイベントが複数回処理される可能性があります。

たとえばギフトイベントが「+500ポイント」という差分イベントで、Redis に対して毎回加算するとします。

1回目 +500
2回目 +500

同じイベントが2回来ただけで +1000 になってしまいます。

そこで更新処理に冪等性を持たせます。

方法としては、イベントごとに一意な event_id を持ち、処理済み event_id を記録します。

event_id = gift_98765
streamer_id = streamer_A
points = 500

すでに処理済みなら再加算しません。

また、差分ではなく最新の累計ポイントを Redis に set する方式であれば、同じイベントが複数回来ても最終状態は同じになります。

ここでさらに注意が必要なのは、次の2処理です。

1. event_id を処理済みにする
2. Redis Sorted Set を更新する

別々に実行すると、1だけ成功して2で障害が起きた場合、再実行時に「処理済み」と判断され、ランキングが永遠に更新されない可能性があります。

そのため、Redis の Lua Script などを利用し、重複チェックとランキング更新をアトミックに実行する方法があります。

復習キーワードは、at-least-once delivery を前提に consumer 側を冪等にするです。


WebSocket の役割

WebSocket サーバーは、Redis のデータを保存する役割ではありません。

ランキング更新を受け取り、ランキング画面を開いているフロントエンドへ最新情報を push する役割です。

今回は次のような流れを想定しました。

Redisランキング更新
↓
Pub/Sub
↓
WebSocket Server
↓
Frontend

最初は「誰にどのイベントを送ったかを5台の WebSocket サーバーで共有する必要がある」と考えましたが、ランキングの broadcast では通常そこまで管理する必要はありません。

各 WebSocket サーバーは、自分自身に接続しているクライアントだけを知っていればよいからです。

       Pub/Sub
    ↙  ↓  ↓  ↘
  WS1 WS2 WS3 WS4
   ↓           ↓
User A       User B

全 WebSocket サーバーに同じランキング更新を broadcast し、それぞれが自身の connection にだけ配信します。

Firestore などに配信済みユーザーを毎回記録すると、かえって大量の読み書きが発生し、新たなボトルネックになり得ます。


毎秒300イベントをそのまま配信しない

ランキング画面が1万人に見られていて、ギフトが毎秒300件来た場合、イベントごとに全ユーザーへ配信すると単純計算で非常に多くの配信が発生します。

ここで重要なのは「WebSocketサーバーを増やす」より先に、そもそもの送信回数を減らせないか考えることです。

今回の要件では、数秒程度の遅延が許容されています。

そのため、たとえば1秒間に届いた300件の更新をまとめ、1秒に1回だけ最新ランキングを配信する設計が可能です。

300件のギフトイベント
↓
Redisは随時更新
↓
1秒ごとに最新ランキングを生成
↓
WebSocketで配信

このあと必要に応じて WebSocket サーバーを水平スケールします。

面接では「スケールアウトできます」だけで終わらず、要件を利用して仕事量そのものを減らすところまで考えたいです。


WebSocket再接続時の考え方

ユーザーの通信が10秒途切れ、その間に3回ランキングが更新されたケースを考えました。

最初は「送れなかったイベントをユーザーごとに保存して、再接続後にリトライする」と考えました。

しかし今回ユーザーが欲しいのは、過去のランキング更新イベントではなく現在の最新ランキングです。

そのため、切断中の全イベントを再送する必要はありません。

WebSocket切断
↓
再接続
↓
最新ランキングを取得
↓
WebSocket購読再開

この設計のほうがシンプルです。

この考え方は重要で、イベント履歴が必要なのか、最終状態だけ必要なのかを要件から判断します。


REST API と WebSocket の競合

初期表示では REST API と WebSocket の両方を使います。

最初に考えたのは、WebSocket を先に接続してから REST API を取得する方法です。

これにより、次の取りこぼしは防ぎやすくなります。

REST取得
↓
この間に更新
↓
WebSocket接続

ただし、逆の競合もあります。

1. WebSocket 接続
2. WebSocket で version=105 を受信
3. 遅れて REST API が version=102 を返す
4. version=102 で画面を上書き

この場合、最新状態が古い状態に巻き戻ります。

そのため、API と WebSocket のランキングには version や updated_at を持たせます。

{
  "version": 105,
  "ranking": []
}

フロント側では、現在表示している version より古いデータを反映しません。

response.version > currentVersion
    -> 更新
それ以外
    -> 無視

ネットワーク処理は順番どおりに完了するとは限らない、という前提を持つことが重要です。


Read Replica が古いデータを返すケース

ランキング取得 API が Read Replica を参照しており、数秒のレプリケーション遅延があるケースも考えました。

ここで「Primary を必ず読むべき」と決めるのではなく、要件と比較して判断します。

今回のリアルタイム要件は数秒程度の遅延を許容しています。そのため Replica の遅延がその範囲であれば、読み込み負荷を Primary から逃がすメリットを優先し、Replica を使い続ける判断も可能です。

ただし、WebSocket ですでに新しい version を受け取ったあとに Replica から古いレスポンスが来る可能性があるため、フロント側の version 比較は必要です。

今回「更新頻度が高いから Replica のままでよい」と答えましたが、より正確には、ユーザー体験として数秒の古さが許容されているため Replica を使えると説明したいです。


Redis障害時の復旧

Redis は Source of Truth ではないため、障害でデータが消えても RDB から再構築できます。

ただし、再構築中にも新しいギフトが送られます。

ここで単純に過去データを Redis に上書きすると、再構築中に入った最新ポイントを古い値で上書きする可能性があります。

そのため、実際には次のような設計を検討します。

  • 再構築開始時点のスナップショットを使う
  • それ以降のイベントを別途リプレイする
  • version や時刻を持たせ、古い更新で新しい状態を上書きしない
  • Outbox やイベントログから未処理イベントを追いつかせる

今回の回答では「再構築しながら新しい更新も反映する」ところまでは考えられましたが、競合時にどちらが勝つかまで意識する必要があります。


障害調査の考え方

「自分が見ているランキングと友達が見ているランキングが違う」という問い合わせを例に、切り分けも行いました。

最初に疑ったのは、どちらかが古いランキングを表示している可能性です。これはよい入り方でした。

ただし、「アプリを再起動して直るか試す」だけでは原因調査として弱いため、version を利用してどの工程で更新が止まったかを追います。

Redis 最新               version=105
WebSocket Server 送信     version=105
Browser WebSocket 受信    version=105
Frontend State            version=102
UI                        version=102

このように各地点で version を観測できると、問題箇所を絞り込めます。

WebSocket サーバーに送信ログがあるなら、その前工程ではなく後工程を優先して調査します。

DevTools の WebSocket Frame で version=105 を受信しているのに UI が102なら、状態管理や version 判定、古い API レスポンスによる上書き、再描画などを疑います。

障害調査では、闇雲にログを見るのではなく、最後に正常だった地点を特定し、その次の工程を確認するという考え方を意識したいです。


監視で見るべきもの

最初は API のエラー監視、ログ監視、死活監視を挙げました。これらは必要ですが、このランキング機能特有の監視も必要です。

見るべきものの例です。

  • ギフト API の成功率、エラー率、レイテンシ
  • ランキング API の成功率、エラー率、レイテンシ
  • Outbox の未処理件数、最古イベントの滞留時間
  • Redis 更新失敗数
  • WebSocket の接続数、切断数、再接続数
  • WebSocket 配信遅延
  • RDB と Redis のランキング乖離
  • ギフト保存からランキング反映までの end-to-end latency

今回のプロダクト要件は「数秒以内にランキングが反映されること」です。

したがって、単に API が200を返しているかではなく、ギフトを送ってから実際にユーザー画面へ反映されるまで何秒かかったかを監視することが重要です。

たとえば「p95 で5秒を超えたらアラート」といった SLO にできます。


フロントエンドの監視

フロントは端末、ブラウザ、回線などユーザー環境に依存するため、サーバーより監視が難しいです。

それでも、リアルタイム機能ではサーバーだけ監視しても「ユーザーが本当に最新ランキングを見られているか」は分かりません。

RUM などを使い、実ユーザー環境で次の情報を収集します。

  • WebSocket メッセージ受信時刻
  • 受信した version
  • UI に反映された version
  • UI 反映完了までの時間
  • API レスポンス時間
  • WebSocket 再接続回数

たとえば、サーバーでは12:00:01に送信し、ブラウザでは12:00:01.2に受信しているのにUI反映が12:00:04.5なら、通信ではなくフロント側の処理が遅いと判断できます。

「サーバーが正常だからユーザー体験も正常」とは限らない、という点を意識したいです。


今回の質問と回答例

1. 最初に何を確認しますか

質問

ギフトが送られたらランキングへリアルタイムに反映したいと言われました。まず何を確認しますか。

回答例

まず、この機能をリアルタイムにしたい理由とユーザー体験を確認します。誰が何を競うランキングなのか、ギフトを送るユーザーにはどんな動機があるのか、現在のランキングはどのように更新されているのかを確認します。そのうえで、どの画面にどの程度の遅延で反映する必要があるのかを確認します。

2. 非機能要件として何を確認しますか

回答例

同時閲覧者数、ピーク時のギフト送信数、許容される更新遅延、一時的な不整合が許容されるか、最終的な整合性がどの程度必要かを確認します。

3. なぜ Redis を使いますか

回答例

ランキングではポイント加算、上位N件取得、順位取得が頻繁に発生するため、Sorted Set を利用できる Redis が適しています。ただし正データは RDB に保存し、Redis は高速参照用の派生データとして扱います。

4. RDB保存後にイベント発行だけ失敗したらどうしますか

回答例

Transactional Outbox を利用します。ギフト保存と Outbox イベントの保存を同じ DB トランザクションで行い、別 Worker が Outbox を読み取って Redis 更新処理へ送ります。これによりイベント発行に失敗しても後から再実行できます。

5. 同じイベントが2回来たらどうしますか

回答例

consumer 側の処理を冪等にします。イベントごとに一意な event_id を持ち、処理済みイベントは再度加算しません。また、重複チェックとランキング更新の間に障害が起きないよう、Redis の Lua Script などを使ってアトミックに処理します。

6. 毎秒300件を1万人へどう配信しますか

回答例

まず、1件ごとに配信する必要があるかを要件から判断します。今回は数秒の遅延が許容されるため、一定時間の更新をまとめて最新ランキングを生成し、数秒に1回配信します。そのうえで WebSocket サーバーを水平スケールし、Pub/Sub で各インスタンスへ更新を配信します。

7. WebSocket サーバーを複数台にしたらどうしますか

回答例

ランキング更新を Pub/Sub で各 WebSocket サーバーへ broadcast します。それぞれのサーバーは自身に接続しているクライアントへだけ配信します。ユーザーごとの配信履歴を中央ストレージで管理する必要はありません。

8. WebSocket切断中のイベントはどうしますか

回答例

ランキングでは過去のイベント列ではなく最新状態が重要なので、切断中のイベントを全件再送する必要はありません。再接続時に最新ランキングのスナップショットを取得し、その後 WebSocket 購読を再開します。

9. RESTとWebSocketの順序競合はどうしますか

回答例

WebSocket を先に接続してから初期データを取得し、取りこぼしを防ぎます。ただし API と WebSocket のレスポンス順序が逆転する可能性があるため、それぞれに version を持たせ、現在より古い version はフロント側で反映しません。

10. Redis障害時はどうしますか

回答例

RDBを Source of Truth とし、Redis は再構築可能な派生データとして扱います。Redis 復旧後に RDB からランキングを再構築します。再構築中の新規イベントも取りこぼさないよう、スナップショットやイベントリプレイ、version 管理などを利用します。

11. ユーザーごとにランキングが違う場合どう調査しますか

回答例

まず各クライアントの version とサーバーの最新 version を比較します。そのうえで、REST API、WebSocket サーバー送信、ブラウザ受信、フロントの状態管理、UI反映の各地点で version を確認し、最後に正常だった地点から次の工程を調査します。

12. リリース後に何を監視しますか

回答例

APIエラー率やレイテンシ、RedisやWebSocketの状態に加え、Outboxの滞留件数、ランキング更新失敗、WebSocket配信遅延、RDBとRedisの乖離、ギフト保存からユーザー画面反映までの end-to-end latency を監視します。フロント側も RUM を使い、実ユーザー環境での受信・描画時間を確認します。


今回の反省点

技術から入ってしまった

WebSocket や Firestore といった技術名を早い段階で出しました。

次回は、まず要件を聞き切り、その要件から生まれる問題を整理してから技術を選びます。

要件 → 問題 → 技術 の順番を徹底します。

RDBを避ける理由が曖昧だった

「パフォーマンスが気になるので RDB ではなく別のDB」という判断をしましたが、RDB が本当にボトルネックになるかを確認していませんでした。

データ量、更新頻度、クエリパターンを見てから判断します。

Firestore の分類を間違えた

Firestore はグラフ指向ではなくドキュメント指向DBです。

データストアを提案するときは、分類よりも「今回必要な操作にどう適しているか」を説明できるようにします。

リトライだけで解決しようとした

RDB保存後のイベント送信失敗に対して、最初はリトライを中心に考えました。

リトライは必要ですが、その前に「処理の途中で不整合が生まれにくい構造にできないか」を考えます。今回なら Transactional Outbox が該当します。

配信済みイベントをユーザーごとに管理しようとした

ランキングでは最終状態が重要なので、過去の更新イベントをすべてユーザーへ届ける必要はありません。

「イベントそのものに価値があるのか」「最新状態だけでよいのか」を整理します。

スケールアウトを先に考えた

1万人への配信負荷に対して、最初に WebSocket サーバーを増やすことを考えました。

まず、数秒の遅延が許容されるという要件を使い、300回の更新を1回にまとめるなど、処理量そのものを減らす方法を考えます。

障害調査で前工程に戻りすぎた

WebSocket サーバーの送信ログが正常なのに、その前工程のログを確認しようとしました。

障害調査では、最後に正常であることが確認できた地点を起点にし、その直後の工程を調べます。

監視がインフラ中心だった

API、ログ、死活監視は挙げられましたが、ランキングが実際にユーザーへ何秒で届いたかというプロダクトレベルの監視が不足していました。

SLO に直結する指標を優先して考えます。


次回の模擬面接で意識するチェックリスト

面接中に詰まったら、次の順番で考えます。


覚えておきたいキーワード

  • Source of Truth
  • Redis Sorted Set
  • Transactional Outbox Pattern
  • Pub/Sub
  • WebSocket
  • horizontal scaling
  • idempotency / 冪等性
  • atomic operation
  • Lua Script
  • at-least-once delivery
  • eventual consistency
  • versioning
  • Read Replica
  • replication lag
  • retry
  • replay
  • snapshot
  • RUM
  • end-to-end latency
  • SLO

技術用語を答えること自体が目的ではありません。それぞれが「どの問題を解決するために必要なのか」を説明できることを目標にします。


次回の自分へのメモ

今回、途中で分からなくなった場面はありましたが、要件を一つずつ確認していくところはできていました。

次回は、設計に入ったあとも焦って技術名を出さず、まず次のように考えます。

この要件で一番困ることは何か。
途中でこの処理が失敗したら何が壊れるか。
同じ処理をもう一度実行しても大丈夫か。
最新状態さえ分かればよいのか、それとも全イベントが必要なのか。
その技術を使わずに、もっと単純に解決できないか。

この問いを繰り返すことで、特定の技術を知っているかどうかではなく、設計判断そのものを説明できるようにしたいです。


覚えておきたいキーワードと意味

今回の模擬面接で出てきた用語を、次回すぐ思い出せるように整理します。

Source of Truth

システム内で「最終的に正しいもの」として扱うデータ源です。

今回の設計では RDB が Source of Truth です。Redis のランキングが壊れたり消えたりしても、RDB のギフト・ポイント情報から再構築できる状態にします。

Redis Sorted Set

各要素に score を持たせ、score 順に並べられる Redis のデータ構造です。

ランキングでは、配信者IDを member、累計ポイントを score として持つことで、ポイント加算、上位N件取得、順位取得を高速に行えます。

派生データ

Source of Truth から生成できるデータです。

今回の Redis ランキングは派生データです。高速に読むために保持しますが、失われても RDB から作り直せることが前提です。

Transactional Outbox Pattern

DBへの業務データ保存と、後続処理用イベントの記録を同一トランザクションで行うパターンです。

gifts と outbox_events を同時に commit することで、「DB保存は成功したがイベント発行だけ失敗した」という取りこぼしを防ぎます。Outbox のイベントは別 Worker が後から配信します。

イベントソーシング

システムの現在状態ではなく、「何が起きたか」というイベント列そのものを正として保存する設計パターンです。

Transactional Outbox とは別物です。Outbox はイベント配送の信頼性を上げるための仕組みで、イベントソーシングは状態管理そのものの設計方針です。

冪等性

同じ処理を1回実行しても複数回実行しても、最終結果が同じになる性質です。

メッセージキューでは同じイベントが再配送されることがあるため、ランキング更新処理は冪等にしておく必要があります。

event_id

イベントを一意に識別するIDです。

同じ event_id がすでに処理済みなら再処理しないことで、重複イベントによる二重加算を防げます。

at-least-once delivery

メッセージを「少なくとも1回は届ける」配送方式です。

取りこぼしは避けやすい一方、同じメッセージが複数回届く可能性があります。そのため consumer 側で冪等性を持たせる設計が必要です。

アトミック処理

複数の処理を、途中状態が外から見えない「ひとまとまりの処理」として実行することです。

今回なら「event_id の重複チェック」と「Redisランキング更新」を別々に行わず、Redis の Lua Script などで一体として実行することで、片方だけ成功する不整合を防ぎます。

Lua Script(Redis)

Redis 上で複数コマンドをまとめて実行するために使えるスクリプトです。

Redis は Lua Script の実行中に他のコマンドを割り込ませないため、複数処理をアトミックに扱いたい場面で利用できます。

Pub/Sub

Publisher がメッセージを発行し、Subscriber がそれを受け取るメッセージ配信モデルです。

今回ではランキング更新を Pub/Sub に流し、複数台の WebSocket サーバーが同じ更新を受け取って、自分に接続しているクライアントへ配信します。

WebSocket

ブラウザとサーバーの間で接続を維持し、双方向に通信できる仕組みです。

今回のように、ランキング更新をサーバー側からフロントへ push したい用途に向いています。

水平スケール

同じ役割のサーバー台数を増やして負荷を分散することです。

WebSocket サーバーを1台から複数台へ増やすような構成です。ただし、台数を増やす前に配信頻度をまとめるなど、処理量そのものを減らせないかも検討します。

Broadcast

同じメッセージを複数の受信者へ一斉に配信することです。

ランキングのように多くのユーザーが同じ最新状態を見る場合は、ユーザーごとの個別イベント管理より broadcast が適しているケースがあります。

Snapshot

ある時点の最新状態をひとまとまりで表したデータです。

切断中のランキング更新イベントをすべて再送する代わりに、再接続時に最新ランキングの snapshot を取得すれば十分なケースがあります。

Eventual Consistency(結果整合性)

一時的にはデータがずれていても、一定時間後には最終的に同じ正しい状態へ収束する考え方です。

今回のランキングでは数秒の遅延や一時的な古さは許容されるため、強い整合性をすべての箇所で求める必要はありません。

version

データの新旧を判定するための連番や識別子です。

REST API と WebSocket のレスポンスに version を持たせ、現在表示中より古い version のデータはフロントで無視することで、古いレスポンスによる巻き戻りを防げます。

Read Replica

Primary DB のデータを複製し、主に読み取り処理を担当する DB です。

読み取り負荷を分散できますが、Primary からの反映に遅延が発生することがあります。今回のように数秒の古さが許容されるなら利用可能ですが、version 管理などで古いデータを安全に扱う必要があります。

レプリケーション遅延

Primary DB の更新が Read Replica に反映されるまでの時間差です。

この遅延によって、WebSocketでは新しいランキングを受け取った直後に、REST APIから古いランキングが返ることがあります。

RUM(Real User Monitoring)

実際のユーザー環境で、画面表示や通信の状態を計測する監視手法です。

WebSocket受信時刻、UI反映時間、APIレイテンシ、再接続回数などを収集することで、サーバーが正常でもユーザー体験が悪化しているケースを検知できます。

SLI / SLO

SLI はサービス品質を測る指標、SLO はその指標に対する目標値です。

今回なら「ギフト保存からランキング反映までの時間」を SLI とし、「p95 で5秒以内」のような目標を SLO として設定できます。

p95

計測値を小さい順に並べたとき、95%のリクエストがこの値以下に収まる境界です。

平均値だけでは一部の遅いユーザーを見落とすため、レイテンシ監視では p95 や p99 がよく使われます。

Backlog / Queue滞留

未処理のジョブやイベントがキューに溜まっている状態です。

Outbox や Worker が正常に処理できていない場合、未処理件数や最古イベントの待ち時間を見ることで、ランキング反映の遅延を検知できます。

リトライ

失敗した処理を再実行する仕組みです。

一時的なネットワーク障害などには有効ですが、単純なリトライだけでは重複処理や「DB保存済みだがイベント消失」のような不整合は防げないため、Outbox や冪等性と組み合わせます。

再構築

派生データが壊れたときに、Source of Truth からもう一度作り直すことです。

今回なら Redis のランキングを RDB のポイント情報から再生成できるようにしておくことを指します。