プロンプトとコンテキスト
このシステム設計の課題は、プラットフォーム、バックエンド、およびSaaSインフラストラクチャの職種に適しています。請求システムはプランや支払いのイベントを発行し、製品サービスはcan tenant T use feature F for subject U?のような認可判定を必要とします。テナント数50,000、対象(subject)数1,000万、ピーク時の判定リクエスト数毎秒100,000件、月間可用性目標99.99%を想定してください。解約または停止されたエンタイトルメントが無期限に使用可能な状態のままになってはならず、同時に請求システムの停止によってすべての読み取りパスがダウンしてはなりません。
面接官が評価しているポイント
- 請求という商業的な真実のソースと、評価済みのエンタイトルメントスナップショットを分離できているか。
- テナントおよび対象の分離、プラン、アドオン、トライアル、拒否(deny)ルールの優先順位を定義できているか。
- イベントの順序付け、キャッシュの陳腐化、取り消しレイテンシー、フェイルオープン対フェイルクローズの判定について論理的に検討できているか。
- バージョン管理されたAPI、監査証跡、オブザーバビリティ、およびリプレイ可能なリカバリパスを提供できているか。
質問すべき明確化の問い
判定がテナント、ユーザー、サービスアカウント、シートのいずれの単位か、機能のサブセットに対する有効化が可能か、解約によるアクセス取り消しをどの程度迅速に行う必要があるか、使用量クォータが判定に含まれるか、すべての判定に説明可能な理由が必要かを確認します。請求イベントが少なくとも1回(at-least-once)配信され、順不同で届く可能性があるかどうかも確認します。これらの回答によって、スナップショットスキーマ、イベント処理、キャッシュTTL、フェイルオーバーポリシーが変わります。
30秒の回答フレームワーク
私なら請求を信頼できる情報源(authoritative source)として維持し、テナント、対象スコープ、機能、バージョンをキーとするエンタイトルメントプロジェクションを構築します。書き込みパスは、順序付けまたは重複排除されたサブスクリプションイベントを消費し、新しいスナップショットを計算して、無効化をパブリッシュします。読み取りAPIは明示的な優先順位に従ってスナップショットを評価し、allow、deny、理由、およびバージョンを返します。リージョンキャッシュが高頻度の読み取りを処理しますが、取り消しトークンまたはバージョンフェンスによって陳腐化したアクセスを制限します。フェイルオープンは低リスクな機能にのみ許可し、有料機能やセキュリティ上機密性の高い機能はフェイルクローズとしてリカバリパスを公開します。すべての変更と判定は監査可能です。
ステップバイステップの詳細設計
- コントラクトの定義。
Evaluate(tenant_id, subject_id, feature, context)は、判定、理由コード、スナップショットのバージョン、および有効期限を返します。コンテキストにはプラン、リージョン、シート、またはロールアウト属性を含めることができます。OpenFeatureは一意のターゲティングキーを必要とし、カスタムフィールドをサポートしているため、単一のフリーフォーム文字列に請求状態を過密に詰め込まないようにします。 - イミュータブルな付与のモデリング。 サブスクリプション製品、アドオン、トライアル、シート、発効・失効時刻、および明示的な拒否をバージョン付きファクトとして保存します。プロジェクションには、解決済みの機能セットと元のファクトIDを保存します。利用停止やコンプライアンスによる拒否は通常の付与をオーバーライドし、期間限定のトライアルは履歴を変更することなく期限切れになります。
- 伝播パスの構築。 請求システムはテナント、サブスクリプションバージョン、イベントID、および発効時刻を含むイベントを発行します。受信トレイ(inbox)はイベントIDの重複を排除し、古いバージョンを拒否して、ファクトとプロジェクションをトランザクションとして書き込みます。送信トレイ(outbox)は
entitlement_version_changedをパブリッシュし、コンシューマーはテナントおよび機能単位で無効化します。ファクトをリプレイすることで、データ破損後にプロジェクションを再構築できます。 - 読み取りの提供。 ステートレスな評価APIがローカルキャッシュまたはリージョンストアを読み取ります。キャッシュキーにはテナント、対象スコープ、機能、およびポリシーバージョンが含まれます。キャッシュエントリはプロジェクションのバージョンと有効期限を保持します。リクエストがキャッシュよりも新しいバージョンフェンスを提示している場合、判定を行う前に信頼できるリージョンストアを読み取ります。
- リスクに応じた整合性の選択。 通常の解約には60秒、不正行為やセキュリティ上の利用停止にはほぼ即時のフェンシングなど、測定可能な取り消しSLOを設定します。高到達性のストアに拒否フェンスをパブリッシュし、サービスはそのフェンスより古いキャッシュ済みの許可(allow)を拒否します。同期チェックのコストを支払うことなく陳腐化した読み取りがゼロになると約束してはなりません。
- 障害とスケーリングの処理。 テナントごとにイベントをパーティショニングしてテナント内の順序を維持し、テナントのハッシュでプロジェクションをシャーディングして、高負荷テナントを隔離します。請求の遅延が発生した場合は、最後に適用されたバージョンを公開してアラートを発報します。キャッシュまたはリージョンストアの障害時は、低リスク機能にのみ制限された陳腐化ウィンドウを使用し、高リスク機能に対しては暗黙的にアクセスを許可するのではなく型付き依存関係エラーを返します。
- 監査と検証。 誰がプランを変更したか、どのイベントバージョンがプロジェクションを生成したか、なぜその判定が下されたかを記録します。イベント遅延、プロジェクションの経過時間、キャッシュヒット率、陳腐化した許可のブロック数、判定レイテンシー、テナント間認可障害を測定します。順不同イベント、重複配信、クロックスキュー、リクエスト処理中の解約、テナント移行、空のプロジェクションからのリプレイをテストします。
質の高い模範解答
私なら、商業的な真実とバージョン管理されたエンタイトルメントプロジェクションを分離します。請求イベントはイベントID、テナント、サブスクリプションバージョン、発効時刻、および変更された製品を保持します。受信トレイは重複を排除して古いバージョンを拒否し、ファクト、解決済み機能スナップショット、および送信トレイ通知をトランザクションとして書き込みます。評価APIはallowまたはdeny、理由、プロジェクションバージョン、有効期限を返します。キャッシュキーにはテナントと対象スコープが含まれるため、ある顧客が別の顧客の判定を読み取ることはできません。
主要なトレードオフはアクセスの取り消しです。私なら通常の解約SLOを60秒に設定し、不正やセキュリティ停止に対しては拒否フェンスをパブリッシュします。キャッシュされた各許可にはプロジェクションバージョンが付与されており、より新しいフェンスが存在する場合はリージョンストアの読み取りが強制されます。低リスクなUI機能はストア停止中に制限付きの陳腐化ウィンドウを使用できますが、有料のデータエクスポートやセキュリティ制御は型付き依存関係エラーでフェイルクローズします。監査レコードは各判定をイベントおよびポリシーバージョンに関連付け、リプレイジョブがイミュータブルなファクトからプロジェクションを再構築します。
よくある間違い
- リクエストごとに請求テーブルを同期的に読み取る → 支払いのレイテンシーや停止が認可の停止につながるため、イミュータブルなファクトを読み取り最適化されたスナップショットに投影すべきです。
- 機能のみをキーとしてキャッシュする → あるテナントや対象が別のスコープの判定を受け取る可能性があるため、キーにテナント、対象スコープ、ポリシーバージョンを含めるべきです。
- イベントを到着順に適用する → 古い解約や更新が新しい状態を上書きする可能性があるため、IDを重複排除し、適用済みバージョンより古いバージョンを拒否すべきです。
- あらゆる場所で即時取り消しを約束する → ネットワークとキャッシュのコストを無視した設計になるため、測定可能な取り消しSLOを設定し、拒否フェンスを適用すべきです。
- 有料機能やセキュリティ機能でフェイルオープンにする → 陳腐化したアクセスが収益や安全性のインシデントにつながるため、リスクごとに機能を分類し、必要に応じてフェイルクローズにすべきです。
フォローアップの質問と回答
テナント内の一部のユーザー(10%のみ)に機能を有効にするにはどうすればよいですか?
商業的なエンタイトルメントとロールアウトのターゲティングを分離しておきます。エンタイトルメントスナップショットはテナントがその機能を所有していることを示し、安定した対象ターゲティングキーを持つ評価コンテキストがロールアウト規則を適用します。サポートエンジニアが「未購入」と「ロールアウトで未選択」を区別できるように、両方の決定を記録します。
解約イベントが遅延した場合はどうなりますか?
プロジェクションの経過時間とイベント遅延を公開し、取り消しSLOに違反する前にアラートを発報し、利用可能であれば請求バージョンまたは拒否フェンスを使用します。ハートビートの途絶から解約を推測してはなりません。イベントが到着したら、それをべき等に適用し、影響を受けるすべてのスコープを無効化します。
シャード間でテナントを移行するにはどうすればよいですか?
テナントのメタデータに移行エポックを書き込み、限定された切り替え期間中はデュアルリードを実施し、古いシャードが許可を提供しないようにフェンスをパブリッシュします。古いコピーを削除する前に、カウント、バージョン、サンプリングされた判定を検証します。ロールバックのためにリプレイ可能なファクトを保持します。