在庫数がマイナスになっている一覧を前に、どこで数字がズレたのかを静かに追っている開発者

AIにトランザクション境界と排他制御を相談する|整合性の詰め方

「在庫がマイナスになっているんですが」

そう言われて一覧を開くと、確かに -1 が並んでいます。ログを追っても、例外は1件も出ていません。処理はすべて成功で終わっています。

コードを読み直しても、おかしなところは見つかりません。在庫を読んで、1を引いて、書き戻す。それだけです。テストも通っています。

同時に2人が同じ商品を買ったときのことだけが、どこにも書かれていなかったのです。

この手のズレは、暇な日には出ません。セールの初日、締め日の夕方、キャンペーンのメールを配信した直後。人が集まった瞬間にだけ、静かに数字が1つ狂います。しかもエラーにならないので、気づくのは数日後、誰かが棚卸しをしたときです。

この記事では、その「同時に来たとき」を前提にした設計——トランザクションの境界と排他制御の詰め方を、AIへの相談の仕方とセットで整理します。トランザクションとは、複数の更新をひとまとめにして、全部成功か全部なかったことにするか、どちらかにする仕組みのこと。排他制御とは、同じデータを同時に触らせないための取り決めのことです。

結論:順番は3つです。①囲む——「壊れてはいけない条件」を1行で書き、それを守るために同時に確定させたい更新だけをトランザクションで囲む。外部API呼び出しやAIの推論待ちは、必ず外に出す。②守る——同時更新を、アプリの条件分岐ではなくデータベース側で止める。楽観ロック・悲観ロック・制約・1文のUPDATEから、先に方式を選ぶ。③諦める——待ちきれないとき、競合に負けたときの引き際を決める。タイムアウト、リトライの上限、呼び出し元への返し方まで書く。

覚え方は 囲む → 守る → 諦める。①がないと②の範囲が決まらず、③がないと本番でロック待ちが積み上がります。

今日ぜんぶやる必要はありません。①の「壊れてはいけない条件を1行書く」だけでも、AIに渡す相談の中身が変わります。

何が起きているのか——同時実行は、コードの見た目に出ない

部品をトレーにまとめて紐で囲う場面、仕切られた1つの記帳台を1人が使い後ろで1人が待つ場面、砂時計を見て列を離れ出直す場面を並べた図
同時更新に強くする順番。囲む→守る→諦める。①がないと②の範囲が決まらず、③がないとロック待ちが積み上がる

打ち手の前に、なぜここが抜けやすいのかを整理します。原因の側を先に見ておくと、AIに渡す前提も書きやすくなります。

1つ目は、トランザクションの境界がコードの上でほとんど見えないこと。アノテーション1行、with ブロック1つ、フレームワークの既定の挙動。書かれていないことのほうが多いので、レビューでも話題に上がりません。境界は「決めたもの」ではなく「気づいたらそうなっていたもの」になりがちです。

2つ目は、「読んで・計算して・書く」の形が、同時実行に弱いこと。在庫を読み、1を引き、書き戻す。この3ステップの途中に別の処理が割り込むと、後から書いたほうが、先に書かれた結果を上書きします。両方とも成功し、両方ともエラーになりません。数字だけが1つ足りません。書き方が雑なのではなく、この形そのものが同時実行に弱いという話です。

3つ目は、AIに頼むと、まさにその形が返ってきやすいこと。「在庫を減らす処理を書いて」と頼めば、読みやすく、テストの通る「読んで・計算して・書く」が出てきます。AIが手を抜いているのではなく、同時に呼ばれるという前提を渡していないだけです(AIにコードを書かせる前に渡す前提と制約|伝え方の型)。

4つ目は、トランザクションの中に、待ち時間の長い処理が紛れ込むこと。外部APIの呼び出し、メール送信、ファイルの生成、そしてAIの推論待ち。AI機能は応答に数秒かかることも珍しくないので、その間ずっとデータベースの行を握り続けることになります。AIを組み込んだ機能ほど、この穴が効いてきます。

5つ目は、分離レベルが環境によって違うこと。分離レベルとは、同時に走るトランザクションから、お互いの途中経過がどこまで見えるかの設定です。たとえば MySQL(InnoDB)と PostgreSQL では既定値が違います。AIは一般論として正しい説明をしますが、あなたの環境の実際の設定までは知りません。ここは自分の環境の値を確認する場所です。

6つ目は、デッドロックが「複数の場所を見比べないと分からない」種類の問題であること。2つの処理が、AとBを違う順番でロックしに行くと、お互いの解放を待って止まります。AIは渡した範囲のコードしか見ていないので、別ファイルにある処理との順番までは揃えてくれません。ここは人がシステム全体を見て決めるところです。

裏を返すと、これは腕前の問題ではなく、前提の渡し方と、見る場所の問題です。見る場所さえ決まっていれば、AIはかなり頼れる相談相手になります。

具体例——つい、こうなりがちという形

どれも「まず動くものを」という自然な流れから生まれます。心当たりのある行があれば、そこだけ見れば十分です。

影響——エラーにならないので、見つかるのが遅い

急いで全部直さないと危ない、という話ではありません。ただ、出方が静かで、出たときには説明が要る種類の問題なので、書いておきます。

少し安心できる話をすると、この領域は打ち手がはっきりしています。作りを入れ替える必要はありません。守りたい条件を1行決めて、更新の書き方を1つ変えるところから始められます。効果も、負荷をかければその場で確かめられます。

明日からやること(囲む・守る・諦める)

上から順に、小さく始められます。今日は①だけで構いません。

1. 囲む——「壊れてはいけない条件」から、境界を決める

ここを飛ばして相談すると、AIの答えも一般論になります。逆に、ここさえ決まれば残りは詰めやすくなります。

2. 守る——同時更新の止め方を、先に選ぶ

方式を選ばずに書き始めると、条件分岐が積み上がっていきます。先に1つ選ぶだけで、レビューも楽になります。

3. 諦める——待てないとき、負けたときの振る舞いを書く

ここが抜けると、詰まったときに全体が巻き込まれます。「うまくいかなかったとき、どうするか」まで書いて、ひとまとまりです。

AIに相談するときのプロンプト(コピーして使えます)

前提を渡さないと、AIは素直に「読んで・計算して・書く」を出してきます。同時実行を前提だと明記し、禁じ手を先に伝えるのがコツです。

ある更新処理について、同時に複数のリクエストが来る前提で、トランザクションの境界と排他制御を相談させてください。

①まず、私が示す「壊れてはいけない条件」が壊れる同時実行のシナリオを、思いつく限り具体的に挙げてください(何と何が、どの順で走ったときか)。
②「読んで→計算して→書く」形の案は出さないでください。同時に来ると片方の更新が消えるためです。
③排他制御の方式を、DBの制約/1文のUPDATE/楽観ロック/悲観ロックの中から2案、それぞれ効かなくなる条件つきで挙げてください。
④トランザクションの中に入れてはいけない処理(外部API・メール送信・AI推論など)が案に混ざっていないか、自分で点検して指摘してください。
⑤複数の行やテーブルを更新する場合、ロックを取る順番を明示してください。
⑥競合して失敗したときに、呼び出し元と利用者へ何を返すかを書いてください。再試行してよいかどうかも添えてください。
⑦分離レベルに依存する部分があれば「DBと設定に依存」と明記し、断定しないでください。

なお、確実でない部分は「不明」と書いてください。埋めなくて構いません。

壊れてはいけない条件:( ) 対象の操作:( ) 使っているDBと版:( ) 想定される同時実行の多さ:( ) 巻き戻せない処理:( )

②の一文が効きます。先に一つ禁じ手を伝えるだけで、返ってくる案の筋が変わります。①は、そのままテストのシナリオになります。

出てきた案は、次の3点だけ自分の目で確かめます。(1)トランザクションの中に外部呼び出しやAI推論が残っていないか、(2)「読んで・計算して・書く」が消えているか、(3)ロックを取る順番が書かれているか。この3点は、もっともらしい説明で埋まっていても抜けていることがあります(設計レビューの見落としをAIに洗わせる|プロンプトの型)。

確認は、テストにも落としておくと安心です。同じ行を同時に2つ更新する、在庫が残り1のときに同時に2件買う、更新の順番が逆の処理を同時に走らせる——この3つが通れば、だいたいの経路は塞がっています(境界値・異常系をAIに考えさせて漏れを減らす手順)。

同時更新に強くするチェックリスト(コピーして使えます)

全部に○が要るわけではありません。気になった行だけで十分です。

① 囲む

② 守る

③ 諦める

この記事のまとめ

数字のズレは、腕前の問題ではなく前提の問題です。同じ行は同時に触られるもの、更新の順番は処理ごとにバラバラになりがちなもの——そこを前提に置くと、打ち手ははっきりします。

やることは3つ。①囲む——壊れてはいけない条件を1行で書き、それを守る更新だけを囲む。巻き戻せない処理は外へ。②守る——制約・1文のUPDATE・楽観ロック・悲観ロックから先に方式を選び、ロックの順番を1つに決める。③諦める——タイムアウトと再試行の上限を決め、負けたときに何を返すかまで書く。

AIに相談するときは、「読んで・計算して・書く案は出さないで」と先に一言添える。それだけで、返ってくる設計の筋がよくなります。

朝のオフィスで、そろった数字を画面で確かめ、同僚と穏やかに言葉を交わしている開発者

マイナスの在庫を見つけたとき、まず自分の書いたコードを疑ったと思います。実際には、コードは指示どおりに動いていて、同じ行が同時に触られていただけでした。

これは、気をつけて防ぐ種類のものではありません。仕組みで受け止める種類のものです。そして仕組みは、条件を1行決めて、更新の書き方を1つ変えるところから作れます。

今日は、あなたのシステムで「壊れてはいけない条件」をひとつだけ思い浮かべて、1行書いてみる。それだけで、次に混み合う日の夕方が、少し静かになります。

よければ、こちらも

関連用語