Skip to main content

coil_cache/
plan.rs

1use crate::{
2    ApplicationCachePolicy, CacheKey, CacheModelError, CacheNamespace, CacheScope, CacheTopology,
3    DistributedCacheBackend, FreshnessPolicy, HttpCachePolicy, InvalidationSet,
4    RequestCoalescingMode, ResponseValidators, VariationKey,
5};
6use std::sync::Arc;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct CachePlanRequest {
10    namespace: CacheNamespace,
11    resource: String,
12    application_policy: Option<ApplicationCachePolicy>,
13    http_policy: HttpCachePolicy,
14    request_coalescing_mode: Option<RequestCoalescingMode>,
15}
16
17impl CachePlanRequest {
18    pub fn new(
19        namespace: CacheNamespace,
20        resource: impl Into<String>,
21        http_policy: HttpCachePolicy,
22    ) -> Result<Self, CacheModelError> {
23        Ok(Self {
24            namespace,
25            resource: crate::types::require_non_empty("cache_resource", resource.into())?,
26            application_policy: None,
27            http_policy,
28            request_coalescing_mode: None,
29        })
30    }
31
32    pub fn with_application_policy(mut self, policy: ApplicationCachePolicy) -> Self {
33        self.application_policy = Some(policy);
34        self
35    }
36
37    pub fn with_request_coalescing_mode(mut self, mode: RequestCoalescingMode) -> Self {
38        self.request_coalescing_mode = Some(mode);
39        self
40    }
41
42    pub fn request_coalescing_mode(&self) -> Option<RequestCoalescingMode> {
43        self.request_coalescing_mode
44    }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48pub struct CacheLayerPlan {
49    pub l1: crate::LocalCacheBackend,
50    pub l2: Option<DistributedCacheBackend>,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct ApplicationCachePlan {
55    key: CacheKey,
56    scope: CacheScope,
57    freshness: FreshnessPolicy,
58    tags: InvalidationSet,
59    layers: CacheLayerPlan,
60    coalescing: RequestCoalescingMode,
61}
62
63impl ApplicationCachePlan {
64    pub fn key(&self) -> &CacheKey {
65        &self.key
66    }
67
68    pub fn scope(&self) -> &CacheScope {
69        &self.scope
70    }
71
72    pub fn freshness(&self) -> FreshnessPolicy {
73        self.freshness
74    }
75
76    pub fn tags(&self) -> &InvalidationSet {
77        &self.tags
78    }
79
80    pub fn layers(&self) -> &CacheLayerPlan {
81        &self.layers
82    }
83
84    pub fn coalescing(&self) -> RequestCoalescingMode {
85        self.coalescing
86    }
87
88    pub fn shared_invalidation(&self) -> bool {
89        self.layers.l2.is_some()
90    }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct HttpCachePlan {
95    scope: CacheScope,
96    variation: Option<VariationKey>,
97    validators: ResponseValidators,
98    surrogate_tags: InvalidationSet,
99    cache_control: String,
100}
101
102impl HttpCachePlan {
103    pub fn scope(&self) -> &CacheScope {
104        &self.scope
105    }
106
107    pub fn variation(&self) -> Option<&VariationKey> {
108        self.variation.as_ref()
109    }
110
111    pub fn validators(&self) -> &ResponseValidators {
112        &self.validators
113    }
114
115    pub fn surrogate_tags(&self) -> &InvalidationSet {
116        &self.surrogate_tags
117    }
118
119    pub fn cache_control(&self) -> &str {
120        &self.cache_control
121    }
122
123    pub fn edge_cacheable(&self) -> bool {
124        self.scope.is_edge_cacheable() && self.scope.is_cacheable()
125    }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct CachePlan {
130    application: Option<ApplicationCachePlan>,
131    http: HttpCachePlan,
132}
133
134impl CachePlan {
135    pub fn application(&self) -> Option<&ApplicationCachePlan> {
136        self.application.as_ref()
137    }
138
139    pub fn http(&self) -> &HttpCachePlan {
140        &self.http
141    }
142}
143
144#[derive(Debug, Clone, Copy)]
145pub struct CachePlanner {
146    topology: CacheTopology,
147}
148
149impl CachePlanner {
150    pub fn new(topology: CacheTopology) -> Self {
151        Self { topology }
152    }
153
154    pub fn topology(&self) -> CacheTopology {
155        self.topology
156    }
157
158    pub fn runtime(&self) -> crate::CacheRuntime {
159        crate::CacheRuntime::new(self.topology)
160    }
161
162    #[cfg(test)]
163    #[doc(hidden)]
164    pub fn local_for_testing(&self) -> crate::CacheRuntime {
165        crate::CacheRuntime::local_for_testing(self.topology)
166    }
167
168    #[cfg(test)]
169    #[doc(hidden)]
170    pub fn local_runtime(&self) -> crate::CacheRuntime {
171        self.local_for_testing()
172    }
173
174    pub fn runtime_with_shared_runtime(
175        &self,
176        shared_runtime: Arc<dyn crate::DistributedCacheRuntime>,
177    ) -> crate::CacheRuntime {
178        crate::CacheRuntime::with_shared_runtime(self.topology, shared_runtime)
179    }
180
181    #[allow(dead_code)]
182    #[doc(hidden)]
183    #[cfg(test)]
184    #[deprecated(note = "use runtime_with_shared_runtime(shared_runtime)")]
185    pub fn shared_runtime(&self) -> crate::CacheRuntime {
186        self.runtime()
187    }
188
189    pub fn plan(&self, request: CachePlanRequest) -> Result<CachePlan, CacheModelError> {
190        let CachePlanRequest {
191            namespace,
192            resource,
193            application_policy,
194            http_policy,
195            request_coalescing_mode,
196        } = request;
197
198        let application = application_policy
199            .map(|policy| {
200                let variation = policy.scope().cache_partition_key();
201                let key = CacheKey::new(namespace.clone(), resource.clone(), variation)?;
202                let coalescing =
203                    request_coalescing_mode.unwrap_or(self.topology.request_coalescing_mode());
204
205                Ok(ApplicationCachePlan {
206                    key,
207                    scope: policy.scope().clone(),
208                    freshness: policy.freshness(),
209                    tags: policy.tags().clone(),
210                    layers: CacheLayerPlan {
211                        l1: self.topology.l1(),
212                        l2: self.topology.l2(),
213                    },
214                    coalescing,
215                })
216            })
217            .transpose()?;
218
219        let http = HttpCachePlan {
220            variation: http_policy.scope().variation_key(),
221            scope: http_policy.scope().clone(),
222            validators: http_policy.validators().clone(),
223            surrogate_tags: http_policy.surrogate_tags().clone(),
224            cache_control: http_policy.cache_control_value(),
225        };
226
227        Ok(CachePlan { application, http })
228    }
229}
230
231impl PartialEq for CachePlanner {
232    fn eq(&self, other: &Self) -> bool {
233        self.topology == other.topology
234    }
235}
236
237impl Eq for CachePlanner {}