テクノロジー · 実用ガイド

Python sqlite3 の “cannot start a transaction within a transaction” は所有者から調べる

ロック待ちの timeout ではなく、同じ connection に transaction が既にあります。Python mode を記録し、一つの outer transaction または SAVEPOINT を使います。

Original diagram mapping Python transaction policy, live connection evidence, and the intended outer transaction owner.
オリジナルイラスト:方針と状態を記録してからトランザクション所有者を選びます。 Watch at Ease

SQLite は active transaction 内の BEGIN を拒否します。最初の transaction は autocommit=False、legacy isolation_level、outer helper、または script 自身が開いた可能性があります。

commit を追加して消す前に autocommit、isolation_level、in_transaction を記録し、unit of work の owner を決めます。

失敗した BEGIN の証拠を固定する

失敗した SQL、autocommit、isolation_level、in_transaction、および同じ接続で直前に実行した DML、BEGIN、SAVEPOINT、executescript、commit、rollback を記録します。

呼び出し階層の近さから所有者を推測しないでください。外側のコンテキストマネージャーや接続ポリシーが、すでにトランザクションを開始している場合があります。

外側の所有者を一つだけ選ぶ

アプリケーション所有モデルでは呼び出し側が作業単位を開始・終了し、下位の helper は BEGIN や COMMIT を実行しません。ドライバー所有モデルでは接続ポリシーと commit/rollback を使います。

スクリプト自身が BEGIN/COMMIT を含む場合は、さらに外側で囲まないでください。意図的な途中失敗を使って rollback を検証します。

意図した入れ子には SAVEPOINT を使う

内側だけを戻せる範囲が必要なら一意な名前の SAVEPOINT を作り、成功時は RELEASE、失敗時は ROLLBACK TO の後に RELEASE します。

再利用可能なライブラリは、通常、自分が所有していない外側のトランザクションを commit すべきではありません。

BEGIN IMMEDIATE はロック時期の選択と考える

BEGIN IMMEDIATE は書き込みロックの取得を早めるだけで、同じ接続のアクティブなトランザクション内では実行できません。

従来モードの DML がすでに DEFERRED を開始しているなら、先行処理が同じ作業単位かを確認してから、維持・commit・境界の再設計を選びます。

commit と rollback の境界を証明する

最初の書き込み後に意図的に失敗させ、期待する作業単位全体が rollback されることを確認します。その後、成功経路と入れ子の helper も試します。

API 境界で in_transaction を検査し、モードや既定値の変更を早期に検出します。

Original diagram comparing a nested BEGIN failure with one outer transaction and an inner SAVEPOINT.
オリジナルイラスト:内側の範囲には二つ目の BEGIN ではなく SAVEPOINT を使います。 Watch at Ease