commands: serverStatus reports what the slab is doing
A `multifora` subdocument -- named so nobody mistakes it for a MongoDB section -- carrying `liveBytes`, `deadBytes`, `slabBytes`, `reclaimedBytes`, `slabRuns`, `freeReadyPages`, `allocTail` and `compactions`. The milestone's gate cannot be read without them. A steady-state size ratio can look respectable while reclamation does nothing at all: the file grows, a rebuild periodically halves it, and the average comes out fine. What distinguishes the two is `reclaimedBytes` rising while `allocTail` stays put, and no ratio shows that. Same for rebuilds -- "the ratio improved" and "the ratio improved because reclamation worked rather than because a rebuild ran" are different results, so `compactions` counts collections rewritten. The byte figures are summed from the collections rather than read off the engine's running totals, so this reports the same side of the comparison `checkpoint` asserts. A counter that had drifted from the catalog would otherwise make the gate measure the drift. Every field is present at zero. A gate that cannot tell "no pages ready" from "field missing" cannot be read at all. 186/186 unit tests in ReleaseFast and ReleaseSafe, 83/83 fuzz, and the full e2e matrix.
This commit is contained in:
58
src/db.zig
58
src/db.zig
@@ -706,6 +706,11 @@ pub const Engine = struct {
|
||||
/// share one tmp path and each ends in a rename onto the log, so two at
|
||||
/// once would publish one compaction's half-written file as the database.
|
||||
compacting: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
|
||||
/// Collections rewritten by a rebuild since the process started. Reported by
|
||||
/// `serverStatus`, because "the ratio improved" and "the ratio improved
|
||||
/// because reclamation worked rather than because a rebuild ran" are
|
||||
/// different results and no ratio distinguishes them. Under `counter_lock`.
|
||||
compactions: u64 = 0,
|
||||
log: storage.Log,
|
||||
/// The data file: documents live here, and the B+tree arenas follow.
|
||||
///
|
||||
@@ -2017,6 +2022,59 @@ pub const Engine = struct {
|
||||
// would have been invalidated for nothing.
|
||||
self.layout_epoch_seq += 1;
|
||||
coll.layout_epoch = self.layout_epoch_seq;
|
||||
self.counter_lock.lockUncancelable(self.io);
|
||||
self.compactions += 1;
|
||||
self.counter_lock.unlock(self.io);
|
||||
}
|
||||
|
||||
/// What the slab is doing, for `serverStatus`. Collections under the same
|
||||
/// catalog-then-collection order everything else uses.
|
||||
///
|
||||
/// It exists because the milestone's own gate cannot be read without it. A
|
||||
/// steady-state size ratio can look healthy while reclamation does nothing
|
||||
/// -- the file grows, a rebuild periodically halves it, and the average
|
||||
/// comes out fine. `reclaimed_bytes` climbing while `alloc_tail` stays put
|
||||
/// is the shape that says the free list is load-bearing; either one alone
|
||||
/// says very little.
|
||||
pub const SlabStats = struct {
|
||||
live_bytes: u64 = 0,
|
||||
dead_bytes: u64 = 0,
|
||||
slab_bytes: u64 = 0,
|
||||
reclaimed_bytes: u64 = 0,
|
||||
slab_runs: u64 = 0,
|
||||
free_ready_pages: u32 = 0,
|
||||
alloc_tail: u32 = 0,
|
||||
compactions: u64 = 0,
|
||||
};
|
||||
|
||||
pub fn slab_stats(self: *Engine) SlabStats {
|
||||
var out: SlabStats = .{};
|
||||
self.catalog_lock.lockSharedUncancelable(self.io);
|
||||
var db_it = self.dbs.iterator();
|
||||
while (db_it.next()) |db_entry| {
|
||||
var coll_it = db_entry.value_ptr.collections.iterator();
|
||||
while (coll_it.next()) |ce| {
|
||||
const coll = ce.value_ptr.*;
|
||||
coll.lock.lockSharedUncancelable(self.io);
|
||||
defer coll.lock.unlockShared(self.io);
|
||||
out.live_bytes += coll.live_bytes;
|
||||
out.slab_bytes += coll.slab_used;
|
||||
out.reclaimed_bytes += coll.reclaimed_bytes;
|
||||
out.slab_runs += coll.slab_runs.items.len;
|
||||
}
|
||||
}
|
||||
self.catalog_lock.unlockShared(self.io);
|
||||
// From the collections rather than the engine's running total, so this
|
||||
// is the same figure `write_catalog` asserts against rather than a
|
||||
// second opinion about it.
|
||||
assert_msg(out.slab_bytes >= out.live_bytes, "the slab cannot hold more live bytes than it has");
|
||||
out.dead_bytes = out.slab_bytes - out.live_bytes;
|
||||
out.free_ready_pages = self.pager.free_ready_pages();
|
||||
out.alloc_tail = self.pager.alloc_tail;
|
||||
self.counter_lock.lockUncancelable(self.io);
|
||||
out.compactions = self.compactions;
|
||||
self.counter_lock.unlock(self.io);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// The engine's dead-byte total, recomputed from the collections that
|
||||
|
||||
Reference in New Issue
Block a user