1use super::{Config, Error};
2use crate::{Context, SyncCompletion};
3use commonware_codec::{Codec, Copying, FixedSize, ReadExt};
4use commonware_cryptography::{Crc32, crc32};
5use commonware_runtime::{
6 BLOB_PAGE_SIZE, Blob, BufMut, Error as RError, Handle, IoBufMut, ReadOptions, WriteOptions,
7 telemetry::metrics::{Counter, Gauge, GaugeExt, MetricsExt as _},
8};
9use commonware_utils::{Span, Widen, futures::try_join_all};
10use futures::FutureExt as _;
11use std::collections::{BTreeMap, BTreeSet, HashMap};
12use tracing::{debug, warn};
13
14const BLOB_NAMES: [&[u8]; 2] = [b"left", b"right"];
16
17struct Info {
19 start: usize,
20 length: usize,
21}
22
23impl Info {
24 const fn new(start: usize, length: usize) -> Self {
26 Self { start, length }
27 }
28}
29
30struct Wrapper<B: Blob, K: Span> {
32 blob: B,
33 version: u64,
34 lengths: HashMap<K, Info>,
35 modified: BTreeSet<K>,
36 data: IoBufMut,
37}
38
39impl<B: Blob, K: Span> Wrapper<B, K> {
40 const fn new(blob: B, version: u64, lengths: HashMap<K, Info>, data: IoBufMut) -> Self {
42 Self {
43 blob,
44 version,
45 lengths,
46 modified: BTreeSet::new(),
47 data,
48 }
49 }
50
51 fn empty(blob: B) -> Self {
53 Self {
54 blob,
55 version: 0,
56 lengths: HashMap::new(),
57 modified: BTreeSet::new(),
58 data: IoBufMut::default(),
59 }
60 }
61}
62
63struct State<B: Blob, K: Span> {
65 cursor: usize,
66 next_version: u64,
67 key_order_changed: u64,
68 blobs: [Wrapper<B, K>; 2],
69 pending: Option<SyncCompletion>,
74}
75
76struct Inner<E: Context, K: Span, V: Codec> {
78 context: E,
79
80 map: BTreeMap<K, V>,
81 partition: String,
82 state: State<E::Blob, K>,
83
84 sync_overwrites: Counter,
85 sync_rewrites: Counter,
86 keys: Gauge,
87}
88
89enum Loaded<B: Blob, K: Span, V> {
91 Valid(BTreeMap<K, V>, Wrapper<B, K>),
93 Invalid(B),
95}
96
97impl<E: Context, K: Span, V: Codec> Inner<E, K, V> {
98 async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
100 let (left_blob, left_len) = context.open(&cfg.partition, BLOB_NAMES[0]).await?;
102 let (right_blob, right_len) = context.open(&cfg.partition, BLOB_NAMES[1]).await?;
103
104 let left = Self::load(&cfg.codec_config, 0, left_blob, left_len).await?;
108 let right = Self::load(&cfg.codec_config, 1, right_blob, right_len).await?;
109 if matches!((&left, &right), (Loaded::Invalid(_), Loaded::Invalid(_))) {
110 return Err(Error::Corruption(
111 "both metadata copies failed validation".into(),
112 ));
113 }
114 let (left_map, left_wrapper) = Self::normalize(left).await?;
115 let (right_map, right_wrapper) = Self::normalize(right).await?;
116
117 let mut map = left_map;
119 let mut cursor = 0;
120 let mut version = left_wrapper.version;
121 if right_wrapper.version > left_wrapper.version {
122 cursor = 1;
123 map = right_map;
124 version = right_wrapper.version;
125 }
126 let next_version = version.checked_add(1).expect("version overflow");
127
128 let sync_rewrites =
130 context.counter("sync_rewrites", "number of syncs that rewrote all data");
131 let sync_overwrites = context.counter(
132 "sync_overwrites",
133 "number of syncs that modified existing data",
134 );
135 let keys = context.gauge("keys", "number of tracked keys");
136
137 let _ = keys.try_set(map.len());
139 Ok(Self {
140 context,
141
142 map,
143 partition: cfg.partition,
144 state: State {
145 cursor,
146 next_version,
147 key_order_changed: next_version, blobs: [left_wrapper, right_wrapper],
149 pending: None,
150 },
151
152 sync_rewrites,
153 sync_overwrites,
154 keys,
155 })
156 }
157
158 async fn load(
159 codec_config: &V::Cfg,
160 index: usize,
161 blob: E::Blob,
162 len: u64,
163 ) -> Result<Loaded<E::Blob, K, V>, Error> {
164 if len == 0 {
166 return Ok(Loaded::Valid(BTreeMap::new(), Wrapper::empty(blob)));
168 }
169
170 let len: usize = len.try_into().expect("blob too large for platform");
173 let buf = blob
174 .read_at(0, len, ReadOptions::DONT_CACHE)
175 .await?
176 .coalesce();
177
178 if buf.len() < 8 + crc32::Digest::SIZE {
182 warn!(blob = index, len = buf.len(), "blob is too short");
183 return Ok(Loaded::Invalid(blob));
184 }
185
186 let bytes = buf.as_ref();
188 let checksum_index = buf.len() - crc32::Digest::SIZE;
189 let stored_checksum = u32::from_be_bytes(bytes[checksum_index..].try_into().unwrap());
190 let computed_checksum = Crc32::checksum(&bytes[..checksum_index]);
191 if stored_checksum != computed_checksum {
192 warn!(
193 blob = index,
194 stored = stored_checksum,
195 computed = computed_checksum,
196 "checksum mismatch"
197 );
198 return Ok(Loaded::Invalid(blob));
199 }
200
201 let version = u64::from_be_bytes(bytes[..8].try_into().unwrap());
203
204 let mut data = BTreeMap::new();
209 let mut lengths = HashMap::new();
210 let mut cursor = u64::SIZE;
211 while cursor < checksum_index {
212 let key =
214 K::read(&mut Copying(&bytes[cursor..])).expect("unable to read key from blob");
215 cursor += key.encode_size();
216
217 let value = V::read_cfg(&mut Copying(&bytes[cursor..]), codec_config)
219 .expect("unable to read value from blob");
220 lengths.insert(key.clone(), Info::new(cursor, value.encode_size()));
221 cursor += value.encode_size();
222 data.insert(key, value);
223 }
224
225 Ok(Loaded::Valid(
226 data,
227 Wrapper::new(blob, version, lengths, buf),
228 ))
229 }
230
231 async fn normalize(
233 copy: Loaded<E::Blob, K, V>,
234 ) -> Result<(BTreeMap<K, V>, Wrapper<E::Blob, K>), Error> {
235 match copy {
236 Loaded::Valid(map, wrapper) => Ok((map, wrapper)),
237 Loaded::Invalid(blob) => {
238 blob.resize(0).await?;
239 blob.sync().await?;
240 Ok((BTreeMap::new(), Wrapper::empty(blob)))
241 }
242 }
243 }
244
245 fn get(&self, key: &K) -> Option<&V> {
247 self.map.get(key)
248 }
249
250 fn get_mut(&mut self, key: &K) -> Option<&mut V> {
252 let value = self.map.get_mut(key)?;
254
255 let cursor = self.state.cursor;
259 self.state.blobs[cursor].modified.insert(key.clone());
260 self.state.blobs[1 - cursor].modified.insert(key.clone());
261
262 Some(value)
263 }
264
265 fn clear(&mut self) {
267 self.map.clear();
269
270 self.state.key_order_changed = self.state.next_version;
272 self.keys.set(0);
273 }
274
275 fn put(&mut self, key: K, value: V) -> Option<V> {
277 let previous = self.map.insert(key.clone(), value);
279
280 if previous.is_some() {
284 let cursor = self.state.cursor;
285 self.state.blobs[cursor].modified.insert(key.clone());
286 self.state.blobs[1 - cursor].modified.insert(key);
287 } else {
288 self.state.key_order_changed = self.state.next_version;
289 }
290 let _ = self.keys.try_set(self.map.len());
291 previous
292 }
293
294 fn upsert(&mut self, key: K, f: impl FnOnce(&mut V))
296 where
297 V: Default,
298 {
299 if let Some(value) = self.get_mut(&key) {
300 f(value);
302 } else {
303 let mut value = V::default();
305 f(&mut value);
306 self.put(key, value);
307 }
308 }
309
310 fn remove(&mut self, key: &K) -> Option<V> {
312 let past = self.map.remove(key);
314
315 if past.is_some() {
317 self.state.key_order_changed = self.state.next_version;
318 }
319 let _ = self.keys.try_set(self.map.len());
320
321 past
322 }
323
324 fn keys(&self) -> impl Iterator<Item = &K> {
326 self.map.keys()
327 }
328
329 fn retain(&mut self, mut f: impl FnMut(&K, &V) -> bool) {
331 let old_len = self.map.len();
333 self.map.retain(|k, v| f(k, v));
334 let new_len = self.map.len();
335
336 if new_len != old_len {
338 self.state.key_order_changed = self.state.next_version;
339 let _ = self.keys.try_set(self.map.len());
340 }
341 }
342
343 async fn wait_for_pending(&mut self) -> Result<(), RError> {
345 let Some(completion) = &self.state.pending else {
349 return Ok(());
350 };
351 completion.clone().await?;
352 self.state.pending = None;
353 Ok(())
354 }
355
356 async fn sync(&mut self) -> Result<(), RError> {
358 self.wait_for_pending().await?;
359 self.write_next_version(false).await?;
360 Ok(())
361 }
362
363 async fn start_sync(&mut self) -> Result<Handle<()>, RError> {
365 self.wait_for_pending().await?;
366 self.write_next_version(true).await
367 }
368
369 async fn write_next_version(&mut self, pipelined: bool) -> Result<Handle<()>, RError> {
371 let cursor = self.state.cursor;
373 let next_version = self.state.next_version;
374 let key_order_changed = self.state.key_order_changed;
375
376 let past_version = self.state.blobs[cursor].version;
382 let next_next_version = next_version.checked_add(1).expect("version overflow");
383
384 let target_cursor = 1 - cursor;
386
387 if key_order_changed < past_version && self.state.blobs[target_cursor].modified.is_empty() {
392 return Ok(Handle::ready(Ok(())));
393 }
394
395 self.state.cursor = target_cursor;
397 self.state.next_version = next_next_version;
398
399 let target = &mut self.state.blobs[target_cursor];
401
402 let mut overwrite = true;
406 if key_order_changed < past_version {
407 for key in target.modified.iter() {
408 let info = target.lengths.get(key).expect("key must exist");
409 let new_value = self.map.get(key).expect("key must exist");
410 if info.length == new_value.encode_size() {
411 let start = info.start;
413 let end = start + info.length;
414 let mut buf = &mut target.data.as_mut()[start..end];
415 new_value.write(&mut buf);
416 } else {
417 overwrite = false;
419 break;
420 }
421 }
422 } else {
423 overwrite = false;
425 }
426
427 if overwrite {
429 (&mut target.data.as_mut()[0..u64::SIZE]).put_u64(next_version);
431
432 let checksum_index = target.data.len() - crc32::Digest::SIZE;
434 let checksum = Crc32::checksum(&target.data.as_ref()[..checksum_index]);
435 (&mut target.data.as_mut()[checksum_index..]).put_u32(checksum);
436
437 let data = std::mem::take(&mut target.data).freeze();
441
442 let full_write = data.len() <= Widen::widen(BLOB_PAGE_SIZE);
445 if full_write {
446 let options = if pipelined {
449 WriteOptions::DONT_CACHE
450 } else {
451 WriteOptions::SYNC | WriteOptions::DONT_CACHE
452 };
453 target.blob.write_at(0, data.clone(), options).await?;
454 } else {
455 let writes = target
457 .modified
458 .iter()
459 .map(|key| {
460 let info = target.lengths.get(key).expect("key must exist");
461 let start = info.start;
462 let end = start + info.length;
463 target.blob.write_at(
464 Widen::widen(start),
465 data.slice(start..end),
466 WriteOptions::DONT_CACHE,
467 )
468 })
469 .chain([
470 target
471 .blob
472 .write_at(0, data.slice(0..u64::SIZE), WriteOptions::DONT_CACHE),
473 target.blob.write_at(
474 Widen::widen(checksum_index),
475 data.slice(checksum_index..checksum_index + crc32::Digest::SIZE),
476 WriteOptions::DONT_CACHE,
477 ),
478 ]);
479 try_join_all(writes).await?;
480 }
481 let sync = if pipelined {
482 Some(target.blob.start_sync().await)
483 } else if full_write {
484 None
486 } else {
487 target.blob.sync().await?;
488 None
489 };
490
491 target.modified.clear();
493
494 target.version = next_version;
496 target.data = IoBufMut::from(data);
497 self.sync_overwrites.inc();
498 return Ok(self.record_pending(sync));
499 }
500
501 target.modified.clear();
503
504 let mut lengths = HashMap::with_capacity(self.map.len());
508 let mut next_data_len = u64::SIZE + crc32::Digest::SIZE;
509 for (key, value) in &self.map {
510 let value_len = value.encode_size();
511 lengths.insert(key.clone(), Info::new(0, value_len));
512 next_data_len += key.encode_size() + value_len;
513 }
514
515 let target_data_len = target.data.len();
518
519 let mut next_data = if target.data.capacity() >= next_data_len {
521 let mut data = std::mem::take(&mut target.data);
522 data.clear();
523 data
524 } else {
525 IoBufMut::with_capacity(next_data_len)
526 };
527 next_data.put_u64(next_version);
528
529 for (key, value) in &self.map {
531 key.write(&mut next_data);
532 let info = lengths.get_mut(key).expect("key must exist");
533 info.start = next_data.len();
534 value.write(&mut next_data);
535 }
536 next_data.put_u32(Crc32::checksum(next_data.as_ref()));
537
538 let next_data = next_data.freeze();
540 let shrinking = next_data.len() < target_data_len;
541
542 let sync = if pipelined {
544 target
545 .blob
546 .write_at(0, next_data.clone(), WriteOptions::DONT_CACHE)
547 .await?;
548 if shrinking {
549 target.blob.resize(next_data.len() as u64).await?;
550 }
551 Some(target.blob.start_sync().await)
552 } else if shrinking {
553 target
554 .blob
555 .write_at(0, next_data.clone(), WriteOptions::DONT_CACHE)
556 .await?;
557 target.blob.resize(next_data.len() as u64).await?;
558 target.blob.sync().await?;
559 None
560 } else {
561 target
564 .blob
565 .write_at(
566 0,
567 next_data.clone(),
568 WriteOptions::SYNC | WriteOptions::DONT_CACHE,
569 )
570 .await?;
571 None
572 };
573
574 target.version = next_version;
576 target.lengths = lengths;
577 target.data = IoBufMut::from(next_data);
578
579 self.sync_rewrites.inc();
580 Ok(self.record_pending(sync))
581 }
582
583 fn record_pending(&mut self, sync: Option<Handle<()>>) -> Handle<()> {
585 let Some(sync) = sync else {
586 return Handle::ready(Ok(()));
587 };
588 let completion: SyncCompletion = sync.boxed().shared();
589 let handle = Handle::from_future(completion.clone());
590 self.state.pending = Some(completion);
591 handle
592 }
593
594 async fn destroy(mut self) -> Result<(), Error> {
596 self.wait_for_pending().await?;
599
600 let State { cursor, blobs, .. } = self.state;
603 drop(blobs);
604 for i in [1 - cursor, cursor] {
605 self.context
606 .remove(&self.partition, Some(BLOB_NAMES[i]))
607 .await?;
608 debug!(blob = i, "destroyed blob");
609 }
610 match self.context.remove(&self.partition, None).await {
611 Ok(()) => {}
612 Err(RError::PartitionMissing(_)) => {
613 }
615 Err(err) => return Err(Error::Runtime(err)),
616 }
617 Ok(())
618 }
619}
620
621pub struct Metadata<E: Context, K: Span, V: Codec>(Box<Inner<E, K, V>>);
626
627impl<E: Context, K: Span, V: Codec> std::fmt::Debug for Metadata<E, K, V> {
628 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
629 f.debug_struct("Metadata")
630 .field("keys", &self.0.map.len())
631 .finish_non_exhaustive()
632 }
633}
634
635impl<E: Context, K: Span, V: Codec> Metadata<E, K, V> {
636 pub async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
638 Ok(Self(Box::new(Inner::init(context, cfg).await?)))
639 }
640
641 pub fn get(&self, key: &K) -> Option<&V> {
643 self.0.get(key)
644 }
645
646 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
648 self.0.get_mut(key)
649 }
650
651 pub fn clear(&mut self) {
654 self.0.clear();
655 }
656
657 pub fn put(&mut self, key: K, value: V) -> Option<V> {
663 self.0.put(key, value)
664 }
665
666 pub async fn put_sync(mut self, key: K, value: V) -> Result<Self, Error> {
671 self.0.put(key, value);
672 self.0.sync().await?;
673 Ok(self)
674 }
675
676 pub fn upsert(&mut self, key: K, f: impl FnOnce(&mut V))
678 where
679 V: Default,
680 {
681 self.0.upsert(key, f);
682 }
683
684 pub async fn upsert_sync(mut self, key: K, f: impl FnOnce(&mut V)) -> Result<Self, Error>
686 where
687 V: Default,
688 {
689 self.0.upsert(key, f);
690 self.0.sync().await?;
691 Ok(self)
692 }
693
694 pub fn remove(&mut self, key: &K) -> Option<V> {
696 self.0.remove(key)
697 }
698
699 pub fn keys(&self) -> impl Iterator<Item = &K> {
701 self.0.keys()
702 }
703
704 pub fn retain(&mut self, f: impl FnMut(&K, &V) -> bool) {
706 self.0.retain(f);
707 }
708
709 pub async fn sync(mut self) -> Result<Self, Error> {
711 self.0.sync().await?;
712 Ok(self)
713 }
714
715 pub async fn start_sync(mut self) -> Result<(Self, Handle<()>), Error> {
723 let handle = self.0.start_sync().await?;
724 Ok((self, handle))
725 }
726
727 pub(crate) async fn wait_for_sync(&mut self) -> Result<(), Error> {
730 Ok(self.0.wait_for_pending().await?)
731 }
732
733 pub async fn destroy(self) -> Result<(), Error> {
739 self.0.destroy().await
740 }
741}