web-dev-qa-db-ja.com

楽観的ロックが機能しない場合はどうすればよいですか?

私はこの次のシナリオを持っています:

  1. ユーザーが [〜#〜] get [〜#〜]/projects/1にリクエストし、 ETag を受け取ります。
  2. ユーザーは、ステップ1のETagで/projects/1[〜#〜] put [〜#〜] リクエストを送信します。
  3. ユーザーは、ステップ#1のETagを使用して/projects/1に別のPUTリクエストを送信します。

ETagが古くなっているため、通常、2番目のPUTリクエストは412応答を受け取ります。最初のPUTリクエストがリソースを変更したため、ETagは一致しなくなりました。

しかし、2つのPUT要求が同時に(またはちょうど1つずつ)送信された場合はどうなりますか?最初のPUT要求には、PUT#2が到着する前にリソースを処理および更新する時間がないため、PUT#2がPUT#1を上書きします。 楽観的ロック の要点は、それが起こらないようにすることです...

11
maximedupre

ETagメカニズムは、楽観的ロックの通信プロトコルのみを指定します。同時更新を検出して楽観的ロックを実施するメカニズムを実装するのは、アプリケーションサービスの責任です。

データベースを使用する一般的なアプリケーションでは、通常、PUTリクエストを処理するときにトランザクションを開いてこれを行います。通常、そのトランザクション内のデータベースの既存の状態を読み取り(読み取りロックを取得するため)、Etagの有効性を確認し、データを上書きします(互換性のない並行トランザクションがある場合に書き込みの競合が発生するような方法で)。次にコミットします。トランザクションを正しく設定すると、両方のコミットが同じデータを同時に更新しようとするため、コミットの1つが失敗するはずです。その後、このトランザクションの失敗を使用して、412を返すか、アプリケーションにとって意味がある場合は、要求を再試行することができます。

21
Lie Ryan

次のペアをアトミックに実行する必要があります。

  • タグの有効性のチェック(つまり、最新)
  • リソースの更新(タグの更新を含む)

他の人はこれをトランザクションと呼んでいます—しかし、基本的に、これら2つの操作のアトミックな実行は、タイミングの偶然によって一方が他方を上書きすることを防ぎます。これがないと、気づいているように、競合状態になります。

全体像を見ると、これは依然として楽観的ロックと見なされます。リソース自体は、更新の目的であるかどうかに関係なく、データを見ているユーザーまたはユーザーによる初期読み取り(GET)によってロックされません。

いくつかのアトミックな動作が必要ですが、これは、複数のネットワーク対話でロックを保持しようとするのではなく、単一の要求(PUT)内で発生します。これは楽観的ロックです。オブジェクトはGETによってロックされていませんが、PUTによって安全に更新できます。

これら2つの操作のアトミック実行を実現する方法も多数あります。リソースをロックすることだけが選択肢ではありません。たとえば、軽量スレッドまたはオブジェクトロックで十分な場合があり、アプリケーションのアーキテクチャと実行コンテキストに依存します。

13
Erik Eidt

実際にE-Tagをチェックしてそのロジックを提供するのは、アプリケーション開発者の責任です。静的コンテンツのE-Tagヘッダーを計算する方法しか知らないため、Webサーバーがあなたのために行うのは魔法ではありません。上記のシナリオを取り上げて、相互作用がどのように発生するかを分析してみましょう。

GET /projects/1

サーバーはリクエストを受信し、このバージョンのレコードのEタグを決定し、実際のコンテンツとともにそれを返します。

200 - OK
E-Tag: "412"
Content-Type: application/json
{modified: false}

これでクライアントにE-Tag値が設定されたので、PUTリクエストにそれを含めることができます。

PUT /projects/1
If-Match: "412"
Content-Type: application/json
{modified: true}

この時点で、アプリケーションは次のことを行う必要があります。

  • E-Tagがまだ正しいことを確認します: "412" == "412"?
  • その場合は、更新して新しいEタグを計算します

成功の応答を送信します。

204 No Content
E-Tag: "543"

別のリクエストが来て、上記のリクエストと同様にPUTを実行しようとすると、サーバーコードが2回目に評価するときに、エラーメッセージを提供する必要があります。

  • E-Tagがまだ正しいことを確認します: "412"!= "543"

失敗したら、失敗応答を送信します。

412 Precondition Failed

これはあなたが実際に書かなければならないコードです。 Eタグは実際には任意のテキスト(HTTP仕様で定義された制限内)にすることができます。数字である必要はありません。ハッシュ値にすることもできます。

1
Berin Loritsch