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}