db: replace engine mutex with writer-preferring RwLock, add lock_read/unlock_read
This commit is contained in:
30
src/db.zig
30
src/db.zig
@@ -1,7 +1,9 @@
|
|||||||
//! In-memory database engine backed by the append-only log. Maps
|
//! In-memory database engine backed by the append-only log. Maps
|
||||||
//! db -> collection -> _id(serialized) -> owned Document. All mutations are
|
//! db -> collection -> _id(serialized) -> owned Document. All mutations are
|
||||||
//! logged and synced before they become visible in memory, so a crash never
|
//! logged and synced before they become visible in memory, so a crash never
|
||||||
//! loses a committed write. Callers must hold `mutex` around a command.
|
//! loses a committed write. Callers must hold the write lock (`lock`) around
|
||||||
|
//! any command that mutates state, and the read lock (`lock_read`) around
|
||||||
|
//! read-only commands so reads overlap with each other.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const bson = @import("bson.zig");
|
const bson = @import("bson.zig");
|
||||||
@@ -22,7 +24,10 @@ pub const Db = struct {
|
|||||||
pub const Engine = struct {
|
pub const Engine = struct {
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
io: std.Io,
|
io: std.Io,
|
||||||
mutex: std.Io.Mutex,
|
// One writer at a time (log append + fsync, map mutation); many
|
||||||
|
// concurrent readers (find/count/aggregate scans). Writer-preferring:
|
||||||
|
// a queued writer blocks new readers rather than starving.
|
||||||
|
rwlock: std.Io.RwLock,
|
||||||
log: storage.Log,
|
log: storage.Log,
|
||||||
dbs: std.StringHashMapUnmanaged(Db),
|
dbs: std.StringHashMapUnmanaged(Db),
|
||||||
seq: u64,
|
seq: u64,
|
||||||
@@ -32,7 +37,7 @@ pub const Engine = struct {
|
|||||||
var engine = Engine{
|
var engine = Engine{
|
||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.io = io,
|
.io = io,
|
||||||
.mutex = std.Io.Mutex.init,
|
.rwlock = .init,
|
||||||
.log = try storage.Log.open(gpa, io, path),
|
.log = try storage.Log.open(gpa, io, path),
|
||||||
.dbs = .empty,
|
.dbs = .empty,
|
||||||
.seq = 0,
|
.seq = 0,
|
||||||
@@ -68,14 +73,25 @@ pub const Engine = struct {
|
|||||||
self.log.close();
|
self.log.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- commands (callers must hold the mutex) ------------------------------
|
// -- commands (callers must hold the matching lock) ---------------------
|
||||||
|
|
||||||
|
/// Exclusive lock: for commands that mutate the engine.
|
||||||
pub fn lock(self: *Engine) !void {
|
pub fn lock(self: *Engine) !void {
|
||||||
try self.mutex.lock(self.io);
|
try self.rwlock.lock(self.io);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unlock(self: *Engine) void {
|
pub fn unlock(self: *Engine) void {
|
||||||
self.mutex.unlock(self.io);
|
self.rwlock.unlock(self.io);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shared lock: for read-only commands (find, count, aggregate, list*).
|
||||||
|
/// Multiple readers may hold it simultaneously; writers wait for them.
|
||||||
|
pub fn lock_read(self: *Engine) !void {
|
||||||
|
try self.rwlock.lockShared(self.io);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unlock_read(self: *Engine) void {
|
||||||
|
self.rwlock.unlockShared(self.io);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a document. Fails with error.DuplicateKey if the _id exists.
|
/// Insert a document. Fails with error.DuplicateKey if the _id exists.
|
||||||
@@ -247,7 +263,7 @@ pub const Engine = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Rewrite the log with only live documents, atomically swapping the file.
|
/// Rewrite the log with only live documents, atomically swapping the file.
|
||||||
/// Callers must hold the mutex.
|
/// Callers must hold the write lock.
|
||||||
pub fn compact(self: *Engine) !void {
|
pub fn compact(self: *Engine) !void {
|
||||||
const tmp_path = try std.fmt.allocPrint(self.gpa, "{s}.tmp", .{self.log.path});
|
const tmp_path = try std.fmt.allocPrint(self.gpa, "{s}.tmp", .{self.log.path});
|
||||||
defer self.gpa.free(tmp_path);
|
defer self.gpa.free(tmp_path);
|
||||||
|
|||||||
Reference in New Issue
Block a user