Watch at Ease看見世界,讀懂生活。

科技 · 實用指南

Python sqlite3 出現「cannot start a transaction within a transaction」?先找交易擁有者

這不是鎖等待逾時,而是同一連線已有作用中交易。記錄 Python 模式,保留原子邊界,只用一個外層交易或 SAVEPOINT。

Original diagram mapping Python transaction policy, live connection evidence, and the intended outer transaction owner.
原創示意圖:先記錄策略與狀態,再選交易擁有者。 Watch at Ease

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,讓模式或預設值的變更能及早暴露。

Original diagram comparing a nested BEGIN failure with one outer transaction and an inner SAVEPOINT.
原創示意圖:內層範圍使用 SAVEPOINT。 Watch at Ease