
先日、妻と一緒に某ファストフード店に行った。
休日には長蛇のレジ待ち行列ができる人気店だ。
モバイルオーダーで注文
この日は日曜日のお昼時ということでレジ前には長蛇の列。
とりあえず席を確保し、それぞれのスマホからモバイルオーダーすることにした。
モバイルオーダーの流れは以下の通り。
- 専用アプリを開く
- 店舗を選択する
- 商品を選ぶ
- 支払い方法を選ぶ
- 購入決定ボタンを押す
- スマホにモバイルオーダー番号が通知される
- 店舗モニタに調理進捗状況がモバイルオーダー番号で表示される
- 料理が出来上がったらスマホに通知来る
- 窓口に行き、モバイルオーダー番号を伝えて商品を受け取る
問題発生
注文を終え、店舗モニタを見てみると
同じモバイルオーダー番号が2行表示されていた。
妻のスマホを確認したところ、同じモバイルオーダー番号が表示されていた。
どうやら、同じタイミングで注文したら、同じ番号が振られてしまったらしい。
一応難なく商品を受け取れた
店舗モニタに同じ番号が2行で表示されてるってことは、別々のオーダーだと認識はされてるっぽい。
別々のオーダーとして店舗調理は順調に流れたようだが、受け渡し担当はだいぶ混乱していた。
レジ付近が混雑していたこともあり、何となく「なぁなぁ」で商品を受け取りことができた。
今回は夫婦でオーダーが重複したからまだ良かったが、他人どうしだったらさらに混乱しそうだ。
システムの仕様を想像してみた
モバイルオーダー番号は主キーではない
モバイルオーダー番号の採番体系は”M”+3桁連番だった。
3桁だと1日の中でも2、3週循環する可能性がありそうだ。
3桁以上の番号は覚えづらいので、採番体系の設計としては正しい。
3桁しかないので、モバイルオーダー番号を主キーとするのは難しい。モバイルオーダー番号とは別にオーダーを特定するような主キーを持っているのだろう。
問題は「1日に同じモバイルオーダー番号は発生する」仕様ということは
「テーブルINSERT時の重複チェックが難しくなる」ということだ。
当然、採番時に排他をかけて防ぐことになるはず。
モバイルオーダー番号の採番方法を想像してみる
シーケンスオブジェクトを使えば、自動的に排他してくれる。
独自の採番テーブルを設計するぐらいの知識を持っていれば、排他ロックは必ず入れるだろう。
ということは、オーダーテーブルの「活きている最新データのモバイルオーダー番号最大値+1」みたいなロジックだろうか。
この場合、データセレクト時に排他ロックを入れることになり、レスポンスに影響しそうだ。
「同じタイミングでオーダーがはいることはない」と割り切ったのか、ただただ経験不足で考慮されなかったのかはよくわからない。
自分が設計するなら
今回のような場合、自分が設計担当なら、採番テーブル使う気がする。
採番値のロックが問題になるようなシステムでもないはずなので、これでいけるはず。
前提条件
- 全店舗が同一インスタンス
- モバイルオーダー番号は1日で循環する
採番テーブルを用意
店舗ごとでモバイルオーダー番号を採番するはずなので、店舗ごとに現在値を保持する「モバイルオーダー番号採番テーブル」を用意する。
項目名 | 制約 | 説明 |
---|---|---|
店舗コード | 主キー | |
現在採番値 | 現在の採番値を保持する(随時更新) | |
最小値 | 採番枠の最小値を保持する | |
最大値 | 採番枠の最大値を保持する |
採番部品を用意
引数:店舗コード
戻り値:モバイルオーダー番号(“M”+3桁連番を返す)
概要:
- 引数で渡された店舗コードを使い、モバイルオーダー番号採番テーブルから現在採番値を取得。(このとき排他ロックをかける)
- 現在採番値が最大値に到達していれば、現在採番値を最小値で更新したうえで、”M”+現在採番地を返却
- 現在採番値が最大値の到達していなければ、現在採番値+1で現在採番値を更新したうえで、”M”+現在採番値を返却
他の方法を却下した理由
シーケンスオブジェクト使ってもいいけど、店舗増えるごとにシーケンスオブジェクト増やすのはいまいちだし、シーケンスオブジェクト名をどこか別テーブルに保持することになるから却下。
シーケンスや採番テーブル使わずに、オーダーテーブルの最大値+1にすると、オーダー入るたびにその店舗の全レコードロックして検索することになるので、レスポンス懸念があるので却下。
まとめ
どうしてこんなバグが残ったのか気になるところ。
設計レビューやテストあたりで発見されそうだけどなぁー。コストの関係で割り切った仕様にしなのかなー。
コメント