#[non_exhaustive]#[repr(u8)]pub enum CommandKind {
Show 17 variants
AssertEdge = 0,
RetireEdge = 1,
UpsertConcept = 2,
WriteBulkAtomic = 3,
RebuildCurrent = 4,
RegisterModel = 5,
Shutdown = 6,
BulkImportChunk = 7,
WriteConceptsChunk = 8,
WriteAnalyticsChunk = 9,
UpsertEmbeddingChunk = 10,
Archive = 11,
RebuildFts = 12,
ShadowRebuild = 13,
Analyze = 14,
Rehydrate = 15,
Checkpoint = 16,
}Expand description
The command kinds the actor can spend a turn on.
One flat enum across both channels rather than one per channel. The question this exists to answer is “which command broke the budget”, and a reader looking at a 400 ms hold does not first want to know which queue it came off. Priority is a property of scheduling; kind is a property of cost.
§#[non_exhaustive], added while it was still free (0.12.8, W4.2)
Adding a variant here is a breaking change without this attribute,
because a downstream match on CommandKind would stop compiling. That is
not hypothetical for this enum: crate::metrics::CommandKind::Rehydrate
did not exist until 0.12.9 precisely because adding it was a break, and
rehydration reported as Archive for several releases as a result. The
codebase has already paid this cost once, which is the argument for paying
the attribute now rather than deciding it at 1.0 when the cost is permanent.
Callers must therefore include a _ => arm. In exchange, this enum can grow
a variant for a command kind that does not exist yet without a major version.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
AssertEdge = 0
RetireEdge = 1
UpsertConcept = 2
WriteBulkAtomic = 3
RebuildCurrent = 4
RegisterModel = 5
Shutdown = 6
BulkImportChunk = 7
WriteConceptsChunk = 8
WriteAnalyticsChunk = 9
UpsertEmbeddingChunk = 10
Archive = 11
RebuildFts = 12
ShadowRebuild = 13
One step of a chunked shadow rebuild (T1.2). Its own kind rather than
folded into RebuildCurrent, because the two have opposite latency
profiles and the whole point of the chunked path is that its turns are
short — averaging them together would hide exactly the improvement.
Analyze = 14
Refreshing the query planner’s statistics (0.12.4, D-149).
Its own kind rather than folded into RebuildFts, though both are
maintenance on derived state: this one is bounded by
PRAGMA analysis_limit and that one is bounded by the size of the
concept table, so averaging their holds together would describe neither.
Rehydrate = 15
Moving archived rows back into the hot file (0.12.9, W4.3, D-152).
Its own kind at last. Through 0.12.8 this reported as
CommandKind::Archive, on the stated ground that rehydration is the
archive path run backwards and shares its budget — true of the budget
and false of the attribution, which is what a metrics surface is for.
An operator reading a long archive hold could not tell whether the
database had archived anything at all, and the two move rows in opposite
directions.
The real reason it stayed folded was that adding a variant was a
breaking change. #[non_exhaustive] (W4.2) is what removed that
obstacle, and this variant is the first thing it bought — which is also
the evidence that the attribute was worth adding rather than a
precaution against a hypothetical.
Appended at the end, per CommandKind::index: the position of
every existing variant is a persisted contract in two languages.
Checkpoint = 16
An explicit PRAGMA wal_checkpoint (0.12.13, W5.2, D-156).
Its own kind because it is the one actor turn that is not a transaction: it moves frames from the WAL back into the main database file, and its duration is a function of how much WAL has accumulated rather than of anything the caller passed. Folding it into any existing kind would make that kind’s hold distribution bimodal for a reason no dashboard could recover.
Appended at the end, per CommandKind::index.
Implementations§
Source§impl CommandKind
impl CommandKind
Sourcepub const ALL: &'static [CommandKind]
pub const ALL: &'static [CommandKind]
Every kind, in declaration order. Indexing into the per-kind arrays is by
position in this slice, so the two must not drift — which is why the
arrays are sized from ALL.len() rather than from a hand-written count.
pub const COUNT: usize
Sourcepub const fn index(self) -> usize
pub const fn index(self) -> usize
This kind’s slot in the per-kind arrays.
§Declaration order is a persisted contract (0.12.8, W4.2)
self as usize means the order of the variants above is the order of
every per-kind array in this module, and the compiler cannot catch a
change to it. Reordering the enum silently reassigns every counter to a
different command: the code compiles, the tests pass, and a histogram
read after the change attributes archive’s holds to rebuild_fts.
New variants go at the end, always — including at the end of
CommandKind::ALL, whose order is what as_str() and the Python
surface enumerate. This binds Python too: BUCKET_BOUNDS_MICROS is a
module constant there and KindMetrics is built by position, so a
reorder here relabels axes in a language the Rust compiler is not
looking at.
#[repr(u8)] is on the enum for the same reason — it pins the
discriminants to the declaration order rather than leaving them to the
compiler — but it pins them to whatever the order is, so it does not
make a reorder safe. Only this rule does.
pub const fn as_str(self) -> &'static str
Sourcepub const fn exempt_from_budget(self) -> bool
pub const fn exempt_from_budget(self) -> bool
Whether this kind is exempt from crate::CHUNK_BUDGET by contract.
The exemptions are the table in CHUNK_BUDGET’s own rustdoc, and they
are carried here so a dashboard can separate “the budget is being
broken” from “the budget does not apply and never claimed to”. Counting
an archive as a budget violation would make the violation count useless
on any database that archives.
The two lists must agree, and since 0.12.9 they are tied together in
both directions by the_budget_exemptions_and_their_documented_table_agree
— the extra-row direction being the one worth having, since a table row
with no code behind it promises a caller an exemption the violation
counter is about to disagree with.
CommandKind::ShadowRebuild is deliberately not exempt. Its fill
chunks are meant to fit the budget and its swap turn is not going to —
the swap rebuilds three indexes under the lock, which is the residual
cost T1.2 could not remove. Both facts are worth seeing, and exempting
the kind would hide the first to excuse the second.
§Rehydrate is exempt, and splitting it out is what made that a
decision rather than an accident (0.12.9, W4.3, D-152)
Until 0.12.8 rehydration reported as CommandKind::Archive and was
therefore exempt by inheritance — nobody had decided it, it fell out
of the borrowed kind. Giving it its own variant would have silently
flipped it to non-exempt, and since a rehydrate is one unchunked
transaction moving rows back across the file boundary, every single one
would have counted as a budget violation. The violation count would then
have become useless on any database that rehydrates, which is precisely
the failure the Archive exemption exists to prevent, arriving by the
back door of a change made for attribution.
So it is exempt, on the merits and now on the record: rehydration is the archive path run backwards and makes the same claim about its hold — that it is bulk movement with no latency bound, and that the caller asked for it explicitly.
§CommandKind::Analyze is not exempt, and that is a decision
(0.12.25, D-168)
It looks like it belongs here. ANALYZE is one indivisible statement —
there is no smaller unit to chunk into — and its cost is set by data
volume, so it cannot meet the budget on a populated ledger. Measured
(examples/analyze_hold.rs): 5.26 ms at 10,000 edges, 19.1 ms at
40,000, against 3 ms. Every call is a violation and always will be.
It stays counted for two reasons.
The table has a Bound column, and this kind cannot fill it in.
Checkpoint’s bound is frames accumulated since the last one;
Archive’s is the session’s row count. The honest entry here would be
“the size of the table, damped 3–4× by analysis_limit”, which is not a
bound but the absence of one. A row that cannot state its bound is this
table admitting the thing it exists to prevent.
And this kind is two callers wearing one name. Analyze covers
crate::Database::optimize as well as crate::Database::analyze,
and close() calls optimize() unconditionally. Exempting the kind
would silence the automatic path — every handle close on a large
ledger holding ~19 ms with nothing reporting it — which is exactly the
call nobody chose to make. That is CommandKind::Rehydrate’s lesson
above, arriving from the other direction: there, a shared kind granted
an exemption nobody had decided; here, a shared kind would launder one.
So the violation is expected, permanent, and must not be “fixed” by
lowering crate::schema::ddl::ANALYSIS_LIMIT. That would buy the
number by sampling too little to separate the two source_id-leading
indices, which is the entire purpose of having statistics
(D-149).
Splitting the kind is scheduled as W10.5, 0.14.0; the exemption question is answerable per-caller once it is split, and not before.
Trait Implementations§
Source§impl Clone for CommandKind
impl Clone for CommandKind
Source§fn clone(&self) -> CommandKind
fn clone(&self) -> CommandKind
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for CommandKind
Source§impl Debug for CommandKind
impl Debug for CommandKind
Source§impl Display for CommandKind
impl Display for CommandKind
impl Eq for CommandKind
Source§impl Hash for CommandKind
impl Hash for CommandKind
Source§impl Ord for CommandKind
impl Ord for CommandKind
Source§fn cmp(&self, other: &CommandKind) -> Ordering
fn cmp(&self, other: &CommandKind) -> Ordering
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl PartialEq for CommandKind
impl PartialEq for CommandKind
Source§impl PartialOrd for CommandKind
impl PartialOrd for CommandKind
impl StructuralPartialEq for CommandKind
Auto Trait Implementations§
impl Freeze for CommandKind
impl RefUnwindSafe for CommandKind
impl Send for CommandKind
impl Sync for CommandKind
impl Unpin for CommandKind
impl UnsafeUnpin for CommandKind
impl UnwindSafe for CommandKind
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<Q, K> Comparable<K> for Q
impl<Q, K> Comparable<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.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