1use serde::{Deserialize, Serialize};
10
11pub const MAX_OPTIMIZATION_DIAGNOSTICS: usize = 8;
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
17#[serde(deny_unknown_fields)]
18pub struct OptimizationPolicy {
19 pub key_schema_version: u32,
20 pub cache_namespace_version: u64,
21 pub admission: OptimizationAdmission,
22 pub expiry: ExpiryBasis,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub batch: Option<BatchPolicy>,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub memory_cache: Option<MemoryCachePolicy>,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
30#[serde(rename_all = "snake_case")]
31pub enum OptimizationAdmission {
32 Refuse,
34 ExecuteIndependent,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
39#[serde(rename_all = "snake_case")]
40pub enum ExpiryBasis {
41 InjectedRuntimeTime,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
47#[serde(deny_unknown_fields)]
48pub struct BatchPolicy {
49 pub max_members: u32,
50 pub max_canonical_bytes: u64,
51 pub max_members_per_scope: u32,
52 pub max_queue_delay_ms: u64,
55 pub max_backend_work: u64,
56 pub allocation: BatchChargeAllocation,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
62#[serde(rename_all = "snake_case")]
63pub enum BatchChargeAllocation {
64 EvenRemainderByMemberIndex,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
74#[serde(deny_unknown_fields)]
75pub struct MemoryCachePolicy {
76 #[serde(
79 default,
80 skip_serializing_if = "std::ops::Not::not",
81 deserialize_with = "acquisition_pages"
82 )]
83 #[schemars(schema_with = "crate::acquisition_record::constructed_schema")]
86 pub acquisition_pages: bool,
87 pub max_bytes: u64,
88 pub max_entries: u64,
89 pub max_entry_bytes: u64,
90 pub max_scope_bytes: u64,
91 pub ttl_ms: u64,
92 pub eviction: EvictionOrder,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
96#[serde(rename_all = "snake_case")]
97pub enum EvictionOrder {
98 Fifo,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
104#[serde(rename_all = "snake_case")]
105pub enum OptimizationMechanism {
106 Independent,
107 Batch,
108 MemoryCache,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
112#[serde(rename_all = "snake_case")]
113pub enum OptimizationOutcome {
114 Bypassed,
115 Miss,
116 Hit,
117 Batched,
118 Refused,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
124#[serde(deny_unknown_fields)]
125pub struct OptimizationRecord {
126 pub mechanism: OptimizationMechanism,
127 pub key_schema_version: u32,
128 pub outcome: OptimizationOutcome,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub entry_id: Option<String>,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub batch_id: Option<String>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub batch_member: Option<u32>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub source_attempt_id: Option<String>,
140 pub age_ms: u64,
141 pub invalidation_generation: u64,
142 pub charge_owner: bool,
143 pub allocated_charge_units: u64,
144 pub shared_liability_units: u64,
145 #[serde(default, skip_serializing_if = "Vec::is_empty")]
148 pub diagnostics: Vec<String>,
149}
150
151impl OptimizationRecord {
152 pub fn diagnose(&mut self, diagnostic: impl Into<String>) {
154 match self.diagnostics.len() {
155 n if n + 1 < MAX_OPTIMIZATION_DIAGNOSTICS => self.diagnostics.push(diagnostic.into()),
156 n if n + 1 == MAX_OPTIMIZATION_DIAGNOSTICS => {
157 self.diagnostics.push("further diagnostics dropped".into());
158 }
159 _ => {}
160 }
161 }
162}
163
164fn acquisition_pages<'de, D: serde::Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
165 if !bool::deserialize(d)? {
166 return Err(serde::de::Error::custom(
167 "omit acquisition_pages when disabled",
168 ));
169 }
170 Ok(true)
171}