pub struct ArticleCache { /* private fields */ }Expand description
Article cache using LRU eviction with TTL
Uses Arc<str> (message ID content without brackets) as key for zero-allocation lookups.
Arc<str> implements Borrow<str>, allowing cache.get(&str) without allocation.
Supports tier-aware TTL: entries from higher tier backends get longer TTLs.
Formula: effective_ttl = base_ttl * (2 ^ tier)
Implementations§
Source§impl ArticleCache
impl ArticleCache
Sourcepub fn new(max_capacity: u64, ttl: Duration) -> Self
pub fn new(max_capacity: u64, ttl: Duration) -> Self
Create a new article cache
§Arguments
max_capacity- Maximum cache size in bytes (uses weighted entries)ttl- Time-to-live for cached articles
Sourcepub async fn get(&self, message_id: &MessageId<'_>) -> Option<CachedArticle>
pub async fn get(&self, message_id: &MessageId<'_>) -> Option<CachedArticle>
Get an article from the cache
Accepts any lifetime MessageId and uses the string content (without brackets) as key.
Zero-allocation: without_brackets() returns &str, which moka accepts directly
for Arc<str> keys via the Borrow<str> trait. This avoids allocating a new Arc<str>
for every cache lookup. See test_arc_str_borrow_lookup test for verification.
Tier-aware TTL: Even if moka hasn’t expired the entry yet, we check if the entry is expired based on tier-aware TTL. Higher tier entries get longer TTLs.
Sourcepub async fn upsert_ingest(
&self,
message_id: MessageId<'_>,
buffer: impl Into<CacheIngestResponse>,
backend: BackendId,
tier: CacheTier,
)
pub async fn upsert_ingest( &self, message_id: MessageId<'_>, buffer: impl Into<CacheIngestResponse>, backend: BackendId, tier: CacheTier, )
Upsert cache entry (insert or update) - ATOMIC OPERATION
Uses moka’s entry().and_upsert_with() for atomic get-modify-store.
This eliminates the race condition of separate get() + insert() calls
and provides key-level locking for concurrent operations.
If entry exists: updates the entry while preserving authoritative missing facts If entry doesn’t exist: inserts new entry
The tier is stored with the entry for tier-aware TTL calculation.
CRITICAL: Always re-insert to refresh TTL while preserving negative availability.
Sourcepub async fn record_backend_has_status(
&self,
message_id: MessageId<'_>,
status_code: StatusCode,
backend: BackendId,
tier: CacheTier,
)
pub async fn record_backend_has_status( &self, message_id: MessageId<'_>, status_code: StatusCode, backend: BackendId, tier: CacheTier, )
Record successful backend availability without storing response payload bytes.
Sourcepub async fn record_backend_missing(
&self,
message_id: MessageId<'_>,
backend_id: BackendId,
)
pub async fn record_backend_missing( &self, message_id: MessageId<'_>, backend_id: BackendId, )
Record that a backend returned 430 for this article - ATOMIC OPERATION
Uses moka’s entry().and_upsert_with() for atomic get-modify-store.
This eliminates the race condition of separate get() + insert() calls
and provides key-level locking for concurrent operations.
If the article is already cached, updates the availability bitset. If not cached, creates a typed missing cache entry. This prevents repeated queries to backends that don’t have the article.
Note: We don’t store the actual backend 430 response because:
- We always send a standardized 430 to clients, never the backend’s response
- The only info we need is the availability bitset (which backends returned 430)
Sourcepub fn entry_count(&self) -> u64
pub fn entry_count(&self) -> u64
Get current number of cached entries (synchronous)
Sourcepub fn weighted_size(&self) -> u64
pub fn weighted_size(&self) -> u64
Get current weighted size in bytes (synchronous)
Trait Implementations§
Source§impl CacheStatsProvider for ArticleCache
impl CacheStatsProvider for ArticleCache
Source§fn display_stats(&self) -> CacheDisplayStats
fn display_stats(&self) -> CacheDisplayStats
Source§impl Clone for ArticleCache
impl Clone for ArticleCache
Source§fn clone(&self) -> ArticleCache
fn clone(&self) -> ArticleCache
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for ArticleCache
impl !UnwindSafe for ArticleCache
impl Freeze for ArticleCache
impl Send for ArticleCache
impl Sync for ArticleCache
impl Unpin for ArticleCache
impl UnsafeUnpin for ArticleCache
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
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> 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