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을 거부합니다. 첫 트랜잭션은 autocommit=False, 레거시 isolation_level, 바깥 helper 또는 스크립트가 열었을 수 있습니다.

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를 실행하세요.

재사용 가능한 라이브러리는 자신이 소유하지 않은 외부 트랜잭션을 보통 commit하면 안 됩니다.

BEGIN IMMEDIATE는 잠금 시점 선택입니다

BEGIN IMMEDIATE는 쓰기 잠금을 더 일찍 획득할 뿐이며 같은 연결의 활성 트랜잭션 안에서는 여전히 실행할 수 없습니다.

레거시 모드의 DML이 이미 DEFERRED를 열었다면 앞선 작업이 같은 단위인지 판단한 뒤 유지, commit 또는 경계 재설계를 선택하세요.

commit과 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