Webアプリケーションで競合状態を防ぐ方法


31

アリスとボブが両方とも商品リストを編集しているeコマースサイトを考えてみましょう。アリスは説明を改善し、ボブは価格を更新しています。同時にAcme Wonderウィジェットの編集を開始します。ボブは最初に終了し、製品を新しい価格で保存します。アリスは説明を更新するのに少し時間がかかり、完了すると、新しい説明で製品を保存します。残念ながら、彼女は価格を古い価格で上書きしますが、これは意図していませんでした。

私の経験では、これらの問題はWebアプリでは非常に一般的です。一部のソフトウェア(Wikiソフトウェアなど)にはこれに対する保護があります-通常、2番目の保存は「編集中にページが更新されました」で失敗します。しかし、ほとんどのWebサイトにはこの保護がありません。

コントローラーメソッド自体がスレッドセーフであることに注意してください。通常、データベーストランザクションを使用します。これにより、アリスとボブがまったく同じ瞬間に保存しようとしても、破損を引き起こさないという意味で安全になります。競合状態は、アリスまたはボブがブラウザに古いデータを持っていることから生じます。

このような競合状態をどのように防ぐことができますか?特に、私は知りたい:

  • 使用できるテクニックは何ですか?たとえば、最終変更時刻の追跡。それぞれの長所と短所は何ですか。
  • 役立つユーザーエクスペリエンスとは何ですか?
  • この保護はどのフレームワークに組み込まれていますか?

あなたはすでに答えを出しています:オブジェクトの変更の日付を追跡し、それを他の変更が更新しようとするデータの年齢と比較することによって。他の何か、例えばそれを効率的に行う方法を知りたいですか?
キリアンフォス14年

@KilianFoth -私は私が知っているし、特にたいかについていくつかの情報を追加しました
paj28

1
あなたの質問はWebアプリケーションに特別なものではありません。デスクトップアプリケーションにもまったく同じ問題があります。一般的なソリューションの戦略がここで説明されていますstackoverflow.com/questions/129329/...
ドク・ブラウン

2
FYI、あなたがあなたの質問に言及し、ロックの形は「として知られているオプティミスティック同時実行制御
TehShrike

回答:


17

「書き込みを読み取る」必要があります。つまり、変更を書き留める前に、レコードを再度読み取り、最後に読み取ってから変更が加えられたかどうかを確認する必要があります。これは、フィールドごとに(細粒度)またはタイムスタンプに基づいて(粗粒度)実行できます。このチェックを行う間、レコードに排他ロックが必要です。変更が行われなかった場合は、変更を書き留めてロックを解除できます。その間にレコードが変更された場合、トランザクションを中止し、ロックを解除してユーザーに通知します。


これは最も実用的なアプローチのようです。これを実装するフレームワークを知っていますか?このスキームの最大の問題は、単純な「編集の競合」メッセージがユーザーを苛立たせることですが、変更セットを(手動または自動で)マージしようとすることは難しいと思います。
paj28 14年

残念ながら、私はこれをすぐにサポートするフレームワークを知りません。編集コンフィリットエラーメッセージは、頻繁に発生しない限り、イライラするものとして認識されるとは思わない。最終的に、タイムスタンプを確認するか、より複雑なマージ機能を実装するかどうかは、システムのユーザー負荷に依存します。
フィル14年

きめ細かなアプローチ(ローカルデータベースコピーに対して)を使用したPC分散データベース製品を保守しました。1人のユーザーが価格を変更し、もう1人が説明を変更した場合-問題ありません!実生活のように。2人のユーザーが価格を変更した場合-2人目のユーザーが謝罪し、変更を再試行します。問題ない!これは、データがデータベースに書き込まれている間以外はロックを必要としません。変更が画面に表示されている間に1人のユーザーが昼食をとり、後でそれを送信するかどうかは関係ありません。リモートデータベースの変更では、レコードのタイムスタンプに基づいていました。

1
Dataflexには、説明したことを行う「reread()」という関数がありました。それ以降のバージョンでは、マルチユーザー環境で安全でした。そして実際、このようなインターリーブされた更新を機能させる唯一の方法でした。

SQLサーバーでこれを行う方法の例を与えることができますか?\
l --''''''--------- '' '' '' '' '' ''

10

私は主に2つの方法を見てきました:

  1. 使用しているページの最終更新のタイムスタンプを非表示の入力に追加します。コミットすると、タイムスタンプが現在のタイムスタンプと照合され、一致しない場合は他の誰かによって更新され、エラーが返されます。

    • pro:複数のユーザーがページの異なる部分を編集できます。エラーページは、2番目のユーザーが新しいページの変更をマージできる差分ページにつながる可能性があります。

    • con:大規模な同時編集中に労力の大部分が無駄になることがあります。

  2. ユーザーがページの編集を開始すると、妥当な時間ロックし、別のユーザーが編集しようとするとエラーページが表示され、ロックが期限切れになるか、最初のユーザーがコミットするまで待たなければなりません。

    • pro:編集作業は無駄になりません。

    • con:悪意のあるユーザーはページを無期限にロックできます。期限切れのロックが設定されたページは、特に対処しない限りコミットできる可能性があります(手法1を使用)


7

Optimistic Concurrency Controlを使用します。

問題のテーブルにversionNumberまたはversionTimestamp列を追加します(整数が最も安全です)。

ユーザー1がレコードを読み取ります:

{id:1, status:unimportant, version:5}

ユーザー2がレコードを読み取ります。

{id:1, status:unimportant, version:5}

ユーザー1がレコードを保存し、これによりバージョンが増加します。

save {id:1, status:important, version:5}
new value {id:1, status:important, version:6}

ユーザー2は、読み取ったレコードを保存しようとします。

save {id:1, status:unimportant, version:5}
ERROR

Hibernate / JPAはこれを@Versionアノテーションで自動的に行うことができます

読み取りレコードの状態をどこか、通常はセッション中に維持する必要があります(これは非表示のフォーム変数よりも安全です)。


ありがとう...特に@Versionについて知っておくと役に立ちます。1つの質問:なぜセッションに状態を保存するのが安全なのですか?その場合、戻るボタンを使用すると物事が混乱するのではないかと心配になります。
paj28 14年

ユーザーはバージョン値を変更できないため、セッションは非表示のフォーム要素よりも安全です。それが心配でない場合は、セッションに関する部分を無視してください
ニールマクギガン14年

この技術は呼ばれている楽観的オフラインロック、それがSQLAlchemyの中にあるだけでなく
paj28

@ paj28-のリンクはSQLAlchemy楽観的なオフラインロックについて何も指し示しておらず、ドキュメントにはありません。もっと役立つリンクがありましたか、それとも一般の人々にSQLAlchemyを紹介しているだけですか
ドワンダーソン

@dwandersonそのリンクのバージョンカウンター部分を意味しました。
paj28

1

一部のオブジェクトリレーショナルマッピング(ORM)システムは、データベースからロードされてから変更されたオブジェクトのフィールドを検出し、それらの値のみを設定するSQL更新ステートメントを作成します。Ruby on RailsのActiveRecordはそのようなORMの1つです。

最終的な影響は、ユーザーが変更しなかったフィールドがデータベースに送信されるUPDATEコマンドに含まれないことです。異なるフィールドを同時に更新する人は、お互いの変更を上書きしません。

使用しているプログラミング言語に応じて、利用可能なORMを調査し、アプリケーションで「ダーティ」とマークされたデータベースの列のみが更新されるかどうかを確認します。


こんにちは、グレッグ。残念ながら、これはこの種の競合状態には役立ちません。私の元の例を考慮すると、Aliceが保存すると、ORMは価格列をダーティと見なして更新します-更新が望ましくない場合でも。
paj28 14年

1
@ paj28 Gregの答えの要点は、「ユーザーが変更しなかったフィールド」です。アリスは価格を変更しなかったため、ORMは「価格」値をデータベースに保存しようとしませんでした。
ロスパターソン14年

@RossPatterson-ORMは、ユーザーが変更したフィールドとブラウザからの古いデータの違いをどのように知るのですか?少なくとも追加の追跡を行わない限り、そうはなりません。Gregの回答を編集してこのような追跡を含める場合、または別の回答を送信する場合は役立ちます。
paj28 14年

@ paj28システムの一部は、ユーザーが何をしたかを知り、ユーザーが行った変更のみを保存する必要があります。ユーザーが価格を変更し、再度変更して送信した場合、「ユーザーが変更したもの」とはみなされません。このレベルの同時実行制御を必要とするシステムがある場合は、この方法で構築する必要があります。そうでない場合、そうではありません。

@nocomprende-一部、確かに-しかし、この答えが示すようにORMではない
-paj28
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.