Version Number パターン

楽観的ロックにパターン名がついていることを書籍で知りました。内容についてまとめておこうと思います。

アプリケーショントランザクションの管理

ここで説明する排他制御は、例えば以下のような一連のシーンにおける排他制御を指します。1,2 はそれぞれ異なるDBトランザクションにあることを想定しています。

  1. DBからデータを取得(SELECT文発行)
  2. データを更新(UPDATE文発行)

更新画面表示 → 更新データを入力 → 更新処理
みたいな流れがまさにそれです。 このような業務的にはひとかたまりの処理をDBトランザクションと区別して「アプリケーショントランザクション」と呼びます。

管理しないとどうなるか

次の場合を考えます。

  1. Aさんが商品Xのデータを表示(price: 1000)
  2. Bさんが商品Xのデータを表示(price: 1000)
  3. Aさんが商品Xのデータを更新(price: 1200)
  4. Bさんが商品Xのデータを更新(price: 1500)

「アプリケーショントランザクション」を管理しない場合、最終的なテーブルの状態は、4.でBさんが更新したデータ(price: 1500)になっています。つまり、Aさんによる更新処理はBさんによる更新処理で上書きされた状態です。 このような事象を「ロストアップデート」と呼び、業務上不整合を引き起こす可能性があります。

Version Number パターン

このような問題の回避策のひとつが「Version Number パターン」です。 Version Number パターンでは、テーブルに Version を管理するためのカラムを用意します。テーブルイメージは以下の通りです。

【product】

product_id product_name price version
1001 商品X 1000 0
1002 商品Y 2000 0

更新時には、抽出条件としてデータ取得時のversionの値を指定します。それと同時に、versionの値を1増加させます。priceを更新する場合を例に挙げると、最終的に発行される更新時のSQLは以下のようになります。

update 
    product 
set
    product_name = n
    , price = p
    , version = version + 1 
where
    product_id = '1001' 
    and version = v
; 

※ n: 取得時のproduct_name、p: 更新時のprice、v: 取得時のversion

以上が Version Number パターンによるアプリケーショントランザクションの管理方法になります。

活用した結果

先ほどと同じ例で、 Version Number パターンを活用した場合を考えてみます。

  1. Aさんが商品Xのデータを表示 → 成功(price: 1000, version: 0)
  2. Bさんが商品Xのデータを表示 → 成功(price: 1000, version: 0)
  3. Aさんが商品Xのデータを更新 → 成功(price: 1200, version: 1)
  4. Bさんが商品Xのデータを更新 → 失敗(レコードなし)

4.の更新処理を実施しても、既にversionは 1 に更新済みのため、レコードが更新されることはありません。あとは適切なエラーメッセージを返してあげて、再度更新処理をしてもらえば問題を回避できます。

参考

Javaデータアクセス実践講座