기술 · 실용 가이드
SQLite database is locked: busy_timeout을 늘리기 전에 쓰기 연결과 트랜잭션 찾기
SQLITE_BUSY를 잠금 타임라인 문제로 보고 쓰기 연결, 트랜잭션 단계와 저널 모드를 확인한 뒤 timeout, WAL 또는 재시도를 선택합니다.

SQLite의 “database is locked”는 필요한 잠금을 얻지 못했다는 뜻일 뿐, 어떤 프로세스나 연결이 충돌을 만들었는지는 알려주지 않습니다.
연결별 타임라인을 만들고 짧게 기다릴 수 있는 경합과 DEFERRED 읽기-쓰기 승격 실패를 구분한 뒤 최소 수정안을 검증하십시오.
실패 증거를 먼저 고정하기
전체 예외, 숫자 코드, SQL, 프로세스, 스레드, 연결 ID와 시간을 기록하고 BEGIN, 첫 쓰기, COMMIT, 체크포인트, 복구 또는 종료 중 어디서 실패했는지 표시합니다.
-wal 또는 -shm 파일을 지우거나 주 데이터베이스만 복사하지 마십시오. 경로를 열 수 있는 앱, 셸, 마이그레이션, 워커와 편집기를 모두 나열합니다.
연결별 트랜잭션 타임라인 만들기
각 연결의 열기, BEGIN, 첫 읽기, 첫 쓰기, commit/rollback, 문 종료와 close를 기록하고 journal_mode, timeout과 트랜잭션 모드도 붙입니다.
쓰기 트랜잭션 안에서 네트워크나 파일 I/O를 기다린다면 재시도 조정보다 먼저 느린 작업을 밖으로 옮깁니다.
대기 가능한 경합과 승격 실패 구분
SQLite는 여러 읽기를 허용하지만 쓰기 트랜잭션은 하나뿐입니다. DEFERRED가 먼저 읽은 뒤 쓰려면 승격이 필요하며 다른 쓰기 연결이 있으면 SQLITE_BUSY를 반환할 수 있습니다.
처음부터 쓰기 슬롯을 얻는 것이 의미에 맞고 반드시 쓰는 작업에만 BEGIN IMMEDIATE를 사용하십시오. 기존 쓰기 연결이 있으면 이 역시 실패할 수 있습니다.
busy_timeout은 짧은 경합에만 사용
timeout은 대기 시간을 정할 뿐 다른 연결을 commit하거나 누수된 커서를 닫거나 긴 트랜잭션을 줄이지 않습니다. 실제 대기 시간과 잠금 보유자를 측정합니다.
멱등 작업이나 부작용이 통제된 전체 트랜잭션만 재시도하고 지터와 횟수 상한을 둡니다.
WAL을 만능 잠금 해제로 보지 않기
WAL은 읽기와 쓰기의 겹침을 개선하지만 쓰기 연결은 여전히 하나입니다. 배타 모드, 정리 또는 복구 중에도 SQLITE_BUSY가 생길 수 있습니다.
모든 프로세스와 파일 시스템의 호환성을 확인하고 DB와 WAL 상태를 안전하게 다루며 적용 후에도 체크포인트와 쓰기 시간을 관찰합니다.
최소 수정 후 재검증
문과 연결을 확실히 닫고 모든 종료 경로에서 commit 또는 rollback하며 쓰기 트랜잭션을 줄이고 불가피한 쓰기 연결을 직렬화합니다.
원래 작업을 연결 ID와 계측으로 재생해 중복 쓰기가 없고 예산 초과 시 명확히 실패하는지 확인합니다. 지속적인 다중 쓰기에는 클라이언트-서버 DB를 검토합니다.
