1use serde::{Deserialize, Serialize};
9
10pub const MAX_OPTIMIZATION_DIAGNOSTICS: usize = 8;
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
16#[serde(deny_unknown_fields)]
17pub struct OptimizationPolicy {
18 pub key_schema_version: u32,
19 pub cache_namespace_version: u64,
20 pub admission: OptimizationAdmission,
21 pub expiry: ExpiryBasis,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub batch: Option<BatchPolicy>,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub memory_cache: Option<MemoryCachePolicy>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub shared_calls: Option<SharedCallPolicy>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub persistent_cache: Option<PersistentCachePolicy>,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
37#[serde(deny_unknown_fields)]
38pub struct SharedCallPolicy {
39 pub max_shared_calls: u32,
41 pub max_waiters_per_call: u32,
43 pub allocation: SharedChargeAllocation,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
47#[serde(rename_all = "snake_case")]
48pub enum SharedChargeAllocation {
49 FinalOwner,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
57#[serde(deny_unknown_fields)]
58pub struct PersistentCachePolicy {
59 pub store_contract: StoreContract,
60 pub max_entry_bytes: u64,
61 pub max_scope_bytes: u64,
62 pub max_namespace_bytes: u64,
63 pub ttl_ms: u64,
64 pub on_store_failure: StoreFailurePolicy,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
71#[serde(deny_unknown_fields)]
72pub struct StoreContract {
73 pub schema: String,
74 pub namespace: String,
75 pub durability: String,
76 pub encryption: String,
77 pub atomicity: String,
78 pub conflict: String,
79 pub expiry: String,
80 pub invalidation: String,
81 pub erasure: String,
82}
83
84impl StoreContract {
85 pub fn terms(&self) -> [(&'static str, &str); 9] {
87 [
88 ("schema", &self.schema),
89 ("namespace", &self.namespace),
90 ("durability", &self.durability),
91 ("encryption", &self.encryption),
92 ("atomicity", &self.atomicity),
93 ("conflict", &self.conflict),
94 ("expiry", &self.expiry),
95 ("invalidation", &self.invalidation),
96 ("erasure", &self.erasure),
97 ]
98 }
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
102#[serde(rename_all = "snake_case")]
103pub enum StoreFailurePolicy {
104 FailClosed,
107 ContinueWithoutCache,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
112#[serde(rename_all = "snake_case")]
113pub enum OptimizationAdmission {
114 Refuse,
116 ExecuteIndependent,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
121#[serde(rename_all = "snake_case")]
122pub enum ExpiryBasis {
123 InjectedRuntimeTime,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
129#[serde(deny_unknown_fields)]
130pub struct BatchPolicy {
131 pub max_members: u32,
132 pub max_canonical_bytes: u64,
133 pub max_members_per_scope: u32,
134 pub max_queue_delay_ms: u64,
137 pub max_backend_work: u64,
138 pub allocation: BatchChargeAllocation,
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
144#[serde(rename_all = "snake_case")]
145pub enum BatchChargeAllocation {
146 EvenRemainderByMemberIndex,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
156#[serde(deny_unknown_fields)]
157pub struct MemoryCachePolicy {
158 #[serde(
161 default,
162 skip_serializing_if = "std::ops::Not::not",
163 deserialize_with = "acquisition_pages"
164 )]
165 #[schemars(schema_with = "crate::acquisition_record::constructed_schema")]
168 pub acquisition_pages: bool,
169 pub max_bytes: u64,
170 pub max_entries: u64,
171 pub max_entry_bytes: u64,
172 pub max_scope_bytes: u64,
173 pub ttl_ms: u64,
174 pub eviction: EvictionOrder,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
178#[serde(rename_all = "snake_case")]
179pub enum EvictionOrder {
180 Fifo,
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
186#[serde(rename_all = "snake_case")]
187pub enum OptimizationMechanism {
188 Independent,
189 Batch,
190 MemoryCache,
191 SharedCall,
194}
195
196#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
197#[serde(rename_all = "snake_case")]
198pub enum OptimizationOutcome {
199 Bypassed,
200 Miss,
201 Hit,
202 Batched,
203 Refused,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
209#[serde(deny_unknown_fields)]
210pub struct OptimizationRecord {
211 pub mechanism: OptimizationMechanism,
212 pub key_schema_version: u32,
213 pub outcome: OptimizationOutcome,
214 #[serde(default, skip_serializing_if = "Option::is_none")]
215 pub entry_id: Option<String>,
216 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub batch_id: Option<String>,
218 #[serde(default, skip_serializing_if = "Option::is_none")]
219 pub batch_member: Option<u32>,
220 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub source_attempt_id: Option<String>,
225 pub age_ms: u64,
226 pub invalidation_generation: u64,
227 pub charge_owner: bool,
228 pub allocated_charge_units: u64,
229 pub shared_liability_units: u64,
230 #[serde(default, skip_serializing_if = "Vec::is_empty")]
233 pub diagnostics: Vec<String>,
234 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub shared: Option<SharedRecord>,
237}
238
239#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
243#[serde(deny_unknown_fields)]
244pub struct SharedRecord {
245 pub shared_call_id: String,
246 pub attempt_id: String,
247 pub waiter_id: String,
248 pub role: SharedRole,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub left: Option<SharedLeave>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub handed_off_to: Option<String>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub received_from: Option<String>,
255 pub final_owner: bool,
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
259#[serde(rename_all = "snake_case")]
260pub enum SharedRole {
261 Led,
262 Joined,
263}
264
265#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
267#[serde(rename_all = "snake_case")]
268pub enum SharedLeave {
269 Cancelled,
270 Deadline,
271}
272
273impl OptimizationRecord {
274 pub fn diagnose(&mut self, diagnostic: impl Into<String>) {
276 match self.diagnostics.len() {
277 n if n + 1 < MAX_OPTIMIZATION_DIAGNOSTICS => self.diagnostics.push(diagnostic.into()),
278 n if n + 1 == MAX_OPTIMIZATION_DIAGNOSTICS => {
279 self.diagnostics.push("further diagnostics dropped".into());
280 }
281 _ => {}
282 }
283 }
284}
285
286fn acquisition_pages<'de, D: serde::Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
287 if !bool::deserialize(d)? {
288 return Err(serde::de::Error::custom(
289 "omit acquisition_pages when disabled",
290 ));
291 }
292 Ok(true)
293}