coil_cache/backend/
client.rs1use 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}