1use serde::{Deserialize, Serialize};
11use std::borrow::Borrow;
12use std::collections::HashSet;
13use std::fmt;
14use std::marker::PhantomData;
15
16use super::cid::Cid;
17use super::error::{Diff, Error, Mutation};
18use super::manifest::{ManifestStore, ManifestStoreScan, NamedRootRetention, RootManifest};
19use super::range::{CursorWindow, RangeCursor, RangeIter, RangePage, ReverseCursor, ReversePage};
20use super::read::{EntryRef, OwnedValueLease, ReadSession, ValueRefView};
21use super::secondary_index::indexed_collection_root_name;
22use super::stats::TreeStats;
23use super::store::Store;
24use super::transaction::{TransactionConflict, TransactionUpdate, TransactionalStore};
25use super::tree::Tree;
26use super::{current_unix_time_millis, KeyValue, Prolly};
27
28pub const VERSIONED_MAP_ROOT_PREFIX: &[u8] = b"maps/versioned/";
30
31pub const DEFAULT_VERSIONED_MAP_RETRIES: usize = 8;
33
34const HEAD_SUFFIX: &[u8] = b"/head";
35const VERSIONS_SUFFIX: &[u8] = b"/versions/";
36const VERSIONED_MAP_BACKUP_FORMAT_VERSION: u64 = 1;
37
38#[allow(dead_code)]
39pub(crate) enum MapWriteAuthority {
40 Unmanaged,
41}
42async fn guard_async_managed_map_write<S>(
43 tx: &super::transaction::AsyncProllyTransaction<'_, S>,
44 map_id: &[u8],
45 authority: MapWriteAuthority,
46) -> Result<(), Error>
47where
48 S: super::store::AsyncStore
49 + super::manifest::AsyncManifestStore
50 + super::transaction::AsyncTransactionalStore,
51 <S as super::store::AsyncStore>::Error: Send + Sync,
52 <S as super::manifest::AsyncManifestStore>::Error: Send + Sync,
53{
54 let canonical_root = indexed_collection_root_name(map_id)?;
55 match (
56 tx.load_named_root(&canonical_root).await?.is_some(),
57 authority,
58 ) {
59 (true, MapWriteAuthority::Unmanaged) => Err(Error::IndexesRequireIndexedMap {
60 map_id: map_id.to_vec(),
61 active_indexes: Vec::new(),
62 }),
63 (false, MapWriteAuthority::Unmanaged) => Ok(()),
64 }
65}
66
67pub(crate) fn guard_managed_map_write<S>(
68 tx: &super::transaction::ProllyTransaction<'_, S>,
69 map_id: &[u8],
70 authority: MapWriteAuthority,
71) -> Result<(), Error>
72where
73 S: Store + ManifestStore + TransactionalStore,
74{
75 let canonical_root = indexed_collection_root_name(map_id)?;
76 match (tx.load_named_root(&canonical_root)?.is_some(), authority) {
77 (true, MapWriteAuthority::Unmanaged) => Err(Error::IndexesRequireIndexedMap {
78 map_id: map_id.to_vec(),
79 active_indexes: Vec::new(),
80 }),
81 (false, MapWriteAuthority::Unmanaged) => Ok(()),
82 }
83}
84
85#[derive(Clone, Debug, Default, PartialEq, Eq)]
87pub struct ProofAuthentication {
88 pub key_id: Vec<u8>,
90 pub context: Vec<u8>,
92 pub issued_at_millis: Option<u64>,
94 pub expires_at_millis: Option<u64>,
96 pub nonce: Vec<u8>,
98}
99
100impl ProofAuthentication {
101 pub fn new(key_id: impl Into<Vec<u8>>) -> Self {
103 Self {
104 key_id: key_id.into(),
105 ..Self::default()
106 }
107 }
108
109 pub fn with_context(mut self, context: impl Into<Vec<u8>>) -> Self {
111 self.context = context.into();
112 self
113 }
114
115 pub fn with_validity(
117 mut self,
118 issued_at_millis: Option<u64>,
119 expires_at_millis: Option<u64>,
120 ) -> Self {
121 self.issued_at_millis = issued_at_millis;
122 self.expires_at_millis = expires_at_millis;
123 self
124 }
125
126 pub fn with_nonce(mut self, nonce: impl Into<Vec<u8>>) -> Self {
128 self.nonce = nonce.into();
129 self
130 }
131}
132
133#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
139pub struct MapVersionId(Cid);
140
141impl MapVersionId {
142 pub fn for_tree(tree: &Tree) -> Result<Self, Error> {
144 let bytes = RootManifest::from_tree(tree).to_bytes()?;
145 Ok(Self(Cid::from_bytes(&bytes)))
146 }
147
148 pub fn as_cid(&self) -> &Cid {
150 &self.0
151 }
152
153 pub fn into_cid(self) -> Cid {
155 self.0
156 }
157
158 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
163 let cid = <[u8; 32]>::try_from(bytes).map_err(|_| {
164 Error::InvalidVersionedMap(format!(
165 "map version identifier must be exactly 32 bytes, got {}",
166 bytes.len()
167 ))
168 })?;
169 Ok(Self(Cid(cid)))
170 }
171}
172
173impl fmt::Display for MapVersionId {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 for byte in self.0.as_bytes() {
176 write!(f, "{byte:02x}")?;
177 }
178 Ok(())
179 }
180}
181
182#[derive(Clone, Debug, PartialEq)]
184pub struct MapVersion {
185 pub id: MapVersionId,
187 pub tree: Tree,
189 pub created_at_millis: Option<u64>,
191 pub is_head: bool,
193}
194
195#[derive(Clone, Debug, Default, PartialEq, Eq)]
201pub struct VersionPruneResult {
202 pub retained: Vec<MapVersionId>,
204 pub removed: Vec<MapVersionId>,
206}
207
208#[derive(Clone, Debug)]
210pub struct VersionedMapBatchResult {
211 pub version: MapVersion,
213 pub stats: super::batch::BatchApplyStats,
215}
216
217#[derive(Clone, Debug, PartialEq)]
219pub struct MapBackupVersion {
220 pub id: MapVersionId,
222 pub created_at_millis: Option<u64>,
224 pub bundle: super::sync::SnapshotBundle,
226}
227
228#[derive(Clone, Debug, PartialEq)]
230pub struct VersionedMapBackup {
231 pub map_id: Vec<u8>,
233 pub head: MapVersionId,
235 pub versions: Vec<MapBackupVersion>,
237}
238
239#[derive(Serialize, Deserialize)]
240struct VersionedMapBackupWire {
241 version: u64,
242 map_id: Vec<u8>,
243 head: MapVersionId,
244 versions: Vec<MapBackupVersionWire>,
245}
246
247#[derive(Serialize, Deserialize)]
248struct MapBackupVersionWire {
249 id: MapVersionId,
250 created_at_millis: Option<u64>,
251 bundle: Vec<u8>,
252}
253
254impl VersionedMapBackup {
255 pub fn verify(&self) -> Result<(), Error> {
257 let mut seen = HashSet::with_capacity(self.versions.len());
258 let mut found_head = false;
259 for version in &self.versions {
260 if !seen.insert(version.id.clone()) {
261 return Err(Error::InvalidVersionedMap(format!(
262 "backup contains duplicate version {}",
263 version.id
264 )));
265 }
266 let verification = version.bundle.verify()?;
267 if !verification.valid {
268 return Err(Error::InvalidVersionedMap(format!(
269 "backup version {} is not self-contained",
270 version.id
271 )));
272 }
273 let actual = MapVersionId::for_tree(&version.bundle.tree)?;
274 if actual != version.id {
275 return Err(Error::InvalidVersionedMap(format!(
276 "backup version {} contains tree {}",
277 version.id, actual
278 )));
279 }
280 found_head |= version.id == self.head;
281 }
282 if !found_head {
283 return Err(Error::InvalidVersionedMap(format!(
284 "backup head {} is absent from its catalog",
285 self.head
286 )));
287 }
288 Ok(())
289 }
290
291 pub fn to_bytes(&self) -> Result<Vec<u8>, Error> {
293 self.verify()?;
294 let wire = VersionedMapBackupWire {
295 version: VERSIONED_MAP_BACKUP_FORMAT_VERSION,
296 map_id: self.map_id.clone(),
297 head: self.head.clone(),
298 versions: self
299 .versions
300 .iter()
301 .map(|version| {
302 Ok(MapBackupVersionWire {
303 id: version.id.clone(),
304 created_at_millis: version.created_at_millis,
305 bundle: version.bundle.to_bytes()?,
306 })
307 })
308 .collect::<Result<Vec<_>, Error>>()?,
309 };
310 serde_cbor::ser::to_vec_packed(&wire).map_err(|err| Error::Serialize(err.to_string()))
311 }
312
313 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
315 let wire: VersionedMapBackupWire =
316 serde_cbor::from_slice(bytes).map_err(|err| Error::Deserialize(err.to_string()))?;
317 if wire.version != VERSIONED_MAP_BACKUP_FORMAT_VERSION {
318 return Err(Error::InvalidVersionedMap(format!(
319 "unsupported backup format version {}",
320 wire.version
321 )));
322 }
323 let backup = Self {
324 map_id: wire.map_id,
325 head: wire.head,
326 versions: wire
327 .versions
328 .into_iter()
329 .map(|version| {
330 Ok(MapBackupVersion {
331 id: version.id,
332 created_at_millis: version.created_at_millis,
333 bundle: super::sync::SnapshotBundle::from_bytes(&version.bundle)?,
334 })
335 })
336 .collect::<Result<Vec<_>, Error>>()?,
337 };
338 backup.verify()?;
339 Ok(backup)
340 }
341}
342
343impl VersionPruneResult {
344 pub fn removed_count(&self) -> usize {
346 self.removed.len()
347 }
348
349 pub fn is_unchanged(&self) -> bool {
351 self.removed.is_empty()
352 }
353}
354
355impl MapVersion {
356 pub(crate) fn new(
357 tree: Tree,
358 created_at_millis: Option<u64>,
359 is_head: bool,
360 ) -> Result<Self, Error> {
361 Ok(Self {
362 id: MapVersionId::for_tree(&tree)?,
363 tree,
364 created_at_millis,
365 is_head,
366 })
367 }
368}
369
370pub struct MapSnapshot<'a, S: Store> {
377 prolly: &'a Prolly<S>,
378 version: MapVersion,
379}
380
381pub struct MapReverseIter<'a, S: Store> {
383 prolly: &'a Prolly<S>,
384 tree: Tree,
385 start: Vec<u8>,
386 prefix: Option<Vec<u8>>,
387 cursor: ReverseCursor,
388 page_size: usize,
389 buffered: std::vec::IntoIter<(Vec<u8>, Vec<u8>)>,
390 finished: bool,
391}
392
393impl<'a, S: Store> MapReverseIter<'a, S> {
394 fn new(
395 prolly: &'a Prolly<S>,
396 tree: Tree,
397 start: Vec<u8>,
398 prefix: Option<Vec<u8>>,
399 page_size: usize,
400 ) -> Self {
401 Self {
402 prolly,
403 tree,
404 start,
405 prefix,
406 cursor: ReverseCursor::end(),
407 page_size: page_size.max(1),
408 buffered: Vec::new().into_iter(),
409 finished: false,
410 }
411 }
412}
413
414impl<S: Store> Iterator for MapReverseIter<'_, S> {
415 type Item = Result<(Vec<u8>, Vec<u8>), Error>;
416
417 fn next(&mut self) -> Option<Self::Item> {
418 loop {
419 if let Some(entry) = self.buffered.next() {
420 return Some(Ok(entry));
421 }
422 if self.finished {
423 return None;
424 }
425 let page = match &self.prefix {
426 Some(prefix) => self.prolly.prefix_reverse_page(
427 &self.tree,
428 prefix,
429 &self.cursor,
430 self.page_size,
431 ),
432 None => {
433 self.prolly
434 .reverse_page(&self.tree, &self.cursor, &self.start, self.page_size)
435 }
436 };
437 match page {
438 Ok(page) => {
439 self.finished = page.next_cursor.is_none();
440 if let Some(cursor) = page.next_cursor {
441 self.cursor = cursor;
442 }
443 self.buffered = page.entries.into_iter();
444 }
445 Err(error) => {
446 self.finished = true;
447 return Some(Err(error));
448 }
449 }
450 }
451 }
452}
453
454pub struct MapComparison<'a, S: Store> {
456 prolly: &'a Prolly<S>,
457 base: MapVersion,
458 target: MapVersion,
459}
460
461pub struct MapMerge<'a, S: Store> {
463 prolly: &'a Prolly<S>,
464 map_id: Vec<u8>,
465 base: MapVersion,
466 head: MapVersion,
467 candidate: MapVersion,
468}
469
470impl<'a, S: Store> MapMerge<'a, S> {
471 fn new(
472 prolly: &'a Prolly<S>,
473 map_id: Vec<u8>,
474 base: MapVersion,
475 head: MapVersion,
476 candidate: MapVersion,
477 ) -> Self {
478 Self {
479 prolly,
480 map_id,
481 base,
482 head,
483 candidate,
484 }
485 }
486
487 pub fn base(&self) -> &MapVersion {
489 &self.base
490 }
491
492 pub fn head(&self) -> &MapVersion {
494 &self.head
495 }
496
497 pub fn candidate(&self) -> &MapVersion {
499 &self.candidate
500 }
501
502 pub fn stream_conflicts<'s>(
504 &'s self,
505 ) -> Result<Box<dyn Iterator<Item = Result<super::error::Conflict, Error>> + 's>, Error> {
506 self.prolly
507 .stream_conflicts(&self.base.tree, &self.head.tree, &self.candidate.tree)
508 }
509
510 pub fn merge(&self, resolver: Option<super::error::Resolver>) -> Result<Tree, Error> {
512 self.prolly.merge(
513 &self.base.tree,
514 &self.head.tree,
515 &self.candidate.tree,
516 resolver,
517 )
518 }
519
520 pub fn merge_with_policy(
522 &self,
523 policies: &super::policy::MergePolicyRegistry,
524 ) -> Result<Tree, Error> {
525 self.merge(Some(policies.as_resolver()))
526 }
527
528 pub fn crdt_merge(&self, config: &super::crdt::CrdtConfig) -> Result<Tree, Error> {
530 self.prolly.crdt_merge(
531 &self.base.tree,
532 &self.head.tree,
533 &self.candidate.tree,
534 config,
535 )
536 }
537
538 pub fn crdt_merge_explain(
540 &self,
541 config: &super::crdt::CrdtConfig,
542 ) -> super::diff::MergeExplanation {
543 self.prolly.crdt_merge_explain(
544 &self.base.tree,
545 &self.head.tree,
546 &self.candidate.tree,
547 config,
548 )
549 }
550
551 pub fn publish(
553 &self,
554 resolver: Option<super::error::Resolver>,
555 ) -> Result<VersionedMapUpdate, Error>
556 where
557 S: ManifestStore + TransactionalStore,
558 {
559 let merged = self.merge(resolver)?;
560 let map = VersionedMap::new(self.prolly, &self.map_id);
561 map.publish_tree_if(Some(&self.head.id), &merged, current_unix_time_millis())
562 }
563
564 pub fn publish_with_policy(
566 &self,
567 policies: &super::policy::MergePolicyRegistry,
568 ) -> Result<VersionedMapUpdate, Error>
569 where
570 S: ManifestStore + TransactionalStore,
571 {
572 self.publish(Some(policies.as_resolver()))
573 }
574
575 pub fn publish_crdt(
577 &self,
578 config: &super::crdt::CrdtConfig,
579 ) -> Result<VersionedMapUpdate, Error>
580 where
581 S: ManifestStore + TransactionalStore,
582 {
583 let merged = self.crdt_merge(config)?;
584 let map = VersionedMap::new(self.prolly, &self.map_id);
585 map.publish_tree_if(Some(&self.head.id), &merged, current_unix_time_millis())
586 }
587}
588
589impl<'a, S: Store> MapComparison<'a, S> {
590 fn new(prolly: &'a Prolly<S>, base: MapVersion, target: MapVersion) -> Self {
591 Self {
592 prolly,
593 base,
594 target,
595 }
596 }
597
598 pub fn base(&self) -> &MapVersion {
600 &self.base
601 }
602
603 pub fn target(&self) -> &MapVersion {
605 &self.target
606 }
607
608 pub fn diff(&self) -> Result<Vec<Diff>, Error> {
610 self.prolly.diff(&self.base.tree, &self.target.tree)
611 }
612
613 pub fn stream_diff<'s>(
615 &'s self,
616 ) -> Result<Box<dyn Iterator<Item = Result<Diff, Error>> + 's>, Error> {
617 self.prolly.stream_diff(&self.base.tree, &self.target.tree)
618 }
619
620 pub fn diff_page(
622 &self,
623 cursor: &RangeCursor,
624 end: Option<&[u8]>,
625 limit: usize,
626 ) -> Result<super::diff::DiffPage, Error> {
627 self.prolly
628 .diff_page(&self.base.tree, &self.target.tree, cursor, end, limit)
629 }
630
631 pub fn structural_diff_page(
633 &self,
634 cursor: Option<&super::diff::StructuralDiffCursor>,
635 limit: usize,
636 ) -> Result<super::diff::StructuralDiffPage, Error> {
637 self.prolly
638 .structural_diff_page(&self.base.tree, &self.target.tree, cursor, limit)
639 }
640
641 pub fn prove_diff_page(
643 &self,
644 cursor: &RangeCursor,
645 end: Option<&[u8]>,
646 limit: usize,
647 ) -> Result<super::proof::ProvedDiffPage, Error> {
648 self.prolly
649 .prove_diff_page(&self.base.tree, &self.target.tree, cursor, end, limit)
650 }
651
652 pub fn stats(&self) -> Result<super::stats::StatsComparison, Error> {
654 self.prolly.stats_diff(&self.base.tree, &self.target.tree)
655 }
656
657 pub fn debug_view(&self) -> Result<super::debug::TreeDebugComparison, Error> {
659 self.prolly
660 .debug_compare_trees(&self.base.tree, &self.target.tree)
661 }
662
663 pub fn publish_changed_spans<I>(&self, spans: I) -> Result<bool, Error>
665 where
666 I: IntoIterator<Item = super::ChangedSpan>,
667 {
668 self.prolly
669 .publish_changed_spans_hint(&self.base.tree, &self.target.tree, spans)
670 }
671
672 pub fn changed_spans(&self) -> Result<Option<super::ChangedSpanHint>, Error> {
674 self.prolly
675 .load_changed_spans_hint(&self.base.tree, &self.target.tree)
676 }
677}
678
679impl<'a, S: Store> MapSnapshot<'a, S> {
680 pub(crate) fn new(prolly: &'a Prolly<S>, version: MapVersion) -> Self {
681 Self { prolly, version }
682 }
683
684 pub(crate) fn from_tree(
685 prolly: &'a Prolly<S>,
686 tree: Tree,
687 is_head: bool,
688 ) -> Result<Self, Error> {
689 Ok(Self::new(prolly, MapVersion::new(tree, None, is_head)?))
690 }
691
692 pub fn version(&self) -> &MapVersion {
694 &self.version
695 }
696
697 pub fn id(&self) -> &MapVersionId {
699 &self.version.id
700 }
701
702 pub fn tree(&self) -> &Tree {
704 &self.version.tree
705 }
706
707 pub fn read<'snapshot>(&'snapshot self) -> Result<ReadSession<'a, 'snapshot, S>, Error> {
709 self.prolly.read(self.tree())
710 }
711
712 pub fn get_with<R>(
714 &self,
715 key: &[u8],
716 read: impl FnOnce(&[u8]) -> R,
717 ) -> Result<Option<R>, Error> {
718 self.prolly.get_with(self.tree(), key, read)
719 }
720
721 pub fn get_value_ref_with<R>(
723 &self,
724 key: &[u8],
725 read: impl for<'value> FnOnce(ValueRefView<'value>) -> R,
726 ) -> Result<Option<R>, Error> {
727 self.prolly.get_value_ref_with(self.tree(), key, read)
728 }
729
730 pub fn get_many_with<K, F>(&self, keys: &[K], visit: F) -> Result<(), Error>
732 where
733 K: AsRef<[u8]>,
734 F: for<'value> FnMut(usize, &[u8], Option<&'value [u8]>),
735 {
736 self.prolly.get_many_with(self.tree(), keys, visit)
737 }
738
739 pub fn scan_range(
741 &self,
742 start: &[u8],
743 end: Option<&[u8]>,
744 visit: impl for<'entry> FnMut(EntryRef<'entry>),
745 ) -> Result<u64, Error> {
746 self.prolly.scan_range(self.tree(), start, end, visit)
747 }
748
749 pub fn scan_prefix(
751 &self,
752 prefix: &[u8],
753 visit: impl for<'entry> FnMut(EntryRef<'entry>),
754 ) -> Result<u64, Error> {
755 self.prolly.scan_prefix(self.tree(), prefix, visit)
756 }
757
758 pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
760 self.prolly.get(self.tree(), key)
761 }
762
763 pub fn get_value_ref(&self, key: &[u8]) -> Result<Option<super::blob::ValueRef>, Error> {
765 self.prolly.get_value_ref(self.tree(), key)
766 }
767
768 pub fn get_large_value<B: super::blob::BlobStore>(
770 &self,
771 blob_store: &B,
772 key: &[u8],
773 ) -> Result<Option<Vec<u8>>, Error> {
774 self.prolly.get_large_value(blob_store, self.tree(), key)
775 }
776
777 pub fn contains_key(&self, key: &[u8]) -> Result<bool, Error> {
779 Ok(self.get(key)?.is_some())
780 }
781
782 pub fn get_many<K: AsRef<[u8]>>(&self, keys: &[K]) -> Result<Vec<Option<Vec<u8>>>, Error> {
784 self.prolly.get_many(self.tree(), keys)
785 }
786
787 pub fn first_entry(&self) -> Result<Option<KeyValue>, Error> {
789 self.prolly.first_entry(self.tree())
790 }
791
792 pub fn last_entry(&self) -> Result<Option<KeyValue>, Error> {
794 self.prolly.last_entry(self.tree())
795 }
796
797 pub fn lower_bound(&self, key: &[u8]) -> Result<Option<KeyValue>, Error> {
799 self.prolly.lower_bound(self.tree(), key)
800 }
801
802 pub fn upper_bound(&self, key: &[u8]) -> Result<Option<KeyValue>, Error> {
804 self.prolly.upper_bound(self.tree(), key)
805 }
806
807 pub fn range<'s>(
809 &'s self,
810 start: &[u8],
811 end: Option<&[u8]>,
812 ) -> Result<RangeIter<'s, S>, Error> {
813 self.prolly.range(self.tree(), start, end)
814 }
815
816 pub fn stream_range<'s>(
818 &'s self,
819 start: &[u8],
820 end: Option<&[u8]>,
821 ) -> Result<RangeIter<'s, S>, Error> {
822 self.range(start, end)
823 }
824
825 pub fn prefix<'s>(&'s self, prefix: &[u8]) -> Result<RangeIter<'s, S>, Error> {
827 self.prolly.prefix(self.tree(), prefix)
828 }
829
830 pub fn stream_prefix<'s>(&'s self, prefix: &[u8]) -> Result<RangeIter<'s, S>, Error> {
832 self.prefix(prefix)
833 }
834
835 pub fn range_page(
837 &self,
838 cursor: &RangeCursor,
839 end: Option<&[u8]>,
840 limit: usize,
841 ) -> Result<RangePage, Error> {
842 self.prolly.range_page(self.tree(), cursor, end, limit)
843 }
844
845 pub fn prefix_page(
847 &self,
848 prefix: &[u8],
849 cursor: &RangeCursor,
850 limit: usize,
851 ) -> Result<RangePage, Error> {
852 self.prolly.prefix_page(self.tree(), prefix, cursor, limit)
853 }
854
855 pub fn reverse_page(
857 &self,
858 cursor: &ReverseCursor,
859 start: &[u8],
860 limit: usize,
861 ) -> Result<ReversePage, Error> {
862 self.prolly.reverse_page(self.tree(), cursor, start, limit)
863 }
864
865 pub fn prefix_reverse_page(
867 &self,
868 prefix: &[u8],
869 cursor: &ReverseCursor,
870 limit: usize,
871 ) -> Result<ReversePage, Error> {
872 self.prolly
873 .prefix_reverse_page(self.tree(), prefix, cursor, limit)
874 }
875
876 pub fn reverse_scan(&self, start: &[u8], page_size: usize) -> MapReverseIter<'a, S> {
880 MapReverseIter::new(
881 self.prolly,
882 self.tree().clone(),
883 start.to_vec(),
884 None,
885 page_size,
886 )
887 }
888
889 pub fn prefix_reverse_scan(&self, prefix: &[u8], page_size: usize) -> MapReverseIter<'a, S> {
891 MapReverseIter::new(
892 self.prolly,
893 self.tree().clone(),
894 prefix.to_vec(),
895 Some(prefix.to_vec()),
896 page_size,
897 )
898 }
899
900 pub fn cursor_window(
902 &self,
903 key: &[u8],
904 end: Option<&[u8]>,
905 limit: usize,
906 ) -> Result<CursorWindow, Error> {
907 self.prolly.cursor_window(self.tree(), key, end, limit)
908 }
909
910 pub fn stats(&self) -> Result<TreeStats, Error> {
912 self.prolly.collect_stats(self.tree())
913 }
914
915 pub fn debug_view(&self) -> Result<super::debug::TreeDebugView, Error> {
917 self.prolly.debug_tree(self.tree())
918 }
919
920 pub fn prove_key(&self, key: &[u8]) -> Result<super::proof::KeyProof, Error> {
922 self.prolly.prove_key(self.tree(), key)
923 }
924
925 pub fn prove_keys<K: AsRef<[u8]>>(
927 &self,
928 keys: &[K],
929 ) -> Result<super::proof::MultiKeyProof, Error> {
930 self.prolly.prove_keys(self.tree(), keys)
931 }
932
933 pub fn prove_range(
935 &self,
936 start: &[u8],
937 end: Option<&[u8]>,
938 ) -> Result<super::proof::RangeProof, Error> {
939 self.prolly.prove_range(self.tree(), start, end)
940 }
941
942 pub fn prove_prefix(&self, prefix: &[u8]) -> Result<super::proof::RangeProof, Error> {
944 self.prolly.prove_prefix(self.tree(), prefix)
945 }
946
947 pub fn prove_range_page(
949 &self,
950 cursor: &RangeCursor,
951 end: Option<&[u8]>,
952 limit: usize,
953 ) -> Result<super::proof::ProvedRangePage, Error> {
954 self.prolly
955 .prove_range_page(self.tree(), cursor, end, limit)
956 }
957
958 pub fn authenticate_proof_bundle(
960 &self,
961 proof_bundle: impl Into<Vec<u8>>,
962 secret: &[u8],
963 authentication: ProofAuthentication,
964 ) -> Result<super::proof::AuthenticatedProofEnvelope, Error> {
965 super::proof::sign_proof_bundle_hmac_sha256(
966 proof_bundle,
967 authentication.key_id,
968 secret,
969 authentication.context,
970 authentication.issued_at_millis,
971 authentication.expires_at_millis,
972 authentication.nonce,
973 )
974 }
975
976 pub fn export(&self) -> Result<super::sync::SnapshotBundle, Error> {
978 self.prolly.export_snapshot(self.tree())
979 }
980
981 pub fn plan_missing_nodes<D: Store>(
983 &self,
984 destination: &D,
985 ) -> Result<super::sync::MissingNodePlan, Error> {
986 self.prolly.plan_missing_nodes(self.tree(), destination)
987 }
988
989 pub fn copy_missing_nodes<D: Store>(
991 &self,
992 destination: &D,
993 ) -> Result<super::sync::MissingNodeCopy, Error> {
994 self.prolly.copy_missing_nodes(self.tree(), destination)
995 }
996
997 pub fn push_to<D>(&self, destination: &VersionedMap<'_, D>) -> Result<MapVersion, Error>
999 where
1000 D: Store + ManifestStore + TransactionalStore,
1001 {
1002 let bundle = self.export()?;
1003 destination.import_as_head(&bundle)
1004 }
1005
1006 pub fn pin_root(&self) -> Result<usize, Error> {
1008 self.prolly.pin_tree_root(self.tree())
1009 }
1010
1011 pub fn pin_path(&self, key: &[u8]) -> Result<usize, Error> {
1013 self.prolly.pin_tree_path(self.tree(), key)
1014 }
1015
1016 pub fn publish_prefix_hint(&self, prefix: &[u8]) -> Result<bool, Error> {
1018 self.prolly.publish_prefix_path_hint(self.tree(), prefix)
1019 }
1020
1021 pub fn hydrate_prefix_hint(&self, prefix: &[u8]) -> Result<bool, Error> {
1023 self.prolly.hydrate_prefix_path_hint(self.tree(), prefix)
1024 }
1025}
1026
1027#[derive(Clone, Debug, PartialEq)]
1029pub enum VersionedMapUpdate {
1030 Applied {
1032 previous: Option<MapVersionId>,
1034 current: MapVersion,
1036 },
1037 Unchanged {
1039 current: Option<MapVersion>,
1041 },
1042 Conflict {
1044 current: Option<MapVersion>,
1046 },
1047}
1048
1049impl VersionedMapUpdate {
1050 pub fn current(&self) -> Option<&MapVersion> {
1052 match self {
1053 Self::Applied { current, .. } => Some(current),
1054 Self::Unchanged { current } | Self::Conflict { current } => current.as_ref(),
1055 }
1056 }
1057
1058 pub fn is_applied(&self) -> bool {
1060 matches!(self, Self::Applied { .. })
1061 }
1062
1063 pub fn is_conflict(&self) -> bool {
1065 matches!(self, Self::Conflict { .. })
1066 }
1067}
1068
1069#[derive(Clone, Debug, Default)]
1071pub struct VersionedMapEditor {
1072 mutations: Vec<Mutation>,
1073}
1074
1075impl VersionedMapEditor {
1076 pub fn new() -> Self {
1078 Self::default()
1079 }
1080
1081 pub fn put(&mut self, key: impl Into<Vec<u8>>, value: impl Into<Vec<u8>>) -> &mut Self {
1083 self.mutations.push(Mutation::Upsert {
1084 key: key.into(),
1085 val: value.into(),
1086 });
1087 self
1088 }
1089
1090 pub fn delete(&mut self, key: impl Into<Vec<u8>>) -> &mut Self {
1092 self.mutations.push(Mutation::Delete { key: key.into() });
1093 self
1094 }
1095
1096 pub fn push(&mut self, mutation: Mutation) -> &mut Self {
1098 self.mutations.push(mutation);
1099 self
1100 }
1101
1102 pub fn len(&self) -> usize {
1104 self.mutations.len()
1105 }
1106
1107 pub fn is_empty(&self) -> bool {
1109 self.mutations.is_empty()
1110 }
1111
1112 fn into_mutations(self) -> Vec<Mutation> {
1113 self.mutations
1114 }
1115}
1116
1117pub struct VersionedMapsTransaction<'tx, 'engine, S>
1124where
1125 S: Store + ManifestStore + TransactionalStore,
1126{
1127 tx: &'tx super::transaction::ProllyTransaction<'engine, S>,
1128 timestamp_millis: u64,
1129}
1130
1131pub trait KeyCodec<K> {
1133 fn encode_key(&self, key: &K) -> Result<Vec<u8>, Error>;
1135
1136 fn decode_key(&self, bytes: &[u8]) -> Result<K, Error>;
1138}
1139
1140#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1142pub struct BytesKeyCodec;
1143
1144impl KeyCodec<Vec<u8>> for BytesKeyCodec {
1145 fn encode_key(&self, key: &Vec<u8>) -> Result<Vec<u8>, Error> {
1146 Ok(key.clone())
1147 }
1148
1149 fn decode_key(&self, bytes: &[u8]) -> Result<Vec<u8>, Error> {
1150 Ok(bytes.to_vec())
1151 }
1152}
1153
1154#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1156pub struct StringKeyCodec;
1157
1158impl KeyCodec<String> for StringKeyCodec {
1159 fn encode_key(&self, key: &String) -> Result<Vec<u8>, Error> {
1160 Ok(key.as_bytes().to_vec())
1161 }
1162
1163 fn decode_key(&self, bytes: &[u8]) -> Result<String, Error> {
1164 String::from_utf8(bytes.to_vec()).map_err(|err| Error::Deserialize(err.to_string()))
1165 }
1166}
1167
1168pub struct TypedVersionedMap<'a, S: Store, K, V, KC, VC> {
1170 inner: VersionedMap<'a, S>,
1171 key_codec: KC,
1172 value_codec: VC,
1173 marker: PhantomData<fn() -> (K, V)>,
1174}
1175
1176#[derive(Clone, Debug)]
1178pub struct TypedMigrationResult {
1179 pub update: VersionedMapUpdate,
1181 pub scanned_values: usize,
1183 pub rewritten_values: usize,
1185}
1186
1187#[derive(Clone, Debug, PartialEq)]
1189pub struct MapChangeEvent {
1190 pub previous: Option<MapVersionId>,
1192 pub current: MapVersion,
1194 pub diffs: Vec<Diff>,
1196}
1197
1198pub struct MapChangeSubscription<'a, S: Store> {
1200 map: VersionedMap<'a, S>,
1201 last_seen: Option<MapVersionId>,
1202}
1203
1204impl<'a, S> MapChangeSubscription<'a, S>
1205where
1206 S: Store + ManifestStore,
1207{
1208 pub fn last_seen(&self) -> Option<&MapVersionId> {
1210 self.last_seen.as_ref()
1211 }
1212
1213 pub fn poll(&mut self) -> Result<Option<MapChangeEvent>, Error> {
1215 let Some(current) = self.map.head()? else {
1216 return Ok(None);
1217 };
1218 if self.last_seen.as_ref() == Some(¤t.id) {
1219 return Ok(None);
1220 }
1221 let previous_tree = match &self.last_seen {
1222 Some(id) => {
1223 self.map
1224 .version(id)?
1225 .ok_or_else(|| {
1226 Error::InvalidVersionedMap(format!(
1227 "subscription resume version {} was pruned",
1228 id
1229 ))
1230 })?
1231 .tree
1232 }
1233 None => self.map.prolly.create(),
1234 };
1235 let diffs = self.map.prolly.diff(&previous_tree, ¤t.tree)?;
1236 let previous = self.last_seen.replace(current.id.clone());
1237 Ok(Some(MapChangeEvent {
1238 previous,
1239 current,
1240 diffs,
1241 }))
1242 }
1243}
1244
1245impl<'a, S: Store, K, V, KC, VC> TypedVersionedMap<'a, S, K, V, KC, VC> {
1246 fn new(inner: VersionedMap<'a, S>, key_codec: KC, value_codec: VC) -> Self {
1247 Self {
1248 inner,
1249 key_codec,
1250 value_codec,
1251 marker: PhantomData,
1252 }
1253 }
1254
1255 pub fn raw(&self) -> &VersionedMap<'a, S> {
1257 &self.inner
1258 }
1259}
1260
1261impl<S, K, V, KC, VC> TypedVersionedMap<'_, S, K, V, KC, VC>
1262where
1263 S: Store + ManifestStore,
1264 V: serde::de::DeserializeOwned,
1265 KC: KeyCodec<K>,
1266 VC: super::value::ValueCodec,
1267{
1268 pub fn get(&self, key: &K) -> Result<Option<V>, Error> {
1270 let key = self.key_codec.encode_key(key)?;
1271 self.inner
1272 .get_with(&key, |bytes| self.value_codec.decode(bytes))?
1273 .transpose()
1274 }
1275
1276 pub fn get_at(&self, id: &MapVersionId, key: &K) -> Result<Option<V>, Error> {
1278 let key = self.key_codec.encode_key(key)?;
1279 self.inner
1280 .get_at_with(id, &key, |bytes| self.value_codec.decode(bytes))?
1281 .transpose()
1282 }
1283
1284 pub fn entries(&self) -> Result<Vec<(K, V)>, Error> {
1286 let Some(snapshot) = self.inner.snapshot()? else {
1287 return Ok(Vec::new());
1288 };
1289 snapshot
1290 .range(&[], None)?
1291 .map(|entry| {
1292 let (key, value) = entry?;
1293 Ok((
1294 self.key_codec.decode_key(&key)?,
1295 self.value_codec.decode(&value)?,
1296 ))
1297 })
1298 .collect()
1299 }
1300}
1301
1302impl<S, K, V, KC, VC> TypedVersionedMap<'_, S, K, V, KC, VC>
1303where
1304 S: Store + ManifestStore + TransactionalStore,
1305 V: serde::Serialize,
1306 KC: KeyCodec<K>,
1307 VC: super::value::ValueCodec,
1308{
1309 pub fn put(&self, key: &K, value: &V) -> Result<MapVersion, Error> {
1311 self.inner.put(
1312 self.key_codec.encode_key(key)?,
1313 self.value_codec.encode(value)?,
1314 )
1315 }
1316
1317 pub fn put_if(
1319 &self,
1320 expected: Option<&MapVersionId>,
1321 key: &K,
1322 value: &V,
1323 ) -> Result<VersionedMapUpdate, Error> {
1324 self.inner.put_if(
1325 expected,
1326 self.key_codec.encode_key(key)?,
1327 self.value_codec.encode(value)?,
1328 )
1329 }
1330
1331 pub fn delete(&self, key: &K) -> Result<MapVersion, Error> {
1333 self.inner.delete(self.key_codec.encode_key(key)?)
1334 }
1335
1336 pub fn migrate_from<Old, OVC>(
1338 &self,
1339 expected: &MapVersionId,
1340 source_codec: &OVC,
1341 mut migrate: impl FnMut(Old) -> Result<V, Error>,
1342 ) -> Result<TypedMigrationResult, Error>
1343 where
1344 Old: serde::de::DeserializeOwned,
1345 OVC: super::value::ValueCodec,
1346 {
1347 let snapshot = self.inner.snapshot_at(expected)?.ok_or_else(|| {
1348 Error::InvalidVersionedMap(format!("unknown migration source version {expected}"))
1349 })?;
1350 let mut mutations = Vec::new();
1351 let mut scanned_values = 0usize;
1352 for entry in snapshot.range(&[], None)? {
1353 let (key, bytes) = entry?;
1354 let old: Old = source_codec.decode(&bytes)?;
1355 let value = migrate(old)?;
1356 mutations.push(Mutation::Upsert {
1357 key,
1358 val: self.value_codec.encode(&value)?,
1359 });
1360 scanned_values += 1;
1361 }
1362 let update = self.inner.apply_if(Some(expected), mutations)?;
1363 Ok(TypedMigrationResult {
1364 update,
1365 scanned_values,
1366 rewritten_values: scanned_values,
1367 })
1368 }
1369}
1370
1371impl<'tx, 'engine, S> VersionedMapsTransaction<'tx, 'engine, S>
1372where
1373 S: Store + ManifestStore + TransactionalStore,
1374{
1375 fn new(
1376 tx: &'tx super::transaction::ProllyTransaction<'engine, S>,
1377 timestamp_millis: u64,
1378 ) -> Self {
1379 Self {
1380 tx,
1381 timestamp_millis,
1382 }
1383 }
1384
1385 pub fn head(&self, map_id: impl AsRef<[u8]>) -> Result<Option<MapVersion>, Error> {
1387 let (_, head_name, _) = versioned_map_names(map_id.as_ref());
1388 self.tx
1389 .load_named_root(&head_name)?
1390 .map(|tree| {
1391 Ok(MapVersion {
1392 id: MapVersionId::for_tree(&tree)?,
1393 tree,
1394 created_at_millis: None,
1395 is_head: true,
1396 })
1397 })
1398 .transpose()
1399 }
1400
1401 pub fn get(&self, map_id: impl AsRef<[u8]>, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
1403 match self.head(map_id)? {
1404 Some(head) => self.tx.get(&head.tree, key),
1405 None => Ok(None),
1406 }
1407 }
1408
1409 pub fn apply(
1411 &self,
1412 map_id: impl AsRef<[u8]>,
1413 mutations: Vec<Mutation>,
1414 ) -> Result<MapVersion, Error> {
1415 self.apply_with_authority(map_id.as_ref(), mutations, MapWriteAuthority::Unmanaged)
1416 }
1417
1418 fn apply_with_authority(
1419 &self,
1420 map_id: &[u8],
1421 mutations: Vec<Mutation>,
1422 authority: MapWriteAuthority,
1423 ) -> Result<MapVersion, Error> {
1424 guard_managed_map_write(self.tx, map_id, authority)?;
1425 let (_, head_name, versions_prefix) = versioned_map_names(map_id);
1426 let current = self.tx.load_named_root(&head_name)?;
1427 let base = current.clone().unwrap_or_else(|| self.tx.create());
1428 let next = self.tx.batch(&base, mutations)?;
1429 if current.as_ref() == Some(&next) {
1430 return Ok(MapVersion {
1431 id: MapVersionId::for_tree(&next)?,
1432 tree: next,
1433 created_at_millis: None,
1434 is_head: true,
1435 });
1436 }
1437
1438 let id = MapVersionId::for_tree(&next)?;
1439 let mut version_name = versions_prefix;
1440 version_name.extend_from_slice(id.as_cid().as_bytes());
1441 match self.tx.load_named_root(&version_name)? {
1442 Some(existing) if existing != next => {
1443 return Err(Error::InvalidVersionedMap(format!(
1444 "content identifier collision for transaction version {}",
1445 id
1446 )));
1447 }
1448 Some(_) => {}
1449 None => {
1450 self.tx
1451 .publish_named_root_at_millis(&version_name, &next, self.timestamp_millis)?
1452 }
1453 }
1454 self.tx
1455 .publish_named_root_at_millis(&head_name, &next, self.timestamp_millis)?;
1456 Ok(MapVersion {
1457 id,
1458 tree: next,
1459 created_at_millis: Some(self.timestamp_millis),
1460 is_head: true,
1461 })
1462 }
1463
1464 pub fn apply_if(
1466 &self,
1467 map_id: impl AsRef<[u8]>,
1468 expected: Option<&MapVersionId>,
1469 mutations: Vec<Mutation>,
1470 ) -> Result<VersionedMapUpdate, Error> {
1471 let current = self.head(map_id.as_ref())?;
1472 if current.as_ref().map(|version| &version.id) != expected {
1473 return Ok(VersionedMapUpdate::Conflict { current });
1474 }
1475 let previous = current.map(|version| version.id);
1476 let current = self.apply(map_id, mutations)?;
1477 if previous.as_ref() == Some(¤t.id) {
1478 Ok(VersionedMapUpdate::Unchanged {
1479 current: Some(current),
1480 })
1481 } else {
1482 Ok(VersionedMapUpdate::Applied { previous, current })
1483 }
1484 }
1485
1486 pub fn put(
1488 &self,
1489 map_id: impl AsRef<[u8]>,
1490 key: impl Into<Vec<u8>>,
1491 value: impl Into<Vec<u8>>,
1492 ) -> Result<MapVersion, Error> {
1493 self.apply(
1494 map_id,
1495 vec![Mutation::Upsert {
1496 key: key.into(),
1497 val: value.into(),
1498 }],
1499 )
1500 }
1501
1502 pub fn delete(
1504 &self,
1505 map_id: impl AsRef<[u8]>,
1506 key: impl Into<Vec<u8>>,
1507 ) -> Result<MapVersion, Error> {
1508 self.apply(map_id, vec![Mutation::Delete { key: key.into() }])
1509 }
1510
1511 pub fn edit(
1513 &self,
1514 map_id: impl AsRef<[u8]>,
1515 edit: impl FnOnce(&mut VersionedMapEditor),
1516 ) -> Result<MapVersion, Error> {
1517 let mut editor = VersionedMapEditor::new();
1518 edit(&mut editor);
1519 self.apply(map_id, editor.into_mutations())
1520 }
1521}
1522
1523pub struct VersionedMap<'a, S: Store> {
1528 prolly: &'a Prolly<S>,
1529 id: Vec<u8>,
1530 root_prefix: Vec<u8>,
1531 head_name: Vec<u8>,
1532 versions_prefix: Vec<u8>,
1533}
1534
1535impl<'a, S: Store> VersionedMap<'a, S> {
1536 pub fn new(prolly: &'a Prolly<S>, id: impl AsRef<[u8]>) -> Self {
1538 let id = id.as_ref().to_vec();
1539 let (root_prefix, head_name, versions_prefix) = versioned_map_names(&id);
1540
1541 Self {
1542 prolly,
1543 id,
1544 root_prefix,
1545 head_name,
1546 versions_prefix,
1547 }
1548 }
1549
1550 pub fn id(&self) -> &[u8] {
1552 &self.id
1553 }
1554
1555 pub fn head_name(&self) -> &[u8] {
1557 &self.head_name
1558 }
1559
1560 pub fn versions_prefix(&self) -> &[u8] {
1562 &self.versions_prefix
1563 }
1564
1565 pub fn typed<K, V, KC, VC>(
1567 &self,
1568 key_codec: KC,
1569 value_codec: VC,
1570 ) -> TypedVersionedMap<'a, S, K, V, KC, VC> {
1571 TypedVersionedMap::new(
1572 VersionedMap::new(self.prolly, &self.id),
1573 key_codec,
1574 value_codec,
1575 )
1576 }
1577
1578 pub fn retention_policy(&self) -> NamedRootRetention {
1580 let mut isolated_prefix = self.root_prefix.clone();
1581 isolated_prefix.push(b'/');
1582 NamedRootRetention::prefix(isolated_prefix)
1583 }
1584
1585 pub fn snapshot(&self) -> Result<Option<MapSnapshot<'a, S>>, Error>
1587 where
1588 S: ManifestStore,
1589 {
1590 self.head()
1591 .map(|version| version.map(|version| MapSnapshot::new(self.prolly, version)))
1592 }
1593
1594 pub fn snapshot_at(&self, id: &MapVersionId) -> Result<Option<MapSnapshot<'a, S>>, Error>
1596 where
1597 S: ManifestStore,
1598 {
1599 self.version(id)
1600 .map(|version| version.map(|version| MapSnapshot::new(self.prolly, version)))
1601 }
1602
1603 pub fn compare(
1605 &self,
1606 base: &MapVersionId,
1607 target: &MapVersionId,
1608 ) -> Result<MapComparison<'a, S>, Error>
1609 where
1610 S: ManifestStore,
1611 {
1612 let base = self
1613 .version(base)?
1614 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {base}")))?;
1615 let target = self
1616 .version(target)?
1617 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {target}")))?;
1618 Ok(MapComparison::new(self.prolly, base, target))
1619 }
1620
1621 pub fn compare_to_head(&self, base: &MapVersionId) -> Result<MapComparison<'a, S>, Error>
1623 where
1624 S: ManifestStore,
1625 {
1626 let base = self
1627 .version(base)?
1628 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {base}")))?;
1629 let target = self.head()?.ok_or_else(|| {
1630 Error::InvalidVersionedMap("map has not been initialized".to_string())
1631 })?;
1632 Ok(MapComparison::new(self.prolly, base, target))
1633 }
1634
1635 pub fn prepare_merge(
1637 &self,
1638 base: &MapVersionId,
1639 candidate: &MapVersionId,
1640 ) -> Result<MapMerge<'a, S>, Error>
1641 where
1642 S: ManifestStore,
1643 {
1644 let base = self
1645 .version(base)?
1646 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {base}")))?;
1647 let candidate = self.version(candidate)?.ok_or_else(|| {
1648 Error::InvalidVersionedMap(format!("unknown map version {candidate}"))
1649 })?;
1650 let head = self.head()?.ok_or_else(|| {
1651 Error::InvalidVersionedMap("map has not been initialized".to_string())
1652 })?;
1653 Ok(MapMerge::new(
1654 self.prolly,
1655 self.id.clone(),
1656 base,
1657 head,
1658 candidate,
1659 ))
1660 }
1661
1662 fn version_name(&self, id: &MapVersionId) -> Vec<u8> {
1663 let mut name = self.versions_prefix.clone();
1664 name.extend_from_slice(id.as_cid().as_bytes());
1665 name
1666 }
1667
1668 fn async_service(
1669 &self,
1670 ) -> AsyncVersionedMap<'_, super::store::SyncStoreAsAsync<std::sync::Arc<S>>> {
1671 AsyncVersionedMap::new(&self.prolly.engine, &self.id)
1672 }
1673}
1674
1675impl<S> VersionedMap<'_, S>
1676where
1677 S: Store + ManifestStore,
1678{
1679 pub fn subscribe(&self) -> Result<MapChangeSubscription<'_, S>, Error> {
1681 Ok(MapChangeSubscription {
1682 map: VersionedMap::new(self.prolly, &self.id),
1683 last_seen: self.head_id()?,
1684 })
1685 }
1686
1687 pub fn subscribe_from(&self, last_seen: Option<MapVersionId>) -> MapChangeSubscription<'_, S> {
1689 MapChangeSubscription {
1690 map: VersionedMap::new(self.prolly, &self.id),
1691 last_seen,
1692 }
1693 }
1694
1695 pub fn is_initialized(&self) -> Result<bool, Error> {
1697 Ok(self.head()?.is_some())
1698 }
1699
1700 pub fn head_id(&self) -> Result<Option<MapVersionId>, Error> {
1702 Ok(self.head()?.map(|version| version.id))
1703 }
1704
1705 pub fn head(&self) -> Result<Option<MapVersion>, Error> {
1707 let service = self.async_service();
1708 let ready_store = self.prolly.engine.store.clone();
1709 let future = service.head();
1710 super::engine::ready::run_ready(ready_store.ready(future))
1711 }
1712
1713 pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
1715 match self.head()? {
1716 Some(version) => self.prolly.get(&version.tree, key),
1717 None => Ok(None),
1718 }
1719 }
1720
1721 pub fn get_with<R>(
1723 &self,
1724 key: &[u8],
1725 read: impl FnOnce(&[u8]) -> R,
1726 ) -> Result<Option<R>, Error> {
1727 match self.head()? {
1728 Some(version) => self.prolly.get_with(&version.tree, key, read),
1729 None => Ok(None),
1730 }
1731 }
1732
1733 pub fn get_lease(&self, key: &[u8]) -> Result<Option<OwnedValueLease>, Error> {
1737 match self.head()? {
1738 Some(version) => self.prolly.read(&version.tree)?.get_lease(key),
1739 None => Ok(None),
1740 }
1741 }
1742
1743 pub fn get_value_ref_with<R>(
1745 &self,
1746 key: &[u8],
1747 read: impl for<'value> FnOnce(ValueRefView<'value>) -> R,
1748 ) -> Result<Option<R>, Error> {
1749 match self.head()? {
1750 Some(version) => self.prolly.get_value_ref_with(&version.tree, key, read),
1751 None => Ok(None),
1752 }
1753 }
1754
1755 pub fn get_large_value<B: super::blob::BlobStore>(
1757 &self,
1758 blob_store: &B,
1759 key: &[u8],
1760 ) -> Result<Option<Vec<u8>>, Error> {
1761 match self.snapshot()? {
1762 Some(snapshot) => snapshot.get_large_value(blob_store, key),
1763 None => Ok(None),
1764 }
1765 }
1766
1767 pub fn contains_key(&self, key: &[u8]) -> Result<bool, Error> {
1769 Ok(self.get(key)?.is_some())
1770 }
1771
1772 pub fn get_many<K: AsRef<[u8]>>(&self, keys: &[K]) -> Result<Vec<Option<Vec<u8>>>, Error> {
1776 let tree = self
1777 .head()?
1778 .map(|version| version.tree)
1779 .unwrap_or_else(|| self.prolly.create());
1780 self.prolly.get_many(&tree, keys)
1781 }
1782
1783 pub fn range<'a>(
1788 &'a self,
1789 start: &[u8],
1790 end: Option<&[u8]>,
1791 ) -> Result<RangeIter<'a, S>, Error> {
1792 let tree = self
1793 .head()?
1794 .map(|version| version.tree)
1795 .unwrap_or_else(|| self.prolly.create());
1796 self.prolly.range(&tree, start, end)
1797 }
1798
1799 pub fn prefix<'a>(&'a self, prefix: &[u8]) -> Result<RangeIter<'a, S>, Error> {
1801 let tree = self
1802 .head()?
1803 .map(|version| version.tree)
1804 .unwrap_or_else(|| self.prolly.create());
1805 self.prolly.prefix(&tree, prefix)
1806 }
1807
1808 pub fn scan_range(
1810 &self,
1811 start: &[u8],
1812 end: Option<&[u8]>,
1813 visit: impl for<'entry> FnMut(EntryRef<'entry>),
1814 ) -> Result<u64, Error> {
1815 let tree = self
1816 .head()?
1817 .map(|version| version.tree)
1818 .unwrap_or_else(|| self.prolly.create());
1819 self.prolly.scan_range(&tree, start, end, visit)
1820 }
1821
1822 pub fn scan_prefix(
1824 &self,
1825 prefix: &[u8],
1826 visit: impl for<'entry> FnMut(EntryRef<'entry>),
1827 ) -> Result<u64, Error> {
1828 let tree = self
1829 .head()?
1830 .map(|version| version.tree)
1831 .unwrap_or_else(|| self.prolly.create());
1832 self.prolly.scan_prefix(&tree, prefix, visit)
1833 }
1834
1835 pub fn range_page(
1841 &self,
1842 cursor: &RangeCursor,
1843 end: Option<&[u8]>,
1844 limit: usize,
1845 ) -> Result<RangePage, Error> {
1846 let tree = self
1847 .head()?
1848 .map(|version| version.tree)
1849 .unwrap_or_else(|| self.prolly.create());
1850 self.prolly.range_page(&tree, cursor, end, limit)
1851 }
1852
1853 pub fn prefix_page(
1855 &self,
1856 prefix: &[u8],
1857 cursor: &RangeCursor,
1858 limit: usize,
1859 ) -> Result<RangePage, Error> {
1860 let tree = self
1861 .head()?
1862 .map(|version| version.tree)
1863 .unwrap_or_else(|| self.prolly.create());
1864 self.prolly.prefix_page(&tree, prefix, cursor, limit)
1865 }
1866
1867 pub fn version(&self, id: &MapVersionId) -> Result<Option<MapVersion>, Error> {
1869 let service = self.async_service();
1870 let ready_store = self.prolly.engine.store.clone();
1871 let future = service.version(id);
1872 super::engine::ready::run_ready(ready_store.ready(future))
1873 }
1874
1875 pub fn get_at(&self, id: &MapVersionId, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
1877 let version = self
1878 .version(id)?
1879 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1880 self.prolly.get(&version.tree, key)
1881 }
1882
1883 pub fn get_at_with<R>(
1885 &self,
1886 id: &MapVersionId,
1887 key: &[u8],
1888 read: impl FnOnce(&[u8]) -> R,
1889 ) -> Result<Option<R>, Error> {
1890 let version = self
1891 .version(id)?
1892 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1893 self.prolly.get_with(&version.tree, key, read)
1894 }
1895
1896 pub fn get_value_ref_at_with<R>(
1898 &self,
1899 id: &MapVersionId,
1900 key: &[u8],
1901 read: impl for<'value> FnOnce(ValueRefView<'value>) -> R,
1902 ) -> Result<Option<R>, Error> {
1903 let version = self
1904 .version(id)?
1905 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1906 self.prolly.get_value_ref_with(&version.tree, key, read)
1907 }
1908
1909 pub fn get_many_at<K: AsRef<[u8]>>(
1911 &self,
1912 id: &MapVersionId,
1913 keys: &[K],
1914 ) -> Result<Vec<Option<Vec<u8>>>, Error> {
1915 let version = self
1916 .version(id)?
1917 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1918 self.prolly.get_many(&version.tree, keys)
1919 }
1920
1921 pub fn range_at<'a>(
1923 &'a self,
1924 id: &MapVersionId,
1925 start: &[u8],
1926 end: Option<&[u8]>,
1927 ) -> Result<RangeIter<'a, S>, Error> {
1928 let version = self
1929 .version(id)?
1930 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1931 self.prolly.range(&version.tree, start, end)
1932 }
1933
1934 pub fn prefix_at<'a>(
1936 &'a self,
1937 id: &MapVersionId,
1938 prefix: &[u8],
1939 ) -> Result<RangeIter<'a, S>, Error> {
1940 let version = self
1941 .version(id)?
1942 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1943 self.prolly.prefix(&version.tree, prefix)
1944 }
1945
1946 pub fn scan_range_at(
1948 &self,
1949 id: &MapVersionId,
1950 start: &[u8],
1951 end: Option<&[u8]>,
1952 visit: impl for<'entry> FnMut(EntryRef<'entry>),
1953 ) -> Result<u64, Error> {
1954 let version = self
1955 .version(id)?
1956 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1957 self.prolly.scan_range(&version.tree, start, end, visit)
1958 }
1959
1960 pub fn scan_prefix_at(
1962 &self,
1963 id: &MapVersionId,
1964 prefix: &[u8],
1965 visit: impl for<'entry> FnMut(EntryRef<'entry>),
1966 ) -> Result<u64, Error> {
1967 let version = self
1968 .version(id)?
1969 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1970 self.prolly.scan_prefix(&version.tree, prefix, visit)
1971 }
1972
1973 pub fn range_page_at(
1975 &self,
1976 id: &MapVersionId,
1977 cursor: &RangeCursor,
1978 end: Option<&[u8]>,
1979 limit: usize,
1980 ) -> Result<RangePage, Error> {
1981 let version = self
1982 .version(id)?
1983 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1984 self.prolly.range_page(&version.tree, cursor, end, limit)
1985 }
1986
1987 pub fn prefix_page_at(
1989 &self,
1990 id: &MapVersionId,
1991 prefix: &[u8],
1992 cursor: &RangeCursor,
1993 limit: usize,
1994 ) -> Result<RangePage, Error> {
1995 let version = self
1996 .version(id)?
1997 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
1998 self.prolly
1999 .prefix_page(&version.tree, prefix, cursor, limit)
2000 }
2001
2002 pub fn diff(&self, base: &MapVersionId, target: &MapVersionId) -> Result<Vec<Diff>, Error> {
2004 let base = self
2005 .version(base)?
2006 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {base}")))?;
2007 let target = self
2008 .version(target)?
2009 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {target}")))?;
2010 self.prolly.diff(&base.tree, &target.tree)
2011 }
2012
2013 pub fn changes_since(&self, base: &MapVersionId) -> Result<Vec<Diff>, Error> {
2015 let base = self
2016 .version(base)?
2017 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {base}")))?;
2018 let head = self.head()?.ok_or_else(|| {
2019 Error::InvalidVersionedMap("map has not been initialized".to_string())
2020 })?;
2021 self.prolly.diff(&base.tree, &head.tree)
2022 }
2023}
2024
2025pub struct AsyncVersionedMap<'a, S: super::store::AsyncStore> {
2027 prolly: &'a super::AsyncProlly<S>,
2028 id: Vec<u8>,
2029 head_name: Vec<u8>,
2030 versions_prefix: Vec<u8>,
2031}
2032
2033pub struct AsyncMapSnapshot<'a, S: super::store::AsyncStore> {
2035 prolly: &'a super::AsyncProlly<S>,
2036 version: MapVersion,
2037}
2038
2039pub struct AsyncMapChangeSubscription<'a, S: super::store::AsyncStore> {
2041 map: AsyncVersionedMap<'a, S>,
2042 last_seen: Option<MapVersionId>,
2043}
2044impl<'a, S: super::store::AsyncStore> AsyncVersionedMap<'a, S> {
2045 pub fn new(prolly: &'a super::AsyncProlly<S>, id: impl AsRef<[u8]>) -> Self {
2047 let id = id.as_ref().to_vec();
2048 let (_, head_name, versions_prefix) = versioned_map_names(&id);
2049 Self {
2050 prolly,
2051 id,
2052 head_name,
2053 versions_prefix,
2054 }
2055 }
2056
2057 pub fn id(&self) -> &[u8] {
2059 &self.id
2060 }
2061
2062 fn version_name(&self, id: &MapVersionId) -> Vec<u8> {
2063 let mut name = self.versions_prefix.clone();
2064 name.extend_from_slice(id.as_cid().as_bytes());
2065 name
2066 }
2067}
2068impl<'a, S> AsyncVersionedMap<'a, S>
2069where
2070 S: super::store::AsyncStore + super::manifest::AsyncManifestStore,
2071 <S as super::store::AsyncStore>::Error: Send + Sync,
2072 <S as super::manifest::AsyncManifestStore>::Error: Send + Sync,
2073{
2074 pub async fn head(&self) -> Result<Option<MapVersion>, Error> {
2076 self.prolly
2077 .load_named_root_manifest(&self.head_name)
2078 .await?
2079 .map(|manifest| {
2080 MapVersion::new(
2081 manifest.to_tree(),
2082 manifest.updated_at_millis.or(manifest.created_at_millis),
2083 true,
2084 )
2085 })
2086 .transpose()
2087 }
2088
2089 pub async fn version(&self, id: &MapVersionId) -> Result<Option<MapVersion>, Error> {
2091 let manifest = self
2092 .prolly
2093 .load_named_root_manifest(&self.version_name(id))
2094 .await?;
2095 let Some(manifest) = manifest else {
2096 return Ok(None);
2097 };
2098 let tree = manifest.to_tree();
2099 let version = (|| {
2100 let actual = MapVersionId::for_tree(&tree)?;
2101 if actual != *id {
2102 return Err(Error::InvalidVersionedMap(format!(
2103 "catalog root does not match async version {id}"
2104 )));
2105 }
2106 Ok(MapVersion {
2107 id: actual,
2108 tree,
2109 created_at_millis: manifest.created_at_millis,
2110 is_head: false,
2111 })
2112 })()?;
2113 let is_head = self.head().await?.is_some_and(|head| head.id == *id);
2114 Ok(Some(MapVersion { is_head, ..version }))
2115 }
2116
2117 pub async fn snapshot(&self) -> Result<Option<AsyncMapSnapshot<'a, S>>, Error> {
2119 Ok(self.head().await?.map(|version| AsyncMapSnapshot {
2120 prolly: self.prolly,
2121 version,
2122 }))
2123 }
2124
2125 pub async fn snapshot_at(
2127 &self,
2128 id: &MapVersionId,
2129 ) -> Result<Option<AsyncMapSnapshot<'a, S>>, Error> {
2130 Ok(self.version(id).await?.map(|version| AsyncMapSnapshot {
2131 prolly: self.prolly,
2132 version,
2133 }))
2134 }
2135
2136 pub async fn subscribe(&self) -> Result<AsyncMapChangeSubscription<'a, S>, Error> {
2138 Ok(AsyncMapChangeSubscription {
2139 map: AsyncVersionedMap::new(self.prolly, &self.id),
2140 last_seen: self.head().await?.map(|version| version.id),
2141 })
2142 }
2143
2144 pub fn subscribe_from(
2146 &self,
2147 last_seen: Option<MapVersionId>,
2148 ) -> AsyncMapChangeSubscription<'a, S> {
2149 AsyncMapChangeSubscription {
2150 map: AsyncVersionedMap::new(self.prolly, &self.id),
2151 last_seen,
2152 }
2153 }
2154
2155 pub async fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
2157 match self.snapshot().await? {
2158 Some(snapshot) => snapshot.get(key).await,
2159 None => Ok(None),
2160 }
2161 }
2162}
2163impl<'a, S> AsyncMapChangeSubscription<'a, S>
2164where
2165 S: super::store::AsyncStore + super::manifest::AsyncManifestStore,
2166 <S as super::store::AsyncStore>::Error: Send + Sync,
2167 <S as super::manifest::AsyncManifestStore>::Error: Send + Sync,
2168{
2169 pub fn last_seen(&self) -> Option<&MapVersionId> {
2171 self.last_seen.as_ref()
2172 }
2173
2174 pub async fn poll(&mut self) -> Result<Option<MapChangeEvent>, Error> {
2176 let Some(current) = self.map.head().await? else {
2177 return Ok(None);
2178 };
2179 if self.last_seen.as_ref() == Some(¤t.id) {
2180 return Ok(None);
2181 }
2182 let previous_tree = match &self.last_seen {
2183 Some(id) => {
2184 self.map
2185 .version(id)
2186 .await?
2187 .ok_or_else(|| {
2188 Error::InvalidVersionedMap(format!(
2189 "async subscription resume version {} was pruned",
2190 id
2191 ))
2192 })?
2193 .tree
2194 }
2195 None => self.map.prolly.create(),
2196 };
2197 let diffs = self.map.prolly.diff(&previous_tree, ¤t.tree).await?;
2198 let previous = self.last_seen.replace(current.id.clone());
2199 Ok(Some(MapChangeEvent {
2200 previous,
2201 current,
2202 diffs,
2203 }))
2204 }
2205}
2206impl<'a, S> AsyncMapSnapshot<'a, S>
2207where
2208 S: super::store::AsyncStore,
2209 <S as super::store::AsyncStore>::Error: Send + Sync,
2210{
2211 pub fn version(&self) -> &MapVersion {
2213 &self.version
2214 }
2215
2216 pub fn tree(&self) -> &Tree {
2218 &self.version.tree
2219 }
2220
2221 pub async fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
2223 self.prolly.get(self.tree(), key).await
2224 }
2225
2226 pub async fn get_many<K: AsRef<[u8]>>(
2228 &self,
2229 keys: &[K],
2230 ) -> Result<Vec<Option<Vec<u8>>>, Error> {
2231 self.prolly.get_many(self.tree(), keys).await
2232 }
2233
2234 pub async fn range<'s>(
2236 &'s self,
2237 start: &[u8],
2238 end: Option<&[u8]>,
2239 ) -> Result<super::range::AsyncRangeIter<'s, S>, Error> {
2240 self.prolly.range(self.tree(), start, end).await
2241 }
2242
2243 pub async fn prefix<'s>(
2245 &'s self,
2246 prefix: &[u8],
2247 ) -> Result<super::range::AsyncRangeIter<'s, S>, Error> {
2248 self.prolly.prefix(self.tree(), prefix).await
2249 }
2250
2251 pub async fn range_page(
2253 &self,
2254 cursor: &RangeCursor,
2255 end: Option<&[u8]>,
2256 limit: usize,
2257 ) -> Result<RangePage, Error> {
2258 self.prolly
2259 .range_page(self.tree(), cursor, end, limit)
2260 .await
2261 }
2262
2263 pub async fn prefix_page(
2265 &self,
2266 prefix: &[u8],
2267 cursor: &RangeCursor,
2268 limit: usize,
2269 ) -> Result<RangePage, Error> {
2270 self.prolly
2271 .prefix_page(self.tree(), prefix, cursor, limit)
2272 .await
2273 }
2274
2275 pub async fn stats(&self) -> Result<TreeStats, Error> {
2277 self.prolly.collect_stats(self.tree()).await
2278 }
2279
2280 pub async fn prove_key(&self, key: &[u8]) -> Result<super::proof::KeyProof, Error> {
2282 self.prolly.prove_key(self.tree(), key).await
2283 }
2284
2285 pub async fn prove_keys<K: AsRef<[u8]>>(
2287 &self,
2288 keys: &[K],
2289 ) -> Result<super::proof::MultiKeyProof, Error> {
2290 self.prolly.prove_keys(self.tree(), keys).await
2291 }
2292
2293 pub async fn prove_range(
2295 &self,
2296 start: &[u8],
2297 end: Option<&[u8]>,
2298 ) -> Result<super::proof::RangeProof, Error> {
2299 self.prolly.prove_range(self.tree(), start, end).await
2300 }
2301
2302 pub async fn prove_prefix(&self, prefix: &[u8]) -> Result<super::proof::RangeProof, Error> {
2304 self.prolly.prove_prefix(self.tree(), prefix).await
2305 }
2306}
2307impl<S> AsyncVersionedMap<'_, S>
2308where
2309 S: super::store::AsyncStore
2310 + super::manifest::AsyncManifestStore
2311 + super::transaction::AsyncTransactionalStore,
2312 <S as super::store::AsyncStore>::Error: Send + Sync,
2313 <S as super::manifest::AsyncManifestStore>::Error: Send + Sync,
2314{
2315 pub async fn apply(&self, mutations: Vec<Mutation>) -> Result<MapVersion, Error> {
2317 self.apply_at_millis(mutations, current_unix_time_millis())
2318 .await
2319 }
2320
2321 pub async fn apply_at_millis(
2323 &self,
2324 mutations: Vec<Mutation>,
2325 timestamp_millis: u64,
2326 ) -> Result<MapVersion, Error> {
2327 let mut last_conflict = None;
2328 for _ in 0..DEFAULT_VERSIONED_MAP_RETRIES {
2329 match self.try_apply(&mutations, None, timestamp_millis).await? {
2330 UpdateAttempt::Applied { current, .. } => return Ok(current),
2331 UpdateAttempt::Unchanged(Some(current)) => return Ok(current),
2332 UpdateAttempt::Unchanged(None) => {
2333 return Err(Error::InvalidVersionedMap(
2334 "empty update did not initialize the index".to_string(),
2335 ));
2336 }
2337 UpdateAttempt::Conflict(conflict) => last_conflict = Some(conflict),
2338 }
2339 }
2340 Err(Error::transaction_conflict(
2341 last_conflict.expect("retry loop records a conflict before exhaustion"),
2342 ))
2343 }
2344
2345 pub async fn apply_if_at_millis(
2347 &self,
2348 expected: Option<&MapVersionId>,
2349 mutations: Vec<Mutation>,
2350 timestamp_millis: u64,
2351 ) -> Result<VersionedMapUpdate, Error> {
2352 match self
2353 .try_apply(&mutations, Some(expected), timestamp_millis)
2354 .await?
2355 {
2356 UpdateAttempt::Applied { previous, current } => {
2357 Ok(VersionedMapUpdate::Applied { previous, current })
2358 }
2359 UpdateAttempt::Unchanged(current) => Ok(VersionedMapUpdate::Unchanged { current }),
2360 UpdateAttempt::Conflict(_) => Ok(VersionedMapUpdate::Conflict {
2361 current: self.head().await?,
2362 }),
2363 }
2364 }
2365
2366 async fn try_apply(
2367 &self,
2368 mutations: &[Mutation],
2369 expected: Option<Option<&MapVersionId>>,
2370 timestamp_millis: u64,
2371 ) -> Result<UpdateAttempt, Error> {
2372 let tx = self.prolly.begin_transaction()?;
2373 guard_async_managed_map_write(&tx, &self.id, MapWriteAuthority::Unmanaged).await?;
2374 let current_tree = tx.load_named_root(&self.head_name).await?;
2375 let current_id = current_tree
2376 .as_ref()
2377 .map(MapVersionId::for_tree)
2378 .transpose()?;
2379
2380 if let Some(expected) = expected {
2381 if current_id.as_ref() != expected {
2382 tx.rollback();
2383 return Ok(UpdateAttempt::Conflict(TransactionConflict::new(
2384 self.head_name.clone(),
2385 None,
2386 None,
2387 )));
2388 }
2389 }
2390
2391 let base = current_tree.clone().unwrap_or_else(|| tx.create());
2392 let next = tx.batch(&base, mutations.to_vec()).await?;
2393 if current_tree.as_ref() == Some(&next) {
2394 let current = Some(MapVersion {
2395 id: current_id
2396 .clone()
2397 .expect("an unchanged existing tree has a version id"),
2398 tree: next,
2399 created_at_millis: None,
2400 is_head: true,
2401 });
2402 return match tx.commit().await? {
2403 TransactionUpdate::Applied { .. } => Ok(UpdateAttempt::Unchanged(current)),
2404 TransactionUpdate::Conflict(conflict) => Ok(UpdateAttempt::Conflict(*conflict)),
2405 };
2406 }
2407
2408 let next_id = MapVersionId::for_tree(&next)?;
2409 let version_name = self.version_name(&next_id);
2410 match tx.load_named_root(&version_name).await? {
2411 Some(existing) if existing != next => {
2412 tx.rollback();
2413 return Err(Error::InvalidVersionedMap(format!(
2414 "content identifier collision for version {}",
2415 next_id
2416 )));
2417 }
2418 Some(_) => {}
2419 None => {
2420 tx.publish_named_root_at_millis(&version_name, &next, timestamp_millis)
2421 .await?;
2422 }
2423 }
2424 tx.publish_named_root_at_millis(&self.head_name, &next, timestamp_millis)
2425 .await?;
2426
2427 match tx.commit().await? {
2428 TransactionUpdate::Applied { .. } => Ok(UpdateAttempt::Applied {
2429 previous: current_id,
2430 current: MapVersion {
2431 id: next_id,
2432 tree: next,
2433 created_at_millis: Some(timestamp_millis),
2434 is_head: true,
2435 },
2436 }),
2437 TransactionUpdate::Conflict(conflict) => Ok(UpdateAttempt::Conflict(*conflict)),
2438 }
2439 }
2440
2441 async fn publish_tree_if(
2442 &self,
2443 expected: Option<&MapVersionId>,
2444 tree: &Tree,
2445 timestamp_millis: u64,
2446 ) -> Result<VersionedMapUpdate, Error> {
2447 let tx = self.prolly.begin_transaction()?;
2448 guard_async_managed_map_write(&tx, &self.id, MapWriteAuthority::Unmanaged).await?;
2449 let current_tree = tx.load_named_root(&self.head_name).await?;
2450 let current_id = current_tree
2451 .as_ref()
2452 .map(MapVersionId::for_tree)
2453 .transpose()?;
2454 if current_id.as_ref() != expected {
2455 tx.rollback();
2456 return Ok(VersionedMapUpdate::Conflict {
2457 current: self.head().await?,
2458 });
2459 }
2460 if current_tree.as_ref() == Some(tree) {
2461 let current = self.head().await?;
2462 return match tx.commit().await? {
2463 TransactionUpdate::Applied { .. } => Ok(VersionedMapUpdate::Unchanged { current }),
2464 TransactionUpdate::Conflict(_) => Ok(VersionedMapUpdate::Conflict {
2465 current: self.head().await?,
2466 }),
2467 };
2468 }
2469
2470 let id = MapVersionId::for_tree(tree)?;
2471 let version_name = self.version_name(&id);
2472 match tx.load_named_root(&version_name).await? {
2473 Some(existing) if existing != *tree => {
2474 tx.rollback();
2475 return Err(Error::InvalidVersionedMap(format!(
2476 "content identifier collision for merged version {}",
2477 id
2478 )));
2479 }
2480 Some(_) => {}
2481 None => {
2482 tx.publish_named_root_at_millis(&version_name, tree, timestamp_millis)
2483 .await?;
2484 }
2485 }
2486 tx.publish_named_root_at_millis(&self.head_name, tree, timestamp_millis)
2487 .await?;
2488 match tx.commit().await? {
2489 TransactionUpdate::Applied { .. } => Ok(VersionedMapUpdate::Applied {
2490 previous: current_id,
2491 current: MapVersion {
2492 id,
2493 tree: tree.clone(),
2494 created_at_millis: Some(timestamp_millis),
2495 is_head: true,
2496 },
2497 }),
2498 TransactionUpdate::Conflict(_) => Ok(VersionedMapUpdate::Conflict {
2499 current: self.head().await?,
2500 }),
2501 }
2502 }
2503
2504 pub async fn put(
2506 &self,
2507 key: impl Into<Vec<u8>>,
2508 value: impl Into<Vec<u8>>,
2509 ) -> Result<MapVersion, Error> {
2510 self.apply(vec![Mutation::Upsert {
2511 key: key.into(),
2512 val: value.into(),
2513 }])
2514 .await
2515 }
2516
2517 pub async fn delete(&self, key: impl Into<Vec<u8>>) -> Result<MapVersion, Error> {
2519 self.apply(vec![Mutation::Delete { key: key.into() }]).await
2520 }
2521
2522 pub async fn edit(
2524 &self,
2525 edit: impl FnOnce(&mut VersionedMapEditor),
2526 ) -> Result<MapVersion, Error> {
2527 let mut editor = VersionedMapEditor::new();
2528 edit(&mut editor);
2529 self.apply(editor.into_mutations()).await
2530 }
2531}
2532impl<S: super::store::AsyncStore> super::AsyncProlly<S> {
2533 pub fn versioned_map(&self, id: impl AsRef<[u8]>) -> AsyncVersionedMap<'_, S> {
2535 AsyncVersionedMap::new(self, id)
2536 }
2537}
2538
2539impl<S> VersionedMap<'_, S>
2540where
2541 S: Store + ManifestStore + ManifestStoreScan,
2542{
2543 pub fn versions(&self) -> Result<Vec<MapVersion>, Error> {
2545 let head_id = self.head()?.map(|head| head.id);
2546 let mut versions = self
2547 .prolly
2548 .list_named_root_manifests()?
2549 .into_iter()
2550 .filter_map(|named| {
2551 let suffix = named.name.strip_prefix(self.versions_prefix.as_slice())?;
2552 if suffix.len() != 32 {
2553 return Some(Err(Error::InvalidVersionedMap(format!(
2554 "invalid version root name under {:?}",
2555 self.versions_prefix
2556 ))));
2557 }
2558 let tree = named.manifest.to_tree();
2559 let actual = match MapVersionId::for_tree(&tree) {
2560 Ok(id) => id,
2561 Err(err) => return Some(Err(err)),
2562 };
2563 if actual.as_cid().as_bytes() != suffix {
2564 return Some(Err(Error::InvalidVersionedMap(format!(
2565 "version catalog key does not match tree content: {}",
2566 actual
2567 ))));
2568 }
2569 Some(Ok(MapVersion {
2570 is_head: head_id.as_ref() == Some(&actual),
2571 id: actual,
2572 tree,
2573 created_at_millis: named.manifest.created_at_millis,
2574 }))
2575 })
2576 .collect::<Result<Vec<_>, _>>()?;
2577
2578 versions.sort_by(|left, right| {
2579 right
2580 .created_at_millis
2581 .cmp(&left.created_at_millis)
2582 .then_with(|| {
2583 left.id
2584 .as_cid()
2585 .as_bytes()
2586 .cmp(right.id.as_cid().as_bytes())
2587 })
2588 });
2589 Ok(versions)
2590 }
2591
2592 pub fn backup(&self) -> Result<VersionedMapBackup, Error> {
2594 let head = self.head()?.ok_or_else(|| {
2595 Error::InvalidVersionedMap("map has not been initialized".to_string())
2596 })?;
2597 let versions = self
2598 .versions()?
2599 .into_iter()
2600 .map(|version| {
2601 Ok(MapBackupVersion {
2602 id: version.id,
2603 created_at_millis: version.created_at_millis,
2604 bundle: self.prolly.export_snapshot(&version.tree)?,
2605 })
2606 })
2607 .collect::<Result<Vec<_>, Error>>()?;
2608 let backup = VersionedMapBackup {
2609 map_id: self.id.clone(),
2610 head: head.id,
2611 versions,
2612 };
2613 backup.verify()?;
2614 Ok(backup)
2615 }
2616}
2617
2618#[allow(clippy::large_enum_variant)]
2619enum UpdateAttempt {
2620 Applied {
2621 previous: Option<MapVersionId>,
2622 current: MapVersion,
2623 },
2624 Unchanged(Option<MapVersion>),
2625 Conflict(TransactionConflict),
2626}
2627
2628impl<S> VersionedMap<'_, S>
2629where
2630 S: Store + ManifestStore + TransactionalStore,
2631{
2632 pub fn initialize(&self) -> Result<MapVersion, Error> {
2634 self.apply(Vec::new())
2635 }
2636
2637 pub fn import_as_head(
2639 &self,
2640 bundle: &super::sync::SnapshotBundle,
2641 ) -> Result<MapVersion, Error> {
2642 self.import_as_head_at_millis(bundle, current_unix_time_millis())
2643 }
2644
2645 pub fn import_as_head_at_millis(
2647 &self,
2648 bundle: &super::sync::SnapshotBundle,
2649 timestamp_millis: u64,
2650 ) -> Result<MapVersion, Error> {
2651 if !bundle.verify()?.valid {
2652 return Err(Error::InvalidVersionedMap(
2653 "snapshot bundle is not self-contained".to_string(),
2654 ));
2655 }
2656 if bundle.tree.config != *self.prolly.config() {
2657 return Err(Error::InvalidVersionedMap(
2658 "snapshot config does not match the managed map engine".to_string(),
2659 ));
2660 }
2661 let tree = self.prolly.import_snapshot(bundle)?;
2662 let id = MapVersionId::for_tree(&tree)?;
2663 let version_name = self.version_name(&id);
2664 let mut last_conflict = None;
2665
2666 for _ in 0..DEFAULT_VERSIONED_MAP_RETRIES {
2667 let tx = self.prolly.begin_transaction()?;
2668 guard_managed_map_write(&tx, &self.id, MapWriteAuthority::Unmanaged)?;
2669 let current = tx.load_named_root(&self.head_name)?;
2670 if current.as_ref() == Some(&tree) {
2671 tx.rollback();
2672 return Ok(MapVersion {
2673 id,
2674 tree,
2675 created_at_millis: Some(timestamp_millis),
2676 is_head: true,
2677 });
2678 }
2679 match tx.load_named_root(&version_name)? {
2680 Some(existing) if existing != tree => {
2681 tx.rollback();
2682 return Err(Error::InvalidVersionedMap(format!(
2683 "content identifier collision for imported version {}",
2684 id
2685 )));
2686 }
2687 Some(_) => {}
2688 None => tx.publish_named_root_at_millis(&version_name, &tree, timestamp_millis)?,
2689 }
2690 tx.publish_named_root_at_millis(&self.head_name, &tree, timestamp_millis)?;
2691 match tx.commit()? {
2692 TransactionUpdate::Applied { .. } => {
2693 return Ok(MapVersion {
2694 id,
2695 tree,
2696 created_at_millis: Some(timestamp_millis),
2697 is_head: true,
2698 });
2699 }
2700 TransactionUpdate::Conflict(conflict) => last_conflict = Some(*conflict),
2701 }
2702 }
2703
2704 Err(Error::transaction_conflict(
2705 last_conflict.expect("retry loop records a conflict before exhaustion"),
2706 ))
2707 }
2708
2709 pub fn restore_backup(&self, backup: &VersionedMapBackup) -> Result<MapVersion, Error> {
2711 backup.verify()?;
2712 if backup.map_id != self.id {
2713 return Err(Error::InvalidVersionedMap(format!(
2714 "backup map id {:?} does not match target {:?}",
2715 backup.map_id, self.id
2716 )));
2717 }
2718 for version in &backup.versions {
2719 if version.bundle.tree.config != *self.prolly.config() {
2720 return Err(Error::InvalidVersionedMap(format!(
2721 "backup version {} uses a different tree config",
2722 version.id
2723 )));
2724 }
2725 self.prolly.import_snapshot(&version.bundle)?;
2726 }
2727
2728 let tx = self.prolly.begin_transaction()?;
2729 guard_managed_map_write(&tx, &self.id, MapWriteAuthority::Unmanaged)?;
2730 if tx.load_named_root(&self.head_name)?.is_some() {
2731 tx.rollback();
2732 return Err(Error::InvalidVersionedMap(
2733 "restore target is already initialized".to_string(),
2734 ));
2735 }
2736 let mut restored_head = None;
2737 for version in &backup.versions {
2738 let tree = &version.bundle.tree;
2739 let name = self.version_name(&version.id);
2740 match tx.load_named_root(&name)? {
2741 Some(existing) if existing != *tree => {
2742 tx.rollback();
2743 return Err(Error::InvalidVersionedMap(format!(
2744 "target version root {} contains different content",
2745 version.id
2746 )));
2747 }
2748 Some(_) => {}
2749 None => tx.publish_named_root_at_millis(
2750 &name,
2751 tree,
2752 version
2753 .created_at_millis
2754 .unwrap_or_else(current_unix_time_millis),
2755 )?,
2756 }
2757 if version.id == backup.head {
2758 restored_head = Some(MapVersion {
2759 id: version.id.clone(),
2760 tree: tree.clone(),
2761 created_at_millis: version.created_at_millis,
2762 is_head: true,
2763 });
2764 }
2765 }
2766 let restored_head = restored_head.expect("verified backup contains its head");
2767 tx.publish_named_root_at_millis(
2768 &self.head_name,
2769 &restored_head.tree,
2770 restored_head
2771 .created_at_millis
2772 .unwrap_or_else(current_unix_time_millis),
2773 )?;
2774 match tx.commit()? {
2775 TransactionUpdate::Applied { .. } => Ok(restored_head),
2776 TransactionUpdate::Conflict(conflict) => Err(Error::TransactionConflict(conflict)),
2777 }
2778 }
2779
2780 pub fn apply(&self, mutations: Vec<Mutation>) -> Result<MapVersion, Error> {
2782 self.apply_at_millis(mutations, current_unix_time_millis())
2783 }
2784
2785 pub fn apply_at_millis(
2787 &self,
2788 mutations: Vec<Mutation>,
2789 timestamp_millis: u64,
2790 ) -> Result<MapVersion, Error> {
2791 let service = self.async_service();
2792 let ready_store = self.prolly.engine.store.clone();
2793 let future = service.apply_at_millis(mutations, timestamp_millis);
2794 super::engine::ready::run_ready(ready_store.ready(future))
2795 }
2796
2797 pub fn apply_if(
2799 &self,
2800 expected: Option<&MapVersionId>,
2801 mutations: Vec<Mutation>,
2802 ) -> Result<VersionedMapUpdate, Error> {
2803 self.apply_if_at_millis(expected, mutations, current_unix_time_millis())
2804 }
2805
2806 pub fn apply_if_at_millis(
2808 &self,
2809 expected: Option<&MapVersionId>,
2810 mutations: Vec<Mutation>,
2811 timestamp_millis: u64,
2812 ) -> Result<VersionedMapUpdate, Error> {
2813 let service = self.async_service();
2814 let ready_store = self.prolly.engine.store.clone();
2815 let future = service.apply_if_at_millis(expected, mutations, timestamp_millis);
2816 super::engine::ready::run_ready(ready_store.ready(future))
2817 }
2818
2819 pub fn put_if(
2821 &self,
2822 expected: Option<&MapVersionId>,
2823 key: impl Into<Vec<u8>>,
2824 value: impl Into<Vec<u8>>,
2825 ) -> Result<VersionedMapUpdate, Error> {
2826 self.apply_if(
2827 expected,
2828 vec![Mutation::Upsert {
2829 key: key.into(),
2830 val: value.into(),
2831 }],
2832 )
2833 }
2834
2835 pub fn delete_if(
2837 &self,
2838 expected: Option<&MapVersionId>,
2839 key: impl Into<Vec<u8>>,
2840 ) -> Result<VersionedMapUpdate, Error> {
2841 self.apply_if(expected, vec![Mutation::Delete { key: key.into() }])
2842 }
2843
2844 pub fn edit_if(
2846 &self,
2847 expected: Option<&MapVersionId>,
2848 edit: impl FnOnce(&mut VersionedMapEditor),
2849 ) -> Result<VersionedMapUpdate, Error> {
2850 let mut editor = VersionedMapEditor::new();
2851 edit(&mut editor);
2852 self.apply_if(expected, editor.into_mutations())
2853 }
2854
2855 pub fn put(
2857 &self,
2858 key: impl Into<Vec<u8>>,
2859 value: impl Into<Vec<u8>>,
2860 ) -> Result<MapVersion, Error> {
2861 self.apply(vec![Mutation::Upsert {
2862 key: key.into(),
2863 val: value.into(),
2864 }])
2865 }
2866
2867 pub fn put_large_value<B: super::blob::BlobStore>(
2869 &self,
2870 blob_store: &B,
2871 key: impl Into<Vec<u8>>,
2872 value: impl Into<Vec<u8>>,
2873 config: super::blob::LargeValueConfig,
2874 ) -> Result<MapVersion, Error> {
2875 let key = key.into();
2876 let value = value.into();
2877 let mut last_conflict = None;
2878 for _ in 0..DEFAULT_VERSIONED_MAP_RETRIES {
2879 let current = self.head()?;
2880 let expected = current.as_ref().map(|version| &version.id);
2881 let tree = current
2882 .as_ref()
2883 .map(|version| version.tree.clone())
2884 .unwrap_or_else(|| self.prolly.create());
2885 let next = self.prolly.put_large_value(
2886 blob_store,
2887 &tree,
2888 key.clone(),
2889 value.clone(),
2890 config.clone(),
2891 )?;
2892 match self.publish_tree_if(expected, &next, current_unix_time_millis())? {
2893 VersionedMapUpdate::Applied { current, .. } => return Ok(current),
2894 VersionedMapUpdate::Unchanged {
2895 current: Some(current),
2896 } => return Ok(current),
2897 VersionedMapUpdate::Conflict { current } => {
2898 last_conflict = current;
2899 }
2900 VersionedMapUpdate::Unchanged { current: None } => {}
2901 }
2902 }
2903 Err(Error::InvalidVersionedMap(format!(
2904 "large-value update exhausted retries at head {:?}",
2905 last_conflict.map(|version| version.id)
2906 )))
2907 }
2908
2909 pub fn put_large_value_if<B: super::blob::BlobStore>(
2911 &self,
2912 blob_store: &B,
2913 expected: Option<&MapVersionId>,
2914 key: impl Into<Vec<u8>>,
2915 value: impl Into<Vec<u8>>,
2916 config: super::blob::LargeValueConfig,
2917 ) -> Result<VersionedMapUpdate, Error> {
2918 let current = self.head()?;
2919 if current.as_ref().map(|version| &version.id) != expected {
2920 return Ok(VersionedMapUpdate::Conflict { current });
2921 }
2922 let tree = current
2923 .map(|version| version.tree)
2924 .unwrap_or_else(|| self.prolly.create());
2925 let next =
2926 self.prolly
2927 .put_large_value(blob_store, &tree, key.into(), value.into(), config)?;
2928 self.publish_tree_if(expected, &next, current_unix_time_millis())
2929 }
2930
2931 pub fn delete(&self, key: impl Into<Vec<u8>>) -> Result<MapVersion, Error> {
2933 self.apply(vec![Mutation::Delete { key: key.into() }])
2934 }
2935
2936 pub fn edit(&self, edit: impl FnOnce(&mut VersionedMapEditor)) -> Result<MapVersion, Error> {
2938 let mut editor = VersionedMapEditor::new();
2939 edit(&mut editor);
2940 self.apply(editor.into_mutations())
2941 }
2942
2943 pub fn append(&self, mutations: Vec<Mutation>) -> Result<MapVersion, Error> {
2945 let mut last_head = None;
2946 for _ in 0..DEFAULT_VERSIONED_MAP_RETRIES {
2947 let current = self.head()?;
2948 let expected = current.as_ref().map(|version| &version.id);
2949 let tree = current
2950 .as_ref()
2951 .map(|version| version.tree.clone())
2952 .unwrap_or_else(|| self.prolly.create());
2953 let next = self.prolly.append_batch(&tree, mutations.clone())?;
2954 match self.publish_tree_if(expected, &next, current_unix_time_millis())? {
2955 VersionedMapUpdate::Applied { current, .. }
2956 | VersionedMapUpdate::Unchanged {
2957 current: Some(current),
2958 } => return Ok(current),
2959 VersionedMapUpdate::Conflict { current } => last_head = current,
2960 VersionedMapUpdate::Unchanged { current: None } => {}
2961 }
2962 }
2963 Err(Error::InvalidVersionedMap(format!(
2964 "append exhausted retries at head {:?}",
2965 last_head.map(|version| version.id)
2966 )))
2967 }
2968
2969 pub fn parallel_apply(
2971 &self,
2972 mutations: Vec<Mutation>,
2973 config: &super::parallel::ParallelConfig,
2974 ) -> Result<VersionedMapBatchResult, Error> {
2975 let mut last_head = None;
2976 for _ in 0..DEFAULT_VERSIONED_MAP_RETRIES {
2977 let current = self.head()?;
2978 let expected = current.as_ref().map(|version| &version.id);
2979 let tree = current
2980 .as_ref()
2981 .map(|version| version.tree.clone())
2982 .unwrap_or_else(|| self.prolly.create());
2983 let applied =
2984 self.prolly
2985 .parallel_batch_with_stats(&tree, mutations.clone(), config)?;
2986 match self.publish_tree_if(expected, &applied.tree, current_unix_time_millis())? {
2987 VersionedMapUpdate::Applied { current, .. }
2988 | VersionedMapUpdate::Unchanged {
2989 current: Some(current),
2990 } => {
2991 return Ok(VersionedMapBatchResult {
2992 version: current,
2993 stats: applied.stats,
2994 });
2995 }
2996 VersionedMapUpdate::Conflict { current } => last_head = current,
2997 VersionedMapUpdate::Unchanged { current: None } => {}
2998 }
2999 }
3000 Err(Error::InvalidVersionedMap(format!(
3001 "parallel batch exhausted retries at head {:?}",
3002 last_head.map(|version| version.id)
3003 )))
3004 }
3005
3006 pub fn rollback_to(&self, id: &MapVersionId) -> Result<MapVersion, Error> {
3011 let target = self
3012 .version(id)?
3013 .ok_or_else(|| Error::InvalidVersionedMap(format!("unknown map version {id}")))?;
3014 let timestamp_millis = current_unix_time_millis();
3015 let mut last_conflict = None;
3016
3017 for _ in 0..DEFAULT_VERSIONED_MAP_RETRIES {
3018 let tx = self.prolly.begin_transaction()?;
3019 guard_managed_map_write(&tx, &self.id, MapWriteAuthority::Unmanaged)?;
3020 let current = tx.load_named_root(&self.head_name)?;
3021 if current.as_ref() == Some(&target.tree) {
3022 return Ok(MapVersion {
3023 is_head: true,
3024 ..target
3025 });
3026 }
3027 tx.publish_named_root_at_millis(&self.head_name, &target.tree, timestamp_millis)?;
3028 match tx.commit()? {
3029 TransactionUpdate::Applied { .. } => {
3030 return Ok(MapVersion {
3031 is_head: true,
3032 ..target
3033 });
3034 }
3035 TransactionUpdate::Conflict(conflict) => last_conflict = Some(*conflict),
3036 }
3037 }
3038
3039 Err(Error::transaction_conflict(
3040 last_conflict.expect("retry loop records a conflict before exhaustion"),
3041 ))
3042 }
3043
3044 fn publish_tree_if(
3045 &self,
3046 expected: Option<&MapVersionId>,
3047 tree: &Tree,
3048 timestamp_millis: u64,
3049 ) -> Result<VersionedMapUpdate, Error> {
3050 let service = self.async_service();
3051 let ready_store = self.prolly.engine.store.clone();
3052 let future = service.publish_tree_if(expected, tree, timestamp_millis);
3053 super::engine::ready::run_ready(ready_store.ready(future))
3054 }
3055}
3056
3057impl<S> VersionedMap<'_, S>
3058where
3059 S: Store + ManifestStore + ManifestStoreScan + TransactionalStore,
3060{
3061 pub fn prune_versions(&self, keep_latest: usize) -> Result<VersionPruneResult, Error> {
3068 self.keep_last(keep_latest)
3069 }
3070
3071 pub fn keep_last(&self, count: usize) -> Result<VersionPruneResult, Error> {
3073 self.prune_with(|versions| {
3074 Ok(versions
3075 .iter()
3076 .take(count)
3077 .map(|version| version.id.clone())
3078 .collect())
3079 })
3080 }
3081
3082 pub fn keep_for(&self, max_age: std::time::Duration) -> Result<VersionPruneResult, Error> {
3084 self.keep_for_at(current_unix_time_millis(), max_age)
3085 }
3086
3087 pub fn keep_for_at(
3089 &self,
3090 now_millis: u64,
3091 max_age: std::time::Duration,
3092 ) -> Result<VersionPruneResult, Error> {
3093 let age_millis = max_age.as_millis().min(u128::from(u64::MAX)) as u64;
3094 let cutoff = now_millis.saturating_sub(age_millis);
3095 self.prune_with(|versions| {
3096 Ok(versions
3097 .iter()
3098 .filter(|version| {
3099 version
3100 .created_at_millis
3101 .map(|created| created >= cutoff)
3102 .unwrap_or(true)
3103 })
3104 .map(|version| version.id.clone())
3105 .collect())
3106 })
3107 }
3108
3109 pub fn keep_versions<I, V>(&self, ids: I) -> Result<VersionPruneResult, Error>
3114 where
3115 I: IntoIterator<Item = V>,
3116 V: Borrow<MapVersionId>,
3117 {
3118 let requested = ids
3119 .into_iter()
3120 .map(|id| id.borrow().clone())
3121 .collect::<HashSet<_>>();
3122 self.prune_with(|versions| {
3123 let present = versions
3124 .iter()
3125 .map(|version| version.id.clone())
3126 .collect::<HashSet<_>>();
3127 let missing = requested.difference(&present).collect::<Vec<_>>();
3128 if !missing.is_empty() {
3129 return Err(Error::InvalidVersionedMap(format!(
3130 "retention requested unknown versions: {:?}",
3131 missing
3132 )));
3133 }
3134 Ok(requested.clone())
3135 })
3136 }
3137
3138 fn prune_with(
3139 &self,
3140 select: impl Fn(&[MapVersion]) -> Result<HashSet<MapVersionId>, Error>,
3141 ) -> Result<VersionPruneResult, Error> {
3142 let mut last_conflict = None;
3143
3144 for _ in 0..DEFAULT_VERSIONED_MAP_RETRIES {
3145 let tx = self.prolly.begin_transaction()?;
3146 guard_managed_map_write(&tx, &self.id, MapWriteAuthority::Unmanaged)?;
3147 let Some(head_tree) = tx.load_named_root(&self.head_name)? else {
3148 tx.rollback();
3149 let versions = self.versions()?;
3150 if versions.is_empty() {
3151 return Ok(VersionPruneResult::default());
3152 }
3153 return Err(Error::InvalidVersionedMap(
3154 "version roots exist without a current head".to_string(),
3155 ));
3156 };
3157 let head_id = MapVersionId::for_tree(&head_tree)?;
3158 let versions = self.versions()?;
3159 if !versions.iter().any(|version| version.id == head_id) {
3160 tx.rollback();
3161 return Err(Error::InvalidVersionedMap(format!(
3162 "current head {} is absent from the version catalog",
3163 head_id
3164 )));
3165 }
3166
3167 let mut retained_ids = select(&versions)?;
3168 retained_ids.insert(head_id);
3169
3170 let retained = versions
3171 .iter()
3172 .filter(|version| retained_ids.contains(&version.id))
3173 .map(|version| version.id.clone())
3174 .collect::<Vec<_>>();
3175 let removed = versions
3176 .iter()
3177 .filter(|version| !retained_ids.contains(&version.id))
3178 .map(|version| version.id.clone())
3179 .collect::<Vec<_>>();
3180
3181 if removed.is_empty() {
3182 tx.rollback();
3183 return Ok(VersionPruneResult { retained, removed });
3184 }
3185
3186 for id in &removed {
3187 let name = self.version_name(id);
3188 if tx.load_named_root(&name)?.is_some() {
3189 tx.delete_named_root(&name)?;
3190 }
3191 }
3192
3193 match tx.commit()? {
3194 TransactionUpdate::Applied { .. } => {
3195 return Ok(VersionPruneResult { retained, removed });
3196 }
3197 TransactionUpdate::Conflict(conflict) => last_conflict = Some(*conflict),
3198 }
3199 }
3200
3201 Err(Error::transaction_conflict(
3202 last_conflict.expect("retry loop records a conflict before exhaustion"),
3203 ))
3204 }
3205}
3206
3207impl<S> VersionedMap<'_, S>
3208where
3209 S: Store + ManifestStore + TransactionalStore + Clone + Send + Sync,
3210{
3211 pub fn initialize_sorted<I, K, V>(&self, entries: I) -> Result<VersionedMapUpdate, Error>
3213 where
3214 I: IntoIterator<Item = (K, V)>,
3215 K: Into<Vec<u8>>,
3216 V: Into<Vec<u8>>,
3217 {
3218 self.rebuild_sorted_if(None, entries)
3219 }
3220
3221 pub fn rebuild_sorted_if<I, K, V>(
3223 &self,
3224 expected: Option<&MapVersionId>,
3225 entries: I,
3226 ) -> Result<VersionedMapUpdate, Error>
3227 where
3228 I: IntoIterator<Item = (K, V)>,
3229 K: Into<Vec<u8>>,
3230 V: Into<Vec<u8>>,
3231 {
3232 let current = self.head()?;
3233 if current.as_ref().map(|version| &version.id) != expected {
3234 return Ok(VersionedMapUpdate::Conflict { current });
3235 }
3236 let mut builder = super::builder::SortedBatchBuilder::new(
3237 self.prolly.store().clone(),
3238 self.prolly.config().clone(),
3239 );
3240 for (key, value) in entries {
3241 builder.add(key.into(), value.into())?;
3242 }
3243 let tree = builder.build()?;
3244 self.publish_tree_if(expected, &tree, current_unix_time_millis())
3245 }
3246
3247 pub fn rebuild_from_iter_if<I, K, V>(
3249 &self,
3250 expected: Option<&MapVersionId>,
3251 entries: I,
3252 ) -> Result<VersionedMapUpdate, Error>
3253 where
3254 I: IntoIterator<Item = (K, V)>,
3255 K: Into<Vec<u8>>,
3256 V: Into<Vec<u8>>,
3257 {
3258 let current = self.head()?;
3259 if current.as_ref().map(|version| &version.id) != expected {
3260 return Ok(VersionedMapUpdate::Conflict { current });
3261 }
3262 let mut builder = super::builder::BatchBuilder::new(
3263 self.prolly.store().clone(),
3264 self.prolly.config().clone(),
3265 );
3266 for (key, value) in entries {
3267 builder.add(key.into(), value.into());
3268 }
3269 let tree = builder.build()?;
3270 self.publish_tree_if(expected, &tree, current_unix_time_millis())
3271 }
3272}
3273
3274#[derive(Clone, Debug, PartialEq, Eq)]
3276pub struct MapCatalogVerification {
3277 pub head: MapVersionId,
3279 pub version_count: usize,
3281 pub reachable_nodes: usize,
3283 pub reachable_bytes: usize,
3285}
3286
3287impl<S> VersionedMap<'_, S>
3288where
3289 S: Store + ManifestStore + ManifestStoreScan,
3290{
3291 pub fn verify_catalog(&self) -> Result<MapCatalogVerification, Error> {
3293 let head = self.head()?.ok_or_else(|| {
3294 Error::InvalidVersionedMap("map has not been initialized".to_string())
3295 })?;
3296 let versions = self.versions()?;
3297 if !versions.iter().any(|version| version.id == head.id) {
3298 return Err(Error::InvalidVersionedMap(format!(
3299 "current head {} is absent from the version catalog",
3300 head.id
3301 )));
3302 }
3303 let trees = versions
3304 .iter()
3305 .map(|version| version.tree.clone())
3306 .collect::<Vec<_>>();
3307 let reachable = self.prolly.mark_reachable(&trees)?;
3308 Ok(MapCatalogVerification {
3309 head: head.id,
3310 version_count: versions.len(),
3311 reachable_nodes: reachable.live_nodes,
3312 reachable_bytes: reachable.live_bytes,
3313 })
3314 }
3315}
3316
3317impl<S> VersionedMap<'_, S>
3318where
3319 S: Store + ManifestStore + ManifestStoreScan + super::store::NodeStoreScan,
3320{
3321 pub fn plan_gc(&self) -> Result<super::gc::GcPlan, Error> {
3327 self.prolly
3328 .plan_store_gc_for_retention(&NamedRootRetention::all())
3329 }
3330
3331 pub fn sweep_gc(&self) -> Result<super::gc::GcSweep, Error> {
3333 self.prolly
3334 .sweep_store_gc_for_retention(&NamedRootRetention::all())
3335 }
3336}
3337
3338impl<S> VersionedMap<'_, S>
3339where
3340 S: Store + ManifestStore + ManifestStoreScan,
3341{
3342 pub fn plan_blob_gc<B: super::blob::BlobStoreScan>(
3347 &self,
3348 blob_store: &B,
3349 ) -> Result<super::gc::BlobGcPlan, Error> {
3350 let roots = self
3351 .prolly
3352 .load_retained_named_roots(&NamedRootRetention::all())?
3353 .trees();
3354 self.prolly.plan_blob_store_gc(blob_store, &roots)
3355 }
3356
3357 pub fn sweep_blob_gc<B: super::blob::BlobStoreScan>(
3359 &self,
3360 blob_store: &B,
3361 ) -> Result<super::gc::BlobGcSweep, Error> {
3362 let roots = self
3363 .prolly
3364 .load_retained_named_roots(&NamedRootRetention::all())?
3365 .trees();
3366 self.prolly.sweep_blob_store_gc(blob_store, &roots)
3367 }
3368}
3369
3370impl<S: Store> Prolly<S> {
3371 pub fn versioned_map(&self, id: impl AsRef<[u8]>) -> VersionedMap<'_, S> {
3373 VersionedMap::new(self, id)
3374 }
3375}
3376
3377impl<S> Prolly<S>
3378where
3379 S: Store + ManifestStore + TransactionalStore,
3380{
3381 pub fn versioned_maps_transaction<T>(
3383 &self,
3384 run: impl FnOnce(&mut VersionedMapsTransaction<'_, '_, S>) -> Result<T, Error>,
3385 ) -> Result<T, Error> {
3386 let timestamp_millis = current_unix_time_millis();
3387 self.transaction(|tx| {
3388 let mut maps = VersionedMapsTransaction::new(tx, timestamp_millis);
3389 run(&mut maps)
3390 })
3391 }
3392}
3393
3394fn append_hex(output: &mut Vec<u8>, bytes: &[u8]) {
3395 const HEX: &[u8; 16] = b"0123456789abcdef";
3396 output.reserve(bytes.len() * 2);
3397 for byte in bytes {
3398 output.push(HEX[(byte >> 4) as usize]);
3399 output.push(HEX[(byte & 0x0f) as usize]);
3400 }
3401}
3402
3403fn versioned_map_names(id: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
3404 let mut root_prefix = VERSIONED_MAP_ROOT_PREFIX.to_vec();
3405 append_hex(&mut root_prefix, id);
3406
3407 let mut head_name = root_prefix.clone();
3408 head_name.extend_from_slice(HEAD_SUFFIX);
3409
3410 let mut versions_prefix = root_prefix.clone();
3411 versions_prefix.extend_from_slice(VERSIONS_SUFFIX);
3412 (root_prefix, head_name, versions_prefix)
3413}
3414
3415#[cfg(test)]
3416mod index_fence_tests {
3417 use super::*;
3418 use crate::prolly::config::Config;
3419 use crate::prolly::secondary_index::indexed_collection_root_name;
3420 use crate::prolly::store::MemStore;
3421
3422 #[test]
3423 fn map_version_id_decodes_only_exact_cid_bytes() {
3424 let expected = MapVersionId::for_tree(&Tree::new(Config::default())).unwrap();
3425 assert_eq!(
3426 MapVersionId::from_bytes(expected.as_cid().as_bytes()).unwrap(),
3427 expected
3428 );
3429 assert!(MapVersionId::from_bytes(&[0; 31]).is_err());
3430 assert!(MapVersionId::from_bytes(&[0; 33]).is_err());
3431 }
3432
3433 #[test]
3434 fn absent_collection_state_read_conflicts_with_later_activation() {
3435 let engine = Prolly::new(MemStore::new(), Config::default());
3436 let tx = engine.begin_transaction().unwrap();
3437 guard_managed_map_write(&tx, b"users", MapWriteAuthority::Unmanaged).unwrap();
3438 let tree = engine
3439 .put(&engine.create(), b"format".to_vec(), vec![1])
3440 .unwrap();
3441 let root = indexed_collection_root_name(b"users").unwrap();
3442 engine.publish_named_root(&root, &tree).unwrap();
3443
3444 let update = tx.commit().unwrap();
3445 assert!(matches!(
3446 update,
3447 TransactionUpdate::Conflict(conflict)
3448 if conflict.name == root
3449 ));
3450 }
3451}