チェーン店のモバイルオーダーのバグを見つけた話

先日、妻と一緒に某ファストフード店に行った。
休日には長蛇のレジ待ち行列ができる人気店だ。

モバイルオーダーで注文

この日は日曜日のお昼時ということでレジ前には長蛇の列。
とりあえず席を確保し、それぞれのスマホからモバイルオーダーすることにした。

モバイルオーダーの流れは以下の通り。

  1. 専用アプリを開く
  2. 店舗を選択する
  3. 商品を選ぶ
  4. 支払い方法を選ぶ
  5. 購入決定ボタンを押す
  6. スマホにモバイルオーダー番号が通知される
  7. 店舗モニタに調理進捗状況がモバイルオーダー番号で表示される
  8. 料理が出来上がったらスマホに通知来る
  9. 窓口に行き、モバイルオーダー番号を伝えて商品を受け取る

問題発生

注文を終え、店舗モニタを見てみると
同じモバイルオーダー番号が2行表示されていた。

妻のスマホを確認したところ、同じモバイルオーダー番号が表示されていた。

どうやら、同じタイミングで注文したら、同じ番号が振られてしまったらしい。

一応難なく商品を受け取れた

店舗モニタに同じ番号が2行で表示されてるってことは、別々のオーダーだと認識はされてるっぽい。

別々のオーダーとして店舗調理は順調に流れたようだが、受け渡し担当はだいぶ混乱していた。

レジ付近が混雑していたこともあり、何となく「なぁなぁ」で商品を受け取りことができた。
今回は夫婦でオーダーが重複したからまだ良かったが、他人どうしだったらさらに混乱しそうだ。

システムの仕様を想像してみた

モバイルオーダー番号は主キーではない

モバイルオーダー番号の採番体系は”M”+3桁連番だった。
3桁だと1日の中でも2、3週循環する可能性がありそうだ。
3桁以上の番号は覚えづらいので、採番体系の設計としては正しい。

3桁しかないので、モバイルオーダー番号を主キーとするのは難しい。モバイルオーダー番号とは別にオーダーを特定するような主キーを持っているのだろう。

問題は「1日に同じモバイルオーダー番号は発生する」仕様ということは

「テーブルINSERT時の重複チェックが難しくなる」ということだ。

当然、採番時に排他をかけて防ぐことになるはず。

モバイルオーダー番号の採番方法を想像してみる

シーケンスオブジェクトを使えば、自動的に排他してくれる。
独自の採番テーブルを設計するぐらいの知識を持っていれば、排他ロックは必ず入れるだろう。

ということは、オーダーテーブルの「活きている最新データのモバイルオーダー番号最大値+1」みたいなロジックだろうか。

この場合、データセレクト時に排他ロックを入れることになり、レスポンスに影響しそうだ。

「同じタイミングでオーダーがはいることはない」と割り切ったのか、ただただ経験不足で考慮されなかったのかはよくわからない。

自分が設計するなら

今回のような場合、自分が設計担当なら、採番テーブル使う気がする。

採番値のロックが問題になるようなシステムでもないはずなので、これでいけるはず。

前提条件

  • 全店舗が同一インスタンス
  • モバイルオーダー番号は1日で循環する

採番テーブルを用意

店舗ごとでモバイルオーダー番号を採番するはずなので、店舗ごとに現在値を保持する「モバイルオーダー番号採番テーブル」を用意する。

項目名制約説明
店舗コード主キー
現在採番値現在の採番値を保持する(随時更新)
最小値採番枠の最小値を保持する
最大値採番枠の最大値を保持する

採番部品を用意

引数:店舗コード

戻り値:モバイルオーダー番号(“M”+3桁連番を返す)
概要:

  1. 引数で渡された店舗コードを使い、モバイルオーダー番号採番テーブルから現在採番値を取得。(このとき排他ロックをかける)
  2. 現在採番値が最大値に到達していれば、現在採番値を最小値で更新したうえで、”M”+現在採番地を返却
  3. 現在採番値が最大値の到達していなければ、現在採番値+1で現在採番値を更新したうえで、”M”+現在採番値を返却

他の方法を却下した理由

シーケンスオブジェクト使ってもいいけど、店舗増えるごとにシーケンスオブジェクト増やすのはいまいちだし、シーケンスオブジェクト名をどこか別テーブルに保持することになるから却下。

シーケンスや採番テーブル使わずに、オーダーテーブルの最大値+1にすると、オーダー入るたびにその店舗の全レコードロックして検索することになるので、レスポンス懸念があるので却下。

まとめ

どうしてこんなバグが残ったのか気になるところ。

設計レビューやテストあたりで発見されそうだけどなぁー。コストの関係で割り切った仕様にしなのかなー。

コメント