代表的な面接トピック

バックエンド面接:S3の条件付き書き込みはどのようにして同時上書きを防ぐのか?

バックエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

複数のクライアントが同じS3オブジェクトに書き込む際、リード・アフター・ライトの上書きをどのように防ぎますか?If-None-MatchとIf-Matchを比較し、リトライとマルチパートアップロードについて説明してください。

プロンプトとコンテキスト

この質問は、バックエンドAPI、オブジェクトストレージ、および並行性制御をテストするものです。AWSの条件付きリクエストは、PutObjectCompleteMultipartUpload、およびCopyObjectに前提条件を追加します。作成時にはキーが存在しないことを要求でき、更新時にはETagが変更されていないことを要求できます。核心となる課題は、クライアントが競合の生じるHEADの後にPUTを実行するのではなく、ストレージ側で書き込みと同時にチェックを評価させることです。

面接官がテストしていること

優れた回答では、作成と更新を区別し、現在のetag-valueとともにIf-None-Match: *またはIf-Matchを選択し、条件不一致をリトライまたはマージの動作にマッピングします。また、マルチパートの完了、弱いETag、不確実なネットワーク結果、および監査シグナルについてもカバーします。「ロックを追加する」だけでは、複数プロセスや障害復旧について説明したことになりません。

最初に確認すべき明確化のための質問

書き込みセマンティクス

これはライトワンスのイミュータブルなオブジェクトですか、それとも読み取ったバージョンに基づく更新ですか?前者は存在の前提条件を使用し、後者はバージョンETagを必要とします。

コンフリクトポリシー

コンフリクト発生時に412を返してクライアントが再読み取りとマージを行うべきですか、それとも試行を中止すべきですか?マニフェストやレジストリは通常、暗黙的に上書きされてはなりません。

アップロードパス

小さなオブジェクトにはPutObjectを使用し、大きなオブジェクトにはマルチパートを使用しますか?最終的な完了リクエストに条件を設定し、中断、リトライ、およびクリーンアップの動作を定義します。

30秒の回答フレームワーク

「私はまず、これが新規作成かバージョン管理された更新かを判断します。作成にはIf-None-Match: *を使用し、S3が既存のキーをアトミックに拒否するようにします。更新では現在のETagを読み取ってIf-Matchを送信します。ETagが変更されていた場合、S3は412を返し、クライアントは再読み取りまたはマージを行います。マルチパートアップロードでは、CompleteMultipartUploadで条件を適用します。タイムアウトは失敗の証明ではないため、リトライ前にオブジェクトを照会するか冪等性レコードを使用し、412、孤立したパート、およびリトライ回数を監視します。」

ステップバイステップの詳細な回答

ステップ 1: Check-then-actを排除する

HEADの後にPUTを実行すると競合が発生します。2つのクライアントが両方とも存在しないことを確認してから書き込む可能性があります。ストレージサービスが現在のオブジェクト状態に対して評価できるように、書き込みリクエスト内に前提条件を含めます。

ステップ 2: 条件を選択する

イミュータブルな作成にはIf-None-Match: *を使用します。これは、現在の表現が存在しない場合にのみ成功します。既存のオブジェクトには強いIf-Matchを使用し、ETagが読み取ったバージョンと一致する場合にのみ書き込みを許可します。不一致の場合は412が返され、新しいコンテンツが保護されます。

ステップ 3: コンフリクトプロトコルを定義する

412に対して盲目的にリトライしてはなりません。現在のオブジェクトをGETし、クライアントの変更がマージ可能かどうかを判断します。マージできない場合はコンフリクトレコードまたは新しいキーを作成します。ホットキーがリトライストームを引き起こさないよう、バックオフとリトライ上限を追加します。

ステップ 4: マルチパートとコピーをカバーする

パートのアップロード中に別のクライアントが同じキーを作成する可能性があるため、最終的なCompleteMultipartUploadにも条件が必要です。コピーまたは置換のワークフローにはETag条件を付与し、ライフサイクルルールによって放棄されたマルチパートアップロードをクリーンアップします。

ステップ 5: タイムアウトを処理して観測する

タイムアウトが発生しても、書き込みがコミットされたかどうかは分かりません。リトライする前にオブジェクトとETagを照会し、作成の意図に対してクライアントコマンドIDを記録します。412の発生率、成功したリトライ率、孤立したパート、およびバージョンドリフトを監視して、コンフリクトが上書きされるのではなく拒否されていることを確認します。

高品質な回答例

私はイミュータブルなデータファイルと更新可能なレジストリを分離します。データファイルの作成にはIf-None-Match: *を使用するため、同一キーのライターは1つだけ成功します。レジストリはETagとともに読み取られ、If-Matchで更新されます。412が発生した場合は、別のバージョンを上書きする代わりに再読み取りとマージがトリガーされます。大きなアップロードではCompleteMultipartUploadで条件を適用し、クリーンアップジョブが失敗したアップロードを回収します。ネットワークタイムアウトの後、リトライする前にオブジェクトとETagを照会します。S3はアトミックなコンフリクトチェックを提供し、クライアントはマージポリシーと冪等性レコードを管理します。これを412、リトライ、および孤立パートのメトリクスで検証します。

よくある間違い

  • 間違い: 存在確認のためにHEADを実行し、その後にPUTを実行する。 → 失敗の理由: 2つのクライアントが同時にチェックを通過する可能性があります。 → 修正方法: PUTでIf-None-Match: *を使用します。
  • 間違い: 更新にIf-None-Matchを使用する。 → 失敗の理由: これは「存在してはならない」という意味であり、「読み取ったバージョンと等しくなければならない」という意味ではありません。 → 修正方法: 強いETagを読み取り、If-Matchを使用します。
  • 間違い: 412の後に元のリクエストを無条件でリトライする。 → 失敗の理由: 繰り返し上書きしたり、リトライストームを引き起こしたりする可能性があります。 → 修正方法: 有界なバックオフを用いて再読み取り、マージ、または中止します。
  • 間違い: 最初のマルチパートリクエストにのみ条件を適用する。 → 失敗の理由: 完了前にオブジェクトの状態が変わる可能性があります。 → 修正方法: CompleteMultipartUploadで再度適用します。

フォローアップと回答

フォローアップ 1: ETagをコンテンツハッシュとして扱えますか?

一概には扱えません。要件はバージョンバリデータです。マルチパートや暗号化のシナリオではETagのセマンティクスが変わる可能性があるため、その操作に対してサービスが文書化しているチェックサムまたはバリデータを使用してください。

フォローアップ 2: 412と409の違いは何ですか?

APIコントラクトに従ってください。412はリクエストの前提条件が失敗したことを意味し、通常はクライアントに再読み取りを求めます。別のコンフリクトステータスはリソースまたは操作の状態を記述する場合があります。数値だけでセマンティクスを推測しないでください。

フォローアップ 3: クライアントがマージできない場合はどうしますか?

現在のバージョンを保持し、ドメインオーナーが解決できるようにコンフリクトレコードまたは新しいキーを作成します。暗黙的な上書きは成功基準ではありません。

フォローアップ 4: 並行性の正確性をどのようにテストしますか?

単一のキーに対して同時に作成と更新を実行し、タイムアウト、重複したマルチパート完了、クリーンアップの失敗を注入します。その上で、最大1つの作成のみが成功すること、古いコンテンツが新しいコンテンツを上書きしないこと、および監査レコードが結果と一致することを検証します。

公開情報ソース

関連する質問