科技 · 实用指南
Python sqlite3 报“cannot start a transaction within a transaction”?先找出事务所有者
这不是锁等待超时,而是同一连接已有活动事务。记录 Python 模式、保住原子边界,并只用一个外层事务或 SAVEPOINT。

SQLite 不允许在活动事务中再执行 BEGIN。Python 中真正难找的是第一个事务由谁打开:autocommit=False 会维持事务,旧 isolation_level 会在 DML 前隐式开始,外层 helper 可能已拥有事务,脚本也可能自带 BEGIN。
不要靠到处插入 commit 消掉异常。先在失败行前记录 autocommit、isolation_level 和 in_transaction,再明确工作单元所有者并选定一种模型。
冻结失败 BEGIN 处的证据
记录失败 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,让模式或默认值变化尽早暴露。
