fix(graph_sqlite): add busy_timeout + stale WAL/SHM cleanup on startup

Root cause: No busy_timeout set → immediate 'database is locked' on restart
when stale WAL/SHM/journal files remain from crashed previous instance.

Fix:
- On open(): clean stale -wal/-shm/-journal files (only if main db exists)
- After sqlite3_open(): set busy_timeout=10000ms (10s wait for locks)
- This eliminates the InMemoryGraph fallback on restart
This commit is contained in:
xiaowei 2026-05-30 17:35:29 +08:00
parent cd49a2801a
commit daabd54649
1 changed files with 19 additions and 0 deletions

View File

@ -7,12 +7,16 @@ package governance
#cgo LDFLAGS: -lsqlite3
#include <sqlite3.h>
#include <stdlib.h>
// busy_timeout 是 Go/C 混合文件中直接使用 C 函数
// sqlite3_busy_timeout 在 sqlite3.h 中声明
*/
import "C"
import (
"encoding/json"
"fmt"
"os"
"strings"
"sync"
"unicode"
@ -32,6 +36,18 @@ func NewSQLiteGraphStore(dbPath string) (*SQLiteGraphStore, error) {
if dbPath == "" {
dbPath = "/var/lib/memoryweave/graph.db"
}
// 清理 stale WAL/SHM 文件(防止旧进程崩溃后留下这些文件导致锁失败)
// 只有当主 db 文件存在时才清理(避免误删新建库的场景)
if _, err := os.Stat(dbPath); err == nil {
for _, suffix := range []string{"-wal", "-shm", "-journal"} {
f := dbPath + suffix
if _, err := os.Stat(f); err == nil {
os.Remove(f)
}
}
}
cPath := C.CString(dbPath)
defer C.free(unsafe.Pointer(cPath))
@ -43,6 +59,9 @@ func NewSQLiteGraphStore(dbPath string) (*SQLiteGraphStore, error) {
return nil, fmt.Errorf("sqlite open graph: %s", msg)
}
// 设 10s busy_timeout——等待旧进程/跨进程锁释放,不立即报 "database is locked"
C.sqlite3_busy_timeout(db, 10000)
gs := &SQLiteGraphStore{db: db, path: dbPath}
if err := gs.migrate(); err != nil {
C.sqlite3_close(db)