Skip to main content

rustev_contract/
optimization.rs

1//! Neutral runtime optimization policy and evidence vocabulary (spec 017).
2//!
3//! These types describe behavior that is part of a plan and observations that
4//! belong to a run. They do not perform I/O or grant authority. This increment
5//! covers runtime-planned batching and bounded memory caching; concurrent
6//! duplicate suppression and persistent caching are later increments under
7//! the same spec.
8
9use serde::{Deserialize, Serialize};
10
11/// Most diagnostics one request's optimization record keeps. Later ones are
12/// dropped and the last kept entry says so.
13pub const MAX_OPTIMIZATION_DIAGNOSTICS: usize = 8;
14
15/// Plan-identified controls for reusable semantic work.
16#[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 when an enabled optimization cannot safely admit the request.
33    Refuse,
34    /// Execute independently without reuse and record the reason.
35    ExecuteIndependent,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
39#[serde(rename_all = "snake_case")]
40pub enum ExpiryBasis {
41    /// The monotonic clock injected into the runtime. It is process-relative,
42    /// which is sound for a memory cache that dies with the process.
43    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    /// Upper bound on time a member may wait for peers. Batches are formed
53    /// within one decision's admission wave, so the runtime waits zero.
54    pub max_queue_delay_ms: u64,
55    pub max_backend_work: u64,
56    pub allocation: BatchChargeAllocation,
57}
58
59/// Deterministic settlement of one observed batch charge across its logical
60/// members. Member order is the runtime-recorded batch index order.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
62#[serde(rename_all = "snake_case")]
63pub enum BatchChargeAllocation {
64    /// Divide integer units evenly. Members with the lowest indices receive
65    /// one extra unit until the remainder is exhausted. The shares are
66    /// allocation evidence; the batch total is settled once, by the charge
67    /// owner, against the summed member reservations.
68    EvenRemainderByMemberIndex,
69}
70
71/// Bounds apply to one plan's cache namespace; no plan's policy evicts
72/// another plan's entries.
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
74#[serde(deny_unknown_fields)]
75pub struct MemoryCachePolicy {
76    /// Opt in to eligible acquisition pages (spec 036, 3.13.7).
77    /// Absence disables reuse; true is the only explicit wire value.
78    #[serde(
79        default,
80        skip_serializing_if = "std::ops::Not::not",
81        deserialize_with = "acquisition_pages"
82    )]
83    // Despite its name, constructed_schema emits only a literal-true boolean
84    // schema, matching this opt-in field's wire contract too.
85    #[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    /// First admitted, first evicted. Cache hits do not reorder entries. A
99    /// per-scope bound evicts only that scope's oldest entries.
100    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/// Per-logical-request runtime observations. Secret key material and cached
122/// content are deliberately absent.
123#[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    /// The attempt that produced the supplied output. For a cache hit it is
136    /// an attempt of an earlier decision, and the record has no attempt of
137    /// its own; replay takes it as the output's origin.
138    #[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    /// Every refusal or diagnostic, in the order observed, at most
146    /// [`MAX_OPTIMIZATION_DIAGNOSTICS`].
147    #[serde(default, skip_serializing_if = "Vec::is_empty")]
148    pub diagnostics: Vec<String>,
149}
150
151impl OptimizationRecord {
152    /// Append a diagnostic without exceeding the bound.
153    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}