長いDBトランザクション中にリクエストを止めたら…何が起きる?
今回紹介するのは、「あるHTTPリクエスト(たとえば新規登録処理など)がサーバー側で長いデータベーストランザクションを走らせている最中に、もしクライアントがそのリクエストをキャンセルしたらどうなるのか?」という、開発現場で見落としがちな疑問を、主要なWebフレームワーク(Rails/Puma, Go, Python/FastAPI)で実験比較した実録記事です。
記事:https://sent-hil.com/2025/07/20/handling-cancellation-of-http-request-with-long-db-transaction.html
各言語・フレームワークの挙動 ― 比べてみた意外な違い
著者はまずRails(PumaをWebサーバーと仮定)とローカル開発環境で検証します。
長いトランザクションを模倣するために pg_sleep(5)
(5秒スリープ)を仕込んだ上で、クライアントは1秒未満でリクエストをキャンセル。その時の挙動を実際のログで確認したところ、
Starting long – running operation at 2025 – 07 – 20 19 : 02 : 48 – 0700
Record created with ID : 10 at 2025 – 07 – 20 19 : 02 : 53 – 0700
Response stream closed before render attempt: false at 2025 – 07 – 20 19 : 02 : 53 – 0700Railsでは、クライアントがHTTPリクエストをキャンセルしても、その最中に走っているDBトランザクションは自動的にはキャンセルされず、結果としてレコードが作成された。
一方Go言語(net/httpパッケージ & database/sql)では、リクエストがキャンセルされるとcontext.Context
がキャンセルイベントを伝播。トランザクションも即時中断され、DBにもレコードは残らないことが明確にログで確認できます。
2025/07/20 19:27:38 Sleep was interrupted: pq: canceling statement due to user request at …
Goではクライアントのキャンセルが即DBに伝わり、走行中のトランザクションが止まる
Python(FastAPI対応、詳細はリンク先のmain.py参照)でもRailsと同様に「途中でリクエストキャンセルしてもトランザクション自体は継続し、完了してしまう」挙動だったとのこと。
どうしてこうなる?裏側の仕組みを解き明かす
Goにおける優れた部分は、リクエストごとに context.Context
を活用している点です。
この仕組みは、「WebサーバーとDB層両方が“キャンセル信号”を認識し、それを受けて長い処理やトランザクション自体を直ちに止める」よう設計されています。
Goのdatabase/sqlの実装解説や該当コードも引用されています。
// awaitDone blocks until the context in Tx is canceled and rolls back
// the transaction if it’s not already done.
…
←tx.ctx.Done()
tx.rollback(discardConnection)
RailsやPythonの場合、クライアント側の切断(たとえば「ブラウザを閉じた」など)がサーバーに伝わっても、
リクエストハンドラ内のロジックやDBトランザクションには特に何の影響も及ぼさない設計が主流です。
「処理を安全に完結させ、後始末を行ったうえでレスポンスしよう」とする堅実な思想ですが、逆に“衝突”や“無駄な処理”が起きかねません。
設計思想の違いが「実世界でどのような動作となるか」に直結する例と言えるでしょう。
これって盲点?開発者としての気づき・考察
普段は「レスポンス失敗ならDBもロールバックされてるだろう」となんとなく信じていませんか?
著者の“実験”は、「頑丈なロジックと思い込んでいる箇所が、実は意図せず副作用(想定外のDB書き込みなど)を残すかもしれない」ことを改めて突きつけます。
これは特に“トランザクション創発型API”(長大な更新処理やバッチのうち、途中でクライアント側が諦める/タイムアウトする/通信途絶がありえる場合)にとって深刻な問題です。
Goのように最新技術では“キャンセル伝播”が積極的に設計に盛り込まれる一方、
既存技術やフレームワークの多くは「完了まで走りきる」のが基本。
「思っていたよりもサーバー側(やDB側)は囲い込み型。キャンセル信号は受け取っていない」――
これは理解しておかないと顧客体験やシステム品質で痛い目を見る盲点ではないでしょうか。
また、記事末尾では「AI(LLM)でのプロトタイピング」の体験談も書かれています。
ただし「生成コードの検証は必須。ブラックボックスとして完全信用してはいけない」という示唆もあり、ここも現場感覚として共感できる内容です。
本記事から得られる“設計”と“運用”のヒント
どのWebフレームワークや言語を使うにせよ――
「クライアントがリクエストを途中で止めたとき、自分たちのサービスはどんな挙動をするのか」
「副作用を最小にし、無駄なDB負荷を避け、必要ならキャンセル伝播ロジックを明示的に組み込んでいるか?」
を一度見直してみることが大切です。
また、ベテランでも実験と検証を惜しまない姿勢、
そしてLLM生成物の過信は禁物――
本記事はその両方を実践した好例と言えます。
設計段階はもちろん、運用・トラブルシュートやコードレビュー時にも、
「本当にリクエストキャンセル時に安全設計になっているか?」と今一度問い直しましょう。
参考:検証用のコードリポジトリ
– Rails例: main.rb
– Go例: main.go
– Python例: main.py
記事全文:https://sent-hil.com/2025/07/20/handling-cancellation-of-http-request-with-long-db-transaction.html
コメント