科技 · 實用指南
Python sqlite3 出現「cannot start a transaction within a transaction」?先找交易擁有者
這不是鎖等待逾時,而是同一連線已有作用中交易。記錄 Python 模式,保留原子邊界,只用一個外層交易或 SAVEPOINT。

SQLite 不允許在作用中交易內再次 BEGIN。Python 中第一個交易可能由 autocommit=False、舊 isolation_level、外層 helper 或腳本本身開啟。
不要到處插入 commit。先記錄 autocommit、isolation_level 與 in_transaction,再指定工作單元的擁有者。
凍結失敗點證據
記錄失敗 SQL、autocommit、isolation_level、in_transaction,以及同一連線最近的 DML、BEGIN、SAVEPOINT、executescript、commit 與 rollback。
不要從呼叫堆疊的距離推斷擁有權;外層內容管理器或連線策略可能早已開啟交易。
只選一個外層交易擁有者
應用程式擁有模式由呼叫者開關工作單元,底層 helper 不執行 BEGIN 或 COMMIT;驅動程式擁有模式則使用連線策略與 commit/rollback。
腳本若自帶 BEGIN/COMMIT,就不要再包一層;使用刻意的中途失敗來驗證回滾。
有意巢狀時使用 SAVEPOINT
內層需要可回滾範圍時,使用唯一名稱的 SAVEPOINT;成功時 RELEASE,失敗時 ROLLBACK TO 後再 RELEASE。
可重用的程式庫通常不應提交它不擁有的外層交易。
把 BEGIN IMMEDIATE 視為鎖定時機選擇
BEGIN IMMEDIATE 只會提前取得寫入鎖,仍不能在同一連線的作用中交易內執行。
若舊模式的 DML 已開啟 DEFERRED,應判斷先前工作是否屬於同一單元,再決定保留、提交或重構邊界。
證明提交與回滾邊界
建立首筆寫入後刻意失敗的測試,確認整個預期單元都會回滾;再覆蓋成功路徑與巢狀 helper。
在 API 邊界斷言 in_transaction,讓模式或預設值的變更能及早暴露。
