feat: Auto-configure WAL mode on database open
- open() now automatically enables WAL mode for file-based databases - Configures: journal_mode=WAL, synchronous=NORMAL, busy_timeout=5000ms - Skips configuration for :memory: databases - Added openRaw() for cases requiring default SQLite behavior - Fixes: Database locked errors under concurrent load (Solo2 incident) Benefits: - Concurrent readers during writes - 5s retry instead of immediate failure - All projects using zcatsql benefit automatically 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
11a75132db
commit
5f8ae72a5a
2 changed files with 37 additions and 0 deletions
|
|
@ -59,6 +59,14 @@ pub const Database = struct {
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
/// Opens a database connection.
|
/// Opens a database connection.
|
||||||
|
///
|
||||||
|
/// For file-based databases, automatically configures for high availability:
|
||||||
|
/// - WAL mode (concurrent reads during writes)
|
||||||
|
/// - synchronous=NORMAL (balance of safety and speed)
|
||||||
|
/// - busy_timeout=5000ms (retry instead of immediate failure)
|
||||||
|
///
|
||||||
|
/// For in-memory databases (`:memory:`), these settings are skipped.
|
||||||
|
/// Use `openRaw()` if you need the default SQLite behavior.
|
||||||
pub fn open(path: [:0]const u8) Error!Self {
|
pub fn open(path: [:0]const u8) Error!Self {
|
||||||
var handle: ?*c.sqlite3 = null;
|
var handle: ?*c.sqlite3 = null;
|
||||||
const result = c.sqlite3_open(path.ptr, &handle);
|
const result = c.sqlite3_open(path.ptr, &handle);
|
||||||
|
|
@ -70,6 +78,35 @@ pub const Database = struct {
|
||||||
return resultToError(result);
|
return resultToError(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply high-availability settings for file-based databases
|
||||||
|
// Skip for in-memory databases where WAL doesn't apply
|
||||||
|
if (!std.mem.eql(u8, path, ":memory:")) {
|
||||||
|
// WAL mode: allows concurrent readers during writes
|
||||||
|
_ = c.sqlite3_exec(handle, "PRAGMA journal_mode = WAL", null, null, null);
|
||||||
|
// NORMAL sync: good balance of durability and performance
|
||||||
|
_ = c.sqlite3_exec(handle, "PRAGMA synchronous = NORMAL", null, null, null);
|
||||||
|
// Wait 5 seconds instead of failing immediately when busy
|
||||||
|
_ = c.sqlite3_busy_timeout(handle, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .handle = handle };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens a database connection without automatic configuration.
|
||||||
|
///
|
||||||
|
/// Unlike `open()`, this does not configure WAL mode or busy timeout.
|
||||||
|
/// Use this when you need full control over SQLite settings.
|
||||||
|
pub fn openRaw(path: [:0]const u8) Error!Self {
|
||||||
|
var handle: ?*c.sqlite3 = null;
|
||||||
|
const result = c.sqlite3_open(path.ptr, &handle);
|
||||||
|
|
||||||
|
if (result != c.SQLITE_OK) {
|
||||||
|
if (handle) |h| {
|
||||||
|
_ = c.sqlite3_close(h);
|
||||||
|
}
|
||||||
|
return resultToError(result);
|
||||||
|
}
|
||||||
|
|
||||||
return .{ .handle = handle };
|
return .{ .handle = handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue