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(
152 zone: &str,
153 id: impl Into<String>,
154 collection: Option<String>,
155 detail: impl Into<String>,
156) -> StoreError {
157 StoreError::StorageIntegrity(crate::api::StorageIntegrityError::new(
158 zone, id, collection, detail,
159 ))
160}
161
162fn page_integrity(
163 page_id: u32,
164 collection: Option<String>,
165 detail: impl Into<String>,
166) -> StoreError {
167 storage_integrity("page", page_id.to_string(), collection, detail)
168}
169
170fn read_meta_payload(pager: &Pager) -> Result<Option<Vec<u8>>, StoreError> {
171 let cs = crate::storage::engine::HEADER_SIZE;
172 let meta_page = match pager.read_page(1) {
173 Ok(page) => page,
174 Err(read_err) => pager.recover_meta_from_shadow().map_err(|shadow_err| {
175 page_integrity(
176 1,
177 None,
178 format!(
179 "metadata page checksum/read failed ({read_err}); shadow recovery also failed ({shadow_err})"
180 ),
181 )
182 })?,
183 };
184 let bytes = meta_page.as_bytes();
185 if bytes.len() < cs + 4 {
186 return Ok(Some(bytes.get(cs..).unwrap_or(&[]).to_vec()));
187 }
188 let header = match reddb_file::decode_native_metadata_overflow_header(&bytes[cs..])
189 .map_err(|err| page_integrity(1, None, err.to_string()))?
190 {
191 Some(header) => header,
192 None => {
193 return Ok(Some(bytes[cs..].to_vec()));
194 }
195 };
196 if bytes.len() < cs + META_V3_PAGE1_HEADER {
197 return Err(page_integrity(
198 1,
199 None,
200 "metadata overflow header is truncated",
201 ));
202 }
203 let total = header.total_payload_bytes as usize;
204 let mut next = header.next_overflow_page_id;
205 let mut payload: Vec<u8> = Vec::with_capacity(total);
206 let first_take = total.min(META_V3_FIRST_PAYLOAD_CAP);
207 payload.extend_from_slice(
208 &bytes[cs + META_V3_PAGE1_HEADER..cs + META_V3_PAGE1_HEADER + first_take],
209 );
210 while next != 0 && payload.len() < total {
211 let ov = pager
212 .read_page(next)
213 .map_err(|err| page_integrity(next, None, err.to_string()))?;
214 let ob = ov.as_bytes();
215 if ob.len() < cs + META_V3_OVERFLOW_HEADER {
216 return Err(page_integrity(
217 next,
218 None,
219 "metadata overflow continuation is truncated",
220 ));
221 }
222 let continuation =
223 reddb_file::decode_native_metadata_overflow_continuation_header(&ob[cs..])
224 .map_err(|err| page_integrity(next, None, err.to_string()))?;
225 let nn = continuation.next_overflow_page_id;
226 let len = continuation.chunk_bytes as usize;
227 let remaining = total - payload.len();
228 let take = len.min(remaining).min(META_V3_OVERFLOW_PAYLOAD_CAP);
229 payload.extend_from_slice(
230 &ob[cs + META_V3_OVERFLOW_HEADER..cs + META_V3_OVERFLOW_HEADER + take],
231 );
232 next = nn;
233 }
234 Ok(Some(payload))
235}
236
237impl UnifiedStore {
238 pub(crate) fn mark_paged_registry_dirty(&self) {
239 self.paged_registry_dirty.store(true, Ordering::Release);
240 }
241
242 pub(crate) fn get_or_create_btree(&self, collection: &str) -> Option<Arc<BTree>> {
248 let pager = self.pager.as_ref()?;
249 if let Some(btree) = self.btree_indices.read().get(collection).cloned() {
250 return Some(btree);
251 }
252 let mut write = self.btree_indices.write();
253 let btree = write
254 .entry(collection.to_string())
255 .or_insert_with(|| Arc::new(BTree::new(Arc::clone(pager))))
256 .clone();
257 Some(btree)
258 }
259
260 pub(crate) fn flush_paged_state(&self) -> Result<(), StoreError> {
261 let Some(pager) = &self.pager else {
262 return Ok(());
263 };
264
265 if self.paged_registry_dirty.load(Ordering::Acquire) {
266 self.flush_paged_registry()?;
267 self.paged_registry_dirty.store(false, Ordering::Release);
268 return Ok(());
269 }
270
271 pager
272 .flush()
273 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))
274 }
275
276 pub(crate) fn flush_paged_registry(&self) -> Result<(), StoreError> {
277 let Some(pager) = &self.pager else {
278 return Ok(());
279 };
280
281 match pager.read_page(1) {
282 Ok(_) => {}
283 Err(PagerError::PageNotFound(_)) => {
284 let meta_page = pager
285 .allocate_page(crate::storage::engine::PageType::Header)
286 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
287 pager
288 .write_page(meta_page.page_id(), meta_page)
289 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
290 }
291 Err(e) => {
292 return Err(StoreError::Io(std::io::Error::other(e.to_string())));
293 }
294 }
295
296 let format_version = STORE_VERSION_V11;
297 self.set_format_version(format_version);
298
299 let collections = self.collections.read();
300 let btree_indices = self.btree_indices.read();
301 let mut collection_roots = Vec::with_capacity(collections.len());
302 for (name, _) in collections.iter() {
303 let root_page = btree_indices
304 .get(name)
305 .map_or(0, |btree| btree.root_page_id());
306 collection_roots.push((name.clone(), root_page));
307 }
308 drop(btree_indices);
309 drop(collections);
310
311 let mut meta_data = Vec::with_capacity(4096);
312 reddb_file::encode_native_paged_metadata_header(
313 &mut meta_data,
314 reddb_file::NativePagedMetadataHeader {
315 format_version,
316 collection_count: collection_roots.len() as u32,
317 },
318 );
319 for (name, root_page) in &collection_roots {
320 reddb_file::encode_native_paged_collection_root(&mut meta_data, name, *root_page);
321 }
322
323 let cross_refs = self.cross_refs.read();
324 let total_refs: usize = cross_refs.values().map(|v| v.len()).sum();
325 meta_data.extend_from_slice(&(total_refs as u32).to_le_bytes());
326 for (source_id, refs) in cross_refs.iter() {
327 for (target_id, ref_type, collection) in refs {
328 reddb_file::encode_native_paged_cross_ref(
329 &mut meta_data,
330 source_id.raw(),
331 target_id.raw(),
332 ref_type.to_byte(),
333 collection,
334 );
335 }
336 }
337
338 let meta_page = build_meta_page1_with_overflow(pager, &meta_data)
339 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
340
341 pager
342 .write_meta_shadow(&meta_page)
343 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
344 pager
345 .write_page(1, meta_page)
346 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
347 pager
348 .flush()
349 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
350
351 Ok(())
352 }
353
354 pub fn pager(&self) -> Option<&Arc<Pager>> {
356 self.pager.as_ref()
357 }
358
359 pub fn config(&self) -> &UnifiedStoreConfig {
363 &self.config
364 }
365
366 pub fn with_config(config: UnifiedStoreConfig) -> Self {
367 Self {
368 config,
369 format_version: AtomicU32::new(STORE_VERSION_V11),
370 next_entity_id: AtomicU64::new(1),
371 collections: RwLock::new(HashMap::new()),
372 cross_refs: RwLock::new(HashMap::new()),
373 reverse_refs: RwLock::new(HashMap::new()),
374 pager: None,
375 db_path: None,
376 btree_indices: RwLock::new(HashMap::new()),
377 context_index: ContextIndex::new(),
378 entity_cache: EntityCache::new(),
379 graph_label_index: RwLock::new(HashMap::new()),
380 paged_registry_dirty: AtomicBool::new(false),
381 commit: None,
382 unindex_cross_refs_fast_path: AtomicU64::new(0),
383 replayed_turbo_inserts: parking_lot::Mutex::new(HashMap::new()),
384 replayed_probabilistic_deltas: parking_lot::Mutex::new(Vec::new()),
385 aux_metadata: RwLock::new(Vec::new()),
386 }
387 }
388
389 pub fn open(path: impl AsRef<Path>) -> Result<Self, StoreError> {
407 Self::open_with_config(path, UnifiedStoreConfig::default())
408 }
409
410 pub fn open_with_config(
411 path: impl AsRef<Path>,
412 config: UnifiedStoreConfig,
413 ) -> Result<Self, StoreError> {
414 let path = path.as_ref();
415 let mut pager_config = PagerConfig::default();
416 if matches!(
424 std::env::var("REDDB_DOUBLE_WRITE").ok().as_deref(),
425 Some("0") | Some("false") | Some("off")
426 ) {
427 pager_config.double_write = false;
428 }
429 let pager = Pager::open(path, pager_config)
430 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
431
432 let wal_path = reddb_file::layout::unified_wal_path(path);
433 let commit = if StoreCommitCoordinator::should_open(&wal_path, config.durability_mode) {
434 Some(Arc::new(
435 StoreCommitCoordinator::open(wal_path, config.durability_mode, config.group_commit)
436 .map_err(StoreError::Io)?,
437 ))
438 } else {
439 None
440 };
441
442 let store = Self {
443 config,
444 format_version: AtomicU32::new(STORE_VERSION_V11),
445 next_entity_id: AtomicU64::new(1),
446 collections: RwLock::new(HashMap::new()),
447 cross_refs: RwLock::new(HashMap::new()),
448 reverse_refs: RwLock::new(HashMap::new()),
449 pager: Some(Arc::new(pager)),
450 db_path: Some(path.to_path_buf()),
451 btree_indices: RwLock::new(HashMap::new()),
452 context_index: ContextIndex::new(),
453 entity_cache: EntityCache::new(),
454 graph_label_index: RwLock::new(HashMap::new()),
455 paged_registry_dirty: AtomicBool::new(false),
456 commit,
457 unindex_cross_refs_fast_path: AtomicU64::new(0),
458 replayed_turbo_inserts: parking_lot::Mutex::new(HashMap::new()),
459 replayed_probabilistic_deltas: parking_lot::Mutex::new(Vec::new()),
460 aux_metadata: RwLock::new(Vec::new()),
461 };
462
463 store.load_from_pages()?;
465 if let Some(commit) = &store.commit {
466 commit.replay_into(&store).map_err(StoreError::Io)?;
467 }
468 store.recover_operational_manifest()?;
469
470 Ok(store)
471 }
472
473 pub(crate) fn recover_operational_manifest(&self) -> Result<(), StoreError> {
474 let Some(path) = &self.db_path else {
475 return Ok(());
476 };
477 let mut collections = self.list_collections();
478 collections.sort();
479 let pending_drops =
480 crate::storage::operational_manifest::OperationalManifest::for_db_path(path)
481 .recover_or_bootstrap(&collections)
482 .map_err(|err| storage_integrity("manifest", "current", None, err.to_string()))?;
483 for name in pending_drops {
484 if self.get_collection(&name).is_some() {
485 self.drop_collection(&name)?;
486 }
487 }
488 Ok(())
489 }
490
491 pub(crate) fn publish_operational_collection_create(
492 &self,
493 name: &str,
494 ) -> Result<(), StoreError> {
495 let Some(path) = &self.db_path else {
496 return Ok(());
497 };
498 crate::storage::operational_manifest::OperationalManifest::for_db_path(path)
499 .create_collection(name)
500 .map_err(StoreError::Io)
501 }
502
503 pub(crate) fn publish_operational_collection_pending_drop(
504 &self,
505 name: &str,
506 ) -> Result<(), StoreError> {
507 let Some(path) = &self.db_path else {
508 return Ok(());
509 };
510 crate::storage::operational_manifest::OperationalManifest::for_db_path(path)
511 .begin_drop_collection(name)
512 .map_err(StoreError::Io)
513 }
514
515 pub(crate) fn publish_operational_collection_drop_finished(
516 &self,
517 name: &str,
518 ) -> Result<(), StoreError> {
519 let Some(path) = &self.db_path else {
520 return Ok(());
521 };
522 crate::storage::operational_manifest::OperationalManifest::for_db_path(path)
523 .finish_drop_collection(name)
524 .map_err(StoreError::Io)
525 }
526
527 fn load_from_pages(&self) -> Result<(), StoreError> {
531 let pager = match &self.pager {
532 Some(p) => p,
533 None => return Ok(()), };
535
536 let page_count = pager.page_count().map_err(|e| {
538 StoreError::Io(std::io::Error::other(format!(
539 "failed to read page count: {}",
540 e
541 )))
542 })?;
543 if page_count <= 1 {
544 return Ok(());
546 }
547
548 if let Some(content_vec) = read_meta_payload(pager)? {
553 let content: &[u8] = &content_vec;
554 if content.len() >= 4 {
555 let mut pos = 0;
556 let mut format_version = STORE_VERSION_V1;
557
558 let collection_count = if let Some(header) =
559 reddb_file::decode_native_paged_metadata_header(content)
560 .map_err(|err| StoreError::Serialization(err.to_string()))?
561 {
562 format_version = header.format_version;
563 pos += reddb_file::METADATA_HEADER_BYTES;
564 header.collection_count as usize
565 } else {
566 let count = u32::from_le_bytes([
567 content[pos],
568 content[pos + 1],
569 content[pos + 2],
570 content[pos + 3],
571 ]) as usize;
572 pos += 4;
573 count
574 };
575
576 self.set_format_version(format_version);
577
578 if pos > content.len() {
579 return Ok(());
580 }
581
582 for _ in 0..collection_count {
584 if let Ok(root) =
585 reddb_file::decode_native_paged_collection_root(content, &mut pos)
586 {
587 let root_page = root.root_page;
589 let name = root.collection;
590
591 let _ = self.create_collection_in_memory(&name);
595
596 if root_page > 0 {
598 let btree = BTree::with_root(Arc::clone(pager), root_page);
599
600 let mut cursor = btree.cursor_first().map_err(|err| {
602 page_integrity(root_page, Some(name.clone()), err.to_string())
603 })?;
604 let manager = self.get_collection(&name);
605 loop {
606 let next = cursor.next().map_err(|err| {
607 page_integrity(root_page, Some(name.clone()), err.to_string())
608 })?;
609 let Some((key, value)) = next else {
610 break;
611 };
612 if let Ok((entity, metadata)) =
614 Self::deserialize_entity_record(&value, self.format_version())
615 {
616 if let Some(m) = &manager {
617 let id = entity.id;
618 if let EntityKind::TableRow { row_id, .. } = &entity.kind {
619 m.register_row_id(*row_id);
620 }
621 self.context_index.index_entity(&name, &entity);
622 let _ = m.insert(entity.clone());
623 if let Some(metadata) = metadata {
624 let _ = m.set_metadata(id, metadata);
625 }
626 self.register_entity_id(id);
627 if self.config.auto_index_refs {
628 self.index_cross_refs(&entity, &name)?;
629 }
630 }
631 }
632 }
633
634 self.btree_indices.write().insert(name, Arc::new(btree));
636 }
637 } else {
638 break;
639 }
640 }
641
642 if format_version >= STORE_VERSION_V2 && pos + 4 <= content.len() {
643 let cross_ref_count = u32::from_le_bytes([
644 content[pos],
645 content[pos + 1],
646 content[pos + 2],
647 content[pos + 3],
648 ]) as usize;
649 pos += 4;
650
651 for _ in 0..cross_ref_count {
652 let Ok(cross_ref) =
653 reddb_file::decode_native_paged_cross_ref(content, &mut pos)
654 else {
655 break;
656 };
657 let source_id = EntityId::new(cross_ref.source_id);
658 let target_id = EntityId::new(cross_ref.target_id);
659 let ref_type = RefType::from_byte(cross_ref.ref_type);
660 let target_collection = cross_ref.target_collection;
661
662 self.cross_refs.write().entry(source_id).or_default().push((
663 target_id,
664 ref_type,
665 target_collection.clone(),
666 ));
667
668 if let Some((collection, mut entity)) = self.get_any(source_id) {
669 let exists = entity.cross_refs().iter().any(|xref| {
670 xref.target == target_id
671 && xref.ref_type == ref_type
672 && xref.target_collection == target_collection
673 });
674 if !exists {
675 entity.cross_refs_mut().push(CrossRef::new(
676 source_id,
677 target_id,
678 target_collection.clone(),
679 ref_type,
680 ));
681 if let Some(manager) = self.get_collection(&collection) {
682 let _ = manager.update(entity);
683 }
684 }
685 }
686 }
687 }
688 }
689 }
690
691 if self.format_version() < STORE_VERSION_V11 {
692 self.set_format_version(STORE_VERSION_V11);
693 }
694
695 Ok(())
696 }
697
698 pub(crate) fn deserialize_entity(
700 data: &[u8],
701 format_version: u32,
702 ) -> Result<UnifiedEntity, StoreError> {
703 let mut pos = 0;
704 Self::read_entity_binary(data, &mut pos, format_version)
705 .map_err(|e| StoreError::Serialization(e.to_string()))
706 }
707
708 pub(crate) fn serialize_entity(entity: &UnifiedEntity, format_version: u32) -> Vec<u8> {
710 let mut buf = Vec::with_capacity(256);
715 Self::write_entity_binary(&mut buf, entity, format_version);
716 buf
717 }
718
719 pub(crate) fn serialize_entity_record(
720 entity: &UnifiedEntity,
721 metadata: Option<&Metadata>,
722 format_version: u32,
723 ) -> Vec<u8> {
724 let entity_bytes = Self::serialize_entity(entity, format_version);
725 let has_meta = matches!(metadata, Some(m) if !m.fields.is_empty());
731 if has_meta {
732 let metadata_bytes = serialize_metadata(metadata);
733 reddb_file::encode_native_entity_record_frame(&entity_bytes, Some(&metadata_bytes))
734 } else {
735 reddb_file::encode_native_entity_record_frame(&entity_bytes, None)
736 }
737 }
738
739 pub(crate) fn deserialize_entity_record(
740 data: &[u8],
741 format_version: u32,
742 ) -> Result<(UnifiedEntity, Option<Metadata>), StoreError> {
743 let Some(frame) = reddb_file::decode_native_entity_record_frame(data)
744 .map_err(|err| StoreError::Serialization(err.to_string()))?
745 else {
746 return Self::deserialize_entity(data, format_version).map(|entity| (entity, None));
747 };
748
749 let entity = Self::deserialize_entity(frame.entity, format_version)?;
750 let metadata = if frame.metadata.is_empty() {
751 None
752 } else {
753 let metadata = deserialize_metadata(frame.metadata)?;
754 if metadata.is_empty() {
755 None
756 } else {
757 Some(metadata)
758 }
759 };
760
761 Ok((entity, metadata))
762 }
763
764 pub fn persist(&self) -> Result<(), StoreError> {
769 let pager = match &self.pager {
770 Some(p) => p,
771 None => {
772 if let Some(path) = &self.db_path {
774 return self
775 .save_to_file(path)
776 .map_err(|e| StoreError::Serialization(e.to_string()));
777 }
778 return Err(StoreError::Io(std::io::Error::other(
779 "No pager or path configured for persistence",
780 )));
781 }
782 };
783
784 match pager.read_page(1) {
785 Ok(_) => {}
786 Err(PagerError::PageNotFound(_)) => {
787 let meta_page = pager
788 .allocate_page(crate::storage::engine::PageType::Header)
789 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
790 pager
791 .write_page(meta_page.page_id(), meta_page)
792 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
793 }
794 Err(e) => {
795 return Err(StoreError::Io(std::io::Error::other(e.to_string())));
796 }
797 }
798
799 if let Some(commit) = &self.commit {
800 commit.force_sync().map_err(StoreError::Io)?;
801 }
802
803 let collections = self.collections.read();
804 let mut btree_indices = self.btree_indices.write();
805
806 let mut collection_roots: Vec<(String, u32)> = Vec::new();
808
809 for (name, manager) in collections.iter() {
812 let btree = btree_indices
813 .entry(name.clone())
814 .or_insert_with(|| Arc::new(BTree::new(Arc::clone(pager))));
815
816 let mut existing_keys = Vec::new();
817 if !btree.is_empty() {
818 let mut cursor = btree.cursor_first().map_err(|e| {
819 StoreError::Io(std::io::Error::other(format!(
820 "B-tree cursor error while rebuilding '{name}': {e}"
821 )))
822 })?;
823 while let Some((key, _)) = cursor.next().map_err(|e| {
824 StoreError::Io(std::io::Error::other(format!(
825 "B-tree scan error while rebuilding '{name}': {e}"
826 )))
827 })? {
828 existing_keys.push(key);
829 }
830 }
831
832 for key in existing_keys {
833 btree.delete(&key).map_err(|e| {
834 StoreError::Io(std::io::Error::other(format!(
835 "B-tree delete error while rebuilding '{name}': {e}"
836 )))
837 })?;
838 }
839
840 let mut records: Vec<(Vec<u8>, Vec<u8>)> = manager
841 .query_all(|_| true)
842 .into_iter()
843 .map(|entity| {
844 let metadata = manager.get_metadata(entity.id);
845 (
846 entity.id.raw().to_be_bytes().to_vec(),
847 Self::serialize_entity_record(
848 &entity,
849 metadata.as_ref(),
850 self.format_version(),
851 ),
852 )
853 })
854 .collect();
855 records.sort_by(|left, right| left.0.cmp(&right.0));
856
857 if !records.is_empty() {
865 btree.bulk_insert_sorted(&records).map_err(|e| {
866 StoreError::Io(std::io::Error::other(format!(
867 "B-tree bulk rebuild error for '{name}': {e}"
868 )))
869 })?;
870 }
871
872 collection_roots.push((name.clone(), btree.root_page_id()));
873 }
874
875 let mut meta_data = Vec::with_capacity(4096);
877
878 let format_version = STORE_VERSION_V11;
879 self.set_format_version(format_version);
880
881 reddb_file::encode_native_paged_metadata_header(
882 &mut meta_data,
883 reddb_file::NativePagedMetadataHeader {
884 format_version,
885 collection_count: collection_roots.len() as u32,
886 },
887 );
888
889 for (name, root_page) in &collection_roots {
891 reddb_file::encode_native_paged_collection_root(&mut meta_data, name, *root_page);
892 }
893
894 let cross_refs = self.cross_refs.read();
896 let total_refs: usize = cross_refs.values().map(|v| v.len()).sum();
897 meta_data.extend_from_slice(&(total_refs as u32).to_le_bytes());
898 for (source_id, refs) in cross_refs.iter() {
899 for (target_id, ref_type, collection) in refs {
900 reddb_file::encode_native_paged_cross_ref(
901 &mut meta_data,
902 source_id.raw(),
903 target_id.raw(),
904 ref_type.to_byte(),
905 collection,
906 );
907 }
908 }
909
910 let meta_page = build_meta_page1_with_overflow(pager, &meta_data)
912 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
913
914 pager
917 .write_meta_shadow(&meta_page)
918 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
919
920 pager
922 .write_page(1, meta_page)
923 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
924
925 pager
927 .sync()
928 .map_err(|e| StoreError::Io(std::io::Error::other(e.to_string())))?;
929
930 if let Some(commit) = &self.commit {
931 commit.truncate().map_err(StoreError::Io)?;
932 }
933
934 Ok(())
935 }
936
937 pub fn is_paged(&self) -> bool {
939 self.pager.is_some()
940 }
941
942 pub(crate) fn collection_root_page(&self, collection: &str) -> Option<u32> {
945 self.btree_indices
946 .read()
947 .get(collection)
948 .map(|btree| btree.root_page_id())
949 .filter(|root| *root != 0)
950 }
951
952 pub fn db_path(&self) -> Option<&Path> {
954 self.db_path.as_deref()
955 }
956}
957
958fn serialize_metadata(metadata: Option<&Metadata>) -> Vec<u8> {
959 let Some(metadata) = metadata else {
960 return Vec::new();
961 };
962 if metadata.is_empty() {
963 return Vec::new();
964 }
965
966 let mut entries: Vec<_> = metadata.iter().collect();
967 entries.sort_by_key(|(a, _)| *a);
968
969 let mut buf = Vec::new();
970 buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
971 for (key, value) in entries {
972 write_string(&mut buf, key);
973 write_metadata_value(&mut buf, value);
974 }
975 buf
976}
977
978fn deserialize_metadata(data: &[u8]) -> Result<Metadata, StoreError> {
979 let mut pos = 0usize;
980 let count = read_u32(data, &mut pos)? as usize;
981 let mut metadata = Metadata::new();
982 for _ in 0..count {
983 let key = read_string(data, &mut pos)?;
984 let value = read_metadata_value(data, &mut pos)?;
985 metadata.set(key, value);
986 }
987 Ok(metadata)
988}
989
990fn write_string(buf: &mut Vec<u8>, value: &str) {
991 reddb_file::encode_native_len_prefixed_str(buf, value);
992}
993
994fn write_bytes(buf: &mut Vec<u8>, value: &[u8]) {
995 reddb_file::encode_native_len_prefixed_bytes(buf, value);
996}
997
998fn write_ref_target(buf: &mut Vec<u8>, target: &crate::storage::unified::RefTarget) {
999 use crate::storage::unified::RefTarget;
1000
1001 match target {
1002 RefTarget::TableRow { table, row_id } => {
1003 buf.push(0);
1004 write_string(buf, table);
1005 buf.extend_from_slice(&row_id.to_le_bytes());
1006 }
1007 RefTarget::Node {
1008 collection,
1009 node_id,
1010 } => {
1011 buf.push(1);
1012 write_string(buf, collection);
1013 buf.extend_from_slice(&node_id.raw().to_le_bytes());
1014 }
1015 RefTarget::Edge {
1016 collection,
1017 edge_id,
1018 } => {
1019 buf.push(2);
1020 write_string(buf, collection);
1021 buf.extend_from_slice(&edge_id.raw().to_le_bytes());
1022 }
1023 RefTarget::Vector {
1024 collection,
1025 vector_id,
1026 } => {
1027 buf.push(3);
1028 write_string(buf, collection);
1029 buf.extend_from_slice(&vector_id.raw().to_le_bytes());
1030 }
1031 RefTarget::Entity {
1032 collection,
1033 entity_id,
1034 } => {
1035 buf.push(4);
1036 write_string(buf, collection);
1037 buf.extend_from_slice(&entity_id.raw().to_le_bytes());
1038 }
1039 }
1040}
1041
1042fn write_metadata_value(buf: &mut Vec<u8>, value: &MetadataValue) {
1043 match value {
1044 MetadataValue::Null => buf.push(0),
1045 MetadataValue::Bool(v) => {
1046 buf.push(1);
1047 buf.push(u8::from(*v));
1048 }
1049 MetadataValue::Int(v) => {
1050 buf.push(2);
1051 buf.extend_from_slice(&v.to_le_bytes());
1052 }
1053 MetadataValue::Float(v) => {
1054 buf.push(3);
1055 buf.extend_from_slice(&v.to_le_bytes());
1056 }
1057 MetadataValue::String(v) => {
1058 buf.push(4);
1059 write_string(buf, v);
1060 }
1061 MetadataValue::Bytes(v) => {
1062 buf.push(5);
1063 write_bytes(buf, v);
1064 }
1065 MetadataValue::Array(values) => {
1066 buf.push(6);
1067 buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
1068 for value in values {
1069 write_metadata_value(buf, value);
1070 }
1071 }
1072 MetadataValue::Object(values) => {
1073 buf.push(7);
1074 let mut entries: Vec<_> = values.iter().collect();
1075 entries.sort_by_key(|(a, _)| *a);
1076 buf.extend_from_slice(&(entries.len() as u32).to_le_bytes());
1077 for (key, value) in entries {
1078 write_string(buf, key);
1079 write_metadata_value(buf, value);
1080 }
1081 }
1082 MetadataValue::Timestamp(v) => {
1083 buf.push(8);
1084 buf.extend_from_slice(&v.to_le_bytes());
1085 }
1086 MetadataValue::Geo { lat, lon } => {
1087 buf.push(9);
1088 buf.extend_from_slice(&lat.to_le_bytes());
1089 buf.extend_from_slice(&lon.to_le_bytes());
1090 }
1091 MetadataValue::Reference(target) => {
1092 buf.push(10);
1093 write_ref_target(buf, target);
1094 }
1095 MetadataValue::References(targets) => {
1096 buf.push(11);
1097 buf.extend_from_slice(&(targets.len() as u32).to_le_bytes());
1098 for target in targets {
1099 write_ref_target(buf, target);
1100 }
1101 }
1102 }
1103}
1104
1105fn read_exact_slice<'a>(
1106 data: &'a [u8],
1107 pos: &mut usize,
1108 len: usize,
1109) -> Result<&'a [u8], StoreError> {
1110 if *pos + len > data.len() {
1111 return Err(StoreError::Serialization(
1112 "truncated metadata payload".to_string(),
1113 ));
1114 }
1115 let slice = &data[*pos..*pos + len];
1116 *pos += len;
1117 Ok(slice)
1118}
1119
1120fn read_u32(data: &[u8], pos: &mut usize) -> Result<u32, StoreError> {
1121 let bytes = read_exact_slice(data, pos, 4)?;
1122 let mut raw = [0u8; 4];
1123 raw.copy_from_slice(bytes);
1124 Ok(u32::from_le_bytes(raw))
1125}
1126
1127fn read_u64(data: &[u8], pos: &mut usize) -> Result<u64, StoreError> {
1128 let bytes = read_exact_slice(data, pos, 8)?;
1129 let mut raw = [0u8; 8];
1130 raw.copy_from_slice(bytes);
1131 Ok(u64::from_le_bytes(raw))
1132}
1133
1134fn read_i64(data: &[u8], pos: &mut usize) -> Result<i64, StoreError> {
1135 let bytes = read_exact_slice(data, pos, 8)?;
1136 let mut raw = [0u8; 8];
1137 raw.copy_from_slice(bytes);
1138 Ok(i64::from_le_bytes(raw))
1139}
1140
1141fn read_f64(data: &[u8], pos: &mut usize) -> Result<f64, StoreError> {
1142 let bytes = read_exact_slice(data, pos, 8)?;
1143 let mut raw = [0u8; 8];
1144 raw.copy_from_slice(bytes);
1145 Ok(f64::from_le_bytes(raw))
1146}
1147
1148fn read_u8(data: &[u8], pos: &mut usize) -> Result<u8, StoreError> {
1149 let bytes = read_exact_slice(data, pos, 1)?;
1150 Ok(bytes[0])
1151}
1152
1153fn read_string(data: &[u8], pos: &mut usize) -> Result<String, StoreError> {
1154 reddb_file::decode_native_len_prefixed_string(data, pos)
1155 .map_err(|err| StoreError::Serialization(err.to_string()))
1156}
1157
1158fn read_bytes(data: &[u8], pos: &mut usize) -> Result<Vec<u8>, StoreError> {
1159 reddb_file::decode_native_len_prefixed_bytes(data, pos)
1160 .map(|bytes| bytes.to_vec())
1161 .map_err(|err| StoreError::Serialization(err.to_string()))
1162}
1163
1164fn read_ref_target(
1165 data: &[u8],
1166 pos: &mut usize,
1167) -> Result<crate::storage::unified::RefTarget, StoreError> {
1168 use crate::storage::unified::RefTarget;
1169
1170 match read_u8(data, pos)? {
1171 0 => Ok(RefTarget::TableRow {
1172 table: read_string(data, pos)?,
1173 row_id: read_u64(data, pos)?,
1174 }),
1175 1 => Ok(RefTarget::Node {
1176 collection: read_string(data, pos)?,
1177 node_id: EntityId::new(read_u64(data, pos)?),
1178 }),
1179 2 => Ok(RefTarget::Edge {
1180 collection: read_string(data, pos)?,
1181 edge_id: EntityId::new(read_u64(data, pos)?),
1182 }),
1183 3 => Ok(RefTarget::Vector {
1184 collection: read_string(data, pos)?,
1185 vector_id: EntityId::new(read_u64(data, pos)?),
1186 }),
1187 4 => Ok(RefTarget::Entity {
1188 collection: read_string(data, pos)?,
1189 entity_id: EntityId::new(read_u64(data, pos)?),
1190 }),
1191 tag => Err(StoreError::Serialization(format!(
1192 "unknown metadata ref target tag {tag}"
1193 ))),
1194 }
1195}
1196
1197fn read_metadata_value(data: &[u8], pos: &mut usize) -> Result<MetadataValue, StoreError> {
1198 match read_u8(data, pos)? {
1199 0 => Ok(MetadataValue::Null),
1200 1 => Ok(MetadataValue::Bool(read_u8(data, pos)? != 0)),
1201 2 => Ok(MetadataValue::Int(read_i64(data, pos)?)),
1202 3 => Ok(MetadataValue::Float(read_f64(data, pos)?)),
1203 4 => Ok(MetadataValue::String(read_string(data, pos)?)),
1204 5 => Ok(MetadataValue::Bytes(read_bytes(data, pos)?)),
1205 6 => {
1206 let count = read_u32(data, pos)? as usize;
1207 let mut values = Vec::with_capacity(count);
1208 for _ in 0..count {
1209 values.push(read_metadata_value(data, pos)?);
1210 }
1211 Ok(MetadataValue::Array(values))
1212 }
1213 7 => {
1214 let count = read_u32(data, pos)? as usize;
1215 let mut values = std::collections::HashMap::with_capacity(count);
1216 for _ in 0..count {
1217 let key = read_string(data, pos)?;
1218 let value = read_metadata_value(data, pos)?;
1219 values.insert(key, value);
1220 }
1221 Ok(MetadataValue::Object(values))
1222 }
1223 8 => Ok(MetadataValue::Timestamp(read_u64(data, pos)?)),
1224 9 => Ok(MetadataValue::Geo {
1225 lat: read_f64(data, pos)?,
1226 lon: read_f64(data, pos)?,
1227 }),
1228 10 => Ok(MetadataValue::Reference(read_ref_target(data, pos)?)),
1229 11 => {
1230 let count = read_u32(data, pos)? as usize;
1231 let mut targets = Vec::with_capacity(count);
1232 for _ in 0..count {
1233 targets.push(read_ref_target(data, pos)?);
1234 }
1235 Ok(MetadataValue::References(targets))
1236 }
1237 tag => Err(StoreError::Serialization(format!(
1238 "unknown metadata value tag {tag}"
1239 ))),
1240 }
1241}