commonware_storage/archive/immutable/
storage.rs1use crate::{
2 archive::{immutable::Config, Error, Identifier},
3 freezer::{self, Checkpoint, Cursor, Freezer},
4 metadata::{self, Metadata},
5 ordinal::{self, Ordinal},
6 Context,
7};
8use commonware_codec::{CodecShared, EncodeSize, FixedSize, Read, ReadExt, Write};
9use commonware_macros::boxed;
10use commonware_runtime::{
11 telemetry::metrics::{Counter, MetricsExt as _},
12 Buf, BufMut, BufferPooler,
13};
14use commonware_utils::{bitmap::BitMap, sequence::prefixed_u64::U64, Array};
15use futures::join;
16use std::collections::BTreeMap;
17use tracing::debug;
18
19const FREEZER_PREFIX: u8 = 0;
21
22const ORDINAL_PREFIX: u8 = 1;
24
25#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
27enum Record {
28 Freezer(Checkpoint),
29 Ordinal(Option<BitMap>),
30}
31
32impl Record {
33 fn freezer(&self) -> &Checkpoint {
35 match self {
36 Self::Freezer(checkpoint) => checkpoint,
37 _ => panic!("incorrect record"),
38 }
39 }
40
41 fn ordinal(&self) -> &Option<BitMap> {
43 match self {
44 Self::Ordinal(indices) => indices,
45 _ => panic!("incorrect record"),
46 }
47 }
48}
49
50impl Write for Record {
51 fn write(&self, buf: &mut impl BufMut) {
52 match self {
53 Self::Freezer(checkpoint) => {
54 buf.put_u8(0);
55 checkpoint.write(buf);
56 }
57 Self::Ordinal(indices) => {
58 buf.put_u8(1);
59 indices.write(buf);
60 }
61 }
62 }
63}
64
65impl Read for Record {
66 type Cfg = ();
67 fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
68 let tag = u8::read(buf)?;
69 match tag {
70 0 => Ok(Self::Freezer(Checkpoint::read(buf)?)),
71 1 => Ok(Self::Ordinal(Option::<BitMap>::read_cfg(
72 buf,
73 &(usize::MAX as u64),
74 )?)),
75 _ => Err(commonware_codec::Error::InvalidEnum(tag)),
76 }
77 }
78}
79
80impl EncodeSize for Record {
81 fn encode_size(&self) -> usize {
82 1 + match self {
83 Self::Freezer(_) => Checkpoint::SIZE,
84 Self::Ordinal(indices) => indices.encode_size(),
85 }
86 }
87}
88
89pub struct Archive<E: BufferPooler + Context, K: Array, V: CodecShared> {
91 items_per_section: u64,
93
94 metadata: Metadata<E, U64, Record>,
96
97 freezer: Freezer<E, K, V>,
99
100 ordinal: Ordinal<E, Cursor>,
102
103 gets: Counter,
105 has: Counter,
106 syncs: Counter,
107}
108
109impl<E: BufferPooler + Context, K: Array, V: CodecShared> Archive<E, K, V> {
110 pub async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
112 let metadata = Metadata::<E, U64, Record>::init(
114 context.child("metadata"),
115 metadata::Config {
116 partition: cfg.metadata_partition,
117 codec_config: (),
118 },
119 )
120 .await?;
121
122 let freezer_key = U64::new(FREEZER_PREFIX, 0);
125 let checkpoint = metadata.get(&freezer_key).map(|freezer| *freezer.freezer());
126
127 let freezer = Freezer::init(
131 context.child("freezer"),
132 freezer::Config {
133 key_partition: cfg.freezer_key_partition,
134 key_write_buffer: cfg.freezer_key_write_buffer,
135 key_page_cache: cfg.freezer_key_page_cache,
136 value_partition: cfg.freezer_value_partition,
137 value_compression: cfg.freezer_value_compression,
138 value_write_buffer: cfg.freezer_value_write_buffer,
139 value_target_size: cfg.freezer_value_target_size,
140 table_partition: cfg.freezer_table_partition,
141 table_initial_size: cfg.freezer_table_initial_size,
142 table_resize_frequency: cfg.freezer_table_resize_frequency,
143 table_resize_chunk_size: cfg.freezer_table_resize_chunk_size,
144 table_replay_buffer: cfg.replay_buffer,
145 codec_config: cfg.codec_config,
146 },
147 checkpoint,
148 )
149 .await?;
150
151 let sections = metadata
154 .keys()
155 .filter(|k| k.prefix() == ORDINAL_PREFIX)
156 .collect::<Vec<_>>();
157 let mut section_bits = BTreeMap::new();
158 for section in sections {
159 let bits = metadata.get(section).unwrap().ordinal();
161
162 let section = section.value();
164 section_bits.insert(section, bits);
165 }
166
167 let ordinal = Ordinal::init(
171 context.child("ordinal"),
172 ordinal::Config {
173 partition: cfg.ordinal_partition,
174 items_per_blob: cfg.items_per_section,
175 write_buffer: cfg.ordinal_write_buffer,
176 replay_buffer: cfg.replay_buffer,
177 },
178 Some(section_bits),
179 )
180 .await?;
181
182 let gets = context.counter("gets", "Number of gets performed");
184 let has = context.counter("has", "Number of has performed");
185 let syncs = context.counter("syncs", "Number of syncs called");
186
187 Ok(Self {
188 items_per_section: cfg.items_per_section.get(),
189 metadata,
190 freezer,
191 ordinal,
192 gets,
193 has,
194 syncs,
195 })
196 }
197
198 async fn get_index(&self, index: u64) -> Result<Option<V>, Error> {
200 let Some(cursor) = self.ordinal.get(index).await? else {
202 return Ok(None);
203 };
204
205 let result = self
207 .freezer
208 .get(freezer::Identifier::Cursor(cursor))
209 .await?;
210
211 Ok(result)
213 }
214
215 async fn get_key(&self, key: &K) -> Result<Option<V>, Error> {
217 let result = self.freezer.get(freezer::Identifier::Key(key)).await?;
219
220 Ok(result)
222 }
223
224 fn initialize_section(&mut self, section: u64) {
226 let bits = BitMap::zeroes(self.items_per_section);
228
229 let key = U64::new(ORDINAL_PREFIX, section);
231 self.metadata.put(key, Record::Ordinal(Some(bits)));
232 debug!(section, "initialized section");
233 }
234}
235
236impl<E: BufferPooler + Context, K: Array, V: CodecShared> crate::archive::Archive
237 for Archive<E, K, V>
238{
239 type Key = K;
240 type Value = V;
241
242 async fn put(&mut self, index: u64, key: K, data: V) -> Result<(), Error> {
243 if self.ordinal.has(index) {
245 return Ok(());
246 }
247
248 let section = index / self.items_per_section;
250 let ordinal_key = U64::new(ORDINAL_PREFIX, section);
251 if self.metadata.get(&ordinal_key).is_none() {
252 self.initialize_section(section);
253 }
254 let record = self.metadata.get_mut(&ordinal_key).unwrap();
255
256 let done = if let Record::Ordinal(Some(bits)) = record {
258 bits.set(index % self.items_per_section, true);
259 bits.count_ones() == self.items_per_section
260 } else {
261 false
262 };
263 if done {
264 *record = Record::Ordinal(None);
265 }
266
267 let cursor = self.freezer.put(key, data).await?;
269
270 self.ordinal.put(index, cursor).await?;
272
273 Ok(())
274 }
275
276 async fn get(&self, identifier: Identifier<'_, K>) -> Result<Option<V>, Error> {
277 self.gets.inc();
278
279 match identifier {
280 Identifier::Index(index) => self.get_index(index).await,
281 Identifier::Key(key) => self.get_key(key).await,
282 }
283 }
284
285 async fn has(&self, identifier: Identifier<'_, K>) -> Result<bool, Error> {
286 self.has.inc();
287
288 match identifier {
289 Identifier::Index(index) => Ok(self.ordinal.has(index)),
290 Identifier::Key(key) => Ok(self.freezer.has(key).await?),
291 }
292 }
293
294 async fn sync(&mut self) -> Result<(), Error> {
295 self.syncs.inc();
296
297 let (freezer_result, ordinal_result) = join!(self.freezer.sync(), self.ordinal.sync());
299 let checkpoint = freezer_result?;
300 ordinal_result?;
301
302 let freezer_key = U64::new(FREEZER_PREFIX, 0);
305 self.metadata
306 .put_sync(freezer_key, Record::Freezer(checkpoint))
307 .await?;
308
309 Ok(())
310 }
311
312 fn next_gap(&self, index: u64) -> (Option<u64>, Option<u64>) {
313 self.ordinal.next_gap(index)
314 }
315
316 fn missing_items(&self, index: u64, max: usize) -> Vec<u64> {
317 self.ordinal.missing_items(index, max)
318 }
319
320 fn ranges(&self) -> impl Iterator<Item = (u64, u64)> {
321 self.ordinal.ranges()
322 }
323
324 fn ranges_from(&self, from: u64) -> impl Iterator<Item = (u64, u64)> {
325 self.ordinal.ranges_from(from)
326 }
327
328 fn first_index(&self) -> Option<u64> {
329 self.ordinal.first_index()
330 }
331
332 fn last_index(&self) -> Option<u64> {
333 self.ordinal.last_index()
334 }
335
336 #[boxed]
337 async fn destroy(self) -> Result<(), Error> {
338 self.ordinal.destroy().await?;
340
341 self.freezer.destroy().await?;
343
344 self.metadata.destroy().await?;
346
347 Ok(())
348 }
349}
350
351#[cfg(all(test, feature = "arbitrary"))]
352mod conformance {
353 use super::*;
354 use commonware_codec::conformance::CodecConformance;
355
356 commonware_conformance::conformance_tests! {
357 CodecConformance<Record>
358 }
359}