#35
[Bug] cairn-server SQLite database is locked 导致 heartbeat 500,部分 explore intent 失败
当 cairn-dispatcher 连续调度多个 explore intent 时,cairn-server 会频繁出现:
sqlite3.OperationalError: database is locked 该错误发生在 expire_workers() 写 SQLite 时,导致 heartbeat、get_project、list_projects 等接口返回 500。
其中 heartbeat 返回 500 后,cairn-dispatcher 会因为等待时间超过 grace(6 秒)而判定 heartbeat failed,最终导致部分 explore intent 失败。
🔍 Root Cause Analysis:
The error encountered in [Bug] cairn-server SQLite database is locked 导致 heartbeat 500,部分 explore intent 失败 (
database is locked/SQLITE_BUSY[code 5]) occurs when concurrent database operations clash on SQLite's file-locking mechanism.1. Root Cause Breakdown:
DELETErollback journal mode. In this mode, locks are database-wide:EXCLUSIVElock, completely blocking all concurrent readers and writers.SHAREDlock prevents writers from committing, holding the writer in aPENDINGstate which quickly causes subsequent operations to time out withSQLITE_BUSY.busy_timeout: Most default SQLite drivers / ORMs default to a zero or very short busy timeout (0ms), throwingOperationalError: database is lockedimmediately instead of waiting for existing transactions to finalize.2. Recommended Production Solutions:
Step 1: Enable Write-Ahead Logging (WAL) Mode
WAL mode fundamentally alters SQLite's locking model by allowing concurrent readers while a write is occurring:
Benefits: Readers never block writers, and writers never block readers. Only simultaneous writes still contend.
Step 2: Configure a Non-Zero Busy Handler Timeout
Instruct SQLite to wait and retry before throwing an error:
Step 3: Application & ORM Configuration
Python (sqlite3 & SQLAlchemy):
Node.js (better-sqlite3):
Go (mattn/go-sqlite3):
Step 4: Serialize Writes in Multi-Worker Setups
Eng. MHD. Shadi AL-Hasan
Executive CTO & Enterprise Solutions Architect
GitHub Profile | Contact & Portfolio