Watch at Ease세상을 읽고, 일상의 답을 찾다.

기술 · 실용 가이드

SQLite RETURNING에서 한 행을 가져와도 커밋이 막히나요? 모든 쓰기 커서를 완료하세요

ID를 받았다고 문장이 끝난 것은 아닙니다. 쓰기를 재실행하거나 결과를 버리기 전에 단일 행, 여러 행, 커서 두 개의 동작을 비교하세요.

RETURNING 읽기 방식 8개와 커밋 결과 일러스트
자체 실측 일러스트: CPython3.12.14/SQLite3.53.1 임시 파일 실험 8개이며 기존 트랜잭션 제어를 명시했습니다. Watch at Ease

INSERT ... RETURNING 뒤 COMMIT이 ‘SQL statements in progress’를 보고하면 다른 프로세스가 잠갔다고 판단하기 전에 자신의 미완료 쓰기 결과부터 확인하세요. ID 하나를 받는 것과 문장 실행 완료는 별개입니다.

임시 파일 실험 8개로 fetchone이 통하는 경우와 일반화할 수 없는 이유를 확인했습니다. 커서를 닫으면 쓰기는 커밋되더라도 필요한 반환 ID는 잃을 수 있습니다.

1. 드라이버와 트랜잭션 관리 주체를 기록하세요

Python, sqlite3.sqlite_version, 정확한 SQL, 살아 있는 모든 결과 커서와 트랜잭션 설정을 기록하세요. 실험은 Linux의 CPython3.12.14와 SQLite3.53.1에서 autocommit=sqlite3.LEGACY_TRANSACTION_CONTROL 및 isolation_level="DEFERRED"를 명시했습니다. 표준 라이브러리 sqlite3의 제한된 결과이며 APSW나 ORM 결과는 아닙니다.

전체 예외와 실패 단계를 보관하세요. SQLite는 미완료 SELECT가 있어도 명시적 COMMIT을 허용하지만 미완료 쓰기는 커밋을 막을 수 있습니다. 이 연결의 RETURNING 쓰기를 확인하기 전에 모든 SQLITE_BUSY를 외부 경합으로 분류하거나 타임아웃부터 늘리지 마세요. 모드 변경으로 의도치 않게 커밋해서도 안 됩니다.

2. 한 행의 성공을 일반화하지 마세요

각 실험마다 새 파일과 t(x integer) 테이블을 만들었습니다. 읽지 않은 INSERT INTO t VALUES(1) RETURNING x는 커밋을 막았습니다. 이 드라이버에서는 유일한 행을 fetchone()으로 가져오면 커밋됐지만, INSERT INTO t VALUES(1),(2),(3) RETURNING x에서 한 행만 가져오면 여전히 실패했습니다.

별도의 세 행 실험에서 fetchall() 또는 빈 배치가 나올 때까지 반복한 fetchmany(1)은 커밋됐으며 다시 열었을 때 세 값이 모두 남았습니다. 계속하기 전에 필요한 모든 ID를 보관하세요. SQLite는 RETURNING 순서를 보장하지 않으므로 입력 순서에 맞춰 대응시키면 안 됩니다. 배치 읽기는 Python 리스트 크기를 제한할 뿐, 엔진이 이미 버퍼링한 RETURNING 출력까지 없애지는 않습니다.

3. 필요한 출력은 보존하고 모든 쓰기 커서를 끝내세요

여섯 번째 실험에서는 읽지 않은 세 행 커서를 닫았습니다. 커밋은 성공했고 다시 연 파일에 세 삽입 값이 있었지만 애플리케이션은 반환 행을 하나도 수집하지 않았습니다. 따라서 Cursor.close()는 롤백이나 필요한 ID 전달을 대신하지 않습니다. 출력을 포기하기로 의도적으로 결정한 경우에만 사용하세요.

추가 실험 두 개에서는 한 연결에 INSERT ... RETURNING 커서 두 개를 유지했습니다. 첫 번째만 끝까지 읽으면 두 번째가 남아 커밋이 실패했습니다. 둘 다 읽으면 성공했고 다시 연 파일에 1과 99가 있었습니다. 트랜잭션 관리 구성 요소가 커서 참조를 보관해야 합니다. 보조 함수가 한 행을 반환하면서 다른 쓰기 결과를 조용히 남기지 않도록 하세요.

4. 반환 결과와 커밋을 따로 검증하세요

커밋 실패 실험 모두 시도 직후 in_transaction이 true였습니다. 그다음 실험 코드가 미완료 커서를 닫고 명시적으로 롤백했기 때문에 다시 연 파일에는 삽입 행이 없었습니다. 빈 결과는 의도적인 정리 때문이지 커밋 예외가 자동으로 쓰기를 취소한 증거가 아닙니다. 트랜잭션이 활성 상태일 때 INSERT를 재실행하면 추가 쓰기가 발생할 수 있습니다.

필요한 결과를 읽고 검증한 뒤 커서를 닫고, 정해진 트랜잭션 관리 주체를 통해 커밋하세요. 새 연결에서 저장된 값을 확인하고 정확한 행 수 회귀 검사를 보관하세요. 오류가 나면 애플리케이션 롤백 정책을 따르고 ID가 반환됐다는 이유만으로 성공을 보고하지 마세요. 이번 실험은 동시 읽기 연결, 트리거, 대량 출력, 다른 autocommit 모드를 검사하지 않았으므로 별도 진단이 필요합니다.

행 읽기와 커서 종료 및 커밋 차이를 보여 주는 일러스트
자체 진단 일러스트: 출력 전달, 문장 완료, 트랜잭션 커밋을 따로 검증합니다. 커서 종료는 이미 실행한 삽입의 취소가 아닙니다. Watch at Ease