1use crate::{
2 Context,
3 archive::{Error, Identifier, immutable::Config},
4 freezer::{self, Checkpoint, Cursor, Freezer},
5 metadata::{self, Metadata},
6 ordinal::{self, Ordinal},
7};
8use commonware_codec::{Buf, CodecShared, EncodeSize, FixedSize, Read, ReadExt, Write};
9use commonware_runtime::{
10 BufMut,
11 telemetry::metrics::{Counter, MetricsExt as _},
12};
13use commonware_utils::{Array, bitmap::BitMap, sequence::prefixed_u64::U64};
14use futures::{TryFutureExt as _, try_join};
15use std::collections::BTreeMap;
16use tracing::{debug, warn};
17
18const FREEZER_PREFIX: u8 = 0;
20
21const ORDINAL_PREFIX: u8 = 1;
23
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26enum Record {
27 Freezer(Checkpoint),
28 Ordinal(Option<BitMap>),
29}
30
31impl Record {
32 fn freezer(&self) -> &Checkpoint {
34 match self {
35 Self::Freezer(checkpoint) => checkpoint,
36 _ => panic!("incorrect record"),
37 }
38 }
39
40 fn ordinal(&self) -> &Option<BitMap> {
42 match self {
43 Self::Ordinal(indices) => indices,
44 _ => panic!("incorrect record"),
45 }
46 }
47}
48
49impl Write for Record {
50 fn write(&self, buf: &mut impl BufMut) {
51 match self {
52 Self::Freezer(checkpoint) => {
53 buf.put_u8(0);
54 checkpoint.write(buf);
55 }
56 Self::Ordinal(indices) => {
57 buf.put_u8(1);
58 indices.write(buf);
59 }
60 }
61 }
62}
63
64impl Read for Record {
65 type Cfg = ();
66 fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
67 let tag = u8::read(buf)?;
68 match tag {
69 0 => Ok(Self::Freezer(Checkpoint::read(buf)?)),
70 1 => Ok(Self::Ordinal(Option::<BitMap>::read_cfg(
71 buf,
72 &(..=usize::MAX as u64).into(),
73 )?)),
74 _ => Err(commonware_codec::Error::InvalidEnum(tag)),
75 }
76 }
77}
78
79impl EncodeSize for Record {
80 fn encode_size(&self) -> usize {
81 1 + match self {
82 Self::Freezer(_) => Checkpoint::SIZE,
83 Self::Ordinal(indices) => indices.encode_size(),
84 }
85 }
86}
87
88struct Inner<E: Context, K: Array, V: CodecShared> {
90 items_per_section: u64,
92
93 metadata: Metadata<E, U64, Record>,
95
96 freezer: Freezer<E, K, V>,
98
99 ordinal: Ordinal<E, Cursor>,
101
102 gets: Counter,
104 has: Counter,
105 syncs: Counter,
106}
107
108impl<E: Context, K: Array, V: CodecShared> Inner<E, K, V> {
109 async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
111 let metadata = Metadata::<E, U64, Record>::init(
113 context.child("metadata"),
114 metadata::Config {
115 partition: cfg.metadata_partition,
116 codec_config: (),
117 },
118 )
119 .await?;
120
121 let freezer_key = U64::new(FREEZER_PREFIX, 0);
124 let checkpoint = metadata.get(&freezer_key).map(|freezer| *freezer.freezer());
125
126 let freezer = Freezer::init(
130 context.child("freezer"),
131 freezer::Config {
132 key_partition: cfg.freezer_key_partition,
133 key_write_buffer: cfg.freezer_key_write_buffer,
134 key_page_cache: cfg.freezer_key_page_cache,
135 value_partition: cfg.freezer_value_partition,
136 value_compression: cfg.freezer_value_compression,
137 value_write_buffer: cfg.freezer_value_write_buffer,
138 value_target_size: cfg.freezer_value_target_size,
139 table_partition: cfg.freezer_table_partition,
140 table_initial_size: cfg.freezer_table_initial_size,
141 table_resize_frequency: cfg.freezer_table_resize_frequency,
142 table_resize_chunk_size: cfg.freezer_table_resize_chunk_size,
143 table_replay_buffer: cfg.replay_buffer,
144 codec_config: cfg.codec_config,
145 },
146 checkpoint,
147 )
148 .await?;
149
150 let sections = metadata
153 .keys()
154 .filter(|k| k.prefix() == ORDINAL_PREFIX)
155 .collect::<Vec<_>>();
156 let mut section_bits = BTreeMap::new();
157 for section in sections {
158 let bits = metadata.get(section).unwrap().ordinal();
160
161 if let Some(bits) = bits
165 && bits.len() != cfg.items_per_section.get()
166 {
167 warn!(
168 section = section.value(),
169 bitmap_len = bits.len(),
170 items_per_section = cfg.items_per_section.get(),
171 "committed section does not match items_per_section"
172 );
173 return Err(Error::RecordCorrupted);
174 }
175
176 let section = section.value();
178 section_bits.insert(section, bits);
179 }
180
181 let ordinal = Ordinal::init(
185 context.child("ordinal"),
186 ordinal::Config {
187 partition: cfg.ordinal_partition,
188 items_per_blob: cfg.items_per_section,
189 write_buffer: cfg.ordinal_write_buffer,
190 replay_buffer: cfg.replay_buffer,
191 },
192 Some(section_bits),
193 )
194 .await?;
195
196 let gets = context.counter("gets", "Number of gets performed");
198 let has = context.counter("has", "Number of has performed");
199 let syncs = context.counter("syncs", "Number of syncs called");
200
201 Ok(Self {
202 items_per_section: cfg.items_per_section.get(),
203 metadata,
204 freezer,
205 ordinal,
206 gets,
207 has,
208 syncs,
209 })
210 }
211
212 async fn get_index(&self, index: u64) -> Result<Option<V>, Error> {
214 let Some(cursor) = self.ordinal.get(index).await? else {
216 return Ok(None);
217 };
218
219 let result = self
221 .freezer
222 .get(freezer::Identifier::Cursor(cursor))
223 .await?;
224
225 Ok(result)
227 }
228
229 async fn get_key(&self, key: &K) -> Result<Option<V>, Error> {
231 let result = self.freezer.get(freezer::Identifier::Key(key)).await?;
233
234 Ok(result)
236 }
237
238 fn initialize_section(&mut self, section: u64) {
240 let bits = BitMap::zeroes(self.items_per_section);
242
243 let key = U64::new(ORDINAL_PREFIX, section);
245 self.metadata.put(key, Record::Ordinal(Some(bits)));
246 debug!(section, "initialized section");
247 }
248
249 async fn put(mut self: Box<Self>, index: u64, key: K, data: &V) -> Result<Box<Self>, Error> {
251 if self.ordinal.has(index) {
253 return Ok(self);
254 }
255
256 let section = index / self.items_per_section;
258 let ordinal_key = U64::new(ORDINAL_PREFIX, section);
259 if self.metadata.get(&ordinal_key).is_none() {
260 self.initialize_section(section);
261 }
262 let record = self.metadata.get_mut(&ordinal_key).unwrap();
263
264 let done = if let Record::Ordinal(Some(bits)) = record {
266 bits.set(index % self.items_per_section, true);
267 bits.count_ones() == self.items_per_section
268 } else {
269 false
270 };
271 if done {
272 *record = Record::Ordinal(None);
273 }
274
275 let cursor;
277 (self.freezer, cursor) = self.freezer.put(key, data).await?;
278
279 self.ordinal = self.ordinal.put(index, cursor).await?;
281
282 Ok(self)
283 }
284
285 async fn get(&self, identifier: Identifier<'_, K>) -> Result<Option<V>, Error> {
287 self.gets.inc();
288
289 match identifier {
290 Identifier::Index(index) => self.get_index(index).await,
291 Identifier::Key(key) => self.get_key(key).await,
292 }
293 }
294
295 async fn has(&self, identifier: Identifier<'_, K>) -> Result<bool, Error> {
297 self.has.inc();
298
299 match identifier {
300 Identifier::Index(index) => Ok(self.ordinal.has(index)),
301 Identifier::Key(key) => Ok(self.freezer.has(key).await?),
302 }
303 }
304
305 async fn sync(mut self: Box<Self>) -> Result<Box<Self>, Error> {
307 self.syncs.inc();
308
309 let ((freezer, checkpoint), ordinal) = try_join!(
311 self.freezer.sync().map_err(Error::from),
312 self.ordinal.sync().map_err(Error::from)
313 )?;
314 self.freezer = freezer;
315 self.ordinal = ordinal;
316
317 let freezer_key = U64::new(FREEZER_PREFIX, 0);
320 self.metadata = self
321 .metadata
322 .put_sync(freezer_key, Record::Freezer(checkpoint))
323 .await?;
324
325 Ok(self)
326 }
327
328 fn next_gap(&self, index: u64) -> (Option<u64>, Option<u64>) {
330 self.ordinal.next_gap(index)
331 }
332
333 fn missing_items(&self, index: u64, max: usize) -> Vec<u64> {
335 self.ordinal.missing_items(index, max)
336 }
337
338 fn ranges(&self) -> impl Iterator<Item = (u64, u64)> {
340 self.ordinal.ranges()
341 }
342
343 fn ranges_from(&self, from: u64) -> impl Iterator<Item = (u64, u64)> {
345 self.ordinal.ranges_from(from)
346 }
347
348 fn first_index(&self) -> Option<u64> {
350 self.ordinal.first_index()
351 }
352
353 fn last_index(&self) -> Option<u64> {
355 self.ordinal.last_index()
356 }
357
358 async fn destroy(self) -> Result<(), Error> {
360 self.metadata.destroy().await?;
363
364 self.ordinal.destroy().await?;
366 self.freezer.destroy().await?;
367 Ok(())
368 }
369}
370
371pub struct Archive<E: Context, K: Array, V: CodecShared>(Box<Inner<E, K, V>>);
376
377impl<E: Context, K: Array, V: CodecShared> std::fmt::Debug for Archive<E, K, V> {
378 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
379 f.debug_struct("Archive")
380 .field("first_index", &self.0.first_index())
381 .field("last_index", &self.0.last_index())
382 .finish_non_exhaustive()
383 }
384}
385
386impl<E: Context, K: Array, V: CodecShared> Archive<E, K, V> {
387 pub async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
389 Ok(Self(Box::new(Inner::init(context, cfg).await?)))
390 }
391}
392
393impl<E: Context, K: Array, V: CodecShared> crate::archive::Archive for Archive<E, K, V> {
394 type Key = K;
395 type Value = V;
396
397 async fn put(mut self, index: u64, key: K, data: &V) -> Result<Self, Error> {
398 self.0 = self.0.put(index, key, data).await?;
399 Ok(self)
400 }
401
402 async fn get(&self, identifier: Identifier<'_, K>) -> Result<Option<V>, Error> {
403 self.0.get(identifier).await
404 }
405
406 async fn has(&self, identifier: Identifier<'_, K>) -> Result<bool, Error> {
407 self.0.has(identifier).await
408 }
409
410 async fn sync(mut self) -> Result<Self, Error> {
411 self.0 = self.0.sync().await?;
412 Ok(self)
413 }
414
415 fn next_gap(&self, index: u64) -> (Option<u64>, Option<u64>) {
416 self.0.next_gap(index)
417 }
418
419 fn missing_items(&self, index: u64, max: usize) -> Vec<u64> {
420 self.0.missing_items(index, max)
421 }
422
423 fn ranges(&self) -> impl Iterator<Item = (u64, u64)> {
424 self.0.ranges()
425 }
426
427 fn ranges_from(&self, from: u64) -> impl Iterator<Item = (u64, u64)> {
428 self.0.ranges_from(from)
429 }
430
431 fn first_index(&self) -> Option<u64> {
432 self.0.first_index()
433 }
434
435 fn last_index(&self) -> Option<u64> {
436 self.0.last_index()
437 }
438
439 async fn destroy(self) -> Result<(), Error> {
440 self.0.destroy().await
441 }
442}
443
444#[cfg(all(test, feature = "arbitrary"))]
445mod conformance {
446 use super::*;
447 use commonware_codec::conformance::CodecConformance;
448
449 commonware_conformance::conformance_tests! {
450 CodecConformance<Record>
451 }
452}