テクノロジー · 実用ガイド
Python sqlite3 の “cannot start a transaction within a transaction” は所有者から調べる
ロック待ちの timeout ではなく、同じ connection に transaction が既にあります。Python mode を記録し、一つの outer transaction または SAVEPOINT を使います。

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 を検査し、モードや既定値の変更を早期に検出します。
