Skip to main content

coil_cache/backend/
client.rs

1use std::fmt;
2use std::sync::Arc;
3
4use crate::{
5    CacheEntry, CacheInstant, CacheKey, CacheLookup, CacheMetrics, CacheModelError, FillDecision,
6    FillLease, InvalidationSet, RequestCoalescingMode,
7};
8
9use super::CacheBackendKind;
10
11pub trait DistributedCacheRuntime: Send + Sync + 'static {
12    fn insert(&self, entry: CacheEntry);
13    fn lookup(&self, key: &CacheKey, now: CacheInstant) -> CacheLookup;
14    fn invalidate(&self, tags: &InvalidationSet) -> Vec<CacheKey>;
15    fn begin_fill(
16        &self,
17        key: &CacheKey,
18        mode: RequestCoalescingMode,
19        holder: String,
20    ) -> FillDecision;
21    fn complete_fill(&self, lease: &FillLease) -> Result<(), CacheModelError>;
22    fn metrics(&self) -> CacheMetrics;
23    fn is_shared_backend(&self) -> bool {
24        true
25    }
26    fn supports_live_shared_state(&self) -> bool {
27        false
28    }
29}
30
31#[derive(Clone)]
32pub struct DistributedCacheClient {
33    kind: CacheBackendKind,
34    shared: bool,
35    runtime: Arc<dyn DistributedCacheRuntime>,
36}
37
38impl DistributedCacheClient {
39    pub fn new(kind: CacheBackendKind, runtime: Arc<dyn DistributedCacheRuntime>) -> Self {
40        Self::with_runtime(kind, runtime)
41    }
42
43    pub fn with_runtime(kind: CacheBackendKind, runtime: Arc<dyn DistributedCacheRuntime>) -> Self {
44        Self::with_shared_runtime(kind, runtime)
45    }
46
47    pub fn with_shared_runtime(
48        kind: CacheBackendKind,
49        runtime: Arc<dyn DistributedCacheRuntime>,
50    ) -> Self {
51        Self {
52            kind,
53            shared: runtime.is_shared_backend(),
54            runtime,
55        }
56    }
57
58    pub fn live_shared_runtime(
59        kind: CacheBackendKind,
60        namespace: impl Into<String>,
61        root: impl Into<std::path::PathBuf>,
62    ) -> Arc<dyn DistributedCacheRuntime> {
63        super::live::live_shared_runtime(kind, namespace, root)
64    }
65
66    pub(crate) fn unavailable_shared_runtime(
67        kind: CacheBackendKind,
68    ) -> Arc<dyn DistributedCacheRuntime> {
69        Arc::new(ExplicitDistributedCacheRuntimeRequired { kind })
70    }
71
72    #[doc(hidden)]
73    pub fn emulated_shared_runtime(kind: CacheBackendKind) -> Arc<dyn DistributedCacheRuntime> {
74        super::testing::test_only_sqlite_shared_runtime(
75            kind,
76            super::testing::test_scope_namespace(),
77        )
78    }
79
80    #[cfg(test)]
81    pub fn test_only_sqlite_shared_runtime(
82        kind: CacheBackendKind,
83        namespace: impl Into<String>,
84    ) -> Arc<dyn DistributedCacheRuntime> {
85        super::testing::test_only_sqlite_shared_runtime(kind, namespace.into())
86    }
87
88    #[cfg(test)]
89    #[doc(hidden)]
90    pub fn local_for_testing(kind: CacheBackendKind) -> Self {
91        Self::with_shared_runtime(kind, Self::emulated_shared_runtime(kind))
92    }
93
94    #[cfg(test)]
95    #[doc(hidden)]
96    #[deprecated(
97        note = "compatibility shim; behaves like local_for_testing(kind). use with_shared_runtime(kind, runtime) or local_for_testing(kind)"
98    )]
99    pub fn shared(kind: CacheBackendKind) -> Self {
100        Self::local_for_testing(kind)
101    }
102
103    #[cfg(test)]
104    #[doc(hidden)]
105    #[deprecated(
106        note = "compatibility shim; behaves like local_for_testing(kind). use with_shared_runtime(kind, runtime) or local_for_testing(kind)"
107    )]
108    pub fn scoped_shared(kind: CacheBackendKind, _scope: impl Into<String>) -> Self {
109        Self::local_for_testing(kind)
110    }
111
112    pub fn kind(&self) -> CacheBackendKind {
113        self.kind
114    }
115
116    pub fn is_shared(&self) -> bool {
117        self.shared
118    }
119
120    pub fn insert(&self, entry: CacheEntry) {
121        self.runtime.insert(entry);
122    }
123
124    pub fn lookup(&self, key: &CacheKey, now: CacheInstant) -> CacheLookup {
125        self.runtime.lookup(key, now)
126    }
127
128    pub fn invalidate(&self, tags: &InvalidationSet) -> Vec<CacheKey> {
129        self.runtime.invalidate(tags)
130    }
131
132    pub fn begin_fill(
133        &self,
134        key: &CacheKey,
135        mode: RequestCoalescingMode,
136        holder: impl Into<String>,
137    ) -> FillDecision {
138        self.runtime.begin_fill(key, mode, holder.into())
139    }
140
141    pub fn complete_fill(&self, lease: &FillLease) -> Result<(), CacheModelError> {
142        self.runtime.complete_fill(lease)
143    }
144
145    pub fn metrics(&self) -> CacheMetrics {
146        self.runtime.metrics()
147    }
148}
149
150impl fmt::Debug for DistributedCacheClient {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        f.debug_struct("DistributedCacheClient")
153            .field("kind", &self.kind)
154            .finish()
155    }
156}
157
158#[derive(Debug)]
159struct ExplicitDistributedCacheRuntimeRequired {
160    kind: CacheBackendKind,
161}
162
163impl ExplicitDistributedCacheRuntimeRequired {
164    fn panic(&self) -> ! {
165        panic!(
166            "distributed cache backend `{kind:?}` requires an explicit shared runtime",
167            kind = self.kind
168        )
169    }
170}
171
172impl DistributedCacheRuntime for ExplicitDistributedCacheRuntimeRequired {
173    fn insert(&self, _entry: CacheEntry) {
174        self.panic();
175    }
176
177    fn lookup(&self, _key: &CacheKey, _now: CacheInstant) -> CacheLookup {
178        self.panic();
179    }
180
181    fn invalidate(&self, _tags: &InvalidationSet) -> Vec<CacheKey> {
182        self.panic();
183    }
184
185    fn begin_fill(
186        &self,
187        _key: &CacheKey,
188        _mode: RequestCoalescingMode,
189        _holder: String,
190    ) -> FillDecision {
191        self.panic();
192    }
193
194    fn complete_fill(&self, _lease: &FillLease) -> Result<(), CacheModelError> {
195        self.panic();
196    }
197
198    fn metrics(&self) -> CacheMetrics {
199        CacheMetrics::default()
200    }
201
202    fn is_shared_backend(&self) -> bool {
203        false
204    }
205}