use serde::{Deserialize, Serialize};
pub const MAX_OPTIMIZATION_DIAGNOSTICS: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct OptimizationPolicy {
pub key_schema_version: u32,
pub cache_namespace_version: u64,
pub admission: OptimizationAdmission,
pub expiry: ExpiryBasis,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub batch: Option<BatchPolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub memory_cache: Option<MemoryCachePolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shared_calls: Option<SharedCallPolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub persistent_cache: Option<PersistentCachePolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub decision_cache: Option<DecisionCachePolicy>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SharedCallPolicy {
pub max_shared_calls: u32,
pub max_waiters_per_call: u32,
pub allocation: SharedChargeAllocation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SharedChargeAllocation {
FinalOwner,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PersistentCachePolicy {
pub store_contract: StoreContract,
pub max_entry_bytes: u64,
pub max_scope_bytes: u64,
pub max_namespace_bytes: u64,
pub ttl_ms: u64,
pub on_store_failure: StoreFailurePolicy,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct StoreContract {
pub schema: String,
pub namespace: String,
pub durability: String,
pub encryption: String,
pub atomicity: String,
pub conflict: String,
pub expiry: String,
pub invalidation: String,
pub erasure: String,
}
impl StoreContract {
pub fn terms(&self) -> [(&'static str, &str); 9] {
[
("schema", &self.schema),
("namespace", &self.namespace),
("durability", &self.durability),
("encryption", &self.encryption),
("atomicity", &self.atomicity),
("conflict", &self.conflict),
("expiry", &self.expiry),
("invalidation", &self.invalidation),
("erasure", &self.erasure),
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StoreFailurePolicy {
FailClosed,
ContinueWithoutCache,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct DecisionCachePolicy {
pub tiers: Vec<DecisionCacheTier>,
pub max_entry_bytes: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_entries: Option<u64>,
pub ttl_ms: u64,
pub volatile: Vec<String>,
pub key_dimensions: Vec<String>,
#[serde(default)]
pub on_refresh_failure: RefreshFailurePolicy,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, schemars::JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum DecisionCacheTier {
Memory,
Persistent,
}
#[derive(
Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum RefreshFailurePolicy {
#[default]
Unresolved,
Recompute,
ServeCachedWithinFreshness,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum OptimizationAdmission {
Refuse,
ExecuteIndependent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExpiryBasis {
InjectedRuntimeTime,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BatchPolicy {
pub max_members: u32,
pub max_canonical_bytes: u64,
pub max_members_per_scope: u32,
pub max_queue_delay_ms: u64,
pub max_backend_work: u64,
pub allocation: BatchChargeAllocation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BatchChargeAllocation {
EvenRemainderByMemberIndex,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MemoryCachePolicy {
#[serde(
default,
skip_serializing_if = "std::ops::Not::not",
deserialize_with = "acquisition_pages"
)]
#[schemars(schema_with = "crate::acquisition_record::constructed_schema")]
pub acquisition_pages: bool,
pub max_bytes: u64,
pub max_entries: u64,
pub max_entry_bytes: u64,
pub max_scope_bytes: u64,
pub ttl_ms: u64,
pub eviction: EvictionOrder,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum EvictionOrder {
Fifo,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum OptimizationMechanism {
Independent,
Batch,
MemoryCache,
SharedCall,
PersistentCache,
DecisionCache,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum OptimizationOutcome {
Bypassed,
Miss,
Hit,
Batched,
Refused,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct OptimizationRecord {
pub mechanism: OptimizationMechanism,
pub key_schema_version: u32,
pub outcome: OptimizationOutcome,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entry_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub batch_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub batch_member: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_attempt_id: Option<String>,
pub age_ms: u64,
pub invalidation_generation: u64,
pub charge_owner: bool,
pub allocated_charge_units: u64,
pub shared_liability_units: u64,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub diagnostics: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shared: Option<SharedRecord>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SharedRecord {
pub shared_call_id: String,
pub attempt_id: String,
pub waiter_id: String,
pub role: SharedRole,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub left: Option<SharedLeave>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handed_off_to: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub received_from: Option<String>,
pub final_owner: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SharedRole {
Led,
Joined,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SharedLeave {
Cancelled,
Deadline,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct DecisionCacheRecord {
pub outcome: DecisionCacheOutcome,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub entry_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_run_record: Option<crate::ids::ContentDigest>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub age_ms: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub generations: Option<crate::cache_entry::EntryGenerations>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub reused: Vec<u32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub refreshed: Vec<u32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mismatched: Vec<u32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub substituted: Vec<SubstitutedInput>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub on_refresh_failure: Option<RefreshFailurePolicy>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub abandoned: Option<AbandonedRun>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub diagnostics: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum DecisionCacheOutcome {
Hit,
Refreshed,
Miss,
Bypassed,
Refused,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct AbandonedRun {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub requests: Vec<crate::run::RequestRecord>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub acquisitions: Vec<crate::acquisition_record::AcquisitionRecord>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SubstitutedInput {
pub input: String,
pub entry_digest: crate::ids::ContentDigest,
}
impl OptimizationRecord {
pub fn diagnose(&mut self, diagnostic: impl Into<String>) {
match self.diagnostics.len() {
n if n + 1 < MAX_OPTIMIZATION_DIAGNOSTICS => self.diagnostics.push(diagnostic.into()),
n if n + 1 == MAX_OPTIMIZATION_DIAGNOSTICS => {
self.diagnostics.push("further diagnostics dropped".into());
}
_ => {}
}
}
}
fn acquisition_pages<'de, D: serde::Deserializer<'de>>(d: D) -> Result<bool, D::Error> {
if !bool::deserialize(d)? {
return Err(serde::de::Error::custom(
"omit acquisition_pages when disabled",
));
}
Ok(true)
}