1use super::*;
2
3pub const SMALL_OBJECT_MODE_DIRECT: &str = "direct-small-object";
4pub const SMALL_OBJECT_MODE_PACKED: &str = "packed-small-object";
5pub const SMALL_OBJECT_MODE_INDEXED_PACK: &str = "indexed-pack";
6pub const SMALL_OBJECT_MODE_AMPLIFICATION_REDUCED: &str = "amplification-reduced";
7
8const SMALL_OBJECT_THRESHOLD_BYTES: usize = 16 * 1024;
9
10const SMALL_OBJECT_CAPABILITIES: &[&str] = &[
11 "packing",
12 "indexing",
13 "metadata-amplification",
14 "read-amplification",
15 "write-amplification",
16 "native-support-state",
17 "semantic-parity",
18 "configuration-admin-surface",
19 "security-governance-impact",
20 "observability-evidence",
21 "failure-mode-behavior",
22 "validation-test-coverage",
23 "product-specific-caveats",
24];
25
26const SMALL_OBJECT_CAVEATS: &[&str] = &[
27 "BucketWarden supports direct small-object writes, reads, lists, metadata, and evidence under the local runtime boundary.",
28 "Small-object threshold classification is explicit and observable, but objects are not packed into shared containers.",
29 "Packed small objects, pack indexes, and read/write/metadata amplification-reduction modes are tracked but fail closed.",
30 "Small-object proof does not claim container packing, compaction, shared-index recovery, or reduced IO amplification.",
31];
32
33const SMALL_OBJECT_FAILURE_MODES: &[&str] = &[
34 "unsupported-small-object-mode-rejected",
35 "packed-small-object-mode-rejected",
36 "indexed-pack-mode-rejected",
37 "amplification-reduction-mode-rejected",
38 "invalid-small-object-threshold-rejected",
39];
40
41#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
42pub struct SmallObjectOptimizationEntry {
43 pub mode: &'static str,
44 pub native_support: bool,
45 pub semantic_parity: &'static str,
46 pub storage_behavior: &'static str,
47 pub amplification_behavior: &'static str,
48 pub failure_mode: &'static str,
49 pub caveat: &'static str,
50}
51
52#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
53pub struct SmallObjectOptimizationReport {
54 pub active_mode: &'static str,
55 pub small_object_threshold_bytes: usize,
56 pub supported_modes: Vec<&'static str>,
57 pub unsupported_modes: Vec<&'static str>,
58 pub capabilities: Vec<&'static str>,
59 pub failure_modes: Vec<&'static str>,
60 pub caveats: Vec<&'static str>,
61 pub entries: Vec<SmallObjectOptimizationEntry>,
62}
63
64#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
65pub struct SmallObjectOptimizationPolicy {
66 pub mode: String,
67 pub threshold_bytes: Option<usize>,
68 pub pack_objects: bool,
69 pub external_index: bool,
70 pub reduce_read_amplification: bool,
71 pub reduce_write_amplification: bool,
72 pub reduce_metadata_amplification: bool,
73}
74
75impl BucketWarden {
76 pub fn small_object_optimization_report(&self) -> SmallObjectOptimizationReport {
77 SmallObjectOptimizationReport {
78 active_mode: SMALL_OBJECT_MODE_DIRECT,
79 small_object_threshold_bytes: SMALL_OBJECT_THRESHOLD_BYTES,
80 supported_modes: vec![SMALL_OBJECT_MODE_DIRECT],
81 unsupported_modes: vec![
82 SMALL_OBJECT_MODE_PACKED,
83 SMALL_OBJECT_MODE_INDEXED_PACK,
84 SMALL_OBJECT_MODE_AMPLIFICATION_REDUCED,
85 ],
86 capabilities: SMALL_OBJECT_CAPABILITIES.to_vec(),
87 failure_modes: SMALL_OBJECT_FAILURE_MODES.to_vec(),
88 caveats: SMALL_OBJECT_CAVEATS.to_vec(),
89 entries: vec![
90 SmallObjectOptimizationEntry {
91 mode: SMALL_OBJECT_MODE_DIRECT,
92 native_support: true,
93 semantic_parity: "Small objects use the same committed object version, metadata, encryption, lock, list, and read semantics as larger objects.",
94 storage_behavior: "Small object bodies are stored directly as object versions rather than packed into shared containers.",
95 amplification_behavior: "No reduced amplification claim is made beyond direct runtime read/write/list paths.",
96 failure_mode: "Invalid threshold or unsupported optimization policy is rejected before claiming support.",
97 caveat: "Direct small-object support does not claim container packing or IO amplification reduction.",
98 },
99 SmallObjectOptimizationEntry {
100 mode: SMALL_OBJECT_MODE_PACKED,
101 native_support: false,
102 semantic_parity: "No packed-object container, offset table, or compaction semantics are claimed.",
103 storage_behavior: "Packed small-object storage is out of the current runtime boundary.",
104 amplification_behavior: "Packing-related amplification reductions are not claimed.",
105 failure_mode: "Packed small-object mode selection is rejected as unsupported.",
106 caveat: "Packing needs container indexes, compaction, recovery, and garbage-collection proof.",
107 },
108 SmallObjectOptimizationEntry {
109 mode: SMALL_OBJECT_MODE_INDEXED_PACK,
110 native_support: false,
111 semantic_parity: "No external pack index or packed-object lookup semantics are claimed.",
112 storage_behavior: "Indexed pack storage is out of the current runtime boundary.",
113 amplification_behavior: "Index-based read amplification reductions are not claimed.",
114 failure_mode: "Indexed pack mode selection is rejected as unsupported.",
115 caveat: "Pack indexing needs durable index and corruption-recovery contracts.",
116 },
117 SmallObjectOptimizationEntry {
118 mode: SMALL_OBJECT_MODE_AMPLIFICATION_REDUCED,
119 native_support: false,
120 semantic_parity: "No metadata, read, or write amplification reduction semantics are claimed.",
121 storage_behavior: "Amplification-reduced storage is out of the current runtime boundary.",
122 amplification_behavior: "Reduced metadata/read/write amplification modes are rejected.",
123 failure_mode: "Amplification-reduction mode selection is rejected as unsupported.",
124 caveat: "Amplification reduction needs measurable storage engine behavior and benchmarks.",
125 },
126 ],
127 }
128 }
129
130 pub fn is_small_object(&self, body_len: usize) -> bool {
131 body_len <= SMALL_OBJECT_THRESHOLD_BYTES
132 }
133
134 pub fn ensure_small_object_mode_supported(&self, mode: &str) -> Result<(), RuntimeError> {
135 let report = self.small_object_optimization_report();
136 if report.supported_modes.contains(&mode) {
137 Ok(())
138 } else {
139 Err(RuntimeError::UnsupportedSmallObjectOptimization(
140 mode.to_string(),
141 ))
142 }
143 }
144
145 pub fn validate_small_object_optimization_policy(
146 &self,
147 policy: &SmallObjectOptimizationPolicy,
148 ) -> Result<(), RuntimeError> {
149 self.ensure_small_object_mode_supported(&policy.mode)?;
150 if policy
151 .threshold_bytes
152 .is_some_and(|threshold| threshold == 0)
153 {
154 return Err(RuntimeError::InvalidSmallObjectOptimizationPolicy(
155 "small-object threshold must be nonzero".to_string(),
156 ));
157 }
158 if policy.pack_objects {
159 return Err(RuntimeError::InvalidSmallObjectOptimizationPolicy(
160 "packed small-object containers are outside the current boundary".to_string(),
161 ));
162 }
163 if policy.external_index {
164 return Err(RuntimeError::InvalidSmallObjectOptimizationPolicy(
165 "external small-object indexes are outside the current boundary".to_string(),
166 ));
167 }
168 if policy.reduce_read_amplification
169 || policy.reduce_write_amplification
170 || policy.reduce_metadata_amplification
171 {
172 return Err(RuntimeError::InvalidSmallObjectOptimizationPolicy(
173 "amplification reduction is outside the current small-object boundary".to_string(),
174 ));
175 }
176 Ok(())
177 }
178}