1use super::{Config, Translator};
2use crate::{
3 archive::{Error, Identifier},
4 index::{unordered::Index, Unordered},
5 journal::segmented::oversized::{
6 Config as OversizedConfig, Oversized, Record as OversizedRecord,
7 },
8 rmap::RMap,
9};
10use commonware_codec::{CodecShared, FixedSize, Read, ReadExt, Write};
11use commonware_macros::boxed;
12use commonware_runtime::{
13 telemetry::metrics::{Counter, Gauge, GaugeExt, MetricsExt as _},
14 Buf, BufMut, BufferPooler, Handle, Metrics, Storage,
15};
16use commonware_utils::Array;
17use futures::{pin_mut, StreamExt};
18use std::collections::{btree_map, BTreeMap, BTreeSet};
19use tracing::debug;
20
21#[derive(Debug, Clone, PartialEq)]
23struct Record<K: Array> {
24 index: u64,
26 key: K,
28 value_offset: u64,
30 value_size: u32,
32}
33
34impl<K: Array> Record<K> {
35 const fn new(index: u64, key: K, value_offset: u64, value_size: u32) -> Self {
37 Self {
38 index,
39 key,
40 value_offset,
41 value_size,
42 }
43 }
44}
45
46impl<K: Array> Write for Record<K> {
47 fn write(&self, buf: &mut impl BufMut) {
48 self.index.write(buf);
49 self.key.write(buf);
50 self.value_offset.write(buf);
51 self.value_size.write(buf);
52 }
53}
54
55impl<K: Array> Read for Record<K> {
56 type Cfg = ();
57
58 fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
59 let index = u64::read(buf)?;
60 let key = K::read(buf)?;
61 let value_offset = u64::read(buf)?;
62 let value_size = u32::read(buf)?;
63 Ok(Self {
64 index,
65 key,
66 value_offset,
67 value_size,
68 })
69 }
70}
71
72impl<K: Array> FixedSize for Record<K> {
73 const SIZE: usize = u64::SIZE + K::SIZE + u64::SIZE + u32::SIZE;
75}
76
77impl<K: Array> OversizedRecord for Record<K> {
78 fn value_location(&self) -> (u64, u32) {
79 (self.value_offset, self.value_size)
80 }
81
82 fn with_location(mut self, offset: u64, size: u32) -> Self {
83 self.value_offset = offset;
84 self.value_size = size;
85 self
86 }
87}
88
89#[cfg(feature = "arbitrary")]
90impl<K: Array> arbitrary::Arbitrary<'_> for Record<K>
91where
92 K: for<'a> arbitrary::Arbitrary<'a>,
93{
94 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
95 Ok(Self {
96 index: u64::arbitrary(u)?,
97 key: K::arbitrary(u)?,
98 value_offset: u64::arbitrary(u)?,
99 value_size: u32::arbitrary(u)?,
100 })
101 }
102}
103
104pub struct Archive<T: Translator, E: BufferPooler + Storage + Metrics, K: Array, V: CodecShared> {
106 items_per_section: u64,
107
108 oversized: Oversized<E, Record<K>, V>,
110
111 pending: BTreeSet<u64>,
115
116 requested: BTreeSet<u64>,
126
127 oldest_allowed: Option<u64>,
129
130 keys: Index<T, u64>,
132
133 indices: BTreeMap<u64, u64>,
135
136 extra_indices: BTreeMap<u64, Vec<u64>>,
139
140 intervals: RMap,
142
143 items_tracked: Gauge,
145 indices_pruned: Counter,
146 unnecessary_reads: Counter,
147 gets: Counter,
148 has: Counter,
149 syncs: Counter,
150}
151
152impl<T: Translator, E: BufferPooler + Storage + Metrics, K: Array, V: CodecShared>
153 Archive<T, E, K, V>
154{
155 const fn section(&self, index: u64) -> u64 {
157 (index / self.items_per_section) * self.items_per_section
158 }
159
160 const fn pruned(&self, index: u64) -> bool {
162 match self.oldest_allowed {
163 Some(oldest_allowed) => index < oldest_allowed,
164 None => false,
165 }
166 }
167
168 fn iter_positions(&self, index: u64) -> impl Iterator<Item = u64> + '_ {
170 self.indices.get(&index).into_iter().copied().chain(
171 self.extra_indices
172 .get(&index)
173 .into_iter()
174 .flat_map(|v| v.iter().copied()),
175 )
176 }
177
178 pub async fn init(context: E, cfg: Config<T, V::Cfg>) -> Result<Self, Error> {
183 let oversized_cfg = OversizedConfig {
185 index_partition: cfg.key_partition,
186 value_partition: cfg.value_partition,
187 index_page_cache: cfg.key_page_cache,
188 index_write_buffer: cfg.key_write_buffer,
189 value_write_buffer: cfg.value_write_buffer,
190 compression: cfg.compression,
191 codec_config: cfg.codec_config,
192 };
193 let mut oversized: Oversized<E, Record<K>, V> =
194 Oversized::init(context.child("oversized"), oversized_cfg).await?;
195
196 let mut indices: BTreeMap<u64, u64> = BTreeMap::new();
198 let mut extra_indices: BTreeMap<u64, Vec<u64>> = BTreeMap::new();
199 let mut keys = Index::new(context.child("index"), cfg.translator.clone());
200 let mut intervals = RMap::new();
201 {
202 debug!("initializing archive from index journal");
203 let stream = oversized.replay(0, 0, cfg.replay_buffer).await?;
204 pin_mut!(stream);
205 while let Some(result) = stream.next().await {
206 let (_section, position, entry) = result?;
207
208 match indices.entry(entry.index) {
210 btree_map::Entry::Vacant(e) => {
211 e.insert(position);
212 }
213 btree_map::Entry::Occupied(_) => {
214 extra_indices.entry(entry.index).or_default().push(position);
215 }
216 }
217
218 keys.insert(&entry.key, entry.index);
220
221 intervals.insert(entry.index);
223 }
224 debug!("archive initialized");
225 }
226
227 let items_tracked = context.gauge("items_tracked", "Number of items tracked");
229 let indices_pruned = context.counter("indices_pruned", "Number of indices pruned");
230 let unnecessary_reads = context.counter(
231 "unnecessary_reads",
232 "Number of unnecessary reads performed during key lookups",
233 );
234 let gets = context.counter("gets", "Number of gets performed");
235 let has = context.counter("has", "Number of has performed");
236 let syncs = context.counter("syncs", "Number of syncs called");
237 let _ = items_tracked.try_set(indices.len());
238
239 Ok(Self {
241 items_per_section: cfg.items_per_section.get(),
242 oversized,
243 pending: BTreeSet::new(),
244 requested: BTreeSet::new(),
245 oldest_allowed: None,
246 indices,
247 extra_indices,
248 intervals,
249 keys,
250 items_tracked,
251 indices_pruned,
252 unnecessary_reads,
253 gets,
254 has,
255 syncs,
256 })
257 }
258
259 async fn get_index(&self, index: u64) -> Result<Option<V>, Error> {
260 self.gets.inc();
262
263 let position = match self.indices.get(&index) {
265 Some(&position) => position,
266 None => return Ok(None),
267 };
268
269 let section = self.section(index);
271 let entry = self.oversized.get(section, position).await?;
272 let (value_offset, value_size) = entry.value_location();
273
274 let value = self
276 .oversized
277 .get_value(section, value_offset, value_size)
278 .await?;
279 Ok(Some(value))
280 }
281
282 async fn get_key(&self, key: &K) -> Result<Option<V>, Error> {
283 self.gets.inc();
285
286 let iter = self.keys.get(key);
288 for index in iter {
289 if self.pruned(*index) {
291 continue;
292 }
293
294 if !self.indices.contains_key(index) {
296 return Err(Error::RecordCorrupted);
297 }
298 let section = self.section(*index);
299
300 for position in self.iter_positions(*index) {
301 let entry = self.oversized.get(section, position).await?;
303
304 if entry.key.as_ref() == key.as_ref() {
306 let (value_offset, value_size) = entry.value_location();
308 let value = self
309 .oversized
310 .get_value(section, value_offset, value_size)
311 .await?;
312 return Ok(Some(value));
313 }
314 self.unnecessary_reads.inc();
315 }
316 }
317
318 Ok(None)
319 }
320
321 async fn has_key(&self, key: &K) -> Result<bool, Error> {
326 for index in self.keys.get(key) {
327 if self.pruned(*index) {
329 continue;
330 }
331
332 if !self.indices.contains_key(index) {
334 return Err(Error::RecordCorrupted);
335 }
336 let section = self.section(*index);
337
338 for position in self.iter_positions(*index) {
339 let entry = self.oversized.get(section, position).await?;
341 if entry.key.as_ref() == key.as_ref() {
342 return Ok(true);
343 }
344 self.unnecessary_reads.inc();
345 }
346 }
347
348 Ok(false)
349 }
350
351 fn has_index(&self, index: u64) -> bool {
352 self.indices.contains_key(&index)
354 }
355
356 async fn put_internal(
357 &mut self,
358 index: u64,
359 key: K,
360 data: V,
361 skip_if_index_exists: bool,
362 ) -> Result<(), Error> {
363 let oldest_allowed = self.oldest_allowed.unwrap_or(0);
365 if index < oldest_allowed {
366 return Err(Error::AlreadyPrunedTo(oldest_allowed));
367 }
368
369 if skip_if_index_exists && self.indices.contains_key(&index) {
371 return Ok(());
372 }
373
374 let section = self.section(index);
376 let entry = Record::new(index, key.clone(), 0, 0);
377 let (position, _, _) = self.oversized.append(section, entry, &data).await?;
378
379 match self.indices.entry(index) {
381 btree_map::Entry::Vacant(e) => {
382 e.insert(position);
383 }
384 btree_map::Entry::Occupied(_) => {
385 self.extra_indices.entry(index).or_default().push(position);
386 }
387 }
388
389 self.intervals.insert(index);
391
392 self.keys
394 .insert_and_retain(&key, index, |v| *v >= oldest_allowed);
395
396 self.pending.insert(section);
398
399 let _ = self.items_tracked.try_set(self.indices.len());
401 Ok(())
402 }
403
404 pub async fn prune(&mut self, min: u64) -> Result<(), Error> {
410 let min = self.section(min);
412
413 if let Some(oldest_allowed) = self.oldest_allowed {
415 if min <= oldest_allowed {
416 return Ok(());
419 }
420 }
421 debug!(min, "pruning archive");
422
423 self.oversized.prune(min).await?;
425
426 self.pending = self.pending.split_off(&min);
428 self.requested = self.requested.split_off(&min);
429
430 loop {
432 let next = match self.indices.first_key_value() {
433 Some((index, _)) if *index < min => *index,
434 _ => break,
435 };
436 self.indices.remove(&next).unwrap();
437 self.extra_indices.remove(&next);
438 self.indices_pruned.inc();
439 }
440
441 if min > 0 {
443 self.intervals.remove(0, min - 1);
444 }
445
446 self.oldest_allowed = Some(min);
448 let _ = self.items_tracked.try_set(self.indices.len());
449 Ok(())
450 }
451}
452
453impl<T: Translator, E: BufferPooler + Storage + Metrics, K: Array, V: CodecShared>
454 crate::archive::Archive for Archive<T, E, K, V>
455{
456 type Key = K;
457 type Value = V;
458
459 async fn put(&mut self, index: u64, key: K, data: V) -> Result<(), Error> {
460 self.put_internal(index, key, data, true).await
461 }
462
463 async fn get(&self, identifier: Identifier<'_, K>) -> Result<Option<V>, Error> {
464 match identifier {
465 Identifier::Index(index) => self.get_index(index).await,
466 Identifier::Key(key) => self.get_key(key).await,
467 }
468 }
469
470 async fn has(&self, identifier: Identifier<'_, K>) -> Result<bool, Error> {
471 self.has.inc();
472 match identifier {
473 Identifier::Index(index) => Ok(self.has_index(index)),
474 Identifier::Key(key) => self.has_key(key).await,
475 }
476 }
477
478 async fn sync(&mut self) -> Result<(), Error> {
479 self.syncs.inc_by(self.pending.len() as u64);
481 self.requested.append(&mut self.pending);
482
483 self.oversized.sync(&self.requested).await?;
486
487 self.requested.clear();
488 Ok(())
489 }
490
491 async fn start_sync(&mut self) -> Result<Handle<()>, Error> {
492 self.syncs.inc_by(self.pending.len() as u64);
494
495 self.requested.append(&mut self.pending);
499 Ok(self.oversized.start_sync(&self.requested).await?)
500 }
501
502 fn next_gap(&self, index: u64) -> (Option<u64>, Option<u64>) {
503 self.intervals.next_gap(index)
504 }
505
506 fn missing_items(&self, index: u64, max: usize) -> Vec<u64> {
507 self.intervals.missing_items(index, max)
508 }
509
510 fn ranges(&self) -> impl Iterator<Item = (u64, u64)> {
511 self.intervals.iter().map(|(&s, &e)| (s, e))
512 }
513
514 fn ranges_from(&self, from: u64) -> impl Iterator<Item = (u64, u64)> {
515 self.intervals.iter_from(from).map(|(&s, &e)| (s, e))
516 }
517
518 fn first_index(&self) -> Option<u64> {
519 self.intervals.first_index()
520 }
521
522 fn last_index(&self) -> Option<u64> {
523 self.intervals.last_index()
524 }
525
526 #[boxed]
527 async fn destroy(self) -> Result<(), Error> {
528 Ok(self.oversized.destroy().await?)
529 }
530}
531
532impl<T: Translator, E: BufferPooler + Storage + Metrics, K: Array, V: CodecShared>
533 crate::archive::MultiArchive for Archive<T, E, K, V>
534{
535 async fn get_all(&self, index: u64) -> Result<Option<Vec<V>>, Error> {
536 self.gets.inc();
538
539 if !self.indices.contains_key(&index) {
541 return Ok(None);
542 }
543
544 let section = self.section(index);
546 let extra_count = self.extra_indices.get(&index).map_or(0, Vec::len);
547
548 let mut values = Vec::with_capacity(1 + extra_count);
549 for position in self.iter_positions(index) {
550 let entry = self.oversized.get(section, position).await?;
552
553 let (value_offset, value_size) = entry.value_location();
555 let value = self
556 .oversized
557 .get_value(section, value_offset, value_size)
558 .await?;
559 values.push(value);
560 }
561 Ok(Some(values))
562 }
563
564 async fn put_multi(&mut self, index: u64, key: K, data: V) -> Result<(), Error> {
565 self.put_internal(index, key, data, false).await
566 }
567
568 async fn has_at(&self, index: u64, key: &K) -> Result<bool, Error> {
569 self.has.inc();
570
571 if self.pruned(index) {
573 return Ok(false);
574 }
575
576 if !self.keys.get(key).any(|candidate| *candidate == index) {
581 return Ok(false);
582 }
583 let section = self.section(index);
584 for position in self.iter_positions(index) {
585 let entry = self.oversized.get(section, position).await?;
586 if entry.key.as_ref() == key.as_ref() {
587 return Ok(true);
588 }
589 self.unnecessary_reads.inc();
590 }
591 Ok(false)
592 }
593}
594
595#[cfg(all(test, feature = "arbitrary"))]
596mod conformance {
597 use super::*;
598 use commonware_codec::conformance::CodecConformance;
599 use commonware_utils::sequence::U64;
600
601 commonware_conformance::conformance_tests! {
602 CodecConformance<Record<U64>>
603 }
604}