Commit 25a489dca4a576eba69a2fb9800f51f6a5858b31

Authored by yibo
1 parent 96cc2bc2

h3-console: sqlite 开 WAL 模式,修复大批量提交下的 database-is-locked 崩溃

8080(LAN)/8081(公网) 两个进程共用同一份 sqlite,各自的 reconciler_loop
每 5 秒都会对进行中的任务逐条 UPDATE。默认 journal_mode=DELETE 下同一
时刻只允许一个写者、读写还互斥,且没设连接 timeout 时 sqlite3 默认只
等 5 秒——队列一次性堆到 200+(一次 fire-and-forget 并发提交 150 个任务
撞出来的)之后,写请求频繁互相撞车,直接抛 database is locked,连
GET /v1/backends 这种只读查询都被堵到 38 秒才返回。持续了 11+ 小时。

db() 连接加 timeout=30 + PRAGMA journal_mode=WAL,允许多读者+单写者
并发,写者互相排队重试而不是直接报错。部署后 35 秒内 0 次锁错误
(此前几乎每个 reconciler tick 都报错),/v1/backends 恢复到 ~0.3 秒。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFw9ZBh3eVjssk6quYQ4Ki
Showing 1 changed file with 10 additions and 1 deletions
h3-console/server.py
... ... @@ -89,7 +89,16 @@ api = APIRouter(dependencies=[Depends(_require_token)]) # 所有 /v1/* 接口
89 89  
90 90  
91 91 def db():
92   - con = sqlite3.connect(DB_PATH)
  92 + # 2026-09-05:console 是 8080(LAN)/8081(公网) 两个独立进程共用同一份 sqlite 文件,各自的
  93 + # reconciler_loop 每 5 秒都会对着"进行中的任务"逐条 UPDATE——队列一旦攒到 200+(一次
  94 + # fire-and-forget 式并发提交 150 个任务撞出来的),两个进程的写请求频繁互相撞车。默认
  95 + # journal_mode=DELETE 下同一时刻只允许一个写者、读写还会互相阻塞,且没设 timeout 时
  96 + # sqlite3 的默认等锁时间只有 5 秒,稍微堆一下就直接抛"database is locked",而不是排队
  97 + # 等一下——GET /v1/backends 这种只读查询也会被前面攒着的写请求堵住,实测卡到 38 秒。
  98 + # WAL 模式允许"多个读者 + 一个写者"并发不互斥,配合更长的 busy timeout 让写者互相排队
  99 + # 重试而不是直接报错,两个改动都是连接级别、幂等,不需要额外迁移。
  100 + con = sqlite3.connect(DB_PATH, timeout=30)
  101 + con.execute("PRAGMA journal_mode=WAL")
93 102 con.row_factory = sqlite3.Row
94 103 return con
95 104  
... ...