Skip to main content

tenferro_runtime/runtime/
cache_owner.rs

1use std::error::Error;
2use std::fmt;
3use std::sync::Arc;
4
5use super::identity::validate_identifier;
6use super::{IdentityError, IdentityKind};
7
8/// Validated runtime cache-owner identifier.
9///
10/// # Examples
11///
12/// ```
13/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
14/// use tenferro_runtime::runtime::CacheOwnerId;
15///
16/// assert_eq!(CacheOwnerId::new("tenferro.cache.owner")?.as_str(), "tenferro.cache.owner");
17/// # Ok(())
18/// # }
19/// ```
20#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
21pub struct CacheOwnerId(Arc<str>);
22
23impl CacheOwnerId {
24    /// Validate a namespaced ASCII cache-owner identifier.
25    ///
26    /// # Errors
27    ///
28    /// Returns [`IdentityError`] when `value` does not match the runtime
29    /// identifier grammar.
30    pub fn new(value: impl Into<Arc<str>>) -> Result<Self, IdentityError> {
31        validate_identifier(value.into(), IdentityKind::CacheOwner).map(Self)
32    }
33
34    /// Borrow the validated identifier text.
35    pub fn as_str(&self) -> &str {
36        &self.0
37    }
38
39    pub(super) fn from_canonical_owner_id(value: Arc<str>) -> Self {
40        Self(value)
41    }
42}
43
44/// Aggregate cache statistics reported by one runtime cache owner.
45///
46/// # Examples
47///
48/// ```
49/// use tenferro_runtime::runtime::CacheStats;
50///
51/// assert_eq!(CacheStats::default().entries, 0);
52/// ```
53#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
54pub struct CacheStats {
55    /// Number of retained cache entries.
56    pub entries: usize,
57    /// Logical retained bytes.
58    pub retained_bytes: usize,
59    /// Cache hits.
60    pub hits: u64,
61    /// Cache misses.
62    pub misses: u64,
63    /// Cache evictions.
64    pub evictions: u64,
65    /// Explicit clears.
66    pub clears: u64,
67}
68
69/// Runtime-owned cache participant.
70pub trait RuntimeCacheOwner: fmt::Debug + Send + Sync + 'static {
71    /// Return this owner's current cache statistics.
72    ///
73    /// # Errors
74    ///
75    /// Returns [`CacheOwnerError`] when the owner cannot report stats.
76    fn cache_stats(&self) -> Result<CacheStats, CacheOwnerError>;
77
78    /// Clear this owner's retained caches.
79    ///
80    /// # Errors
81    ///
82    /// Returns [`CacheOwnerError`] when the owner cannot clear its caches.
83    fn clear_caches(&self) -> Result<(), CacheOwnerError>;
84}
85
86/// Cloneable typed cache-owner failure source.
87#[derive(Clone)]
88pub struct CacheOwnerError {
89    source: Arc<dyn Error + Send + Sync>,
90}
91
92impl CacheOwnerError {
93    /// Wrap a typed cache-owner failure source.
94    pub fn new(source: Arc<dyn Error + Send + Sync>) -> Self {
95        Self { source }
96    }
97
98    /// Return the original shared source.
99    pub fn source_arc(&self) -> &Arc<dyn Error + Send + Sync> {
100        &self.source
101    }
102}
103
104impl fmt::Debug for CacheOwnerError {
105    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
106        formatter
107            .debug_struct("CacheOwnerError")
108            .field("source", &self.source.to_string())
109            .finish()
110    }
111}
112
113impl fmt::Display for CacheOwnerError {
114    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(formatter, "{}", self.source)
116    }
117}
118
119impl Error for CacheOwnerError {
120    fn source(&self) -> Option<&(dyn Error + 'static)> {
121        Some(self.source.as_ref())
122    }
123}
124
125/// Failure reported by one named runtime cache owner.
126#[derive(Clone, Debug)]
127pub struct CacheOwnerFailure {
128    /// Cache owner that failed.
129    pub owner: CacheOwnerId,
130    /// Typed owner source.
131    pub source: CacheOwnerError,
132}
133
134#[derive(Clone, Copy, Debug)]
135pub(super) enum FrozenCacheOwnerKind {
136    Engine,
137    Extension,
138}
139
140#[derive(Clone)]
141pub(super) struct FrozenCacheOwner {
142    pub(super) id: CacheOwnerId,
143    pub(super) kind: FrozenCacheOwnerKind,
144    pub(super) owner: Arc<dyn RuntimeCacheOwner>,
145}
146
147impl fmt::Debug for FrozenCacheOwner {
148    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
149        formatter
150            .debug_struct("FrozenCacheOwner")
151            .field("id", &self.id)
152            .field("kind", &self.kind)
153            .field("owner_strong_count", &Arc::strong_count(&self.owner))
154            .finish()
155    }
156}
157
158/// Runtime state failure shared by preparation and cache management.
159#[derive(Debug, thiserror::Error)]
160pub enum RuntimeStateError {
161    /// A synchronization primitive was poisoned by a panic in another thread.
162    #[error("{lock} poisoned")]
163    Poisoned {
164        /// Static lock name.
165        lock: &'static str,
166    },
167}
168
169/// Aggregated runtime cache-management failure.
170#[derive(Debug, thiserror::Error)]
171pub enum RuntimeCacheError {
172    /// Runtime and/or registered cache owners failed.
173    #[error("runtime cache operation failed")]
174    Aggregate {
175        /// Runtime state failure, if one occurred.
176        runtime: Option<RuntimeStateError>,
177        /// Owner failures in deterministic owner order.
178        owners: Box<[CacheOwnerFailure]>,
179    },
180}