1use super::*;
2use crate::storage::unified::entity_cache::EntityCache;
3use parking_lot::RwLock;
4
5const META_PAGE_CONTENT_CAP: usize =
24 crate::storage::engine::PAGE_SIZE - crate::storage::engine::HEADER_SIZE;
25const META_V3_PAGE1_HEADER: usize = reddb_file::METADATA_OVERFLOW_HEADER_BYTES;
26const META_V3_OVERFLOW_HEADER: usize = reddb_file::METADATA_OVERFLOW_CONTINUATION_HEADER_BYTES;
27const META_V3_FIRST_PAYLOAD_CAP: usize = META_PAGE_CONTENT_CAP - META_V3_PAGE1_HEADER;
28const META_V3_OVERFLOW_PAYLOAD_CAP: usize = META_PAGE_CONTENT_CAP - META_V3_OVERFLOW_HEADER;
29
30fn free_existing_overflow_chain(pager: &Pager) -> Result<(), PagerError> {
31 let cs = crate::storage::engine::HEADER_SIZE;
32 let page = match pager.read_page(1) {
33 Ok(p) => p,
34 Err(_) => return Ok(()),
35 };
36 let bytes = page.as_bytes();
37 if bytes.len() < cs + META_V3_PAGE1_HEADER {
38 return Ok(());
39 }
40 let Some(header) =
41 reddb_file::decode_native_metadata_overflow_header(&bytes[cs..]).map_err(|err| {
42 PagerError::InvalidDatabase(format!("invalid metadata overflow header: {err}"))
43 })?
44 else {
45 return Ok(());
46 };
47 let mut next = header.next_overflow_page_id;
48 while next != 0 {
49 let ov = match pager.read_page(next) {
50 Ok(p) => p,
51 Err(_) => break,
52 };
53 let ob = ov.as_bytes();
54 let nn = match reddb_file::decode_native_metadata_overflow_continuation_header(&ob[cs..]) {
55 Ok(header) => header.next_overflow_page_id,
56 Err(_) => 0,
57 };
58 let _ = pager.free_page(next);
59 next = nn;
60 }
61 Ok(())
62}
63
64fn build_meta_page1_with_overflow(
65 pager: &Pager,
66 meta_data: &[u8],
67) -> Result<crate::storage::engine::Page, PagerError> {
68 use crate::storage::engine::{Page, PageType, HEADER_SIZE};
69 free_existing_overflow_chain(pager)?;
70
71 let mut page1 = Page::new(PageType::Header, 1);
72 let cs = HEADER_SIZE;
73
74 if meta_data.len() <= META_PAGE_CONTENT_CAP {
75 let buf = page1.as_bytes_mut();
77 buf[cs..cs + meta_data.len()].copy_from_slice(meta_data);
78 return Ok(page1);
79 }
80
81 let first_chunk = &meta_data[..META_V3_FIRST_PAYLOAD_CAP];
85 let mut tail = &meta_data[META_V3_FIRST_PAYLOAD_CAP..];
86 let mut chunks: Vec<&[u8]> = Vec::new();
87 while !tail.is_empty() {
88 let take = tail.len().min(META_V3_OVERFLOW_PAYLOAD_CAP);
89 chunks.push(&tail[..take]);
90 tail = &tail[take..];
91 }
92
93 let mut overflow_pages: Vec<Page> = Vec::with_capacity(chunks.len());
94 let mut overflow_ids: Vec<u32> = Vec::with_capacity(chunks.len());
95 for _ in 0..chunks.len() {
96 let pg = pager.allocate_page(PageType::Overflow)?;
97 overflow_ids.push(pg.page_id());
98 overflow_pages.push(pg);
99 }
100
101 for i in 0..chunks.len() {
102 let next = if i + 1 < chunks.len() {
103 overflow_ids[i + 1]
104 } else {
105 0u32
106 };
107 let len = chunks[i].len() as u32;
108 let buf = overflow_pages[i].as_bytes_mut();
109 reddb_file::encode_native_metadata_overflow_continuation_header(
110 &mut buf[cs..cs + META_V3_OVERFLOW_HEADER],
111 reddb_file::NativeMetadataOverflowContinuationHeader {
112 next_overflow_page_id: next,
113 chunk_bytes: len,
114 },
115 )
116 .map_err(|err| PagerError::InvalidDatabase(err.to_string()))?;
117 buf[cs + 8..cs + 8 + chunks[i].len()].copy_from_slice(chunks[i]);
118 }
119 for (idx, page) in overflow_pages.into_iter().enumerate() {
120 let id = overflow_ids[idx];
121 pager.write_page(id, page)?;
122 }
123
124 let format_version = reddb_file::decode_native_paged_metadata_header(meta_data)
126 .ok()
127 .flatten()
128 .map_or(0, |header| header.format_version);
129
130 let buf = page1.as_bytes_mut();
131 reddb_file::encode_native_metadata_overflow_header(
132 &mut buf[cs..cs + META_V3_PAGE1_HEADER],
133 reddb_file::NativeMetadataOverflowHeader {
134 format_version,
135 total_payload_bytes: meta_data.len() as u32,
136 next_overflow_page_id: overflow_ids[0],
137 },
138 )
139 .map_err(|err| PagerError::InvalidDatabase(err.to_string()))?;
140 buf[cs + META_V3_PAGE1_HEADER..cs + META_V3_PAGE1_HEADER + first_chunk.len()]
141 .copy_from_slice(first_chunk);
142
143 Ok(page1)
144}
145
146fn storage_integrity(
147 zone: &str,
148 id: impl Into<String>,
149 collection: Option<String>,
150 detail: impl Into<String>,
151) -> StoreError {
152 StoreError::StorageIntegrity(crate::api::StorageIntegrityError::new(
153 zone, id, collection, detail,
154 ))
155}
156
157fn page_integrity(
158 page_id: u32,
159 collection: Option<String>,
160 detail: impl Into<String>,
161) -> StoreError {
162 storage_integrity("page", page_id.to_string(), collection, detail)
163}
164
165fn read_meta_payload(pager: &Pager) -> Result<Option<Vec<u8>>, StoreError> {
177 let cs = crate::storage::engine::HEADER_SIZE;
178 let meta_page = pager.read_manifest_page().map_err(|err| {
179 storage_integrity(
180 "manifest",
181 "manifest",
182 None,
183 format!("internal manifest zone read failed ({err})"),
184 )
185 })?;
186 let bytes = meta_page.as_bytes();
187 if bytes.len() < cs + 4 {
188 return Ok(Some(bytes.get(cs..).unwrap_or(&[]).to_vec()));
189 }
190 let header = match reddb_file::decode_native_metadata_overflow_header(&bytes[cs..])
191 .map_err(|err| page_integrity(1, None, err.to_string()))?
192 {
193 Some(header) => header,
194 None => {
195 return Ok(Some(bytes[cs..].to_vec()));
196 }
197 };
198 if bytes.len() < cs + META_V3_PAGE1_HEADER {
199 return Err(page_integrity(
200 1,
201 None,
202 "metadata overflow header is truncated",
203 ));
204 }
205 let total = header.total_payload_bytes as usize;
206 let mut next = header.next_overflow_page_id;
207 let mut payload: Vec<u8> = Vec::with_capacity(total);
208 let first_take = total.min(META_V3_FIRST_PAYLOAD_CAP);
209 payload.extend_from_slice(
210 &bytes[cs + META_V3_PAGE1_HEADER..cs + META_V3_PAGE1_HEADER + first_take],
211 );
212 while next != 0 && payload.len() < total {
213 let ov = pager
214 .read_page(next)
215 .map_err(|err| page_integrity(next, None, err.to_string()))?;
216 let ob = ov.as_bytes();
217 if ob.len() < cs + META_V3_OVERFLOW_HEADER {
218 return Err(page_integrity(
219 next,
220 None,
221 "metadata overflow continuation is truncated",
222 ));
223 }
224 let continuation =
225 reddb_file::decode_native_metadata_overflow_continuation_header(&ob[cs..])
226 .map_err(|err| page_integrity(next, None, err.to_string()))?;
227 let nn = continuation.next_overflow_page_id;
228 let len = continuation.chunk_bytes as usize;
229 let remaining = total - payload.len();
230 let take = len.min(remaining).min(META_V3_OVERFLOW_PAYLOAD_CAP);
231 payload.extend_from_slice(
232 &ob[cs + META_V3_OVERFLOW_HEADER..cs + META_V3_OVERFLOW_HEADER + take],
233 );
234 next = nn;
235 }
236 Ok(Some(payload))
237}
238
239impl UnifiedStore {
240 pub(crate) fn mark_paged_registry_dirty(&self) {
241 self.paged_registry_dirty.store(true, Ordering::Release);
242 }
243
244 pub(crate) fn get_or_create_btree(&self, collection: &str) -> Option<Arc<BTree>> {
250 let pager = self.pager.as_ref()?;
251 if let Some(btree) = self.btree_indices.read().get(collection).cloned() {
252 return Some(btree);
253 }
254 let mut write = self.btree_indices.write();
255 let btree = write
256 .entry(collection.to_string())
257 .or_insert_with(|| Arc::new(BTree::new(Arc::clone(pager))))
258 .clone();
259 Some(btree)
260 }
261
262 pub(crate) fn flush_paged_state(&self) -> Result<(), StoreError> {
263 let Some(pager) = &self.pager else {
264 return Ok(());
265 };
266
267 if self.paged_registry_dirty.load(Ordering::Acquire) {
268 self.flush_paged_registry()?;
269 self.paged_registry_dirty.store(false, Ordering::Release);
270 return Ok(());
271 }
272
273 pager
274 .flush()
275 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))
276 }
277
278 pub(crate) fn flush_paged_registry(&self) -> Result<(), StoreError> {
279 let Some(pager) = &self.pager else {
280 return Ok(());
281 };
282
283 match pager.read_page(1) {
284 Ok(_) => {}
285 Err(PagerError::PageNotFound(_)) => {
286 let meta_page = pager
287 .allocate_page(crate::storage::engine::PageType::Header)
288 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
289 pager
290 .write_page(meta_page.page_id(), meta_page)
291 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
292 }
293 Err(e) => {
294 return Err(StoreError::Io(std::io::Error::other(e.to_string())));
295 }
296 }
297
298 let format_version = STORE_VERSION_V11;
299 self.set_format_version(format_version);
300
301 let collections = self.collections.read();
302 let btree_indices = self.btree_indices.read();
303 let mut collection_roots = Vec::with_capacity(collections.len());
304 for (name, _) in collections.iter() {
305 let root_page = btree_indices
306 .get(name)
307 .map_or(0, |btree| btree.root_page_id());
308 collection_roots.push((name.clone(), root_page));
309 }
310 drop(btree_indices);
311 drop(collections);
312
313 let mut meta_data = Vec::with_capacity(4096);
314 reddb_file::encode_native_paged_metadata_header(
315 &mut meta_data,
316 reddb_file::NativePagedMetadataHeader {
317 format_version,
318 collection_count: collection_roots.len() as u32,
319 },
320 );
321 for (name, root_page) in &collection_roots {
322 reddb_file::encode_native_paged_collection_root(&mut meta_data, name, *root_page);
323 }
324
325 let cross_refs = self.cross_refs.read();
326 let total_refs: usize = cross_refs.values().map(|v| v.len()).sum();
327 meta_data.extend_from_slice(&(total_refs as u32).to_le_bytes());
328 for (source_id, refs) in cross_refs.iter() {
329 for (target_id, ref_type, collection) in refs {
330 reddb_file::encode_native_paged_cross_ref(
331 &mut meta_data,
332 source_id.raw(),
333 target_id.raw(),
334 ref_type.to_byte(),
335 collection,
336 );
337 }
338 }
339
340 let meta_page = build_meta_page1_with_overflow(pager, &meta_data)
341 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
342
343 pager
344 .write_page(1, meta_page)
345 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
346 pager
347 .flush()
348 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
349
350 Ok(())
351 }
352
353 pub fn pager(&self) -> Option<&Arc<Pager>> {
355 self.pager.as_ref()
356 }
357
358 pub fn page_cache_bytes_in_use(&self) -> u64 {
362 self.pager.as_ref().map_or(0, |pager| {
363 pager.cache_len() as u64 * crate::storage::memory_pools::PAGE_CACHE_PAGE_SIZE_BYTES
364 })
365 }
366
367 pub fn wal_buffer_bytes_in_use(&self) -> u64 {
370 self.commit
371 .as_ref()
372 .map_or(0, |commit| commit.buffered_bytes())
373 }
374
375 pub fn segment_memory_bytes(&self) -> u64 {
379 let managers: Vec<Arc<SegmentManager>> =
380 self.collections.read().values().map(Arc::clone).collect();
381
382 managers
383 .iter()
384 .map(|manager| manager.resident_bytes())
385 .fold(0, u64::saturating_add)
386 }
387
388 pub fn config(&self) -> &UnifiedStoreConfig {
392 &self.config
393 }
394
395 pub fn with_config(config: UnifiedStoreConfig) -> Self {
396 Self {
397 config,
398 format_version: AtomicU32::new(STORE_VERSION_V11),
399 next_entity_id: AtomicU64::new(1),
400 collections: RwLock::new(HashMap::new()),
401 cross_refs: RwLock::new(HashMap::new()),
402 reverse_refs: RwLock::new(HashMap::new()),
403 pager: None,
404 db_path: None,
405 btree_indices: RwLock::new(HashMap::new()),
406 context_index: ContextIndex::new(),
407 entity_cache: EntityCache::new(),
408 graph_label_index: RwLock::new(HashMap::new()),
409 paged_registry_dirty: AtomicBool::new(false),
410 commit: None,
411 unindex_cross_refs_fast_path: AtomicU64::new(0),
412 replayed_turbo_inserts: parking_lot::Mutex::new(HashMap::new()),
413 replayed_probabilistic_deltas: parking_lot::Mutex::new(Vec::new()),
414 aux_metadata: RwLock::new(Vec::new()),
415 snapshot_manager: std::sync::OnceLock::new(),
416 vault_publication_lock: parking_lot::ReentrantMutex::new(()),
417 pending_vault_versions: RwLock::new(std::collections::HashSet::new()),
418 }
419 }
420
421 pub fn open(path: impl AsRef<Path>) -> Result<Self, StoreError> {
439 Self::open_with_config(path, UnifiedStoreConfig::default())
440 }
441
442 pub fn open_with_config(
443 path: impl AsRef<Path>,
444 config: UnifiedStoreConfig,
445 ) -> Result<Self, StoreError> {
446 let path = path.as_ref();
447 let mut pager_config = PagerConfig::default();
448 if let Some(slots) = config.page_cache_slots {
452 pager_config.cache_size = slots;
453 }
454 if matches!(
462 std::env::var("REDDB_DOUBLE_WRITE").ok().as_deref(),
463 Some("0") | Some("false") | Some("off")
464 ) {
465 pager_config.double_write = false;
466 }
467 let pager = Pager::open(path, pager_config)
468 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
469
470 let wal_path = reddb_file::layout::unified_wal_path(path);
471 let commit = Some(Arc::new(
474 StoreCommitCoordinator::open(wal_path, config.durability_mode, config.group_commit)
475 .map_err(StoreError::Io)?,
476 ));
477
478 let store = Self {
479 config,
480 format_version: AtomicU32::new(STORE_VERSION_V11),
481 next_entity_id: AtomicU64::new(1),
482 collections: RwLock::new(HashMap::new()),
483 cross_refs: RwLock::new(HashMap::new()),
484 reverse_refs: RwLock::new(HashMap::new()),
485 pager: Some(Arc::new(pager)),
486 db_path: Some(path.to_path_buf()),
487 btree_indices: RwLock::new(HashMap::new()),
488 context_index: ContextIndex::new(),
489 entity_cache: EntityCache::new(),
490 graph_label_index: RwLock::new(HashMap::new()),
491 paged_registry_dirty: AtomicBool::new(false),
492 commit,
493 unindex_cross_refs_fast_path: AtomicU64::new(0),
494 replayed_turbo_inserts: parking_lot::Mutex::new(HashMap::new()),
495 replayed_probabilistic_deltas: parking_lot::Mutex::new(Vec::new()),
496 aux_metadata: RwLock::new(Vec::new()),
497 snapshot_manager: std::sync::OnceLock::new(),
498 vault_publication_lock: parking_lot::ReentrantMutex::new(()),
499 pending_vault_versions: RwLock::new(std::collections::HashSet::new()),
500 };
501
502 store.load_from_pages()?;
504 if let Some(commit) = &store.commit {
505 commit.replay_into(&store).map_err(StoreError::Io)?;
506 }
507 store.recover_operational_manifest()?;
508
509 Ok(store)
510 }
511
512 pub(crate) fn recover_operational_manifest(&self) -> Result<(), StoreError> {
513 let Some(path) = &self.db_path else {
514 return Ok(());
515 };
516 let mut collections = self.list_collections();
517 collections.sort();
518 let pending_drops =
519 crate::storage::operational_manifest::OperationalManifest::for_db_path(path)
520 .recover_or_bootstrap(&collections)
521 .map_err(|err| storage_integrity("manifest", "current", None, err.to_string()))?;
522 for name in pending_drops {
523 if self.get_collection(&name).is_some() {
524 self.drop_collection(&name)?;
525 }
526 }
527 Ok(())
528 }
529
530 pub(crate) fn prepare_operational_collection_create(
531 &self,
532 name: &str,
533 ) -> Result<Option<reddb_file::PreparedCollectionCreate<reddb_file::StdVfs>>, StoreError> {
534 let Some(path) = &self.db_path else {
535 return Ok(None);
536 };
537 crate::storage::operational_manifest::OperationalManifest::for_db_path(path)
538 .prepare_collection_create(name)
539 .map(Some)
540 .map_err(StoreError::Io)
541 }
542
543 pub(crate) fn prepare_operational_collection_drop(
544 &self,
545 name: &str,
546 ) -> Result<Option<reddb_file::PreparedCollectionDrop<reddb_file::StdVfs>>, StoreError> {
547 let Some(path) = &self.db_path else {
548 return Ok(None);
549 };
550 crate::storage::operational_manifest::OperationalManifest::for_db_path(path)
551 .prepare_collection_drop(name)
552 .map(Some)
553 .map_err(StoreError::Io)
554 }
555
556 fn load_from_pages(&self) -> Result<(), StoreError> {
560 let pager = match &self.pager {
561 Some(p) => p,
562 None => return Ok(()), };
564
565 let page_count = pager.page_count().map_err(|e| {
567 StoreError::Io(std::io::Error::other(format!(
568 "failed to read page count: {}",
569 e
570 )))
571 })?;
572 if page_count <= 1 {
573 return Ok(());
575 }
576
577 if let Some(content_vec) = read_meta_payload(pager)? {
582 let content: &[u8] = &content_vec;
583 if content.len() >= 4 {
584 let mut pos = 0;
585 let mut format_version = STORE_VERSION_V1;
586
587 let collection_count = if let Some(header) =
588 reddb_file::decode_native_paged_metadata_header(content)
589 .map_err(|err| StoreError::Serialization(err.to_string()))?
590 {
591 format_version = header.format_version;
592 pos += reddb_file::METADATA_HEADER_BYTES;
593 header.collection_count as usize
594 } else {
595 let count = u32::from_le_bytes([
596 content[pos],
597 content[pos + 1],
598 content[pos + 2],
599 content[pos + 3],
600 ]) as usize;
601 pos += 4;
602 count
603 };
604
605 self.set_format_version(format_version);
606
607 if pos > content.len() {
608 return Ok(());
609 }
610
611 for _ in 0..collection_count {
613 if let Ok(root) =
614 reddb_file::decode_native_paged_collection_root(content, &mut pos)
615 {
616 let root_page = root.root_page;
618 let name = root.collection;
619
620 let _ = self.create_collection_in_memory(&name);
624
625 if root_page > 0 {
627 let btree = BTree::with_root(Arc::clone(pager), root_page);
628
629 let mut cursor = btree.cursor_first().map_err(|err| {
631 page_integrity(root_page, Some(name.clone()), err.to_string())
632 })?;
633 let manager = self.get_collection(&name);
634 loop {
635 let next = cursor.next().map_err(|err| {
636 page_integrity(root_page, Some(name.clone()), err.to_string())
637 })?;
638 let Some((key, value)) = next else {
639 break;
640 };
641 if let Ok((entity, metadata)) =
643 Self::deserialize_entity_record(&value, self.format_version())
644 {
645 if let Some(m) = &manager {
646 let id = entity.id;
647 if let EntityKind::TableRow { row_id, .. } = &entity.kind {
648 m.register_row_id(*row_id);
649 }
650 self.context_index.index_entity(&name, &entity);
651 let _ = m.insert(entity.clone());
652 if let Some(metadata) = metadata {
653 let _ = m.set_metadata(id, metadata);
654 }
655 self.register_entity_id(id);
656 if self.config.auto_index_refs {
657 self.index_cross_refs(&entity, &name)?;
658 }
659 }
660 }
661 }
662
663 self.btree_indices.write().insert(name, Arc::new(btree));
665 }
666 } else {
667 break;
668 }
669 }
670
671 if format_version >= STORE_VERSION_V2 && pos + 4 <= content.len() {
672 let cross_ref_count = u32::from_le_bytes([
673 content[pos],
674 content[pos + 1],
675 content[pos + 2],
676 content[pos + 3],
677 ]) as usize;
678 pos += 4;
679
680 for _ in 0..cross_ref_count {
681 let Ok(cross_ref) =
682 reddb_file::decode_native_paged_cross_ref(content, &mut pos)
683 else {
684 break;
685 };
686 let source_id = EntityId::new(cross_ref.source_id);
687 let target_id = EntityId::new(cross_ref.target_id);
688 let ref_type = RefType::from_byte(cross_ref.ref_type);
689 let target_collection = cross_ref.target_collection;
690
691 self.cross_refs.write().entry(source_id).or_default().push((
692 target_id,
693 ref_type,
694 target_collection.clone(),
695 ));
696
697 if let Some((collection, mut entity)) = self.get_any(source_id) {
698 let exists = entity.cross_refs().iter().any(|xref| {
699 xref.target == target_id
700 && xref.ref_type == ref_type
701 && xref.target_collection == target_collection
702 });
703 if !exists {
704 entity.cross_refs_mut().push(CrossRef::new(
705 source_id,
706 target_id,
707 target_collection.clone(),
708 ref_type,
709 ));
710 if let Some(manager) = self.get_collection(&collection) {
711 let _ = manager.update(entity);
712 }
713 }
714 }
715 }
716 }
717 }
718 }
719
720 if self.format_version() < STORE_VERSION_V11 {
721 self.set_format_version(STORE_VERSION_V11);
722 }
723
724 Ok(())
725 }
726
727 pub(crate) fn deserialize_entity(
729 data: &[u8],
730 format_version: u32,
731 ) -> Result<UnifiedEntity, StoreError> {
732 let mut pos = 0;
733 Self::read_entity_binary(data, &mut pos, format_version)
734 .map_err(|e| StoreError::Serialization(e.to_string()))
735 }
736
737 pub(crate) fn serialize_entity(entity: &UnifiedEntity, format_version: u32) -> Vec<u8> {
739 let mut buf = Vec::with_capacity(256);
744 Self::write_entity_binary(&mut buf, entity, format_version);
745 buf
746 }
747
748 pub(crate) fn serialize_entity_record(
749 entity: &UnifiedEntity,
750 metadata: Option<&Metadata>,
751 format_version: u32,
752 ) -> Vec<u8> {
753 let entity_bytes = Self::serialize_entity(entity, format_version);
754 let has_meta = matches!(metadata, Some(m) if !m.fields.is_empty());
760 if has_meta {
761 let metadata_bytes = serialize_metadata(metadata);
762 reddb_file::encode_native_entity_record_frame(&entity_bytes, Some(&metadata_bytes))
763 } else {
764 reddb_file::encode_native_entity_record_frame(&entity_bytes, None)
765 }
766 }
767
768 pub(crate) fn deserialize_entity_record(
769 data: &[u8],
770 format_version: u32,
771 ) -> Result<(UnifiedEntity, Option<Metadata>), StoreError> {
772 let Some(frame) = reddb_file::decode_native_entity_record_frame(data)
773 .map_err(|err| StoreError::Serialization(err.to_string()))?
774 else {
775 return Self::deserialize_entity(data, format_version).map(|entity| (entity, None));
776 };
777
778 let entity = Self::deserialize_entity(frame.entity, format_version)?;
779 let metadata = if frame.metadata.is_empty() {
780 None
781 } else {
782 let metadata = deserialize_metadata(frame.metadata)?;
783 if metadata.is_empty() {
784 None
785 } else {
786 Some(metadata)
787 }
788 };
789
790 Ok((entity, metadata))
791 }
792
793 pub fn persist(&self) -> Result<(), StoreError> {
798 let _vault_guard = self.vault_publication_lock.lock();
799 let pending_vault_versions = self.pending_vault_versions.read();
800 let pager = match &self.pager {
801 Some(p) => p,
802 None => {
803 if let Some(path) = &self.db_path {
805 return self
806 .save_to_file(path)
807 .map_err(|e| StoreError::Serialization(e.to_string()));
808 }
809 return Err(StoreError::Io(std::io::Error::other(
810 "No pager or path configured for persistence",
811 )));
812 }
813 };
814
815 match pager.read_page(1) {
816 Ok(_) => {}
817 Err(PagerError::PageNotFound(_)) => {
818 let meta_page = pager
819 .allocate_page(crate::storage::engine::PageType::Header)
820 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
821 pager
822 .write_page(meta_page.page_id(), meta_page)
823 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
824 }
825 Err(e) => {
826 return Err(StoreError::Io(std::io::Error::other(e.to_string())));
827 }
828 }
829
830 if let Some(commit) = &self.commit {
831 commit.force_sync().map_err(StoreError::Io)?;
832 }
833
834 let collections = self.collections.read();
835 let mut btree_indices = self.btree_indices.write();
836
837 let mut collection_roots: Vec<(String, u32)> = Vec::new();
839
840 for (name, manager) in collections.iter() {
843 let btree = btree_indices
844 .entry(name.clone())
845 .or_insert_with(|| Arc::new(BTree::new(Arc::clone(pager))));
846
847 let mut existing_keys = Vec::new();
848 if !btree.is_empty() {
849 let mut cursor = btree.cursor_first().map_err(|e| {
850 StoreError::Io(std::io::Error::other(format!(
851 "B-tree cursor error while rebuilding '{name}': {e}"
852 )))
853 })?;
854 while let Some((key, _)) = cursor.next().map_err(|e| {
855 StoreError::Io(std::io::Error::other(format!(
856 "B-tree scan error while rebuilding '{name}': {e}"
857 )))
858 })? {
859 existing_keys.push(key);
860 }
861 }
862
863 for key in existing_keys {
864 btree.delete(&key).map_err(|e| {
865 StoreError::Io(std::io::Error::other(format!(
866 "B-tree delete error while rebuilding '{name}': {e}"
867 )))
868 })?;
869 }
870
871 let mut records: Vec<(Vec<u8>, Vec<u8>)> = manager
872 .query_all(|entity| {
873 !pending_vault_versions.contains(&entity.id)
874 && (!matches!(entity.data, EntityData::Row(_) | EntityData::Vector(_))
875 || !self
876 .snapshot_manager
877 .get()
878 .is_some_and(|manager| manager.is_aborted(entity.xmin)))
879 })
880 .into_iter()
881 .map(|mut entity| {
882 if matches!(entity.data, EntityData::Row(_))
885 && self
886 .snapshot_manager
887 .get()
888 .is_some_and(|manager| manager.is_aborted(entity.xmax))
889 {
890 entity.xmax = 0;
891 }
892 let metadata = manager.get_metadata(entity.id);
893 (
894 entity.id.raw().to_be_bytes().to_vec(),
895 Self::serialize_entity_record(
896 &entity,
897 metadata.as_ref(),
898 self.format_version(),
899 ),
900 )
901 })
902 .collect();
903 records.sort_by(|left, right| left.0.cmp(&right.0));
904
905 if !records.is_empty() {
913 btree.bulk_insert_sorted(&records).map_err(|e| {
914 StoreError::Io(std::io::Error::other(format!(
915 "B-tree bulk rebuild error for '{name}': {e}"
916 )))
917 })?;
918 }
919
920 collection_roots.push((name.clone(), btree.root_page_id()));
921 }
922
923 let mut meta_data = Vec::with_capacity(4096);
925
926 let format_version = STORE_VERSION_V11;
927 self.set_format_version(format_version);
928
929 reddb_file::encode_native_paged_metadata_header(
930 &mut meta_data,
931 reddb_file::NativePagedMetadataHeader {
932 format_version,
933 collection_count: collection_roots.len() as u32,
934 },
935 );
936
937 for (name, root_page) in &collection_roots {
939 reddb_file::encode_native_paged_collection_root(&mut meta_data, name, *root_page);
940 }
941
942 let cross_refs = self.cross_refs.read();
944 let total_refs: usize = cross_refs.values().map(|v| v.len()).sum();
945 meta_data.extend_from_slice(&(total_refs as u32).to_le_bytes());
946 for (source_id, refs) in cross_refs.iter() {
947 for (target_id, ref_type, collection) in refs {
948 reddb_file::encode_native_paged_cross_ref(
949 &mut meta_data,
950 source_id.raw(),
951 target_id.raw(),
952 ref_type.to_byte(),
953 collection,
954 );
955 }
956 }
957
958 let meta_page = build_meta_page1_with_overflow(pager, &meta_data)
960 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
961
962 pager
966 .write_page(1, meta_page)
967 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
968
969 pager
971 .sync()
972 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
973
974 if let Some(commit) = &self.commit {
975 commit.truncate().map_err(StoreError::Io)?;
976 }
977
978 Ok(())
979 }
980
981 pub fn is_paged(&self) -> bool {
983 self.pager.is_some()
984 }
985
986 pub(crate) fn collection_root_page(&self, collection: &str) -> Option<u32> {
989 self.btree_indices
990 .read()
991 .get(collection)
992 .map(|btree| btree.root_page_id())
993 .filter(|root| *root != 0)
994 }
995
996 pub fn db_path(&self) -> Option<&Path> {
998 self.db_path.as_deref()
999 }
1000}
1001
1002fn serialize_metadata(metadata: Option<&Metadata>) -> Vec<u8> {
1003 let Some(metadata) = metadata else {
1004 return Vec::new();
1005 };
1006 if metadata.is_empty() {
1007 return Vec::new();
1008 }
1009
1010 let mut entries: Vec<_> = metadata.iter().collect();
1011 entries.sort_by_key(|(a, _)| *a);
1012
1013 let mut buf = Vec::new();
1014 buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
1015 for (key, value) in entries {
1016 write_string(&mut buf, key);
1017 write_metadata_value(&mut buf, value);
1018 }
1019 buf
1020}
1021
1022fn deserialize_metadata(data: &[u8]) -> Result<Metadata, StoreError> {
1023 let mut pos = 0usize;
1024 let count = read_u32(data, &mut pos)? as usize;
1025 let mut metadata = Metadata::new();
1026 for _ in 0..count {
1027 let key = read_string(data, &mut pos)?;
1028 let value = read_metadata_value(data, &mut pos)?;
1029 metadata.set(key, value);
1030 }
1031 Ok(metadata)
1032}
1033
1034fn write_string(buf: &mut Vec<u8>, value: &str) {
1035 reddb_file::encode_native_len_prefixed_str(buf, value);
1036}
1037
1038fn write_bytes(buf: &mut Vec<u8>, value: &[u8]) {
1039 reddb_file::encode_native_len_prefixed_bytes(buf, value);
1040}
1041
1042fn write_ref_target(buf: &mut Vec<u8>, target: &crate::storage::unified::RefTarget) {
1043 use crate::storage::unified::RefTarget;
1044
1045 match target {
1046 RefTarget::TableRow { table, row_id } => {
1047 buf.push(0);
1048 write_string(buf, table);
1049 buf.extend_from_slice(&row_id.to_le_bytes());
1050 }
1051 RefTarget::Node {
1052 collection,
1053 node_id,
1054 } => {
1055 buf.push(1);
1056 write_string(buf, collection);
1057 buf.extend_from_slice(&node_id.raw().to_le_bytes());
1058 }
1059 RefTarget::Edge {
1060 collection,
1061 edge_id,
1062 } => {
1063 buf.push(2);
1064 write_string(buf, collection);
1065 buf.extend_from_slice(&edge_id.raw().to_le_bytes());
1066 }
1067 RefTarget::Vector {
1068 collection,
1069 vector_id,
1070 } => {
1071 buf.push(3);
1072 write_string(buf, collection);
1073 buf.extend_from_slice(&vector_id.raw().to_le_bytes());
1074 }
1075 RefTarget::Entity {
1076 collection,
1077 entity_id,
1078 } => {
1079 buf.push(4);
1080 write_string(buf, collection);
1081 buf.extend_from_slice(&entity_id.raw().to_le_bytes());
1082 }
1083 }
1084}
1085
1086fn write_metadata_value(buf: &mut Vec<u8>, value: &MetadataValue) {
1087 match value {
1088 MetadataValue::Null => buf.push(0),
1089 MetadataValue::Bool(v) => {
1090 buf.push(1);
1091 buf.push(u8::from(*v));
1092 }
1093 MetadataValue::Int(v) => {
1094 buf.push(2);
1095 buf.extend_from_slice(&v.to_le_bytes());
1096 }
1097 MetadataValue::Float(v) => {
1098 buf.push(3);
1099 buf.extend_from_slice(&v.to_le_bytes());
1100 }
1101 MetadataValue::String(v) => {
1102 buf.push(4);
1103 write_string(buf, v);
1104 }
1105 MetadataValue::Bytes(v) => {
1106 buf.push(5);
1107 write_bytes(buf, v);
1108 }
1109 MetadataValue::Array(values) => {
1110 buf.push(6);
1111 buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
1112 for value in values {
1113 write_metadata_value(buf, value);
1114 }
1115 }
1116 MetadataValue::Object(values) => {
1117 buf.push(7);
1118 let mut entries: Vec<_> = values.iter().collect();
1119 entries.sort_by_key(|(a, _)| *a);
1120 buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
1121 for (key, value) in entries {
1122 write_string(buf, key);
1123 write_metadata_value(buf, value);
1124 }
1125 }
1126 MetadataValue::Timestamp(v) => {
1127 buf.push(8);
1128 buf.extend_from_slice(&v.to_le_bytes());
1129 }
1130 MetadataValue::Geo { lat, lon } => {
1131 buf.push(9);
1132 buf.extend_from_slice(&lat.to_le_bytes());
1133 buf.extend_from_slice(&lon.to_le_bytes());
1134 }
1135 MetadataValue::Reference(target) => {
1136 buf.push(10);
1137 write_ref_target(buf, target);
1138 }
1139 MetadataValue::References(targets) => {
1140 buf.push(11);
1141 buf.extend_from_slice(&(targets.len() as u32).to_le_bytes());
1142 for target in targets {
1143 write_ref_target(buf, target);
1144 }
1145 }
1146 }
1147}
1148
1149fn read_exact_slice<'a>(
1150 data: &'a [u8],
1151 pos: &mut usize,
1152 len: usize,
1153) -> Result<&'a [u8], StoreError> {
1154 if *pos + len > data.len() {
1155 return Err(StoreError::Serialization(
1156 "truncated metadata payload".to_string(),
1157 ));
1158 }
1159 let slice = &data[*pos..*pos + len];
1160 *pos += len;
1161 Ok(slice)
1162}
1163
1164fn read_u32(data: &[u8], pos: &mut usize) -> Result<u32, StoreError> {
1165 let bytes = read_exact_slice(data, pos, 4)?;
1166 let mut raw = [0u8; 4];
1167 raw.copy_from_slice(bytes);
1168 Ok(u32::from_le_bytes(raw))
1169}
1170
1171fn read_u64(data: &[u8], pos: &mut usize) -> Result<u64, StoreError> {
1172 let bytes = read_exact_slice(data, pos, 8)?;
1173 let mut raw = [0u8; 8];
1174 raw.copy_from_slice(bytes);
1175 Ok(u64::from_le_bytes(raw))
1176}
1177
1178fn read_i64(data: &[u8], pos: &mut usize) -> Result<i64, StoreError> {
1179 let bytes = read_exact_slice(data, pos, 8)?;
1180 let mut raw = [0u8; 8];
1181 raw.copy_from_slice(bytes);
1182 Ok(i64::from_le_bytes(raw))
1183}
1184
1185fn read_f64(data: &[u8], pos: &mut usize) -> Result<f64, StoreError> {
1186 let bytes = read_exact_slice(data, pos, 8)?;
1187 let mut raw = [0u8; 8];
1188 raw.copy_from_slice(bytes);
1189 Ok(f64::from_le_bytes(raw))
1190}
1191
1192fn read_u8(data: &[u8], pos: &mut usize) -> Result<u8, StoreError> {
1193 let bytes = read_exact_slice(data, pos, 1)?;
1194 Ok(bytes[0])
1195}
1196
1197fn read_string(data: &[u8], pos: &mut usize) -> Result<String, StoreError> {
1198 reddb_file::decode_native_len_prefixed_string(data, pos)
1199 .map_err(|err| StoreError::Serialization(err.to_string()))
1200}
1201
1202fn read_bytes(data: &[u8], pos: &mut usize) -> Result<Vec<u8>, StoreError> {
1203 reddb_file::decode_native_len_prefixed_bytes(data, pos)
1204 .map(|bytes| bytes.to_vec())
1205 .map_err(|err| StoreError::Serialization(err.to_string()))
1206}
1207
1208fn read_ref_target(
1209 data: &[u8],
1210 pos: &mut usize,
1211) -> Result<crate::storage::unified::RefTarget, StoreError> {
1212 use crate::storage::unified::RefTarget;
1213
1214 match read_u8(data, pos)? {
1215 0 => Ok(RefTarget::TableRow {
1216 table: read_string(data, pos)?,
1217 row_id: read_u64(data, pos)?,
1218 }),
1219 1 => Ok(RefTarget::Node {
1220 collection: read_string(data, pos)?,
1221 node_id: EntityId::new(read_u64(data, pos)?),
1222 }),
1223 2 => Ok(RefTarget::Edge {
1224 collection: read_string(data, pos)?,
1225 edge_id: EntityId::new(read_u64(data, pos)?),
1226 }),
1227 3 => Ok(RefTarget::Vector {
1228 collection: read_string(data, pos)?,
1229 vector_id: EntityId::new(read_u64(data, pos)?),
1230 }),
1231 4 => Ok(RefTarget::Entity {
1232 collection: read_string(data, pos)?,
1233 entity_id: EntityId::new(read_u64(data, pos)?),
1234 }),
1235 tag => Err(StoreError::Serialization(format!(
1236 "unknown metadata ref target tag {tag}"
1237 ))),
1238 }
1239}
1240
1241fn read_metadata_value(data: &[u8], pos: &mut usize) -> Result<MetadataValue, StoreError> {
1242 match read_u8(data, pos)? {
1243 0 => Ok(MetadataValue::Null),
1244 1 => Ok(MetadataValue::Bool(read_u8(data, pos)? != 0)),
1245 2 => Ok(MetadataValue::Int(read_i64(data, pos)?)),
1246 3 => Ok(MetadataValue::Float(read_f64(data, pos)?)),
1247 4 => Ok(MetadataValue::String(read_string(data, pos)?)),
1248 5 => Ok(MetadataValue::Bytes(read_bytes(data, pos)?)),
1249 6 => {
1250 let count = read_u32(data, pos)? as usize;
1251 let mut values = Vec::with_capacity(count);
1252 for _ in 0..count {
1253 values.push(read_metadata_value(data, pos)?);
1254 }
1255 Ok(MetadataValue::Array(values))
1256 }
1257 7 => {
1258 let count = read_u32(data, pos)? as usize;
1259 let mut values = std::collections::HashMap::with_capacity(count);
1260 for _ in 0..count {
1261 let key = read_string(data, pos)?;
1262 let value = read_metadata_value(data, pos)?;
1263 values.insert(key, value);
1264 }
1265 Ok(MetadataValue::Object(values))
1266 }
1267 8 => Ok(MetadataValue::Timestamp(read_u64(data, pos)?)),
1268 9 => Ok(MetadataValue::Geo {
1269 lat: read_f64(data, pos)?,
1270 lon: read_f64(data, pos)?,
1271 }),
1272 10 => Ok(MetadataValue::Reference(read_ref_target(data, pos)?)),
1273 11 => {
1274 let count = read_u32(data, pos)? as usize;
1275 let mut targets = Vec::with_capacity(count);
1276 for _ in 0..count {
1277 targets.push(read_ref_target(data, pos)?);
1278 }
1279 Ok(MetadataValue::References(targets))
1280 }
1281 tag => Err(StoreError::Serialization(format!(
1282 "unknown metadata value tag {tag}"
1283 ))),
1284 }
1285}