1use super::{Config, Error};
2use crate::Context;
3use commonware_codec::{Codec, FixedSize, ReadExt};
4use commonware_cryptography::{crc32, Crc32};
5use commonware_runtime::{
6 telemetry::metrics::{Counter, Gauge, GaugeExt, MetricsExt as _},
7 Blob, BufMut, Error as RError, IoBufMut,
8};
9use commonware_utils::Span;
10use futures::future::try_join_all;
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}
70
71pub struct Metadata<E: Context, K: Span, V: Codec> {
73 context: E,
74
75 map: BTreeMap<K, V>,
76 partition: String,
77 state: State<E::Blob, K>,
78
79 sync_overwrites: Counter,
80 sync_rewrites: Counter,
81 keys: Gauge,
82}
83
84impl<E: Context, K: Span, V: Codec> Metadata<E, K, V> {
85 pub async fn init(context: E, cfg: Config<V::Cfg>) -> Result<Self, Error> {
87 let (left_blob, left_len) = context.open(&cfg.partition, BLOB_NAMES[0]).await?;
89 let (right_blob, right_len) = context.open(&cfg.partition, BLOB_NAMES[1]).await?;
90
91 let (left_map, left_wrapper) =
93 Self::load(&context, &cfg.codec_config, 0, left_blob, left_len).await?;
94 let (right_map, right_wrapper) =
95 Self::load(&context, &cfg.codec_config, 1, right_blob, right_len).await?;
96
97 let mut map = left_map;
99 let mut cursor = 0;
100 let mut version = left_wrapper.version;
101 if right_wrapper.version > left_wrapper.version {
102 cursor = 1;
103 map = right_map;
104 version = right_wrapper.version;
105 }
106 let next_version = version.checked_add(1).expect("version overflow");
107
108 let sync_rewrites =
110 context.counter("sync_rewrites", "number of syncs that rewrote all data");
111 let sync_overwrites = context.counter(
112 "sync_overwrites",
113 "number of syncs that modified existing data",
114 );
115 let keys = context.gauge("keys", "number of tracked keys");
116
117 let _ = keys.try_set(map.len());
119 Ok(Self {
120 context,
121
122 map,
123 partition: cfg.partition,
124 state: State {
125 cursor,
126 next_version,
127 key_order_changed: next_version, blobs: [left_wrapper, right_wrapper],
129 },
130
131 sync_rewrites,
132 sync_overwrites,
133 keys,
134 })
135 }
136
137 async fn load(
138 context: &E,
139 codec_config: &V::Cfg,
140 index: usize,
141 blob: E::Blob,
142 len: u64,
143 ) -> Result<(BTreeMap<K, V>, Wrapper<E::Blob, K>), Error> {
144 if len == 0 {
146 return Ok((BTreeMap::new(), Wrapper::empty(blob)));
148 }
149
150 let len: usize = len.try_into().expect("blob too large for platform");
152 let buf = blob
153 .read_at(0, len)
154 .await?
155 .coalesce_with_pool(context.storage_buffer_pool());
156
157 if buf.len() < 8 + crc32::Digest::SIZE {
161 warn!(
163 blob = index,
164 len = buf.len(),
165 "blob is too short: truncating"
166 );
167 blob.resize(0).await?;
168 blob.sync().await?;
169 return Ok((BTreeMap::new(), Wrapper::empty(blob)));
170 }
171
172 let checksum_index = buf.len() - crc32::Digest::SIZE;
174 let stored_checksum =
175 u32::from_be_bytes(buf.as_ref()[checksum_index..].try_into().unwrap());
176 let computed_checksum = Crc32::checksum(&buf.as_ref()[..checksum_index]);
177 if stored_checksum != computed_checksum {
178 warn!(
180 blob = index,
181 stored = stored_checksum,
182 computed = computed_checksum,
183 "checksum mismatch: truncating"
184 );
185 blob.resize(0).await?;
186 blob.sync().await?;
187 return Ok((BTreeMap::new(), Wrapper::empty(blob)));
188 }
189
190 let version = u64::from_be_bytes(buf.as_ref()[..8].try_into().unwrap());
192
193 let mut data = BTreeMap::new();
198 let mut lengths = HashMap::new();
199 let mut cursor = u64::SIZE;
200 while cursor < checksum_index {
201 let key = K::read(&mut buf.as_ref()[cursor..].as_ref())
203 .expect("unable to read key from blob");
204 cursor += key.encode_size();
205
206 let value = V::read_cfg(&mut buf.as_ref()[cursor..].as_ref(), codec_config)
208 .expect("unable to read value from blob");
209 lengths.insert(key.clone(), Info::new(cursor, value.encode_size()));
210 cursor += value.encode_size();
211 data.insert(key, value);
212 }
213
214 Ok((data, Wrapper::new(blob, version, lengths, buf)))
216 }
217
218 pub fn get(&self, key: &K) -> Option<&V> {
220 self.map.get(key)
221 }
222
223 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
225 let value = self.map.get_mut(key)?;
227
228 let cursor = self.state.cursor;
232 self.state.blobs[cursor].modified.insert(key.clone());
233 self.state.blobs[1 - cursor].modified.insert(key.clone());
234
235 Some(value)
236 }
237
238 pub fn clear(&mut self) {
241 self.map.clear();
243
244 self.state.key_order_changed = self.state.next_version;
246 self.keys.set(0);
247 }
248
249 pub fn put(&mut self, key: K, value: V) -> Option<V> {
255 let previous = self.map.insert(key.clone(), value);
257
258 if previous.is_some() {
262 let cursor = self.state.cursor;
263 self.state.blobs[cursor].modified.insert(key.clone());
264 self.state.blobs[1 - cursor].modified.insert(key);
265 } else {
266 self.state.key_order_changed = self.state.next_version;
267 }
268 let _ = self.keys.try_set(self.map.len());
269 previous
270 }
271
272 pub async fn put_sync(&mut self, key: K, value: V) -> Result<(), Error> {
277 self.put(key, value);
278 self.sync().await
279 }
280
281 pub fn upsert(&mut self, key: K, f: impl FnOnce(&mut V))
283 where
284 V: Default,
285 {
286 if let Some(value) = self.get_mut(&key) {
287 f(value);
289 } else {
290 let mut value = V::default();
292 f(&mut value);
293 self.put(key, value);
294 }
295 }
296
297 pub async fn upsert_sync(&mut self, key: K, f: impl FnOnce(&mut V)) -> Result<(), Error>
299 where
300 V: Default,
301 {
302 self.upsert(key, f);
303 self.sync().await
304 }
305
306 pub fn remove(&mut self, key: &K) -> Option<V> {
308 let past = self.map.remove(key);
310
311 if past.is_some() {
313 self.state.key_order_changed = self.state.next_version;
314 }
315 let _ = self.keys.try_set(self.map.len());
316
317 past
318 }
319
320 pub fn keys(&self) -> impl Iterator<Item = &K> {
322 self.map.keys()
323 }
324
325 pub fn retain(&mut self, mut f: impl FnMut(&K, &V) -> bool) {
327 let old_len = self.map.len();
329 self.map.retain(|k, v| f(k, v));
330 let new_len = self.map.len();
331
332 if new_len != old_len {
334 self.state.key_order_changed = self.state.next_version;
335 let _ = self.keys.try_set(self.map.len());
336 }
337 }
338
339 pub async fn sync(&mut self) -> Result<(), Error> {
341 let cursor = self.state.cursor;
343 let next_version = self.state.next_version;
344 let key_order_changed = self.state.key_order_changed;
345
346 let past_version = self.state.blobs[cursor].version;
352 let next_next_version = next_version.checked_add(1).expect("version overflow");
353
354 let target_cursor = 1 - cursor;
356
357 if key_order_changed < past_version && self.state.blobs[target_cursor].modified.is_empty() {
362 return Ok(());
363 }
364
365 self.state.cursor = target_cursor;
367 self.state.next_version = next_next_version;
368
369 let target = &mut self.state.blobs[target_cursor];
371
372 let mut overwrite = true;
376 if key_order_changed < past_version {
377 for key in target.modified.iter() {
378 let info = target.lengths.get(key).expect("key must exist");
379 let new_value = self.map.get(key).expect("key must exist");
380 if info.length == new_value.encode_size() {
381 let start = info.start;
383 let end = start + info.length;
384 let mut buf = &mut target.data.as_mut()[start..end];
385 new_value.write(&mut buf);
386 } else {
387 overwrite = false;
389 break;
390 }
391 }
392 } else {
393 overwrite = false;
395 }
396
397 if overwrite {
399 (&mut target.data.as_mut()[0..u64::SIZE]).put_u64(next_version);
401
402 let checksum_index = target.data.len() - crc32::Digest::SIZE;
404 let checksum = Crc32::checksum(&target.data.as_ref()[..checksum_index]);
405 (&mut target.data.as_mut()[checksum_index..]).put_u32(checksum);
406
407 let data = std::mem::take(&mut target.data).freeze();
410
411 let writes = target
414 .modified
415 .iter()
416 .map(|key| {
417 let info = target.lengths.get(key).expect("key must exist");
418 let start = info.start;
419 let end = start + info.length;
420 target.blob.write_at(start as u64, data.slice(start..end))
421 })
422 .chain([
423 target.blob.write_at(0, data.slice(0..u64::SIZE)),
424 target.blob.write_at(
425 checksum_index as u64,
426 data.slice(checksum_index..checksum_index + crc32::Digest::SIZE),
427 ),
428 ]);
429 try_join_all(writes).await?;
430 target.blob.sync().await?;
431
432 target.modified.clear();
434
435 target.version = next_version;
437 target.data = data.into_mut_with_pool(self.context.storage_buffer_pool());
438 self.sync_overwrites.inc();
439 return Ok(());
440 }
441
442 target.modified.clear();
444
445 let mut lengths = HashMap::with_capacity(self.map.len());
449 let mut next_data_len = u64::SIZE + crc32::Digest::SIZE;
450 for (key, value) in &self.map {
451 let value_len = value.encode_size();
452 lengths.insert(key.clone(), Info::new(0, value_len));
453 next_data_len += key.encode_size() + value_len;
454 }
455
456 let target_data_len = target.data.len();
459
460 let mut next_data = if target.data.capacity() >= next_data_len {
462 let mut data = std::mem::take(&mut target.data);
463 data.clear();
464 data
465 } else {
466 self.context.storage_buffer_pool().alloc(next_data_len)
467 };
468 next_data.put_u64(next_version);
469
470 for (key, value) in &self.map {
472 key.write(&mut next_data);
473 let info = lengths.get_mut(key).expect("key must exist");
474 info.start = next_data.len();
475 value.write(&mut next_data);
476 }
477 next_data.put_u32(Crc32::checksum(next_data.as_ref()));
478
479 let next_data = next_data.freeze();
481 if next_data.len() < target_data_len {
482 target.blob.write_at(0, next_data.clone()).await?;
483 target.blob.resize(next_data.len() as u64).await?;
484 target.blob.sync().await?;
485 } else {
486 target.blob.write_at_sync(0, next_data.clone()).await?;
489 }
490
491 target.version = next_version;
493 target.lengths = lengths;
494 target.data = next_data.into_mut_with_pool(self.context.storage_buffer_pool());
495
496 self.sync_rewrites.inc();
497 Ok(())
498 }
499
500 pub async fn destroy(self) -> Result<(), Error> {
502 let state = self.state;
503 for (i, wrapper) in state.blobs.into_iter().enumerate() {
504 drop(wrapper.blob);
505 self.context
506 .remove(&self.partition, Some(BLOB_NAMES[i]))
507 .await?;
508 debug!(blob = i, "destroyed blob");
509 }
510 match self.context.remove(&self.partition, None).await {
511 Ok(()) => {}
512 Err(RError::PartitionMissing(_)) => {
513 }
515 Err(err) => return Err(Error::Runtime(err)),
516 }
517 Ok(())
518 }
519}