リアルタイム通知サービス — システム設計計画
- 要件の再確認と容量見積もり
トラフィック計算:
- 1億DAU x 1日あたり10イベント = 1日あたり10億件の通知トリガーイベント。
- 平均 = 10億 / 86,400 ≈ 11,600 イベント/秒。ピーク時は5倍 ≈ 58,000 イベント/秒。
- ファンアウト乗数: ほとんどのイベント(いいね、コメント、フォロー、DM)は正確に1人の受信者を対象とするため、ファンアウトは〜1:1です。ただし、フォロワー数の多いアカウント(有名人の投稿)からのイベントは数百万人の受信者にファンアウトする可能性があります。これらは別の「バルクファンアウト」パスとして扱います。バルクキャンペーンを含め、ピーク時の配信能力を80,000〜100,000件...
全文を表示 ▼
リアルタイム通知サービス — システム設計計画
- 要件の再確認と容量見積もり
トラフィック計算:
- 1億DAU x 1日あたり10イベント = 1日あたり10億件の通知トリガーイベント。
- 平均 = 10億 / 86,400 ≈ 11,600 イベント/秒。ピーク時は5倍 ≈ 58,000 イベント/秒。
- ファンアウト乗数: ほとんどのイベント(いいね、コメント、フォロー、DM)は正確に1人の受信者を対象とするため、ファンアウトは〜1:1です。ただし、フォロワー数の多いアカウント(有名人の投稿)からのイベントは数百万人の受信者にファンアウトする可能性があります。これらは別の「バルクファンアウト」パスとして扱います。バルクキャンペーンを含め、ピーク時の配信能力を80,000〜100,000件/秒と想定します。
- ストレージ: ユーザーあたり過去100件の通知履歴。1行あたり約400バイトと仮定 -> 100 x 400B = 40KB/ユーザー -> 1億ユーザー ≈ 4TBのホット履歴、レプリケーション係数3で約12TB。容易にシャーディング可能。
- 接続数: ピーク時にDAUの20〜25%がオンラインであると仮定 ≈ 20〜25MのライブWebSocket接続。ゲートウェイノードあたり約100K接続(チューニングされたLinux、epoll)で、200〜250ノードプラス余裕があります。
非機能要件ターゲット:
- オンラインユーザーのエンドツーエンドp99レイテンシは2秒未満(イベント取り込みからクライアントackまでを測定)。
- 99.9%の可用性 = 月あたり約43分のダウンタイム。
- クライアント/サーバーサイドのべき等重複排除による少なくとも1回の配信。
- 高レベルアーキテクチャ
この設計は、取り込みパス(高速、耐久性、書き込み最適化)と配信パス(チャネル固有、リトライ可能)を明確に分割したイベント駆動型パイプラインです。
フロー:
プロデューサーサービス(投稿サービス、ソーシャルグラフサービス、コメントサービス、メッセージングサービス)
-> 通知API/ゲートウェイ(gRPC + REST、認証、レート制限、スキーマ検証、べき等キー)
-> Kafkaトピック notification.events(受信者ユーザーIDがわかっている場合はそれ、不明な場合はアクターIDでパーティション分割)
-> ファンアウトサービス(受信者を解決し、有名人イベントを展開し、集約ルールを適用)
-> Kafkaトピック notification.deliveries(受信者ごとに1メッセージ)
-> 設定とポリシーサービス(キャッシュ経由のインラインルックアップ:チャネルオプトイン、サイレントアワー、ミュート/ブロック、ダイジェストかインスタントか)
-> ルーター/ディスパッチャーがチャネルごとのトピックに書き込み:
deliver.websocket, deliver.mobile_push, deliver.web_push, deliver.email
-> チャネルワーカー:
WebSocketディスパッチャー -> 接続レジストリールックアップ -> リアルタイムゲートウェイノード -> クライアント
モバイルプッシュワーカー -> APNs (iOS) / FCM (Android)
Webプッシュワーカー -> ブラウザプッシュサービス経由のVAPID/Web Pushプロトコル
メールワーカー -> SES / SendGrid、テンプレートレンダリングとダイジェストバッチ処理付き
-> 並行して、履歴ライターコンシューマーがすべての配信を通知履歴ストアに永続化し、未読カウンターをインクリメントします。
横断的なコンポーネント: テンプレートサービス、デバイスレジストリ、接続レジストリ、重複排除ストア、デッドレターキュー、オブザーバビリティスタック、および同じKafkaストリームを消費する管理者/分析プレーン。
- 主要コンポーネントと責任
通知APIゲートウェイ
- 内部プロデューサー(gRPC、レジストリ内のprotobufスキーマ)およびクライアント読み取りAPI(履歴、既読マーク、設定用のREST/GraphQL)の単一のエントリポイント。
- 責任: 認証(サービス間mTLS、クライアント用JWT)、テナントごとおよびプロデューサーごとのレート制限/クォータ、リクエスト検証、べき等キーの受け入れ、およびKafkaへの即時耐久性のある追記。すぐに202 Acceptedを返します。インラインで配信作業は行いません。
イベント取り込み/Kafka
- 耐久性があり、再生可能なログ。
notification.eventsは、約256パーティション、レプリケーション係数3、min.insync.replicas=2、acks=all、保持期間7日間で、再生とインシデント回復に使用します。 - プロデューサーと低速なサードパーティプロバイダーを分離し、5倍のピークに対する自然なバックプレッシャーとバッファリングを提供します。
ファンアウトサービス
- イベントを受信者リストに解決します。1:1イベントの場合はパススルーです。1:Nイベント(フォロワーへの新しい投稿、グループスレッドアクティビティ)の場合は、ソーシャルグラフサービスをページングし、受信者メッセージをバッチで発行します。
- ハイブリッドファンアウト戦略: 通常のアカウントの場合はプッシュベース(受信者ごとに書き込み)、フォロワー数がしきい値(例: 100万人以上)を超えるアカウントの場合はプルベース/遅延実行(読み取り時に受信者が具体化される単一の「フィードマーカー」を書き込みます)。これにより、有名人のイベントがホットパスで5000万メッセージの書き込みストームを生成するのを防ぎます。
- バルク展開を別の低優先度Kafkaトピックにレート制限するため、インタラクティブ通知を決して飢えさせません。
- 集約/合算の適用: 25件の行ではなく、「アリスと他の24人があなたの投稿にいいねしました」のように、(受信者、投稿ID、タイプ)をキーとする短いタンブリングウィンドウ(例: 30〜60秒)をRedisで使用します。
設定とポリシーサービス
- ユーザーごとのチャネル設定、カテゴリレベルのオプトイン、タイムゾーン付きのサイレントアワー、デバイスレベルの設定、ミュート/ブロックリスト、および法的同意フラグ(GDPR/CAN-SPAM)を保存および提供します。
- 読み取りパスはRedisにキャッシュされ、ライトスルー無効化が行われます。p99ルックアップターゲットは5ミリ秒未満です。ソースオブトゥルースはリレーショナルストアにあります。
- ユーザーエクスペリエンスとプロバイダーのクォータを保護するため、周波数キャッピング(例: ユーザーあたり最大N回のプッシュ/時間)を強制します。
ルーター/ディスパッチャー
- 承認された配信をチャネル固有の作業項目にマッピングします。接続レジストリを使用して「オンラインの場合はアプリ内+WebSocketのみ。それ以外の場合はモバイルプッシュ」を決定し、設定に応じてインスタント送信の代わりにメールダイジェストをスケジュールします。
リアルタイムゲートウェイ(WebSocketレイヤー)
- WebSocket接続を保持するステートフルティア(フォールバック: Server-Sent Events、次にロングポーリング)。
- 接続時: 認証、登録(user_id、device_id) -> 接続レジストリ(Redis Cluster、TTL +ハートビート更新)内のgateway_node_id、その後、未読バックログをプッシュします。
- 配信時: ディスパッチャーがノードをルックアップし、内部gRPCストリームまたはノードごとのKafka/Redis Pub/Subチャネル経由で送信します。ゲートウェイはソケットに書き込み、クライアントackを待ちます。
- 30秒ごとのハートビート。ハートビートの欠落はレジストリエントリをエビクトし、将来の通知は自動的にモバイルプッシュにフォールバックします。
チャネルワーカー
- モバイルプッシュワーカー: APNs(HTTP/2多重化、トークンベース認証)およびFCMへのバッチ処理。プロバイダーごとの同時実行制限、指数バックオフとジッターを処理し、デバイスレジストリから無効または未登録のデバイストークンを削除します。
- Webプッシュワーカー: VAPIDキーとペイロード暗号化を備えたWeb Pushプロトコル。
- メールワーカー: テンプレートをレンダリングし、インスタント、毎時、毎日のダイジェストをサポートします。プロバイダーのWebhook経由でバウンス/苦情を処理し、抑制リストを維持します。
- すべてのワーカーは、notification_idをキーとするべき等コンシューマーであり、最終ステータスを
delivery.statusトピックに書き戻します。
履歴ライターと読み取りパス
- 通知を永続化し、Redisで未読カウンターを維持するコンシューマー(カウンターはストアから定期的に同期されます)。
- 読み取りAPIは履歴ストアから最後の100件の通知を提供し、ユーザーごとの最初のページのRedisキャッシュを備えており、通常の読み取りは10ミリ秒未満です。
サポートサービス
- テンプレートサービス: バージョン管理されたローカライズされたテンプレートと変数補間。コードのデプロイからコピーの変更を分離します。
- デバイスレジストリ: device_token、プラットフォーム、app_version、ロケール、タイムゾーン、最終アクセス時刻。
- 重複排除ストア: Redis、(producer_id, idempotency_key)に24時間TTLを設定し、少なくとも1回の配信を実質的に1回として機能させます。
- スケジューラー: 遅延/スケジュールされた通知とダイジェストウィンドウ用(Redisソートセット内の時間バケット化されたキューまたはKafka遅延トピックラダーのような専用スケジューラー)。
- DLQ + リプレイヤー: 問題のあるメッセージは、修正後に一時停止され、再生可能になります。
- データモデルとデータベースの選択
a) 通知履歴 — Cassandra(またはScyllaDB / DynamoDB)
テーブル: notifications_by_user
- パーティションキー: user_id
- クラスタリングキー: created_at DESC, notification_id
- カラム: type, actor_id(s), target_type, target_id, aggregated_count, preview_text, image_url, deep_link, read_at, channels_sent, created_at
- TTL: 90日。アプリケーションは読み取りを100行に制限します。
正当性: アクセスパターンは、単一のよく知られたパーティションキーと時間順のスライスです。これはCassandraの得意分野です。書き込みのスケーラビリティ(毎秒60〜100K件の書き込みが必要)、可用性のためのマルチリージョンマスターレスレプリケーション、調整可能な一貫性(書き込みQUORUM/LOCAL_QUORUM、読み取りLOCAL_ONE)、および保持のためのネイティブTTLを提供します。結合や複数行トランザクションは不要なため、リレーショナルストアはシャーディングの苦痛と書き込み増幅のリスクを増やすだけです。
b) 未読カウンターとホットな最初のページ — Redis Cluster
unread:{user_id}整数カウンター。notif:page0:{user_id}キャッシュされたJSONリスト。
正当性: カウンターはアプリを開くたびに読み取られ(非常に高いQPS、読み取りあたりの値は低い)、単桁ミリ秒である必要があります。Redisはこれを安価に吸収します。Cassandraカウンターは比較的高価でエラーが発生しやすいです。カウンターは非同期に同期されるため、ドリフトは自己修復します。
c) ユーザー設定とデバイストークン — PostgreSQL(user_idでシャーディング)とRedis読み取りスルーキャッシュ
テーブル: user_preferences(user_id, category, channel, enabled, quiet_hours_start, quiet_hours_end, timezone, updated_at), devices(device_id, user_id, platform, push_token, locale, last_seen, active), suppression_list(email, reason, created_at)。
正当性: このデータは低ボリューム、読み取りヘビー、リレーショナル(ユーザー -> デバイス -> カテゴリごとの設定)であり、正確性と監査可能性(同意レコードにはコンプライアンスの重みがある)のためのトランザクションと制約から利益を得ます。ボリュームは小さく(約1億行)、容易にシャーディングでき、ほとんどすべてをキャッシュできます。
d) 接続レジストリ — Redis Cluster
conn:{user_id}-> {device_id, gateway_node_id, connected_at}のセット、TTL 90秒、ハートビートで更新。
正当性: 一時的で、非常に高いチャーン率、高速である必要があります。耐久性は不要です。エントリが失われた場合、プッシュフォールバックに正常に低下するためです。
e) 重複排除/べき等性 — TTL付きRedis、バックアップなし(損失はまれな重複のリスクのみ)。
f) テンプレートと設定 — PostgreSQL + アセット用のオブジェクトストレージ、エッジでキャッシュ。
g) 分析 — Kafkaからデータレイク(S3/Parquet)およびウェアハウス(Snowflake/BigQuery)にストリーミングされるイベント、配信率、開封率、レイテンシレポート用。ニアリアルタイム運用ダッシュボード用ClickHouse。
- テクノロジースタックの推奨事項
- 言語/ランタイム: Go(ゲートウェイ、リアルタイムゲートウェイ、ワーカー用。ゴルーチンと低メモリ/接続が数百万のソケットに適しています)。Java/Kotlin(Kafka Streams中心の集約には許容範囲)。
- メッセージング: Apache Kafka(マネージド: MSK/Confluent)をバックボーンとして使用。チャネルごとおよび優先度クラスごとに別々のトピック。
- リアルタイムトランスポート: TLS経由のWebSocket、SSEおよびロングポーリングのフォールバック付き。NLB/L4ロードバランシング(接続ドレイン付き)。スティッキーローティングは不要(レジストリがノードIDを格納するため)。
- キャッシュ: Redis Cluster(またはElasticache/MemoryDB)(カウンター、設定、レジストリ、重複排除、レート制限用)。
- データストア: Cassandra/ScyllaDB(履歴用)。PostgreSQL(Aurora)(設定/デバイス用)。S3 + ウェアハウス(分析用)。
- プッシュプロバイダー: APNs、FCM、Web Push(VAPID)。SES経由のメール(SendGridをフェイルオーバー用のプロバイダー抽象化レイヤーの後ろのセカンダリプロバイダーとして使用)。
- インフラストラクチャ: Kubernetes(HPA/KEDAによるKafkaコンシューマーラグ(CPUだけでなく)でのスケーリング付き)、Envoy/Istioサービスメッシュ(mTLSおよびリトライ用)、Terraform(IaC用)。
- オブザーバビリティ: OpenTelemetryトレーシング(プロデューサーイベントからクライアントackまで伝播されるトレースID)、Prometheus + Grafanaメトリクス、Loki/ELKでの構造化ログ、SLOに対するPagerDutyアラート。
- 信頼性ライブラリ: 各外部プロバイダーのサーキットブレーカーとバルクヘッド、トークンバケットレートリミッター、ジッター付き指数バックオフ。
- スケーラビリティ、レイテンシ、および可用性の戦略
スケーラビリティ
- すべてのステートレスコンポーネント(API、ファンアウト、ルーター、ワーカー)は水平にスケールします。Kafkaパーティション数は並列処理の天井となるため、初日から現在のピークの5〜10倍のパーティションをプロビジョニングします(再パーティション分割は運用上困難です)。
- KEDAによるコンシューマーラグのスケーリング。5倍のスパイクが数秒以内にスケールアウトをトリガーするようにします。スケーリングは即時ではないため、30〜40%のヘッドルームのウォームバッファを維持します。
- Cassandra、Postgres、Redis全体でuser_idによる一貫したシャーディング。これにより、単一ユーザーのデータが共locatされ、ホットパーティションの緩和が均一になります。
- 優先度レーン: インタラクティブ通知(DM、自分の投稿へのコメント)は専用コンシューマーグループを持つ高優先度トピックを使用します。バルク/マーケティング/有名人ファンアウトは、スロットルされた低優先度レーンを使用します。これにより、ユーザーが実際に気にする通知の2秒SLOが保証されます。
- ハイブリッドプッシュ/プルファンアウト(前述)および非常にホットなターゲット用のソルトパーティションキーによる有名人/ホットキー処理。
低レイテンシ(p99 2秒未満)
- クリティカルパスは意図的に短くされています: API書き込み -> Kafka -> ファンアウト -> 設定キャッシュヒット -> レジストリールックアップ -> WebSocket書き込み。すべてのルックアップはRedis(5ミリ秒未満)で行われ、同期データベース書き込みは配信をブロックしません。
- 履歴の永続化と分析は非同期であり、配信パスから外れています。
- Kafkaプロデューサーは、レイテンシとバッチ処理のバランスを取るためにlinger.ms=5と圧縮(lz4)でチューニングされています。コンシューマーは処理後に手動コミットを使用します。
- ユーザーを最も近いリージョンにピン留めしたマルチリージョンデプロイメントにより、RTTを削減します。WebSocket接続はリージョンエッジで終了します。
- 集約ウィンドウはタイプごとに設定可能で、DMのようなレイテンシクリティカルなタイプでは無効になります。
- 継続的な合成プローブにより、リージョンごとおよびチャネルごとの実際のエンドツーエンドレイテンシを測定します。
高可用性(99.9%以上)
- 単一障害点はありません: すべてのティアでマルチAZ、Kafka RF=3(min.insync.replicas=2)、Cassandra RF=3(LOCAL_QUORUM書き込み)、Postgres(同期スタンバイと自動フェイルオーバー付き)。
- リアルタイムおよび配信ティアのアクティブ/アクティブマルチリージョン。Cassandraはクロスリージョンを非同期にレプリケートし、Postgresはリージョンごとの読み取りレプリカを使用し、指定された書き込みリージョンがあります。
- グレースフルデグラデーションラダー: WebSocketパスが正常でない場合はモバイルプッシュにフォールバックします。設定キャッシュがダウンしている場合はPostgresにフォールバックし、その後保守的なデフォルトにフォールバックします。Cassandraの書き込みが失敗した場合は、リアルタイム配信を継続し、後でKafkaから履歴書き込みを再生します。
- ジッター付き指数バックオフによるリトライ、キャップされた試行、その後DLQ(アラートと再生ツール付き)。
- 外部プロバイダーごとのサーキットブレーカー。APNsの障害がワーカー スレッドを枯渇させ、メールを停止させるのを防ぎます。
- 少なくとも1回の配信と、サーバーおよびクライアントでのnotification_idによる重複排除。クライアントは、未読バックログを再生する際に再接続時にも重複排除を行います。
- 信頼性のプラクティス: カオス/ゲームデー演習(ゲートウェイノードをキルしてプッシュフォールバックを確認)、ピーク時の5倍の負荷テスト、ブルー/グリーンおよびカナリアデプロイ、チャネルごとのキルスイッチのフィーチャーフラグ、高優先度トラフィックの前に低優先度トラフィックをドロップするバックプレッシャー。
- ボトルネックとトレードオフ
潜在的なボトルネック
- サードパーティプロバイダー(APNs/FCM/メール): スループットを制御できないため、最も厳しい制約です。HTTP/2経由の接続プーリング、バッチ処理、プロバイダーごとのレートリミッター、メールのマルチプロバイダーフェイルオーバー、およびスパイクのキューベースの平滑化で軽減します。
- 有名人ファンアウト: 単一の投稿で数千万件の配信が発生する可能性があります。ハイブリッドプッシュ/プル、スロットルされたバルクレーン、および集約によって軽減されます。
- WebSocket接続ティア: ノードあたりのメモリとファイルディスクリプタ、およびデプロイやネットワークの瞬断後のサンダーシングハードリコネクト。チューニングされたカーネル制限、ジッターと指数バックオフによるクライアントサイド強制リコネクト、デプロイ中の接続ドレインの遅延で軽減します。
- Redisホットキー: バイラル投稿の集約キーまたは共有カウンター。キーの塩漬け、短いTTLを持つローカルインプロセスキャッシュ、およびクライアントサイドシャーディングで軽減します。
- 毎秒10万件の書き込みでのCassandra書き込み増幅とコンパクション圧力。時間ウィンドウコンパクション(TWCS)を時間系列TTLデータに適したものにし、低価値の通知タイプを履歴に格納しないことで軽減します。
- 低カーディナリティキーによるパーティションの偏り。常に配信トピックでrecipient_user_idでパーティション分割します。
明示的なトレードオフ
- 完全に一度(exactly-once)よりも少なくとも一度(at-least-once): 異種外部プロバイダー全体での完全に一度は非現実的でコストがかかります。まれな重複を受け入れ、べき等キーとクライアントサイドの重複排除で解決します。これはより安価で、はるかに利用可能です。
- カウンターと履歴の最終的な一貫性(eventual consistency)対強い一貫性: 一時的に1つずれている未読バッジは許容できます。それを保証するために数秒のレイテンシは許容できません。同期ジョブがドリフトを制限します。
- 単一データベースに対するポリグロット永続化: 運用上のサーフェス(3つのデータストアとRedis)が増えますが、各ワークロードは適切なエンジンを得ます。単一のPostgresクラスタは、1日あたり10億件の書き込みでボトルネックになります。単一のCassandraクラスタでは、設定と同意監査が厄介になります。
- ほとんどのユーザーに対するプッシュベースのファンアウト、有名人に対するプルベース: コードの複雑さが増しますが、無限の書き込みストームを回避する唯一の方法です。
- 集約はユーザーエクスペリエンスを向上させ、配信量を劇的に削減しますが、集約可能なタイプにはウィンドウの遅延が追加されます。ウィンドウは短く保ち、DMは除外します。
- マルチリージョンアクティブ/アクティブはコストを増加させ、クロスリージョンの一貫性の微妙さを導入しますが、可用性とレイテンシのターゲットによって正当化されます。予算が制約されている場合、単一リージョン(ウォームスタンバイ付き)の姿勢でも99.9%を満たしますが、RTOは長くなります。
- 単純なマネージドキュー(SQS)よりもKafka: 運用上の負担は大きいですが、再生、キーごとの順序付けされたパーティション、複数の独立したコンシューマーグループ、およびインシデント回復のための7日間の保持が必要です。
- 過去約100件の通知のみを90日間のTTLで格納することは、コストと読み取りパフォーマンスのためにアーカイブの完全性を犠牲にします。長期データは、必要に応じて分析レイクに保存されます。
- ロールアウト計画(段階的)
フェーズ1: 取り込みAPI + Kafka + 履歴ライター + 読み取りAPI(アプリ内通知のみ)。耐久性のあるバックボーンを確立します。
フェーズ2: リアルタイムゲートウェイ、接続レジストリ、未読カウンター、プッシュフォールバック付きWebSocket配信。
フェーズ3: モバイルプッシュおよびWebプッシュワーカー、デバイスレジストリ、設定サービスおよびサイレントアワー。
フェーズ4: メールインスタント+ダイジェスト、テンプレートサービス、抑制処理。
フェーズ5: 集約/合算、ハイブリッド有名人ファンアウト、周波数キャッピング。
フェーズ6: マルチリージョンアクティブ/アクティブ、カオスエンジニアリング、ピーク時の5倍の負荷検証、SLOダッシュボードとエラーバジェット。
- 主要メトリクスとSLO
- チャネルごとのエンドツーエンドp50/p95/p99レイテンシ(SLO: WebSocketでp99 2秒未満)。
- チャネルごとの配信成功率。プロバイダーのエラー率とトークン無効化率。
- Kafkaコンシューマーラグ(トピックごと)(主要な自動スケーリングおよびページングシグナル)。
- WebSocket接続数、チャーン率、ack率。
- 同期によって検出された未読カウンターのドリフト。
- DLQの深さと経過時間。
- クライアントの観点から測定されたAPIごとの可用性。月間エラーバジェットに対する追跡。
判定
勝利票
3 / 3
平均スコア
総合点
総評
回答Aは、定量化された容量推定値、明確に分離された取り込みパスと配信パス、詳細なデータモデル、優先度レーン、ハイブリッドファンアウト、チャネル固有の回復力、および実行可能な可用性とレイテンシ測定を備えた、傑出した、非常に具体的な設計です。その最も強力な機能は、明示的な運用設定、WebSocket容量計画、障害劣化パス、および異常に徹底的なボトルネックとトレードオフ分析です。主な弱点は、ソースサービスでアウトボックスまたはトランザクション的なイベント発行パターンを明示的に使用していないため、元のビジネストランザクションと永続的なKafka取り込みとの間に潜在的なギャップが生じることです。一部のマルチリージョン整合性の詳細も、高レベルで扱われています。
採点詳細を表示 ▼
設計の質
重み 30%永続的なイベント駆動パイプライン、分離されたファンアウトおよびチャネルトピック、ポリシールーティング、接続レジストリ、リアルタイムゲートウェイ、非同期履歴ライター、および優先度レーンは、明確な責任を持つ論理的なアーキテクチャを形成しています。主なギャップは、明示的なソースサービスのアウトボックスの欠如であり、ビジネストランザクションが通知イベントを取り込みAPIに到達する前にコミットされる可能性があります。1つの論理的な履歴レコードと複数のチャネル配信の関係も、より正確に述べることができます。
完全性
重み 20%要求されたすべての領域に対処し、容量推定値、接続サイジング、詳細なスキーマ、チャネルの動作、コンプライアンス制御、オブザーバビリティ、ロールアウトフェーズ、および測定可能なSLOでそれを超えています。軽微な省略には、ソーストランザクション発行と、ストアが単に読み取りを制限してTTLを適用するのではなく、厳密な最後の100ポリシーをどのように強制するかについてのより正確な説明が含まれます。
トレードオフの説明力
重み 20%回答は、アット least-once配信とエグザクトリー-once配信、結果整合性と強い整合性、ポリグロット永続性、プッシュファンアウトとプルファンアウト、集計レイテンシ、マルチリージョンコスト、Kafkaとより単純なキュー、および保持制限を明示的かつ正確に検討しています。トレードオフは要件に結び付けられており、抽象的にリストされるのではなく、緩和戦略を伴っています。
拡張性・信頼性
重み 20%設計は、平均およびピークトラフィックを定量化し、同時WebSocket容量を推定し、Kafkaラグによってコンシューマーをスケーリングし、ウォームヘッドルームを予約し、優先トラフィックを分離し、セレブリティファンアウトを処理し、マルチAZレプリケーション、クォーラム設定、リトライ、DLQ、サーキットブレーカー、フォールバックパス、ロードテスト、およびカオス演習を指定します。一部のアクティブ/アクティブなリージョンデータ動作とソースイベントの原子性には、さらなる詳細が必要です。
分かりやすさ
重み 10%その長さにもかかわらず、番号付けされた構成、明示的なフロー、命名されたコンポーネント、スキーマ、およびスケーリング、可用性、ボトルネック、ロールアウト、メトリクスに関する個別のセクションにより、計画はナビゲートしやすくなっています。1億行のシャーディングを些細なことと呼ぶなど、いくつかの記述は過度に自信があるか圧縮されており、一部のチャネル/履歴のセマンティクスはより慎重に表現される可能性があります。
総合点
総評
卓越した回答であり、シニアレベルのシステム設計を体現しています。包括的で、構造化されており、定量的な根拠があり、トレードオフと運用上の現実に対する深い理解を示しています。設計は非常に詳細で、具体的な技術選択とスケーラビリティおよび信頼性に関する具体的な戦略が含まれています。段階的なロールアウト計画と専用のメトリクス/SLOセクションの追加により、純粋に理論的な設計を超え、本番稼働可能なドキュメントのように感じられます。
採点詳細を表示 ▼
設計の質
重み 30%アーキテクチャは非常に堅牢で、詳細かつ明確に説明されています。イベント駆動型のフローは明確であり、API、Fan-outサービス、チャネル固有のワーカーなどのコンポーネント間の関心の分離は優れています。セレブリティアカウント向けのハイブリッドプッシュ/プルファンアウト戦略の組み込みは、問題空間に対する洗練された理解を示しています。
完全性
重み 20%この回答は非常に包括的です。プロンプトのすべての部分を詳細に説明しており、詳細な容量推定、段階的なロールアウト計画、および主要なメトリクスとSLO専用セクションを含めることで、期待を上回っています。この詳細レベルは、シニアエンジニアの設計ドキュメントに期待されるものです。
トレードオフの説明力
重み 20%ボトルネックとトレードオフに関する議論は優れています。潜在的な問題を特定するだけでなく、at-least-once配信をexactly-onceよりも優先することや、ポリグロット永続性を使用することなど、設計上の妥協点を明確にリストアップしています。その理由は鋭く、簡潔であり、成熟したエンジニアリングの視点を示しています。
拡張性・信頼性
重み 20%スケーラビリティと信頼性に関する戦略は、詳細かつ具体的です。KEDAを使用したKafkaコンシューマーラグの自動スケーリング、異なるトラフィックタイプに対する優先レーンの使用、および段階的な劣化ラダーの実装など、具体的なアプローチに言及しています。カオスエンジニアリングのようなプラクティスの組み込みは、信頼性に対する積極的なアプローチを示しています。
分かりやすさ
重み 10%回答は非常に明確で、よく構造化されています。番号付きセクション、冒頭のテキストベースのフロー図、簡潔な箇条書きの使用により、大量の技術情報が非常に理解しやすくなっています。要件からメトリクスへの論理的な流れは完璧です。
総合点
総評
回答Aは、ほぼスタッフレベルの設計ドキュメントです。具体的なキャパシティ計算(イベント/秒、ストレージサイジング、WebSocket接続数、ノード推定)から始まり、クリーンな取り込みと配信の分割、具体的な設定詳細(Kafkaパーティション数、レプリケーション設定、ack、保持期間)、セレブリティアカウント向けのハイブリッドプッシュ/プルファンアウト、2秒SLOを保護するための優先レーン、明示的な正当性を持つストアごとのデータモデル、正常な劣化ラダー、カオス/ゲームデーのプラクティス、段階的なロールアウト計画、SLOメトリクスへと進みます。トレードオフのセクションは格別です。各選択肢(at-least-once vs exactly-once、ポリグロット永続化、Kafka vs SQS、集約レイテンシのコスト、マルチリージョンのコスト)は、代替案とその却下理由とともに記載されています。わずかな弱点としては、密度が高く読みにくい場合があること、プロデューサー側のイベント耐久性のためのアウトボックスパターンが議論されていないことです。
採点詳細を表示 ▼
設計の質
重み 30%優れたアーキテクチャ:明確な取り込み/配信分割、具体的なフォロワーしきい値を持つハイブリッドプッシュ/プルファンアウト、インタラクティブイベントの2秒SLOを保証する優先レーン、サイジングされた接続ティア(20〜25Mソケット、ノードあたり約100K)、および具体的なKafkaトポロジー(256パーティション、RF=3、min.insync.replicas=2、7日間の保持期間)。コンポーネントの責任は正確であり、クリティカルパスは同期DB書き込みから意図的に解放されています。
完全性
重み 20%プロンプトのすべての要件に加えて、追加項目もカバーしています。ストレージ計算を含むキャパシティ推定、正当性を持つ6つのストアの完全なデータモデル、4つのチャネルすべて、ダイジェスト、コンプライアンス(GDPR/CAN-SPAM、抑制リスト)、SLOを含むオブザーバビリティ、段階的なロールアウト計画、および専用のボトルネック/トレードオフセクション。プロンプトの欠落はありません。
トレードオフの説明力
重み 20%優れたトレードオフセクション。各決定は代替案とその却下理由を挙げています(at-least-once vs exactly-once、ポリグロット永続化 vs 単一DB、Kafka vs SQS、プッシュ vs プルファンアウト、アクティブ-アクティブコスト vs ウォームスタンバイ、集約レイテンシ vs ボリューム)。トレードオフは、単一のセクションに限定されず、正当化の中に織り込まれています。
拡張性・信頼性
重み 20%具体的かつ定量化されています。パーティションの過剰プロビジョニングの根拠、30〜40%のウォームヘッドルームを持つコンシューマーラグに対するKEDAオートスケーリング、ソルトされたホットキー、書き込みレートのためのTWCSコンパクション、明示的なCassandra/Postgres/Kafkaレプリケーションとクォーラム設定、正常な劣化ラダー、プロバイダーごとのサーキットブレーカー、カオステストと5倍の負荷検証、DLQリプレイツール。1億DAU/ピーク時5倍/99.9%のターゲットに直接メカニズムを結びつけています。
分かりやすさ
重み 10%要件からロールアウト、メトリクスまで、優れた番号付き構造。テキスト形式のフロー図により、パイプラインを簡単に追跡できます。詳細の密度が高いため、セクションが重くなることがありますが、見出しと一貫したフォーマットにより、ナビゲートしやすくなっています。