排他制御のテストについて

前置き

下記のようなシチュエーションのテストを行いたいとなりました。

ユーザAが任意のデータの編集画面を開く
⇒ユーザBが同一データの編集画面を開き更新する
⇒ユーザAがデータを更新する

上記を自分は「排他制御のテスト」と呼んでいたのですが、意味が通じるのか気になり調べました。
調べた結果を関連する内容も含めて纏めます。

そもそも「排他制御(Exclusive Control)」とは?

排他制御とは、複数のユーザーやプログラムが同じデータに対して同時に読み書き・更新を行おうとした際に、データがおかしくならないように順序立てて管理(コントロール)する仕組みのことです。

システム上のデータベースやファイルは「同時に何人でもアクセスできてしまう」という性質を持っています。そのため、システム側で明示的にアクセスをコントロール(制御)してあげないと、データに矛盾が生じてしまいます。

なぜ排他制御が必要なのか?(具体例)

もし排他制御がなかった場合にどんなトラブルが起きるのか、具体的なシチュエーションを2つ紹介します。

例1:ECサイトの在庫管理(データ不整合)

1つしか残っていない限定商品を、ユーザーAとユーザーBが「まったく同じタイミング」で購入ボタンを押した場合:

【排他制御なし】
ユーザーA:残り1個の在庫を確認 ➔ 購入処理開始
ユーザーB:残り1個の在庫を確認 ➔ 購入処理開始
ユーザーA:在庫を「0個」にして注文完了
ユーザーB:在庫を「0個」にして注文完了
➔ 結果:在庫1個に対して2人の注文が確定してしまう

排他制御を入れることで、「先に処理を開始したユーザーAの完了を待ち、ユーザーBには『在庫切れです』と正しく伝える」ことができるようになります。

例2:Web画面での編集の打ち消し(ロストアップデート)

同じデータをユーザーAとユーザーBが同時に編集しようとした場合:

【排他制御なし】
1. ユーザーAが編集画面を開く(内容:バージョン1)
2. ユーザーBが編集画面を開く(内容:バージョン1)
3. ユーザーBが内容を変更して【保存】(内容:バージョン2に更新)
4. ユーザーAが内容を変更して【保存】(内容:バージョン3に更新)
➔ 結果:ユーザーBがせっかく更新した内容が、後から保存したAによって消えてしまう

このように、「せっかく更新したデータが消える(失われる)現象」をロストアップデート(更新喪失)と呼びます。これ防ぐのも排他制御の重要な役割です。

主な排他制御のアプローチ(楽観と悲観)

排他制御には楽観排他と悲観排他という2種類のアプローチがあります。
どちらかが正しいというわけではなく、用途に応じて使い分けられています。テストを行う場合は仕様を把握する必要があります。

種類楽観的排他制御(楽観ロック)悲観的排他制御(悲観ロック)
考え方「めったに衝突しないだろう」と楽観的に考える「絶対に衝突するだろう」と悲観的に考える
仕組み保存する瞬間に「自分が開いた時からデータが変更されていないか」を確認する画面を開いた(あるいは処理を開始した)瞬間にデータに鍵をかける
失敗時の挙動後から保存しようとした人に「他の人に更新されました」とエラーを出す後から開こうとした人に「他の人が編集中です」と開かせない
主な用途現代のWebシステム全般(SNS、ブログ、業務管理画面など)チケット予約、銀行の口座取引など絶対に重複が許されない処理

冒頭の話に戻る

確認した結果、「排他制御のテスト」だとテストの目的としては正しいですが、事象やテストパターンを指す呼称であれば

  • ロストアップデート(Lost Update / 更新喪失)の確認テスト
  • 楽観的ロック(楽観排他)の検証テスト

などがありました。よりフランクな表現としては

  • 同時更新(ダブル更新)テスト
  • 競合(コンフリクト)テスト
  • 割り込み更新テスト

などもありました。現場で伝わる表現にしていこうと感じました。

余談:冒頭シチュエーションの期待結果について

冒頭のように操作をしたときの期待結果について、今の現場でよく見る制御としては

  • 楽観排他
  • 制御しない

のどちらかが多いです。

「制御しない」のは「発生しても影響が少ないから」「運用上発生しないから」「開発コストを抑えたいから」等によって発生することがあります。
制御しない仕様であっても「エラーとならず想定通り上書きされるか」など試験を行うかは、コストと相談しつつ検討・実施します。