プロンプトとスコープ
テナント数20,000、1時間あたり100万件の操作、短時間の10倍のバースト、p95の承認判定が100ミリ秒未満であることを想定します。顧客は操作を実行する前にクレジットを購入します。各操作のコストは変動し、リクエストは並行して届く可能性があり、利用イベントの配信はat-least-onceです。設計では、監査証跡を維持しながらマイナス残高と二重請求を防止する必要があります。これは購入済み価値の台帳であり、レートリミッターや継続的なサブスクリプション請求システムではありません。
面接官がテストしていること
- 不変のクレジット移動と高速な残高リードモデルの分離。
- authorize、capture、release、refund、grantの各操作のべき等化。
- 並行リクエスト、遅延イベント、リトライ、有効期限切れ、照合の処理。
- 一貫性境界、ホットテナント、可観測性、リカバリの説明。
確認すべき明確化のための質問
クレジットに有効期限があるか、失敗した操作が予約後に解放できるか、返金がどのように承認されるか、実行前にコストが判明しているか、1つのテナントが複数リージョンで消費可能かを尋ねます。必要な判定レイテンシと、一時的な過剰承認が許容されるかを確認します。
30秒での回答
付与、予約、キャプチャ、解放、返金、失効を記録する、追記のみ(append-only)の不変な台帳を使用します。トランザクショナルな残高プロジェクションが高速な読み取りをサポートし、べき等キーによってすべての変更操作が再現可能になります。承認処理は利用可能なクレジットをアトミックにチェックし、有効期限付きの予約を作成します。処理が成功すればキャプチャされ、失敗した場合は解放され、放棄された予約は回収機能(reclaimer)が処理します。アウトボックス、再生可能なイベント、照合処理によってプロジェクションと台帳を比較し、監査可能な補償エントリで不整合を修復します。
ステップごとの詳細解説
1. クレジット状態のモデル化
1つのテナント/アカウントキー、整数のクレジット単位、台帳シーケンス、ならびに付与、予約、キャプチャ、解放、返金、失効の合計を含むプロジェクションを保持します。各変更は、operationid、idempotencykey、理由、アクター、タイムスタンプとともに保存します。過去のエントリを決して編集せず、修正時は補償エントリを追記します。
2. 二重使用のない承認
テナントのホットキーに対して単一のトランザクションまたは線形化可能な条件付き更新を使用します。利用可能なクレジットが要求コスト以上の場合にのみ予約を作成します。重複したべき等キーは元の決定を返し、異なるパラメータで再利用されたキーは拒否されます。キャプチャと解放が両方とも成功してしまわないよう、予約の有効期限とステータス遷移を条件付きにしておきます。
3. 実行とイベントの接続
ビジネスリクエストには予約識別子が含まれます。キャプチャは確認された成功に従い、解放は失敗またはキャンセルされた操作に従います。利用イベントは重複したり遅延したりする可能性があるため、コンシューマはカウンターを盲目的に減算するのではなく、event_idで重複排除し、不明な予約を照合します。アウトボックスはトランザクション後にコミットされた台帳の変更を発行します。
4. スケール、代替案、リカバリ
テナントごとにパーティショニングし、1つのテナントをホームライターにルーティングし、アドミッションコントロールやシリアライズされたコマンドストリームによって極端にホットなテナントを分離します。各テナントの書き込みレートが中程度の場合はリレーショナルなトランザクションがよりシンプルな適合策です。クエリの単純さよりも再生や順序付けが重要な非常にホットなテナントには、イベントソーシングによるコマンドストリームが望まれます。読み取りは、鮮度の遅れ(staleness)が明示されている場合にのみレプリカを使用できます。スイーパーが放棄された予約を失効させ、リプレイによって台帳からプロジェクションを再構築します。アラートは、利用可能額のマイナス、有効期限切れ後のキャプチャ、リプレイラグ、照合の差分をカバーします。
優れた回答例
有効期限、返金の承認権限、マルチリージョンでの消費、実行前にコストが判明しているかどうかを明確にします。信頼できる唯一の情報源(source of truth)はテナントごとの不変な台帳であり、残高テーブルは再構築可能なプロジェクションです。authorizeは、べき等キーと有効期限のもとでコストをアトミックに予約し、captureは成功後にその予約を確定(キャプチャ)し、releaseまたは有効期限切れによってそれを返却します。重複または遅延したイベントは重複排除されて照合されます。テナントアフィニティを持つ書き込みによってリージョン間の二重使用を回避します。リージョンが独立して動作する必要がある場合は、上限が定められた割り当てを受け取り、その負債は明示的に照合されます。すべての修正は追記専用の補償エントリとなります。
よくある間違い
- 変更可能なカウンターのみ → リトライや返金が監査不能になる → 操作の識別情報を持つ不変エントリを追記する。
- リクエスト受信時の課金 → 失敗した処理でもクレジットが消費される → 最初に予約し、成功後にのみキャプチャする。
- リトライを新規消費として処理 → 1つの操作に対して二重に請求される → 同一のべき等キーを再利用する。
- 予約が回収されない → クラッシュしたワーカーがクレジットを永久にロックする → 有効期限と条件付きスイーパーを追加する。
- リージョン間の結果整合的な読み取り → 2つのライターが過剰消費する可能性がある → 単一のライターまたは明示的なリージョン予算を使用する。
- 履歴を修正するために古い行を編集する → 監査証跡の信頼性が失われる → 補償エントリを追記する。
フォローアップの質問と回答
承認後にクライアントがタイムアウトしました。何が起こりますか?
予約をクエリするか、同じべき等キーで次の遷移を再試行します。元の予約がキャプチャ、解放、または失効するまで、再度承認を行わないでください。
月締め後の返金はどのように処理しますか?
元のキャプチャと承認記録を参照する返金エントリを追記します。プロジェクションは利用可能クレジットを増加させ、台帳は両方のイベントとその順序を保持します。
2つのリージョンが同じアカウントを並行して消費できますか?
厳密な二重使用防止セマンティクスを実現するためには、単一ライターまたはテナントのホームリージョンを推奨します。上限付きのローカル消費が必要な場合は、明示的なリージョン予算を割り当てて負債を照合します。一時的な許容最大超過額を明記してください。
残高が正しいことをどのように証明しますか?
定期的に台帳をリプレイし、算出された利用可能額をプロジェクションと比較し、キャプチャ結果をビジネス操作の結果と比較します。すべての修復に対してべき等な補償エントリと監査レコードを発行します。