pub struct FactsCache { /* private fields */ }Expand description
A bounded cache of key projections, sized off the same max_keys as the
StatsTable it shadows, counting what the bound
costs (RFC 09 §5.1 O6).
Why this exists (issue #107). Projecting a key is not free — a
KeyFacts owns a String per subject chunk plus the resolved
SubjectFacts — so every observer caches it, and zengui’s cache was a
plain HashMap that grew one entry per distinct key ever seen. The engine’s
key table is bounded and counts its evictions; the projection cache shadowing
it was, in stats.rs’s own words, “merely a leak with better manners”.
Why an LRU and not “prune to the stats table”, which is the obvious fix:
- the table is fed from samples, and an observer also projects liveliness token keys, which never enter it. Pruning to the table would delete and re-project those on every tick, and drop them from any “keys seen” list;
- a frontend holds a key-tree snapshot, not a key list, so membership means walking the tree per tick — O(n) allocation on the render thread at up to 50k keys, where this is O(1) amortised on the insert path;
- “evicted because the table evicted it” and “evicted because it was never in the table” are different facts, and one counter over both is exactly what O6 forbids.
Recency is last-observed, not last-rendered, which is what keeps
get a pure read: a &self render path can look keys up
without touching the ordering, so no interior mutability and no signature
churn in the views.
The bound and the batch eviction are BoundedLru’s — shared with the
StatsTable this shadows, which is where the
argument for both was written. The ledger stays here: inserted /
evicted are this cache’s own facts, not the table’s (O6).
Implementations§
Source§impl FactsCache
impl FactsCache
Sourcepub fn with_capacity(max_keys: usize) -> FactsCache
pub fn with_capacity(max_keys: usize) -> FactsCache
A cache bounded at max_keys projections. Pass the same bound the
stats table was built with: the cache cannot usefully outgrow the table
it shadows, and one number makes that one sentence.
Sourcepub fn ensure(&mut self, base: &str, key: &str, slices: Option<&SliceSet>)
pub fn ensure(&mut self, base: &str, key: &str, slices: Option<&SliceSet>)
Project key if it is not cached yet; bump its recency either way.
The single insert point — the whole bound rests on that being true.
Sourcepub fn get(&self, key: &str) -> Option<&KeyFacts>
pub fn get(&self, key: &str) -> Option<&KeyFacts>
A cached projection, if it is still held. Pure: recency is not touched,
so this is safe to call from a &self render path.
pub fn keys(&self) -> impl Iterator<Item = &str>
Sourcepub fn iter(&self) -> impl Iterator<Item = (&str, &KeyFacts)>
pub fn iter(&self) -> impl Iterator<Item = (&str, &KeyFacts)>
Every held projection, keyed. Unordered (the map is a HashMap) —
callers that need determinism collect into an ordered structure, which
is what both doctor and field context builders do.
pub fn len(&self) -> usize
pub fn is_empty(&self) -> bool
pub fn max_keys(&self) -> usize
Sourcepub fn evicted(&self) -> u64
pub fn evicted(&self) -> u64
Projections retired to stay within the bound.
Displayed, never hidden: a cache that stopped growing and a bus that went quiet look identical from the outside (RFC 09 §5.1 O6).
Sourcepub fn inserted(&self) -> u64
pub fn inserted(&self) -> u64
Projections made since the last clear.
The other half of the O6 ledger, and the reason it is a public number
rather than an internal one: inserted == len() + evicted() is the
conservation law, and without this counter it cannot be checked from
outside. Note it counts insertions, not distinct keys — a key evicted
and later re-observed is projected again, which is precisely the cost
the bound is trading against.
Sourcepub fn resolve_all(&mut self, slices: &SliceSet)
pub fn resolve_all(&mut self, slices: &SliceSet)
Re-resolve every held projection against a newly-loaded slice set — what a registry arriving after the first samples calls for.
Trait Implementations§
Source§impl Debug for FactsCache
impl Debug for FactsCache
Auto Trait Implementations§
impl Freeze for FactsCache
impl RefUnwindSafe for FactsCache
impl Send for FactsCache
impl Sync for FactsCache
impl Unpin for FactsCache
impl UnsafeUnpin for FactsCache
impl UnwindSafe for FactsCache
Blanket Implementations§
Source§impl<Source> AccessAs for Source
impl<Source> AccessAs for Source
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
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> 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> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more