Skip to main content

coil_cache/backend/
mod.rs

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