1#[cfg(all(test, feature = "arbitrary"))]
103mod conformance;
104mod storage;
105
106use std::num::{NonZeroU64, NonZeroUsize};
107pub use storage::Ordinal;
108use thiserror::Error;
109
110#[derive(Debug, Error)]
112pub enum Error {
113 #[error("runtime error: {0}")]
114 Runtime(#[from] commonware_runtime::Error),
115 #[error("invalid blob name: {0}")]
116 InvalidBlobName(String),
117 #[error("invalid record: {0}")]
118 InvalidRecord(u64),
119 #[error("missing record at {0}")]
120 MissingRecord(u64),
121}
122
123#[derive(Clone)]
125pub struct Config {
126 pub partition: String,
128
129 pub items_per_blob: NonZeroU64,
131
132 pub write_buffer: NonZeroUsize,
134
135 pub replay_buffer: NonZeroUsize,
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::utils::bits_for_indices;
143 use commonware_codec::{FixedSize, Read, ReadExt, Write};
144 use commonware_cryptography::Crc32;
145 use commonware_formatting::hex;
146 use commonware_macros::{test_group, test_traced};
147 use commonware_runtime::{
148 Blob, Buf, BufMut, Metrics as _, Runner, Storage, Supervisor as _, WriteOptions,
149 deterministic,
150 };
151 use commonware_utils::{NZU64, NZUsize, bitmap::BitMap, sequence::FixedBytes};
152 use rand::Rng;
153 use std::collections::BTreeMap;
154
155 const DEFAULT_ITEMS_PER_BLOB: u64 = 1000;
156 const DEFAULT_WRITE_BUFFER: usize = 4096;
157 const DEFAULT_REPLAY_BUFFER: usize = 1024 * 1024;
158
159 #[test_traced]
160 fn test_put_get() {
161 let executor = deterministic::Runner::default();
163 executor.start(|context| async move {
164 let cfg = Config {
166 partition: "test-ordinal".into(),
167 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
168 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
169 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
170 };
171 let mut store =
172 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
173 .await
174 .expect("Failed to initialize store");
175
176 let value = FixedBytes::new([42u8; 32]);
177
178 assert!(!store.has(0));
180
181 store = store
183 .put(0, value.clone())
184 .await
185 .expect("Failed to put data");
186
187 assert!(store.has(0));
189
190 let retrieved = store
192 .get(0)
193 .await
194 .expect("Failed to get data")
195 .expect("Data not found");
196 assert_eq!(retrieved, value);
197
198 store = store.sync().await.expect("Failed to sync data");
200
201 let buffer = context.encode();
203 assert!(buffer.contains("gets_total 1"), "{}", buffer);
204 assert!(buffer.contains("puts_total 1"), "{}", buffer);
205 assert!(buffer.contains("has_total 2"), "{}", buffer);
206 assert!(buffer.contains("syncs_total 1"), "{}", buffer);
207 assert!(buffer.contains("pruned_total 0"), "{}", buffer);
208
209 let retrieved = store
211 .get(0)
212 .await
213 .expect("Failed to get data")
214 .expect("Data not found");
215 assert_eq!(retrieved, value);
216 });
217 }
218
219 #[test_traced]
220 fn test_sync_does_not_report_success_while_flush_fails() {
221 let executor = deterministic::Runner::default();
222 executor.start(|context| async move {
223 let cfg = Config {
224 partition: "test-ordinal".into(),
225 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
226 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
227 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
228 };
229 let mut store =
230 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
231 .await
232 .expect("Failed to initialize store");
233
234 store = store
235 .put(0, FixedBytes::new([42u8; 32]))
236 .await
237 .expect("Failed to put data");
238
239 let section = 0u64.to_be_bytes();
241 context
242 .remove(&cfg.partition, Some(§ion))
243 .await
244 .expect("Failed to remove blob");
245
246 assert!(store.sync().await.is_err(), "sync unexpectedly succeeded");
248 });
249 }
250
251 #[test_traced]
252 fn test_multiple_indices() {
253 let executor = deterministic::Runner::default();
255 executor.start(|context| async move {
256 let cfg = Config {
258 partition: "test-ordinal".into(),
259 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
260 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
261 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
262 };
263 let mut store =
264 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
265 .await
266 .expect("Failed to initialize store");
267
268 let indices = vec![
270 (0u64, FixedBytes::new([0u8; 32])),
271 (5u64, FixedBytes::new([5u8; 32])),
272 (10u64, FixedBytes::new([10u8; 32])),
273 (100u64, FixedBytes::new([100u8; 32])),
274 (1000u64, FixedBytes::new([200u8; 32])), ];
276
277 for (index, value) in &indices {
278 store = store
279 .put(*index, value.clone())
280 .await
281 .expect("Failed to put data");
282 }
283
284 store = store.sync().await.expect("Failed to sync");
286
287 for (index, value) in &indices {
289 let retrieved = store
290 .get(*index)
291 .await
292 .expect("Failed to get data")
293 .expect("Data not found");
294 assert_eq!(&retrieved, value);
295 }
296 });
297 }
298
299 #[test_traced]
300 fn test_sparse_indices() {
301 let executor = deterministic::Runner::default();
303 executor.start(|context| async move {
304 let cfg = Config {
306 partition: "test-ordinal".into(),
307 items_per_blob: NZU64!(100), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
309 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
310 };
311 let mut store =
312 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
313 .await
314 .expect("Failed to initialize store");
315
316 let indices = vec![
318 (0u64, FixedBytes::new([0u8; 32])),
319 (99u64, FixedBytes::new([99u8; 32])), (100u64, FixedBytes::new([100u8; 32])), (500u64, FixedBytes::new([200u8; 32])), ];
323
324 for (index, value) in &indices {
325 store = store
326 .put(*index, value.clone())
327 .await
328 .expect("Failed to put data");
329 }
330
331 assert!(!store.has(1));
333 assert!(!store.has(50));
334 assert!(!store.has(101));
335 assert!(!store.has(499));
336
337 store = store.sync().await.expect("Failed to sync");
339
340 for (index, value) in &indices {
341 let retrieved = store
342 .get(*index)
343 .await
344 .expect("Failed to get data")
345 .expect("Data not found");
346 assert_eq!(&retrieved, value);
347 }
348 });
349 }
350
351 #[test_traced]
352 fn test_next_gap() {
353 let executor = deterministic::Runner::default();
355 executor.start(|context| async move {
356 let cfg = Config {
358 partition: "test-ordinal".into(),
359 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
360 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
361 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
362 };
363 let mut store =
364 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
365 .await
366 .expect("Failed to initialize store");
367
368 store = store.put(1, FixedBytes::new([1u8; 32])).await.unwrap();
370 store = store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
371 store = store.put(11, FixedBytes::new([11u8; 32])).await.unwrap();
372 store = store.put(14, FixedBytes::new([14u8; 32])).await.unwrap();
373
374 let (current_end, start_next) = store.next_gap(0);
376 assert!(current_end.is_none());
377 assert_eq!(start_next, Some(1));
378
379 let (current_end, start_next) = store.next_gap(1);
380 assert_eq!(current_end, Some(1));
381 assert_eq!(start_next, Some(10));
382
383 let (current_end, start_next) = store.next_gap(10);
384 assert_eq!(current_end, Some(11));
385 assert_eq!(start_next, Some(14));
386
387 let (current_end, start_next) = store.next_gap(11);
388 assert_eq!(current_end, Some(11));
389 assert_eq!(start_next, Some(14));
390
391 let (current_end, start_next) = store.next_gap(12);
392 assert!(current_end.is_none());
393 assert_eq!(start_next, Some(14));
394
395 let (current_end, start_next) = store.next_gap(14);
396 assert_eq!(current_end, Some(14));
397 assert!(start_next.is_none());
398 });
399 }
400
401 #[test_traced]
402 fn test_missing_items() {
403 let executor = deterministic::Runner::default();
405 executor.start(|context| async move {
406 let cfg = Config {
408 partition: "test-ordinal".into(),
409 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
410 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
411 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
412 };
413 let mut store =
414 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
415 .await
416 .expect("Failed to initialize store");
417
418 assert_eq!(store.missing_items(0, 5), Vec::<u64>::new());
420 assert_eq!(store.missing_items(100, 10), Vec::<u64>::new());
421
422 store = store.put(1, FixedBytes::new([1u8; 32])).await.unwrap();
424 store = store.put(2, FixedBytes::new([2u8; 32])).await.unwrap();
425 store = store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
426 store = store.put(6, FixedBytes::new([6u8; 32])).await.unwrap();
427 store = store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
428
429 assert_eq!(store.missing_items(0, 5), vec![0, 3, 4, 7, 8]);
431 assert_eq!(store.missing_items(0, 6), vec![0, 3, 4, 7, 8, 9]);
432 assert_eq!(store.missing_items(0, 7), vec![0, 3, 4, 7, 8, 9]);
433
434 assert_eq!(store.missing_items(3, 3), vec![3, 4, 7]);
436 assert_eq!(store.missing_items(4, 2), vec![4, 7]);
437
438 assert_eq!(store.missing_items(1, 3), vec![3, 4, 7]);
440 assert_eq!(store.missing_items(2, 4), vec![3, 4, 7, 8]);
441 assert_eq!(store.missing_items(5, 2), vec![7, 8]);
442
443 assert_eq!(store.missing_items(11, 5), Vec::<u64>::new());
445 assert_eq!(store.missing_items(100, 10), Vec::<u64>::new());
446
447 store = store.put(1000, FixedBytes::new([100u8; 32])).await.unwrap();
449
450 let items = store.missing_items(11, 10);
452 assert_eq!(items, vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
453
454 let items = store.missing_items(990, 15);
456 assert_eq!(
457 items,
458 vec![990, 991, 992, 993, 994, 995, 996, 997, 998, 999]
459 );
460
461 store = store.sync().await.unwrap();
463 assert_eq!(store.missing_items(0, 5), vec![0, 3, 4, 7, 8]);
464 assert_eq!(store.missing_items(3, 3), vec![3, 4, 7]);
465
466 store = store.put(9999, FixedBytes::new([99u8; 32])).await.unwrap();
468 store = store
469 .put(10001, FixedBytes::new([101u8; 32]))
470 .await
471 .unwrap();
472
473 let items = store.missing_items(9998, 5);
475 assert_eq!(items, vec![9998, 10000]);
476 });
477 }
478
479 #[test_traced]
480 fn test_restart() {
481 let executor = deterministic::Runner::default();
483 executor.start(|context| async move {
484 let cfg = Config {
485 partition: "test-ordinal".into(),
486 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
487 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
488 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
489 };
490
491 {
493 let mut store =
494 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
495 .await
496 .expect("Failed to initialize store");
497
498 let values = vec![
499 (0u64, FixedBytes::new([0u8; 32])),
500 (100u64, FixedBytes::new([100u8; 32])),
501 (1000u64, FixedBytes::new([200u8; 32])),
502 ];
503
504 for (index, value) in &values {
505 store = store
506 .put(*index, value.clone())
507 .await
508 .expect("Failed to put data");
509 }
510
511 store.sync().await.expect("Failed to sync store");
512 }
513
514 {
516 let mut bits0 = BitMap::zeroes(DEFAULT_ITEMS_PER_BLOB);
517 bits0.set(0, true);
518 bits0.set(100, true);
519 let mut bits1 = BitMap::zeroes(DEFAULT_ITEMS_PER_BLOB);
520 bits1.set(0, true);
521 let bits0 = Some(bits0);
522 let bits1 = Some(bits1);
523 let mut bits = BTreeMap::new();
524 bits.insert(0, &bits0);
525 bits.insert(1, &bits1);
526 let store = Ordinal::<_, FixedBytes<32>>::init(
527 context.child("second"),
528 cfg.clone(),
529 Some(bits),
530 )
531 .await
532 .expect("Failed to initialize store");
533
534 let values = vec![
535 (0u64, FixedBytes::new([0u8; 32])),
536 (100u64, FixedBytes::new([100u8; 32])),
537 (1000u64, FixedBytes::new([200u8; 32])),
538 ];
539
540 for (index, value) in &values {
541 let retrieved = store
542 .get(*index)
543 .await
544 .expect("Failed to get data")
545 .expect("Data not found");
546 assert_eq!(&retrieved, value);
547 }
548
549 let (current_end, start_next) = store.next_gap(0);
551 assert_eq!(current_end, Some(0));
552 assert_eq!(start_next, Some(100));
553 }
554 });
555 }
556
557 #[test_traced]
558 fn test_invalid_record() {
559 let executor = deterministic::Runner::default();
561 executor.start(|context| async move {
562 let cfg = Config {
563 partition: "test-ordinal".into(),
564 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
565 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
566 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
567 };
568
569 {
571 let mut store =
572 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
573 .await
574 .expect("Failed to initialize store");
575
576 store = store
577 .put(0, FixedBytes::new([42u8; 32]))
578 .await
579 .expect("Failed to put data");
580 store.sync().await.expect("Failed to sync store");
581 }
582
583 {
585 let (blob, _) = context
586 .open("test-ordinal", &0u64.to_be_bytes())
587 .await
588 .unwrap();
589 blob.write_at(32, vec![0xFF], WriteOptions::SYNC)
591 .await
592 .unwrap();
593 }
594
595 {
597 let store =
598 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
599 .await
600 .expect("Failed to initialize store");
601
602 let result = store.get(0).await.unwrap();
603 assert!(result.is_none());
604
605 assert!(!store.has(0));
606 }
607 });
608 }
609
610 #[test_traced]
611 fn test_get_nonexistent() {
612 let executor = deterministic::Runner::default();
614 executor.start(|context| async move {
615 let cfg = Config {
617 partition: "test-ordinal".into(),
618 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
619 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
620 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
621 };
622 let store =
623 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
624 .await
625 .expect("Failed to initialize store");
626
627 let retrieved = store.get(999).await.expect("Failed to get data");
629 assert!(retrieved.is_none());
630
631 assert!(!store.has(999));
633 });
634 }
635
636 #[test_traced]
637 fn test_destroy() {
638 let executor = deterministic::Runner::default();
640 executor.start(|context| async move {
641 let cfg = Config {
642 partition: "test-ordinal".into(),
643 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
644 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
645 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
646 };
647
648 {
650 let mut store =
651 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
652 .await
653 .expect("Failed to initialize store");
654
655 store = store
656 .put(0, FixedBytes::new([0u8; 32]))
657 .await
658 .expect("Failed to put data");
659 store = store
660 .put(1000, FixedBytes::new([100u8; 32]))
661 .await
662 .expect("Failed to put data");
663
664 store.destroy().await.expect("Failed to destroy store");
666 }
667
668 {
670 let store =
671 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
672 .await
673 .expect("Failed to initialize store");
674
675 assert!(store.get(0).await.unwrap().is_none());
677 assert!(store.get(1000).await.unwrap().is_none());
678 assert!(!store.has(0));
679 assert!(!store.has(1000));
680 }
681 });
682 }
683
684 #[test_traced]
685 fn test_partial_record_write() {
686 let executor = deterministic::Runner::default();
688 executor.start(|context| async move {
689 let cfg = Config {
690 partition: "test-ordinal".into(),
691 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
692 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
693 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
694 };
695
696 {
698 let mut store =
699 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
700 .await
701 .expect("Failed to initialize store");
702
703 store = store
704 .put(0, FixedBytes::new([42u8; 32]))
705 .await
706 .expect("Failed to put data");
707 store = store
708 .put(1, FixedBytes::new([43u8; 32]))
709 .await
710 .expect("Failed to put data");
711 store.sync().await.expect("Failed to sync store");
712 }
713
714 {
716 let (blob, _) = context
717 .open("test-ordinal", &0u64.to_be_bytes())
718 .await
719 .unwrap();
720 blob.write_at(36, vec![0xFF; 32], WriteOptions::SYNC)
722 .await
723 .unwrap();
724 }
725
726 {
728 let store =
729 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
730 .await
731 .expect("Failed to initialize store");
732
733 assert!(!store.has(0));
734 assert!(!store.has(1));
735
736 let store = store.put(1, FixedBytes::new([44u8; 32])).await.unwrap();
738 assert_eq!(
739 store.get(1).await.unwrap().unwrap(),
740 FixedBytes::new([44u8; 32])
741 );
742 }
743 });
744 }
745
746 #[test_traced]
747 fn test_corrupted_value() {
748 let executor = deterministic::Runner::default();
750 executor.start(|context| async move {
751 let cfg = Config {
752 partition: "test-ordinal".into(),
753 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
754 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
755 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
756 };
757
758 {
760 let mut store =
761 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
762 .await
763 .expect("Failed to initialize store");
764
765 store = store
766 .put(0, FixedBytes::new([42u8; 32]))
767 .await
768 .expect("Failed to put data");
769 store = store
770 .put(1, FixedBytes::new([43u8; 32]))
771 .await
772 .expect("Failed to put data");
773 store.sync().await.expect("Failed to sync store");
774 }
775
776 {
778 let (blob, _) = context
779 .open("test-ordinal", &0u64.to_be_bytes())
780 .await
781 .unwrap();
782 blob.write_at(10, hex!("0xFFFFFFFF").to_vec(), WriteOptions::SYNC)
784 .await
785 .unwrap();
786 }
787
788 {
790 let store =
791 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
792 .await
793 .expect("Failed to initialize store");
794
795 assert!(!store.has(0));
796 assert!(!store.has(1));
797 }
798 });
799 }
800
801 #[test_traced]
802 fn test_crc_corruptions() {
803 let executor = deterministic::Runner::default();
805 executor.start(|context| async move {
806 let cfg = Config {
807 partition: "test-ordinal".into(),
808 items_per_blob: NZU64!(10), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
810 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
811 };
812
813 {
815 let mut store =
816 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
817 .await
818 .expect("Failed to initialize store");
819
820 store = store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
822 store = store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
823 store = store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
824 store = store.put(15, FixedBytes::new([15u8; 32])).await.unwrap();
825 store.sync().await.expect("Failed to sync store");
826 }
827
828 {
830 let (blob, _) = context
832 .open("test-ordinal", &0u64.to_be_bytes())
833 .await
834 .unwrap();
835 blob.write_at(32, vec![0xFF], WriteOptions::SYNC)
836 .await
837 .unwrap(); let (blob, _) = context
841 .open("test-ordinal", &1u64.to_be_bytes())
842 .await
843 .unwrap();
844 blob.write_at(5, vec![0xFF; 4], WriteOptions::SYNC)
845 .await
846 .unwrap(); }
848
849 {
851 let store =
852 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
853 .await
854 .expect("Failed to initialize store");
855
856 assert!(!store.has(0));
857 assert!(!store.has(5));
858 assert!(!store.has(10));
859 assert!(!store.has(15));
860 }
861 });
862 }
863
864 #[test_traced]
865 fn test_extra_bytes_in_blob() {
866 let executor = deterministic::Runner::default();
868 executor.start(|context| async move {
869 let cfg = Config {
870 partition: "test-ordinal".into(),
871 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
872 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
873 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
874 };
875
876 {
878 let mut store =
879 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
880 .await
881 .expect("Failed to initialize store");
882
883 store = store
884 .put(0, FixedBytes::new([42u8; 32]))
885 .await
886 .expect("Failed to put data");
887 store = store
888 .put(1, FixedBytes::new([43u8; 32]))
889 .await
890 .expect("Failed to put data");
891 store.sync().await.expect("Failed to sync store");
892 }
893
894 {
896 let (blob, size) = context
897 .open("test-ordinal", &0u64.to_be_bytes())
898 .await
899 .unwrap();
900 let mut garbage = vec![0xFF; 32]; let invalid_crc = 0xDEADBEEFu32;
904 garbage.extend_from_slice(&invalid_crc.to_be_bytes());
905 assert_eq!(garbage.len(), 36); blob.write_at(size, garbage, WriteOptions::SYNC)
907 .await
908 .unwrap();
909 }
910
911 {
913 let store =
914 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
915 .await
916 .expect("Failed to initialize store");
917
918 assert!(!store.has(0));
919 assert!(!store.has(1));
920
921 let store = store.put(2, FixedBytes::new([44u8; 32])).await.unwrap();
923 assert_eq!(
924 store.get(2).await.unwrap().unwrap(),
925 FixedBytes::new([44u8; 32])
926 );
927 }
928 });
929 }
930
931 #[test_traced]
932 fn test_zero_filled_records() {
933 let executor = deterministic::Runner::default();
935 executor.start(|context| async move {
936 let cfg = Config {
937 partition: "test-ordinal".into(),
938 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
939 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
940 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
941 };
942
943 {
945 let (blob, _) = context
946 .open("test-ordinal", &0u64.to_be_bytes())
947 .await
948 .unwrap();
949
950 let zeros = vec![0u8; 36 * 5]; blob.write_at(0, zeros, WriteOptions::SYNC).await.unwrap();
953
954 let mut valid_record = vec![44u8; 32];
956 let crc = Crc32::checksum(&valid_record);
957 valid_record.extend_from_slice(&crc.to_be_bytes());
958 blob.write_at(36 * 5, valid_record, WriteOptions::SYNC)
959 .await
960 .unwrap();
961 }
962
963 {
965 let mut section = BitMap::zeroes(DEFAULT_ITEMS_PER_BLOB);
966 section.set(5, true);
967 let section = Some(section);
968 let mut bits = BTreeMap::new();
969 bits.insert(0, §ion);
970 let store = Ordinal::<_, FixedBytes<32>>::init(
971 context.child("storage"),
972 cfg.clone(),
973 Some(bits),
974 )
975 .await
976 .expect("Failed to initialize store");
977
978 for i in 0..5 {
980 assert!(!store.has(i));
981 }
982
983 assert!(store.has(5));
985 assert_eq!(
986 store.get(5).await.unwrap().unwrap(),
987 FixedBytes::new([44u8; 32])
988 );
989 }
990 });
991 }
992
993 fn test_operations_and_restart(num_values: usize) -> String {
994 let executor = deterministic::Runner::default();
996 executor.start(|mut context| async move {
997 let cfg = Config {
998 partition: "test-ordinal".into(),
999 items_per_blob: NZU64!(100), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1001 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1002 };
1003
1004 let mut store =
1006 Ordinal::<_, FixedBytes<128>>::init(context.child("first"), cfg.clone(), None)
1007 .await
1008 .expect("Failed to initialize store");
1009
1010 let mut values = Vec::new();
1012 let mut rng_index = 0u64;
1013
1014 for _ in 0..num_values {
1015 let mut index_bytes = [0u8; 8];
1017 context.fill_bytes(&mut index_bytes);
1018 let index_offset = u64::from_be_bytes(index_bytes) % 1000;
1019 let index = rng_index + index_offset;
1020 rng_index = index + 1;
1021
1022 let mut value = [0u8; 128];
1024 context.fill_bytes(&mut value);
1025 let value = FixedBytes::<128>::new(value);
1026
1027 store = store
1028 .put(index, value.clone())
1029 .await
1030 .expect("Failed to put data");
1031 values.push((index, value));
1032 }
1033
1034 store = store.sync().await.expect("Failed to sync");
1036
1037 for (index, value) in &values {
1039 let retrieved = store
1040 .get(*index)
1041 .await
1042 .expect("Failed to get data")
1043 .expect("Data not found");
1044 assert_eq!(&retrieved, value);
1045 }
1046
1047 for i in 0..10 {
1049 let _ = store.next_gap(i * 100);
1050 }
1051
1052 store.sync().await.expect("Failed to sync store");
1054
1055 let owned_bits = bits_for_indices(NZU64!(100), values.iter().map(|(index, _)| *index));
1057 let bits = owned_bits
1058 .iter()
1059 .map(|(section, bitmap)| (*section, bitmap))
1060 .collect();
1061 let mut store =
1062 Ordinal::<_, FixedBytes<128>>::init(context.child("second"), cfg, Some(bits))
1063 .await
1064 .expect("Failed to initialize store");
1065
1066 for (index, value) in &values {
1068 let retrieved = store
1069 .get(*index)
1070 .await
1071 .expect("Failed to get data")
1072 .expect("Data not found");
1073 assert_eq!(&retrieved, value);
1074 }
1075
1076 for _ in 0..10 {
1078 let mut index_bytes = [0u8; 8];
1079 context.fill_bytes(&mut index_bytes);
1080 let index = u64::from_be_bytes(index_bytes) % 10000;
1081
1082 let mut value = [0u8; 128];
1083 context.fill_bytes(&mut value);
1084 let value = FixedBytes::<128>::new(value);
1085
1086 store = store.put(index, value).await.expect("Failed to put data");
1087 }
1088
1089 store.sync().await.expect("Failed to sync");
1091
1092 context.auditor().state()
1094 })
1095 }
1096
1097 #[test_group("slow")]
1098 #[test_traced]
1099 fn test_determinism() {
1100 let state1 = test_operations_and_restart(100);
1101 let state2 = test_operations_and_restart(100);
1102 assert_eq!(state1, state2);
1103 }
1104
1105 #[test_traced]
1106 fn test_prune_basic() {
1107 let executor = deterministic::Runner::default();
1109 executor.start(|context| async move {
1110 let cfg = Config {
1111 partition: "test-ordinal".into(),
1112 items_per_blob: NZU64!(100), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1114 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1115 };
1116
1117 let mut store =
1118 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1119 .await
1120 .expect("Failed to initialize store");
1121
1122 let values = vec![
1124 (0u64, FixedBytes::new([0u8; 32])), (50u64, FixedBytes::new([50u8; 32])), (100u64, FixedBytes::new([100u8; 32])), (150u64, FixedBytes::new([150u8; 32])), (200u64, FixedBytes::new([200u8; 32])), (300u64, FixedBytes::new([44u8; 32])), ];
1131
1132 for (index, value) in &values {
1133 store = store
1134 .put(*index, value.clone())
1135 .await
1136 .expect("Failed to put data");
1137 }
1138 store = store.sync().await.unwrap();
1139
1140 for (index, value) in &values {
1142 assert_eq!(store.get(*index).await.unwrap().unwrap(), *value);
1143 }
1144
1145 store = store.prune(150).await.unwrap();
1147 let buffer = context.encode();
1148 assert!(buffer.contains("pruned_total 1"));
1149
1150 assert!(!store.has(0));
1152 assert!(!store.has(50));
1153 assert!(store.get(0).await.unwrap().is_none());
1154 assert!(store.get(50).await.unwrap().is_none());
1155
1156 assert!(store.has(100));
1158 assert!(store.has(150));
1159 assert!(store.has(200));
1160 assert!(store.has(300));
1161 assert_eq!(store.get(100).await.unwrap().unwrap(), values[2].1);
1162 assert_eq!(store.get(150).await.unwrap().unwrap(), values[3].1);
1163 assert_eq!(store.get(200).await.unwrap().unwrap(), values[4].1);
1164 assert_eq!(store.get(300).await.unwrap().unwrap(), values[5].1);
1165
1166 store = store.prune(250).await.unwrap();
1168 let buffer = context.encode();
1169 assert!(buffer.contains("pruned_total 2"));
1170
1171 assert!(!store.has(100));
1173 assert!(!store.has(150));
1174 assert!(store.get(100).await.unwrap().is_none());
1175 assert!(store.get(150).await.unwrap().is_none());
1176
1177 assert!(store.has(200));
1179 assert!(store.has(300));
1180 assert_eq!(store.get(200).await.unwrap().unwrap(), values[4].1);
1181 assert_eq!(store.get(300).await.unwrap().unwrap(), values[5].1);
1182 });
1183 }
1184
1185 #[test_traced]
1186 fn test_prune_with_gaps() {
1187 let executor = deterministic::Runner::default();
1189 executor.start(|context| async move {
1190 let cfg = Config {
1191 partition: "test-ordinal".into(),
1192 items_per_blob: NZU64!(100),
1193 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1194 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1195 };
1196
1197 let mut store =
1198 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1199 .await
1200 .expect("Failed to initialize store");
1201
1202 store = store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1204 store = store.put(105, FixedBytes::new([105u8; 32])).await.unwrap();
1205 store = store.put(305, FixedBytes::new([49u8; 32])).await.unwrap();
1206 store = store.sync().await.unwrap();
1207
1208 let (current_end, next_start) = store.next_gap(0);
1210 assert!(current_end.is_none());
1211 assert_eq!(next_start, Some(5));
1212
1213 let (current_end, next_start) = store.next_gap(5);
1214 assert_eq!(current_end, Some(5));
1215 assert_eq!(next_start, Some(105));
1216
1217 store = store.prune(150).await.unwrap();
1219
1220 assert!(!store.has(5));
1222 assert!(store.get(5).await.unwrap().is_none());
1223
1224 assert!(store.has(105));
1226 assert!(store.has(305));
1227
1228 let (current_end, next_start) = store.next_gap(0);
1229 assert!(current_end.is_none());
1230 assert_eq!(next_start, Some(105));
1231
1232 let (current_end, next_start) = store.next_gap(105);
1233 assert_eq!(current_end, Some(105));
1234 assert_eq!(next_start, Some(305));
1235 });
1236 }
1237
1238 #[test_traced]
1239 fn test_prune_no_op() {
1240 let executor = deterministic::Runner::default();
1242 executor.start(|context| async move {
1243 let cfg = Config {
1244 partition: "test-ordinal".into(),
1245 items_per_blob: NZU64!(100),
1246 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1247 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1248 };
1249
1250 let mut store =
1251 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1252 .await
1253 .expect("Failed to initialize store");
1254
1255 store = store.put(100, FixedBytes::new([100u8; 32])).await.unwrap();
1257 store = store.put(200, FixedBytes::new([200u8; 32])).await.unwrap();
1258 store = store.sync().await.unwrap();
1259
1260 store = store.prune(50).await.unwrap();
1262
1263 assert!(store.has(100));
1265 assert!(store.has(200));
1266 let buffer = context.encode();
1267 assert!(buffer.contains("pruned_total 0"));
1268
1269 store = store.prune(100).await.unwrap();
1271
1272 assert!(store.has(100));
1274 assert!(store.has(200));
1275 let buffer = context.encode();
1276 assert!(buffer.contains("pruned_total 0"));
1277 });
1278 }
1279
1280 #[test_traced]
1281 fn test_prune_empty_store() {
1282 let executor = deterministic::Runner::default();
1284 executor.start(|context| async move {
1285 let cfg = Config {
1286 partition: "test-ordinal".into(),
1287 items_per_blob: NZU64!(100),
1288 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1289 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1290 };
1291
1292 let mut store =
1293 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1294 .await
1295 .expect("Failed to initialize store");
1296
1297 store = store.prune(1000).await.unwrap();
1299
1300 store = store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1302 assert!(store.has(0));
1303 });
1304 }
1305
1306 #[test_traced]
1307 fn test_prune_after_restart() {
1308 let executor = deterministic::Runner::default();
1310 executor.start(|context| async move {
1311 let cfg = Config {
1312 partition: "test-ordinal".into(),
1313 items_per_blob: NZU64!(100),
1314 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1315 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1316 };
1317
1318 {
1320 let mut store =
1321 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1322 .await
1323 .expect("Failed to initialize store");
1324
1325 store = store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1326 store = store.put(100, FixedBytes::new([100u8; 32])).await.unwrap();
1327 store = store.put(200, FixedBytes::new([200u8; 32])).await.unwrap();
1328 store.sync().await.unwrap();
1329 }
1330
1331 {
1333 let mut bits0 = BitMap::zeroes(100);
1334 bits0.set(0, true);
1335 let mut bits1 = BitMap::zeroes(100);
1336 bits1.set(0, true);
1337 let mut bits2 = BitMap::zeroes(100);
1338 bits2.set(0, true);
1339 let bits0 = Some(bits0);
1340 let bits1 = Some(bits1);
1341 let bits2 = Some(bits2);
1342 let mut bits = BTreeMap::new();
1343 bits.insert(0, &bits0);
1344 bits.insert(1, &bits1);
1345 bits.insert(2, &bits2);
1346 let mut store = Ordinal::<_, FixedBytes<32>>::init(
1347 context.child("second"),
1348 cfg.clone(),
1349 Some(bits),
1350 )
1351 .await
1352 .expect("Failed to initialize store");
1353
1354 assert!(store.has(0));
1356 assert!(store.has(100));
1357 assert!(store.has(200));
1358
1359 store = store.prune(150).await.unwrap();
1361
1362 assert!(!store.has(0));
1364 assert!(store.has(100));
1365 assert!(store.has(200));
1366
1367 store.sync().await.unwrap();
1368 }
1369
1370 {
1372 let mut bits1 = BitMap::zeroes(100);
1373 bits1.set(0, true);
1374 let mut bits2 = BitMap::zeroes(100);
1375 bits2.set(0, true);
1376 let bits1 = Some(bits1);
1377 let bits2 = Some(bits2);
1378 let mut bits = BTreeMap::new();
1379 bits.insert(1, &bits1);
1380 bits.insert(2, &bits2);
1381 let store = Ordinal::<_, FixedBytes<32>>::init(
1382 context.child("third"),
1383 cfg.clone(),
1384 Some(bits),
1385 )
1386 .await
1387 .expect("Failed to initialize store");
1388
1389 assert!(!store.has(0));
1390 assert!(store.has(100));
1391 assert!(store.has(200));
1392
1393 let (current_end, next_start) = store.next_gap(0);
1395 assert!(current_end.is_none());
1396 assert_eq!(next_start, Some(100));
1397 }
1398 });
1399 }
1400
1401 #[test_traced]
1402 fn test_prune_multiple_operations() {
1403 let executor = deterministic::Runner::default();
1405 executor.start(|context| async move {
1406 let cfg = Config {
1407 partition: "test-ordinal".into(),
1408 items_per_blob: NZU64!(50), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1410 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1411 };
1412
1413 let mut store =
1414 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1415 .await
1416 .expect("Failed to initialize store");
1417
1418 let mut values = Vec::new();
1420 for i in 0..10 {
1421 let index = i * 50 + 25; let value = FixedBytes::new([i as u8; 32]);
1423 store = store.put(index, value.clone()).await.unwrap();
1424 values.push((index, value));
1425 }
1426 store = store.sync().await.unwrap();
1427
1428 for i in 1..5 {
1430 let prune_index = i * 50 + 10;
1431 store = store.prune(prune_index).await.unwrap();
1432
1433 for (index, _) in &values {
1435 if *index < prune_index {
1436 assert!(!store.has(*index), "Index {index} should be pruned");
1437 } else {
1438 assert!(store.has(*index), "Index {index} should not be pruned");
1439 }
1440 }
1441 }
1442
1443 let buffer = context.encode();
1445 assert!(buffer.contains("pruned_total 4"));
1446
1447 for i in 4..10 {
1449 let index = i * 50 + 25;
1450 assert!(store.has(index));
1451 assert_eq!(
1452 store.get(index).await.unwrap().unwrap(),
1453 values[i as usize].1
1454 );
1455 }
1456 });
1457 }
1458
1459 #[test_traced]
1460 fn test_prune_blob_boundaries() {
1461 let executor = deterministic::Runner::default();
1463 executor.start(|context| async move {
1464 let cfg = Config {
1465 partition: "test-ordinal".into(),
1466 items_per_blob: NZU64!(100),
1467 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1468 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1469 };
1470
1471 let mut store =
1472 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1473 .await
1474 .expect("Failed to initialize store");
1475
1476 store = store.put(0, FixedBytes::new([0u8; 32])).await.unwrap(); store = store.put(99, FixedBytes::new([99u8; 32])).await.unwrap(); store = store.put(100, FixedBytes::new([100u8; 32])).await.unwrap(); store = store.put(199, FixedBytes::new([199u8; 32])).await.unwrap(); store = store.put(200, FixedBytes::new([200u8; 32])).await.unwrap(); store = store.sync().await.unwrap();
1483
1484 store = store.prune(100).await.unwrap();
1488 assert!(!store.has(0));
1489 assert!(!store.has(99));
1490 assert!(store.has(100));
1491 assert!(store.has(199));
1492 assert!(store.has(200));
1493
1494 store = store.prune(199).await.unwrap();
1496 assert!(store.has(100));
1497 assert!(store.has(199));
1498 assert!(store.has(200));
1499
1500 store = store.prune(200).await.unwrap();
1502 assert!(!store.has(100));
1503 assert!(!store.has(199));
1504 assert!(store.has(200));
1505
1506 let buffer = context.encode();
1507 assert!(buffer.contains("pruned_total 2"));
1508 });
1509 }
1510
1511 #[test_traced]
1512 fn test_prune_non_contiguous_sections() {
1513 let executor = deterministic::Runner::default();
1515 executor.start(|context| async move {
1516 let cfg = Config {
1517 partition: "test-ordinal".into(),
1518 items_per_blob: NZU64!(100),
1519 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1520 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1521 };
1522
1523 let mut store =
1524 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1525 .await
1526 .expect("Failed to initialize store");
1527
1528 store = store.put(0, FixedBytes::new([0u8; 32])).await.unwrap(); store = store.put(250, FixedBytes::new([50u8; 32])).await.unwrap(); store = store.put(500, FixedBytes::new([44u8; 32])).await.unwrap(); store = store.put(750, FixedBytes::new([45u8; 32])).await.unwrap(); store = store.sync().await.unwrap();
1534
1535 assert!(store.has(0));
1537 assert!(store.has(250));
1538 assert!(store.has(500));
1539 assert!(store.has(750));
1540
1541 store = store.prune(300).await.unwrap();
1543
1544 assert!(!store.has(0)); assert!(!store.has(250)); assert!(store.has(500)); assert!(store.has(750)); let buffer = context.encode();
1551 assert!(buffer.contains("pruned_total 2"));
1552
1553 store = store.prune(600).await.unwrap();
1555
1556 assert!(!store.has(500)); assert!(store.has(750)); let buffer = context.encode();
1561 assert!(buffer.contains("pruned_total 3"));
1562
1563 store = store.prune(1000).await.unwrap();
1565
1566 assert!(!store.has(750)); let buffer = context.encode();
1570 assert!(buffer.contains("pruned_total 4"));
1571 });
1572 }
1573
1574 #[test_traced]
1575 fn test_prune_removes_correct_pending() {
1576 let executor = deterministic::Runner::default();
1578 executor.start(|context| async move {
1579 let cfg = Config {
1580 partition: "test-ordinal".into(),
1581 items_per_blob: NZU64!(100),
1582 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1583 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1584 };
1585 let mut store =
1586 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1587 .await
1588 .expect("Failed to initialize store");
1589
1590 store = store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1592 store = store.sync().await.unwrap();
1593
1594 store = store.put(10, FixedBytes::new([10u8; 32])).await.unwrap(); store = store.put(110, FixedBytes::new([110u8; 32])).await.unwrap(); assert!(store.has(5));
1600 assert!(store.has(10));
1601 assert!(store.has(110));
1602
1603 store = store.prune(150).await.unwrap();
1605
1606 assert!(!store.has(5));
1608 assert!(!store.has(10));
1609
1610 assert!(store.has(110));
1612 assert_eq!(
1613 store.get(110).await.unwrap().unwrap(),
1614 FixedBytes::new([110u8; 32])
1615 );
1616
1617 store = store.sync().await.unwrap();
1619 assert!(store.has(110));
1620 assert_eq!(
1621 store.get(110).await.unwrap().unwrap(),
1622 FixedBytes::new([110u8; 32])
1623 );
1624 });
1625 }
1626
1627 #[test_traced]
1628 fn test_init_without_bits_deletes_existing_data() {
1629 let executor = deterministic::Runner::default();
1631 executor.start(|context| async move {
1632 let cfg = Config {
1633 partition: "test-ordinal".into(),
1634 items_per_blob: NZU64!(10), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1636 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1637 };
1638
1639 {
1641 let mut store =
1642 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1643 .await
1644 .expect("Failed to initialize store");
1645
1646 store = store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1648 store = store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1649 store = store.put(9, FixedBytes::new([9u8; 32])).await.unwrap();
1650
1651 store = store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
1653 store = store.put(15, FixedBytes::new([15u8; 32])).await.unwrap();
1654
1655 store = store.put(25, FixedBytes::new([25u8; 32])).await.unwrap();
1657
1658 store.sync().await.unwrap();
1659 }
1660
1661 {
1663 let store =
1664 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
1665 .await
1666 .expect("Failed to initialize store");
1667
1668 assert!(!store.has(0));
1669 assert!(!store.has(5));
1670 assert!(!store.has(9));
1671 assert!(!store.has(10));
1672 assert!(!store.has(15));
1673 assert!(!store.has(25));
1674 assert!(!store.has(1));
1675 assert!(!store.has(11));
1676 assert!(!store.has(20));
1677 }
1678 });
1679 }
1680
1681 #[test_traced]
1682 fn test_init_empty_hashmap() {
1683 let executor = deterministic::Runner::default();
1685 executor.start(|context| async move {
1686 let cfg = Config {
1687 partition: "test-ordinal".into(),
1688 items_per_blob: NZU64!(10),
1689 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1690 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1691 };
1692
1693 {
1695 let mut store =
1696 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1697 .await
1698 .expect("Failed to initialize store");
1699
1700 store = store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1701 store = store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
1702 store = store.put(20, FixedBytes::new([20u8; 32])).await.unwrap();
1703
1704 store.sync().await.unwrap();
1705 }
1706
1707 {
1709 let bits: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1710 let store = Ordinal::<_, FixedBytes<32>>::init(
1711 context.child("second"),
1712 cfg.clone(),
1713 Some(bits),
1714 )
1715 .await
1716 .expect("Failed to initialize store with bits");
1717
1718 assert!(!store.has(0));
1720 assert!(!store.has(10));
1721 assert!(!store.has(20));
1722 }
1723
1724 {
1726 let mut section = BitMap::zeroes(10);
1727 section.set(0, true);
1728 let section = Some(section);
1729 let mut bits = BTreeMap::new();
1730 bits.insert(0, §ion);
1731 let result = Ordinal::<_, FixedBytes<32>>::init(
1732 context.child("third"),
1733 cfg.clone(),
1734 Some(bits),
1735 )
1736 .await;
1737 assert!(matches!(result, Err(Error::MissingRecord(0))));
1738 }
1739 });
1740 }
1741
1742 #[test_traced]
1743 fn test_init_selective_sections() {
1744 let executor = deterministic::Runner::default();
1746 executor.start(|context| async move {
1747 let cfg = Config {
1748 partition: "test-ordinal".into(),
1749 items_per_blob: NZU64!(10),
1750 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1751 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1752 };
1753
1754 {
1756 let mut store =
1757 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1758 .await
1759 .expect("Failed to initialize store");
1760
1761 for i in 0..10 {
1763 store = store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1764 }
1765
1766 for i in 10..20 {
1768 store = store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1769 }
1770
1771 for i in 20..30 {
1773 store = store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1774 }
1775
1776 store.sync().await.unwrap();
1777 }
1778
1779 {
1781 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1782
1783 let mut bitmap = BitMap::zeroes(10);
1785 bitmap.set(2, true); bitmap.set(5, true); bitmap.set(8, true); let bitmap_option = Some(bitmap);
1789
1790 bits_map.insert(1, &bitmap_option);
1791
1792 let store = Ordinal::<_, FixedBytes<32>>::init(
1793 context.child("second"),
1794 cfg.clone(),
1795 Some(bits_map),
1796 )
1797 .await
1798 .expect("Failed to initialize store with bits");
1799
1800 assert!(store.has(12));
1802 assert!(store.has(15));
1803 assert!(store.has(18));
1804
1805 assert!(!store.has(10));
1807 assert!(!store.has(11));
1808 assert!(!store.has(13));
1809 assert!(!store.has(14));
1810 assert!(!store.has(16));
1811 assert!(!store.has(17));
1812 assert!(!store.has(19));
1813
1814 for i in 0..10 {
1816 assert!(!store.has(i));
1817 }
1818 for i in 20..30 {
1819 assert!(!store.has(i));
1820 }
1821
1822 assert_eq!(
1824 store.get(12).await.unwrap().unwrap(),
1825 FixedBytes::new([12u8; 32])
1826 );
1827 assert_eq!(
1828 store.get(15).await.unwrap().unwrap(),
1829 FixedBytes::new([15u8; 32])
1830 );
1831 assert_eq!(
1832 store.get(18).await.unwrap().unwrap(),
1833 FixedBytes::new([18u8; 32])
1834 );
1835 }
1836
1837 {
1841 let mut bitmap = BitMap::zeroes(10);
1842 bitmap.set(0, true); let bitmap_option = Some(bitmap);
1844 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1845 bits_map.insert(1, &bitmap_option);
1846 let store = Ordinal::<_, FixedBytes<32>>::init(
1847 context.child("third"),
1848 cfg.clone(),
1849 Some(bits_map),
1850 )
1851 .await
1852 .expect("Failed to initialize store with bits");
1853 assert!(store.has(10));
1854 assert!(matches!(store.get(10).await, Err(Error::InvalidRecord(10))));
1855 }
1856 });
1857 }
1858
1859 #[test_traced]
1860 fn test_init_none_option_all_records_exist() {
1861 let executor = deterministic::Runner::default();
1863 executor.start(|context| async move {
1864 let cfg = Config {
1865 partition: "test-ordinal".into(),
1866 items_per_blob: NZU64!(5),
1867 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1868 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1869 };
1870
1871 {
1873 let mut store =
1874 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1875 .await
1876 .expect("Failed to initialize store");
1877
1878 for i in 5..10 {
1880 store = store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1881 }
1882
1883 store.sync().await.unwrap();
1884 }
1885
1886 {
1888 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1889 let none_option: Option<BitMap> = None;
1890 bits_map.insert(1, &none_option);
1891
1892 let store = Ordinal::<_, FixedBytes<32>>::init(
1893 context.child("second"),
1894 cfg.clone(),
1895 Some(bits_map),
1896 )
1897 .await
1898 .expect("Failed to initialize store with bits");
1899
1900 for i in 5..10 {
1902 assert!(store.has(i));
1903 assert_eq!(
1904 store.get(i).await.unwrap().unwrap(),
1905 FixedBytes::new([i as u8; 32])
1906 );
1907 }
1908 }
1909 });
1910 }
1911
1912 #[test_traced]
1913 #[should_panic(expected = "Failed to initialize store with bits: MissingRecord(6)")]
1914 fn test_init_none_option_missing_record_panics() {
1915 let executor = deterministic::Runner::default();
1917 executor.start(|context| async move {
1918 let cfg = Config {
1919 partition: "test-ordinal".into(),
1920 items_per_blob: NZU64!(5),
1921 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1922 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1923 };
1924
1925 {
1927 let mut store =
1928 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1929 .await
1930 .expect("Failed to initialize store");
1931
1932 store = store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1934 store = store.put(7, FixedBytes::new([7u8; 32])).await.unwrap();
1936 store = store.put(8, FixedBytes::new([8u8; 32])).await.unwrap();
1937 store = store.put(9, FixedBytes::new([9u8; 32])).await.unwrap();
1938
1939 store.sync().await.unwrap();
1940 }
1941
1942 {
1945 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1946 let none_option: Option<BitMap> = None;
1947 bits_map.insert(1, &none_option);
1948
1949 let _store = Ordinal::<_, FixedBytes<32>>::init(
1950 context.child("second"),
1951 cfg.clone(),
1952 Some(bits_map),
1953 )
1954 .await
1955 .expect("Failed to initialize store with bits");
1956 }
1957 });
1958 }
1959
1960 #[test_traced]
1961 fn test_init_mixed_sections() {
1962 let executor = deterministic::Runner::default();
1964 executor.start(|context| async move {
1965 let cfg = Config {
1966 partition: "test-ordinal".into(),
1967 items_per_blob: NZU64!(5),
1968 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1969 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1970 };
1971
1972 {
1974 let mut store =
1975 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1976 .await
1977 .expect("Failed to initialize store");
1978
1979 for i in 0..5 {
1981 store = store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1982 }
1983
1984 store = store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1986 store = store.put(7, FixedBytes::new([7u8; 32])).await.unwrap();
1987 store = store.put(9, FixedBytes::new([9u8; 32])).await.unwrap();
1988
1989 for i in 10..15 {
1991 store = store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1992 }
1993
1994 store.sync().await.unwrap();
1995 }
1996
1997 {
1999 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
2000
2001 let none_option: Option<BitMap> = None;
2003 bits_map.insert(0, &none_option);
2004
2005 let mut bitmap1 = BitMap::zeroes(5);
2007 bitmap1.set(0, true); bitmap1.set(2, true); let bitmap1_option = Some(bitmap1);
2011 bits_map.insert(1, &bitmap1_option);
2012
2013 let store = Ordinal::<_, FixedBytes<32>>::init(
2016 context.child("second"),
2017 cfg.clone(),
2018 Some(bits_map),
2019 )
2020 .await
2021 .expect("Failed to initialize store with bits");
2022
2023 for i in 0..5 {
2025 assert!(store.has(i));
2026 assert_eq!(
2027 store.get(i).await.unwrap().unwrap(),
2028 FixedBytes::new([i as u8; 32])
2029 );
2030 }
2031
2032 assert!(store.has(5));
2034 assert!(store.has(7));
2035 assert!(!store.has(6));
2036 assert!(!store.has(8));
2037 assert!(!store.has(9)); for i in 10..15 {
2041 assert!(!store.has(i));
2042 }
2043 }
2044 });
2045 }
2046
2047 #[test_traced]
2048 fn test_marked_record_damage_surfaces_at_get() {
2049 let executor = deterministic::Runner::default();
2051 executor.start(|context| async move {
2052 let cfg = Config {
2053 partition: "test-ordinal".into(),
2054 items_per_blob: NZU64!(5),
2055 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
2056 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
2057 };
2058
2059 {
2061 let mut store =
2062 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
2063 .await
2064 .expect("Failed to initialize store");
2065
2066 for i in 0..5 {
2068 store = store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
2069 }
2070
2071 store.sync().await.unwrap();
2072 }
2073
2074 {
2076 let (blob, _) = context
2077 .open("test-ordinal", &0u64.to_be_bytes())
2078 .await
2079 .unwrap();
2080 let offset = 2 * 36 + 32; blob.write_at(offset, vec![0xFF], WriteOptions::SYNC)
2083 .await
2084 .unwrap();
2085 }
2086
2087 {
2089 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
2090
2091 let mut bitmap = BitMap::zeroes(5);
2095 bitmap.set(0, true); bitmap.set(2, true); bitmap.set(4, true); let bitmap_option = Some(bitmap);
2099 bits_map.insert(0, &bitmap_option);
2100
2101 let store = Ordinal::<_, FixedBytes<32>>::init(
2102 context.child("second"),
2103 cfg.clone(),
2104 Some(bits_map),
2105 )
2106 .await
2107 .expect("Failed to initialize store with bits");
2108 assert_eq!(
2109 store.get(0).await.unwrap(),
2110 Some(FixedBytes::new([0u8; 32]))
2111 );
2112 assert!(store.get(2).await.is_err());
2113 assert_eq!(
2114 store.get(4).await.unwrap(),
2115 Some(FixedBytes::new([4u8; 32]))
2116 );
2117 }
2118 });
2119 }
2120
2121 #[derive(Debug, PartialEq, Eq)]
2123 pub struct DummyValue {
2124 pub value: u64,
2125 }
2126
2127 impl Write for DummyValue {
2128 fn write(&self, buf: &mut impl BufMut) {
2129 self.value.write(buf);
2130 }
2131 }
2132
2133 impl Read for DummyValue {
2134 type Cfg = ();
2135
2136 fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
2137 let value = u64::read(buf)?;
2138 if value == 0 {
2139 return Err(commonware_codec::Error::Invalid(
2140 "DummyValue",
2141 "value must be non-zero",
2142 ));
2143 }
2144 Ok(Self { value })
2145 }
2146 }
2147
2148 impl FixedSize for DummyValue {
2149 const SIZE: usize = u64::SIZE;
2150 }
2151
2152 #[test_traced]
2153 fn test_init_skip_unparseable_record() {
2154 let executor = deterministic::Runner::default();
2156 executor.start(|context| async move {
2157 let cfg = Config {
2158 partition: "test-ordinal".into(),
2159 items_per_blob: NZU64!(1),
2160 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
2161 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
2162 };
2163
2164 {
2166 let mut store =
2167 Ordinal::<_, DummyValue>::init(context.child("first"), cfg.clone(), None)
2168 .await
2169 .expect("Failed to initialize store");
2170
2171 store = store.put(1, DummyValue { value: 1 }).await.unwrap();
2173 store = store.put(2, DummyValue { value: 0 }).await.unwrap(); store = store.put(4, DummyValue { value: 4 }).await.unwrap();
2175
2176 store = store.sync().await.unwrap();
2177
2178 assert!(matches!(store.get(2).await, Err(Error::InvalidRecord(2))));
2180 }
2181
2182 {
2184 let store =
2185 Ordinal::<_, DummyValue>::init(context.child("second"), cfg.clone(), None)
2186 .await
2187 .expect("Failed to initialize store");
2188
2189 assert!(!store.has(1));
2190 assert!(!store.has(2));
2191 assert!(!store.has(4));
2192 }
2193 });
2194 }
2195}