db/pager: the concurrency test writes the way the server does
"a checkpoint runs alongside writers on several collections" drove its writers through `Engine.lock()` -- the legacy whole-engine lock, which the server has not used since the locks were decomposed. That serialized the writers against each other, so the overlap the test is named for never happened: `write_catalog` takes each collection's lock shared, and nothing it was racing against took that lock at all. Drive them the way `commands.zig` dispatch does instead: catalog shared, then the target collection exclusive. The test then does what it says, and immediately found something -- two appenders on different collections calling `bytes_mut` at the same time corrupt the pager's `dirty` set, which is an unsynchronized hash map. ReleaseSafe aborts in `getOrPutContextAdapted`; three runs in five. `dirty` is test-only instrumentation (`track_dirty = builtin.is_test`), so this is a harness bug rather than a server one -- but it is the one shared structure on a write path whose writers are otherwise kept apart by owning different pages, and it needs a lock of its own. Six ReleaseSafe runs clean afterwards. 163/163 unit tests in ReleaseFast and ReleaseSafe, 82/82 fuzz.
This commit is contained in:
15
src/db.zig
15
src/db.zig
@@ -3146,8 +3146,19 @@ test "a checkpoint runs alongside writers on several collections" {
|
||||
var doc = make_doc(alloc, @intCast(i), "user") catch return error.Canceled;
|
||||
defer doc.deinit();
|
||||
{
|
||||
e.lock() catch return error.Canceled;
|
||||
defer e.unlock();
|
||||
// The server's discipline, not the legacy whole-engine
|
||||
// lock: catalog shared, then the target collection
|
||||
// exclusive (commands.zig dispatch). `write_catalog` takes
|
||||
// the same two in the same order, and that is the whole
|
||||
// reason its walk of a collection's counters and extents is
|
||||
// safe -- a writer that skipped the collection lock would
|
||||
// not be excluded by it, and the test would be checking
|
||||
// nothing.
|
||||
e.lock_catalog(false) catch return error.Canceled;
|
||||
defer e.unlock_catalog(false);
|
||||
const coll = (e.lock_collection("app", name, true, true) catch
|
||||
return error.Canceled) orelse return error.Canceled;
|
||||
defer e.unlock_collection(coll, true);
|
||||
e.insert("app", name, &doc, undefined) catch return error.Canceled;
|
||||
}
|
||||
// As the dispatch epilogue does (commands.zig): the append bumps
|
||||
|
||||
Reference in New Issue
Block a user