回答A: OpenAI GPT-5.5
スケーラブルな通知サービスは、イベントの取り込み、プリファレンス評価、通知の永続化、チャネル固有の配信、および配信追跡を分離する、非同期イベント駆動型プラットフォームとして構築する必要があります。最も重要な設計原則は、フォロー、いいね、コメント、ダイレクトメッセージなどのソースプロダクトシステムが、プッシュ通知プロバイダー、メールプロバイダー、またはユーザーデバイスを同期的に呼び出してはならないということです。それらは、メッセージングレイヤーに耐久性のあるイベントを発行し、通知プラットフォームは、強力なリトライおよびべき等性保証をもって、それらのイベントを独立して処理する必要があります。 アーキテクチャは、大まか...
全文を表示 ▼
スケーラブルな通知サービスは、イベントの取り込み、プリファレンス評価、通知の永続化、チャネル固有の配信、および配信追跡を分離する、非同期イベント駆動型プラットフォームとして構築する必要があります。最も重要な設計原則は、フォロー、いいね、コメント、ダイレクトメッセージなどのソースプロダクトシステムが、プッシュ通知プロバイダー、メールプロバイダー、またはユーザーデバイスを同期的に呼び出してはならないということです。それらは、メッセージングレイヤーに耐久性のあるイベントを発行し、通知プラットフォームは、強力なリトライおよびべき等性保証をもって、それらのイベントを独立して処理する必要があります。 アーキテクチャは、大まかに言うと、イベントプロデューサー、取り込みAPI、耐久性のあるイベントログ、通知プロセッサー、ユーザープリファレンスサービス、テンプレートおよびパーソナライゼーションサービス、通知ストア、チャネルファンアウトキュー、チャネル固有の配信ワーカー、サードパーティプロバイダー統合、インアプリ配信用のリアルタイムゲートウェイ、およびオブザーバビリティ/リトライインフラストラクチャといったコンポーネントを含みます。 プロダクトサービスは、ユーザー向けの操作が発生したときに通知イベントを生成します。たとえば、ソーシャルグラフサービスは新しいフォロワーイベントを発行し、投稿サービスはいねまたはコメントイベントを発行し、メッセージングサービスはダイレクトメッセージイベントを発行します。各イベントには、イベントID、イベントタイプ、アクターユーザーID、受信者ユーザーIDまたは受信者セット、オブジェクトID、作成タイムスタンプ、およびレンダリングに必要なメタデータが含まれます。プロデューサーはこれらのイベントを通知取り込みAPIに、または直接耐久性のあるメッセージバスに送信します。取り込みAPIはスキーマを検証し、プロデューサーを認証し、べき等性キーを割り当てるか検証し、プロデューサーに応答する前にイベントを耐久性のあるログに書き込みます。これにより、ダウンストリームプロセッサーが失敗した場合でも通知の損失を防ぎます。 耐久性のあるメッセージングバックボーンには、Apache Kafka、Amazon MSK、Google Pub/Sub、またはPulsarを使用します。Kafka/Pulsarは、高スループット、パーティション順序、保持、リプレイ、コンシューマーグループ、および耐久性のあるストレージを提供するため、適しています。毎秒50,000件の通知リクエストの場合、イベントストリームは、必要に応じてユーザーレベルの順序付けのために受信者ユーザーIDで、または厳密なユーザーごとの順序付けがあまり重要でない場合はイベントIDでパーティション化する必要があります。受信者によるパーティション化は、単一ユーザーのアプリ内通知の順序誤りを回避するのに役立ちますが、有名人アカウントやグループイベントのホットパーティションを作成する可能性があります。数百万人のフォロワーに通知を生成するような大規模なファンアウトの場合、別のファンアウトサービスが受信者をバッチに分割し、多数のパーティションにわたって派生した受信者ごとの通知ジョブを発行する必要があります。 通知プロセッサーは、耐久性のあるイベントログから生のイベントを消費します。それらの責任は、受信者を決定し、ユーザープリファレンスを取得し、レート制限とサイレントアワーを適用し、イベントを重複排除し、チャネル固有の通知レコードを生成し、配信ジョブを発行することです。ユーザーの投稿へのコメントのような直接イベントの場合、受信者セットは小さくなります。有名人が投稿するようなファンアウトイベントの場合、プロセッサーはすべてのファンアウトを同期的に行うことを避けるべきです。ファンアウトジョブを作成し、ソーシャルグラフストアからのバッチ読み取りを使用して、受信者をシャードで処理する必要があります。これにより、1つの非常に大きなイベントが通常の通知の低レイテンシパスをブロックするのを防ぎます。 ユーザープリファレンスサービスは、ユーザーがいいね、コメント、フォロワー、ダイレクトメッセージに対してプッシュ、アプリ内、またはメール通知を希望するかどうかなどの設定を保存します。プリファレンスは、DynamoDB、Cassandra、ScyllaDB、またはシャーディングされたリレーショナルデータベースなどの高可用性データベースに保存する必要があります。アクセスパターンは主にユーザーIDと通知タイプによるキーバリュー検索であるため、分散キーバリューまたはワイドカラムストアが適切です。2秒のレイテンシターゲットを満たすために、プリファレンスはRedis、Memcached、または短いTTLを持つローカルインプロセスキャッシュにもキャッシュする必要があります。プリファレンス更新はソースオブトゥルースデータベースに書き込まれ、無効化イベントを通じてキャッシュに伝播されます。トレードオフは、キャッシュの鮮度により、最近変更されたプリファレンスが適用されるまでに数秒かかる可能性があることです。プリファレンスの厳密な一貫性が必要な場合は、プロセッサーはキャッシュミス時または最近更新されたユーザーに対してデータベースへの読み取りを行うことができます。 テンプレートおよびパーソナライゼーションサービスは、通知コンテンツをレンダリングします。「Alex liked your post」や「Maya commented: ...」のようなテンプレートにイベントタイプをマッピングします。ローカライゼーション、ディープリンク、画像URL、およびチャネル固有のペイロード制約を処理します。テンプレート定義は設定データベースに保存でき、変更が頻繁ではないため積極的にキャッシュできます。レンダリングは配信ジョブが発行される前に行われるべきであり、これにより各ジョブは自己完結型になり、安全にリトライできるようになります。 通知ストアは、ユーザーが見ることができるアプリ内通知および配信状態のソースオブトゥルースです。適切な選択肢は、Cassandra、DynamoDB、ScyllaDB、または受信者ユーザーIDでパーティション化され、通知タイムスタンプでソートされたその他の水平スケーラブルストアです。主なアクセスパターンは「ユーザーXの最新の通知を取得する」であるため、テーブルは受信者_user_idをパーティションキーとして、created_atまたはnotification_idをソートキーとして使用できます。サービスは、アプリ内配信ジョブの発行と同時、またはその前にアプリ内通知レコードを書き込みます。レコードには、通知ID、受信者、タイプ、コンテンツ、ステータス、既読/未読状態、タイムスタンプ、および重複排除キーが含まれます。このストアは、WebSocket配信が失敗した場合でも、ユーザーがアプリを開いたときに通知を確認できることを保証します。 プリファレンスとテンプレートが適用された後、プロセッサーは個別のチャネルキューにジョブを発行します:プッシュキュー、アプリ内キュー、およびメールキュー。各チャネルには異なるレイテンシと信頼性の特性があるため、キューを分離することは重要です。プッシュおよびアプリ内キューはレイテンシに敏感であり、最小限のバックログで高スループットのためにプロビジョニングする必要があります。メールはレイテンシに敏感ではなく、より長い遅延、プロバイダーのスロットリング、およびバッチ処理を許容できます。個別のキューは、遅いメールプロバイダーがプッシュ配信に影響を与えるのを防ぎます。 プッシュ配信ワーカーはプッシュキューから消費し、Apple Push Notification service、Firebase Cloud Messaging、またはその他のモバイルプッシュプロバイダーに通知を送信します。デバイスートークンは、トークン、プラットフォーム、アプリバージョン、ロケール、および最終アクセス時刻スタンプでキー付けされたユーザーIDのデバイスレジストリに保存されます。レジストリは分散キーバリューストアを使用し、アクティブなトークンをキャッシュできます。プッシュワーカーは、プロバイダーの応答を処理し、無効なトークンを削除し、指数バックオフで一時的な障害をリトライし、配信試行を記録する必要があります。プッシュプロバイダーの確認は、ユーザーが通知を見たことを保証するのではなく、プロバイダーがそれを受け入れたことを示すだけなので、システムはプロバイダーの受け入れと実際のユーザーの受信を区別する必要があります。 アプリ内配信には2つのパスがあります。まず、通知は通知ストアに永続化されます。次に、アプリ内配信ワーカーがリアルタイムゲートウェイを介して、現在接続されているユーザーのデバイスに送信します。ゲートウェイは、WebSocket、HTTP/2ストリーム、またはモバイルプッシュのような永続接続インフラストラクチャを使用して実装できます。ゲートウェイノードは、メモリ内でユーザー接続状態を維持し、プレゼンス情報を分散プレゼンスサービスに発行します。ルーティングレイヤーまたはRedis/NATSベースのプレゼンスマップは、どのゲートウェイノードが現在ユーザーの接続を所有しているかをアプリ内ワーカーに伝えます。ユーザーがオフラインであるか、ゲートウェイの送信が失敗した場合でも、永続化された通知は次のセッションでアプリの通知受信トレイAPIを介して取得されるため、通知は失われません。低レイテンシのために、ゲートウェイノードはユーザーの近くに地域的に展開されるべきであり、アプリ内キューは可能な限り同じ地域でワーカーによって処理されるべきです。 メール配信ワーカーはメールキューから消費し、SES、SendGrid、またはMailgunなどのプロバイダーを介して送信します。プロバイダーのフェイルオーバー、バウンス処理、抑制リスト、購読解除コンプライアンス、およびプロバイダーごとのレート制限をサポートする必要があります。メール通知は、いいねのような低優先度のイベントタイプに対してバッチ処理またはダイジェスト処理できますが、ダイレクトメッセージやセキュリティ関連のイベントは即座に送信される場合があります。メールは遅く、より高価であるため、ユーザープリファレンスとレート制限は特に重要です。 信頼性は、耐久性のある書き込み、少なくとも1回の処理、べき等性、リトライ、およびデッドレターキューによって達成されます。取り込みレイヤーは、イベントがKafka/Pulsarに耐久性を持って書き込まれた後にのみプロデューサーに応答します。コンシューマーは、通知レコードの書き込みと下流のチャネルジョブの発行に成功した後のみオフセットをコミットします。リトライは重複を作成する可能性があるため、すべてのイベントと通知には安定したべき等性キーが必要です。たとえば、いいね通知のキーはrecipient_id + actor_id + post_id + event_typeになる可能性があり、コメント通知のキーにはcomment_idが含まれる場合があります。通知ストアはこのキーの一意性を強制するか、プロセッサーが条件付き書き込みを実行します。配信ワーカーも試行IDとべき等な状態遷移を使用する必要があり、重複ジョブが重複するアプリ内レコードや重複メールを作成しないようにします。システムは少なくとも1回の配信を保証しますが、正確に1回の配信は保証しないため、クライアントも通知IDで重複を排除する必要があります。 デッドレターキューは、ポイズンメッセージ、不正な形式のイベント、繰り返しのプロバイダー障害、またはレンダリングできないレコードに必要です。リプレイツールを使用すると、オペレーターは問題を修正し、元の耐久性のあるログまたはデッドレターキューからイベントを再処理できます。Kafkaの保持期間は、運用回復をサポートするのに十分な長さである必要があります。たとえば数日間です。重要なメタデータと配信状態も、監査のために通知データベースに永続化する必要があります。 1日あたり1億人のアクティブユーザーと毎秒50,000件の通知リクエストという規模の要件を満たすために、すべての主要サービスは水平スケーラブルで、可能な限りステートレスである必要があります。取り込みAPIはロードバランサーの後ろでスケールします。Kafka/Pulsarトピックは、ピークスループットとコンシューマーの並列処理をサポートするのに十分な幅でパーティション化されます。プロセッサーと配信ワーカーは、オートスケーリンググループまたはKubernetesデプロイメントで実行され、キューラグ、CPU、プロバイダーレイテンシ、およびリクエストレートに基づいてスケールします。データベースはユーザーIDでパーティション化され、負荷を分散します。ホットキーの問題は、シャーディングされたファンアウトジョブ、有名人ユーザーの特別な処理、およびバックプレッシャーで処理する必要があります。非常に大規模なファンアウトの場合、システムは低優先度の通知でプルベースのファンアウトを使用する場合があります。フォロワーごとに1つの通知をすぐに書き込むのではなく、イベントを1回保存し、ユーザーがアプリを開いたときにそれをマテリアライズします。これにより書き込み増幅が削減されますが、読み取りの複雑さが増し、ダイレクトメッセージやコメントには適さない場合があります。 プッシュおよびアプリ内通知の99%に対する2秒のレイテンシターゲットは、クリティカルパスを短く保つことによって達成されます:プロデューサーから耐久性のあるログへ、キャッシュからのプロセッサープリファレンスルックアップ、通知レコード書き込み、チャネルキューへの発行、およびウォームワーカーによる即時配信。プッシュおよびアプリ内ワーカーはピーク負荷のために過剰にプロビジョニングされるべきであり、キューはダイレクトメッセージやコメントが低優先度のいいねよりも優先されるように優先レーンを使用する必要があります。地域展開はネットワークレイテンシを削減します。複数の地域にいるユーザーの場合、ルーティングは受信者のホーム地域に基づいて行うことができ、災害復旧のためにクロスリージョンレプリケーションを行います。設計は、内部処理時間だけでなく、イベント作成からプロバイダーの受け入れまたはゲートウェイ送信までのエンドツーエンドレイテンシを測定する必要があります。 オブザーバビリティは不可欠です。プラットフォームは、リクエストレート、パーティションごとのキューラグ、処理レイテンシ、プリファレンスキャッシュヒット率、通知作成失敗率、プロバイダーエラー率、リトライ回数、デッドレターボリューム、WebSocket配信成功率、プッシュプロバイダー受け入れレイテンシ、およびエンドツーエンドのp50/p95/p99レイテンシを追跡する必要があります。分散トレーシングは、元のイベントIDをすべてのコンポーネントに引き継ぐ必要があります。アラートは、キューラグの増加、失敗率の上昇、プロバイダーのスロットリング、データベース書き込みレイテンシ、およびレイテンシSLOの未達時に発生する必要があります。 いくつかのトレードオフがあります。KafkaまたはPulsarは運用上の複雑さを増しますが、この規模での少なくとも1回の配信に必要な耐久性、リプレイ、およびスループットを提供します。CassandraまたはDynamoDBは高い書き込みスケーラビリティと可用性を提供しますが、アドホッククエリの機能は限られているため、分析は別のデータウェアハウスに送信する必要があります。プリファレンスのキャッシュはレイテンシを改善しますが、短期間の一貫性の問題が発生します。少なくとも1回の配信は、正確に1回の配信よりもはるかにシンプルで信頼性が高いですが、システムはべき等性キーとクライアントサイドの重複排除を使用して重複を許容する必要があります。書き込み時のファンアウトは受信トレイの読み取りを高速化しますが、高フォロワーイベントではコストがかかる可能性があります。読み取り時のファンアウトは書き込みを節約しますが、ランキングと読み取り時のマテリアライゼーションを複雑にします。ハイブリッドモデルが通常最善です:ダイレクト、重要、および通常のボリュームの通知には書き込み時のファンアウト、大規模な低優先度のイベントには読み取り時のファンアウトまたはバッチファンアウトを使用します。 要約すると、サービスは、パーティション化されたメッセージストリーム、ステートレスプロセッサー、キャッシュされたプリファレンスルックアップ、永続化された通知ストレージ、個別のチャネルキュー、チャネル固有のワーカー、および強力なリトライ/べき等性メカニズムを備えた、耐久性のあるイベント駆動型アーキテクチャを使用する必要があります。この設計は、毎秒50,000件の通知リクエストに水平スケーリングし、キャッシュと低レイテンシキューを通じてプッシュおよびアプリ内配信を2秒のp99ターゲット内に維持し、イベントと通知レコードを永続化してから進捗を承認することで通知が失われないことを保証します。
判定
勝利票
3 / 3
平均スコア
総合点
総評
回答Aは、明確なコンポーネントの責任、現実的なデータフロー、運用上の懸念への強い配慮を備えた、包括的で一貫性のあるハイレベルアーキテクチャを提示しています。すべての必須チャネル、レイテンシ目標、少なくとも1回の配信セマンティクス、プリファレンス処理、大規模なファンアウトシナリオ、べき等性、リトライ、永続化、オブザーバビリティに直接対応しています。技術的な議論はバランスが取れており、ニュアンスに富んでおり、ファンアウトオンライト対ファンアウトオンリード、キャッシュの一貫性、Kafka/Pulsarの運用上の複雑さといった具体的なトレードオフが含まれています。主な弱点は、やや長すぎてもう少し簡潔にできる可能性があることですが、技術的には強力で、プロンプトに強く適合しています。
採点詳細を表示 ▼
設計の質
重み 30%このアーキテクチャは、取り込み、耐久性のあるログ、プロセッサ、プリファレンスサービス、テンプレートサービス、通知ストア、チャネルごとのキュー、配信ワーカー、リアルタイムゲートウェイ、オブザーバビリティがすべて一貫して組み合わされており、エンドツーエンドでよく構造化されています。また、永続化されたアプリ内状態とリアルタイム配信を区別し、ファンアウトを最優先事項として扱っています。
完全性
重み 20%すべての必須通知タイプ、ユーザープリファレンス、スケール、レイテンシ、信頼性、テクノロジーの選択、トレードオフをカバーしています。また、デバイスレジストリ、デッドレターキュー、べき等性キー、ファンアウトバッチ処理、リージョンデプロイメント、オブザーバビリティ、リカバリツールなど、重要な欠落していた実用的な懸念事項も追加しています。
トレードオフの説明力
重み 20%Kafka/Pulsar、NoSQLの選択肢、キャッシュの一貫性、少なくとも1回対正確に1回、ファンアウトオンライト対ファンアウトオンリードについて、強力な比較推論を提供しています。これらのトレードオフは具体的であり、ワークロードと製品の動作に直接結びついています。
拡張性・信頼性
重み 20%これは大きな強みです。この設計は、水平スケーリング、パーティショニング、チャネルごとのキューの分離、ホットキー緩和、リトライ、コンシューマーオフセット処理、重複排除のための条件付き書き込み、デッドレターキュー、リプレイ、確認前の耐久性を明確に説明しています。少なくとも1回の配信と2秒のターゲットを、現実的なメカニズムで直接サポートしています。
分かりやすさ
重み 10%説明は、長文であるにもかかわらず、明確で論理的に順序付けられており、正確です。データフローをうまく伝えていますが、長さのために、より構造化された回答よりも少し密度が高く、すぐにスキャンしにくくなっています。
総合点
総評
回答Aは、非常に詳細かつ堅牢なシステム設計を提供しています。セレブリティアカウントのファンアウト、特定の冪等性キーの構築、少なくとも1回の配信のニュアンスなど、複雑な分散システム特有の課題に対する深い理解を示しています。アーキテクチャは非常に精緻で、論理的根拠が明確であり、洗練されたソリューションとトレードオフの議論をもって全ての要件に明示的に対応しており、シニアソフトウェアエンジニアに期待される専門知識を反映しています。
採点詳細を表示 ▼
設計の質
重み 30%回答Aは、非常に詳細かつ論理的なアーキテクチャを提示しており、関心を明確に分離し、大規模ファンアウトや2パスアプリ内配信のような複雑なシナリオに対して堅牢なソリューションを提供しています。コンポーネント間の相互作用も明確に定義されています。
完全性
重み 20%回答Aは、特定の冪等性キーの例、詳細なオブザーバビリティ、オンライト対オンリードのファンアウト戦略のニュアンスなど、高度なトピックを含む全ての要件を包括的に扱っており、非常に完全な理解を示しています。
トレードオフの説明力
重み 20%回答Aは、設計全体を通してトレードオフの議論を統合し、基本的なシステム設計のトレードオフ(例:少なくとも1回対ちょうど1回、ファンアウト戦略)を明示的に強調しており、技術選択を超えた影響に対する深い理解を示しています。
拡張性・信頼性
重み 20%回答Aは、スケーラビリティと信頼性の両方について優れたカバレッジを提供しており、パーティショニング戦略、コンシューマーオフセットコミット、確認応答前の永続的な書き込み、ホットキー処理、優先度付きキューなどの具体的なメカニズムを詳述し、実装の詳細に対する強い理解を示しています。
分かりやすさ
重み 10%回答Aは非常に明瞭で、構成が良く、専門的な言葉遣いがされているため、その深さにもかかわらず複雑な設計を容易に追うことができます。論理的な流れは優れています。
総合点
総評
回答Aは、セレブリティのファンアウト、書き込み時ファンアウトと読み込み時ファンアウトのハイブリッド、冪等性キーの構築、WebSocketのプレゼンスルーティング、リージョン展開、キューの優先レーン、プロバイダーの受け入れとユーザーの受信の区別といった、微妙で重要な問題に取り組んだ、深く考察されたプロセンドリブンなシステム設計を提供します。トレードオフは表面的なリストアップではなく、文脈の中で議論されています。物語は長いですが首尾一貫しており、シニアレベルの深さを示しています。軽微な弱点:視覚的な図や、スキャンを容易にする構造化された見出し/表が不足しています。
採点詳細を表示 ▼
設計の質
重み 30%包括的なコンポーネントの内訳。ファンアウト、受信者ごとのパーティショニング、プレゼンスルーティング、個別のチャネルキュー、および信頼できるソースとしての永続的な通知ストアの洗練された処理。セレブリティのファンアウトや優先レーンなどの微妙な問題も扱っています。
完全性
重み 20%取り込み、永続ログ、プロセッサ、設定、テンプレート、通知ストア、チャネルキュー、ワーカー、WebSocketゲートウェイ、デバイスレジストリ、DLQ、オブザーバビリティ、リージョン展開を網羅し、4つの要件すべてを明示的に処理しています。
トレードオフの説明力
重み 20%文脈の中で具体的なトレードオフを議論しています:at-least-once対exactly-once、書き込み時ファンアウト対読み込み時ファンアウトのハイブリッド、キャッシュの鮮度低下対一貫性、受信者ごとのパーティショニング対イベントID、Kafkaの運用上の複雑さ対耐久性のメリット。
拡張性・信頼性
重み 20%強力な信頼性のストーリー:ACK前の永続書き込み、下流の成功後のオフセットコミット、具体的な例を含む冪等性キー、再生ツールを備えたDLQ、ホットキー緩和、レイテンシのためのリージョン展開、優先レーン。
分かりやすさ
重み 10%構造化された文章ですが、非常に長く、視覚的な補助が少ないです。論理的な流れにもかかわらず、密な段落はスキャンを困難にしています。