#[non_exhaustive]pub struct MetricsSnapshot {
pub turns: u64,
pub depth_samples: u64,
pub high_depth_mean: f64,
pub high_depth_max: u64,
pub low_depth_mean: f64,
pub low_depth_max: u64,
pub longest: Option<(CommandKind, Duration)>,
pub low_starved_turns: u64,
pub low_starved_run_max: u64,
pub kinds: Vec<KindSnapshot>,
}Expand description
What the actor has done since the database was opened.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.turns: u64Commands executed, i.e. the sum of KindSnapshot::turns. The two agree
by construction rather than by coincidence.
depth_samples: u64Loop iterations that took a queue-depth reading. Always at least
turns + 1 on a live actor, because the reading is taken on the way in
to a select! that has not resolved yet. This is the denominator of the
two means below, and it is exposed so the difference is visible rather
than looking like drift.
high_depth_mean: f64§high_depth_max: u64§low_depth_mean: f64§low_depth_max: u64§longest: Option<(CommandKind, Duration)>The longest hold since open and what caused it. None before the first
turn, and — honestly — also when every turn so far took under a
microsecond, which on this path does not happen.
low_starved_turns: u64Turns spent on high-priority work while low-priority work was already queued (0.12.10, W4.4, D-153).
The actor’s select! is biased and has no floor, so this is the
measurement of a bound the design has always had and never observed.
On its own it is not alarming: a busy database should prefer
interactive writes, and this counter rising is that working. Read it
beside Self::low_starved_run_max, which is the number with teeth.
low_starved_run_max: u64The longest unbroken run of the above — i.e. the most turns any single low-priority command has waited (0.12.10, W4.4, D-153).
This is the one that answers “can low-priority work be starved”. A large
low_starved_turns spread over a long session says the tiers are doing
their job; a large run says one specific chunk, rebuild or archive sat
behind that many interactive writes in a row.
§There is deliberately no forced-yield policy, and the reason changed
(0.13.26, W10.4, D-199)
It used to be “adding one now would be fixing a bound nobody has
observed being hit”. That premise died twice. D-153 hit the bound
completely on a synthetic burst, and W10.4 then hit it on an ordinary
one: four closed-loop writers — each awaiting its own write before
issuing the next, which is what application code does — starve the low
tier for essentially all of their writes
(examples/fairness_probe.rs). The run is bounded by how long the
caller keeps offering interactive work, not by concurrency and not by
anything in this crate.
What replaced it is the floor’s own price. “After N starved turns,
take one low-priority command” cannot choose which command — the low
queue is an mpsc channel and its head is not inspectable — and at least
one low-priority kind is exempt from crate::CHUNK_BUDGET by
contract: an crate::Database::archive was measured at 3.3 s
unwindowed on an 8,000-key backlog. So the floor would add an unbounded
term to the interactive worst case in order to unblock work that is
declared not to be latency-sensitive, which is the tier split running
backwards.
The lever that does work belongs to the caller: 1 ms of think time between a writer’s writes takes four writers from ~78 to ~2. Which makes this field the instrument for a decision the caller owns rather than a defect report about the actor.
kinds: Vec<KindSnapshot>Implementations§
Source§impl MetricsSnapshot
impl MetricsSnapshot
Sourcepub fn budget_violations(&self) -> Vec<&KindSnapshot>
pub fn budget_violations(&self) -> Vec<&KindSnapshot>
Kinds that broke the budget, worst first. The one-line answer to “is the 3 ms bound holding?”.
Trait Implementations§
Source§impl Clone for MetricsSnapshot
impl Clone for MetricsSnapshot
Source§fn clone(&self) -> MetricsSnapshot
fn clone(&self) -> MetricsSnapshot
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for MetricsSnapshot
impl Debug for MetricsSnapshot
Source§impl PartialEq for MetricsSnapshot
impl PartialEq for MetricsSnapshot
impl StructuralPartialEq for MetricsSnapshot
Auto Trait Implementations§
impl Freeze for MetricsSnapshot
impl RefUnwindSafe for MetricsSnapshot
impl Send for MetricsSnapshot
impl Sync for MetricsSnapshot
impl Unpin for MetricsSnapshot
impl UnsafeUnpin for MetricsSnapshot
impl UnwindSafe for MetricsSnapshot
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request