1use crate::common::Position;
2use crate::common::{PageID, Tsn};
3use crate::db::{DB_SCHEMA_VERSION, read_conditional};
4use crate::events_tree_nodes::EventLeafNode;
5use crate::free_lists_tree_nodes::{
6 FreeListInternalNode, FreeListLeafNode, FreeListLeafValue, FreeListTsnLeafNode,
7};
8use crate::header_node::HeaderNode;
9use crate::node::Node;
10use crate::page::{
11 PAGE_HEADER_SIZE, Page, page_approx_deserialized_bytes, page_as_header_node,
12 serialize_page_into,
13};
14use crate::pager::Pager;
15use crate::tags_tree_nodes::TagsLeafNode;
16use crate::tags_tree_nodes::set_tag_key_width;
17use moka::sync::Cache;
18use std::collections::BTreeMap;
19use std::collections::HashMap;
20use std::collections::VecDeque;
21use std::path::{Path, PathBuf};
22use std::str::FromStr;
23use std::sync::{Arc, Mutex};
24use std::thread::sleep;
25use std::time::Duration;
26use umadb_dcb::DcbError::InternalError;
27use umadb_dcb::{DcbError, DcbQuery, DcbResult};
28
29const GET_LATEST_HEADER_RETRIES: usize = 5;
30const GET_LATEST_HEADER_DELAY: Duration = Duration::from_millis(10);
31const HEADER_PAGE_ID_0: PageID = PageID(0);
32const HEADER_PAGE_ID_1: PageID = PageID(1);
33
34pub const DEFAULT_PAGE_SIZE: usize = 4096;
35pub const DEFAULT_DB_FILENAME: &str = "uma.db";
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38pub enum ReadMethod {
39 #[default]
40 FileIo,
41 Mmap,
42}
43
44impl FromStr for ReadMethod {
45 type Err = String;
46
47 fn from_str(s: &str) -> Result<Self, Self::Err> {
48 match s.to_lowercase().as_str() {
49 "fileio" => Ok(ReadMethod::FileIo),
50 "mmap" => Ok(ReadMethod::Mmap),
51 _ => Err(format!("Unknown read method: {}", s)),
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
57pub struct StorageOptions {
58 pub db_path: PathBuf,
59 pub page_size: usize,
60 pub read_method: ReadMethod,
61 pub page_cache_max_pages: usize,
62 pub page_cache_max_mb: usize,
63 pub zero_fill_pages: bool,
64}
65
66impl Default for StorageOptions {
67 fn default() -> Self {
68 Self {
69 db_path: DEFAULT_DB_FILENAME.into(),
70 page_size: DEFAULT_PAGE_SIZE,
71 read_method: ReadMethod::default(),
72 page_cache_max_pages: 0,
73 page_cache_max_mb: 0,
74 zero_fill_pages: true,
75 }
76 }
77}
78
79impl StorageOptions {
80 pub fn db_path<P: AsRef<Path>>(self, db_path: P) -> Self {
81 let p = db_path.as_ref();
82 let db_path = if p.is_dir() {
83 p.join(DEFAULT_DB_FILENAME)
84 } else {
85 p.to_path_buf()
86 };
87 Self { db_path, ..self }
88 }
89
90 pub fn page_size(self, page_size: usize) -> Self {
91 Self { page_size, ..self }
92 }
93
94 pub fn read_method(self, read_method: ReadMethod) -> Self {
95 Self {
96 read_method,
97 ..self
98 }
99 }
100
101 pub fn page_cache_max_pages(self, page_cache_max_pages: usize) -> Self {
102 Self {
103 page_cache_max_pages,
104 ..self
105 }
106 }
107
108 pub fn page_cache_max_mb(self, page_cache_max_mb: usize) -> Self {
109 Self {
110 page_cache_max_mb,
111 ..self
112 }
113 }
114
115 pub fn zero_fill_pages(self, zero_fill_pages: bool) -> Self {
116 Self {
117 zero_fill_pages,
118 ..self
119 }
120 }
121}
122
123#[derive(Debug, Default)]
134pub struct ReaderTsnRegistry {
135 counts: Mutex<BTreeMap<Tsn, usize>>,
136}
137
138impl ReaderTsnRegistry {
139 pub fn register(&self, tsn: Tsn) {
141 let mut counts = self.counts.lock().unwrap();
142 *counts.entry(tsn).or_insert(0) += 1;
143 }
144
145 pub fn unregister(&self, tsn: Tsn) {
148 let mut counts = self.counts.lock().unwrap();
149 if let Some(count) = counts.get_mut(&tsn) {
150 *count -= 1;
151 if *count == 0 {
152 counts.remove(&tsn);
153 }
154 }
155 }
156
157 pub fn min(&self) -> Option<Tsn> {
159 self.counts
160 .lock()
161 .unwrap()
162 .first_key_value()
163 .map(|(&tsn, _)| tsn)
164 }
165
166 #[cfg(test)]
168 pub fn live_reader_count(&self) -> usize {
169 self.counts.lock().unwrap().values().copied().sum()
170 }
171
172 #[cfg(test)]
174 pub fn contains_tsn(&self, tsn: Tsn) -> bool {
175 self.counts.lock().unwrap().contains_key(&tsn)
176 }
177
178 #[cfg(test)]
180 pub fn live_tsns_sorted(&self) -> Vec<Tsn> {
181 let counts = self.counts.lock().unwrap();
182 let mut out = Vec::new();
183 for (&tsn, &count) in counts.iter() {
184 for _ in 0..count {
185 out.push(tsn);
186 }
187 }
188 out
189 }
190}
191
192struct WriterState {
197 headers: [Page; 2],
199 scratch: Vec<u8>,
201}
202
203pub struct Mvcc {
205 pub pager: Pager,
206 pub reader_tsns: Arc<ReaderTsnRegistry>,
207 pub page_size: usize,
208 pub max_node_size: usize,
209 writer_state: Mutex<WriterState>,
210 pub verbose: bool,
211 pub zero_fill_pages: bool,
212 read_method: ReadMethod,
213 page_cache: Option<Cache<PageID, Arc<Page>>>,
214}
215
216impl Mvcc {
217 pub fn new(verbose: bool, options: StorageOptions) -> DcbResult<Self> {
218 let pager = Pager::new(&options.db_path, options.page_size)?;
219 println!(
220 "UmaDB opened file {}",
221 options.db_path.canonicalize()?.display()
222 );
223
224 println!(
225 "UmaDB reading with {}",
226 match options.read_method {
227 ReadMethod::Mmap => "memory map",
228 ReadMethod::FileIo => "file I/O",
229 }
230 );
231
232 println!(
233 "UmaDB zero-fill pages: {}",
234 if options.zero_fill_pages {
235 "true"
236 } else {
237 "false"
238 }
239 );
240
241 let page_cache = if options.page_cache_max_mb > 0 {
242 println!("UmaDB page cache max MB: {}", options.page_cache_max_mb);
243 Some(
244 Cache::builder()
245 .max_capacity(1000000 * options.page_cache_max_mb as u64)
246 .weigher(|_page_id, page: &Arc<Page>| {
247 page_approx_deserialized_bytes(page.as_ref()).min(u32::MAX as usize) as u32
248 })
249 .build(),
250 )
251 } else if options.page_cache_max_pages > 0 {
252 println!(
253 "UmaDB page cache max pages: {}",
254 options.page_cache_max_pages
255 );
256 Some(Cache::new(options.page_cache_max_pages as u64))
257 } else {
258 println!("UmaDB page cache not enabled");
259 None
260 };
261
262 let mvcc = Self {
263 pager,
264 reader_tsns: Arc::new(ReaderTsnRegistry::default()),
265 page_size: options.page_size,
266 max_node_size: options.page_size - PAGE_HEADER_SIZE,
267 writer_state: Mutex::new(WriterState {
268 headers: [
269 Page {
270 page_id: PageID(0),
271 node: Node::Header(HeaderNode::default()),
272 },
273 Page {
274 page_id: PageID(1),
275 node: Node::Header(HeaderNode::default()),
276 },
277 ],
278 scratch: vec![0u8; options.page_size],
279 }),
280 verbose,
281 zero_fill_pages: options.zero_fill_pages,
282 read_method: options.read_method,
283 page_cache,
284 };
285
286 if mvcc.pager.is_file_new {
287 set_tag_key_width(crate::tags_tree_nodes::TAG_HASH_LEN);
289
290 let initial_tsn = Tsn(0);
292 let initial_free_lists_tree_root_id = PageID(2);
293 let initial_events_tree_root_id = PageID(3);
294 let initial_tags_tree_root_id = PageID(4);
295 let initial_next_page_id = PageID(5);
296 let initial_next_position = Position(1);
297 mvcc.update_header(
298 HEADER_PAGE_ID_0,
299 initial_tsn,
300 initial_free_lists_tree_root_id,
301 initial_events_tree_root_id,
302 initial_tags_tree_root_id,
303 PageID(0),
304 initial_next_page_id,
305 initial_next_position,
306 )?;
307 mvcc.update_header(
308 HEADER_PAGE_ID_1,
309 initial_tsn,
310 initial_free_lists_tree_root_id,
311 initial_events_tree_root_id,
312 initial_tags_tree_root_id,
313 PageID(0),
314 initial_next_page_id,
315 initial_next_position,
316 )?;
317
318 let free_list_leaf = FreeListLeafNode {
320 keys: Vec::new(),
321 values: Vec::new(),
322 };
323 let free_list_page = Page::new(
324 initial_free_lists_tree_root_id,
325 Node::FreeListLeaf(free_list_leaf),
326 );
327
328 let event_leaf = EventLeafNode {
330 keys: Vec::new(),
331 values: Vec::new(),
332 };
333 let position_page = Page::new(initial_events_tree_root_id, Node::EventLeaf(event_leaf));
334
335 let tags_leaf = TagsLeafNode {
337 keys: Vec::new(),
338 values: Vec::new(),
339 };
340 let tags_page = Page::new(initial_tags_tree_root_id, Node::TagsLeaf(tags_leaf));
341
342 let _ = mvcc.write_pages([&free_list_page, &position_page, &tags_page].into_iter())?;
344
345 mvcc.fsync()?;
347 } else {
348 if let Ok(header_page) = mvcc.get_latest_header_page() {
350 let header_latest = page_as_header_node(&header_page)?;
351 if header_latest.schema_version > DB_SCHEMA_VERSION {
353 let schema_version = header_latest.schema_version;
354 return Err(InternalError(format!(
355 "Software version is too old. This software supports schema version {DB_SCHEMA_VERSION} but the database file has schema version {schema_version}."
356 )));
357 }
358
359 if header_latest.schema_version == 0 {
361 set_tag_key_width(8);
362 } else {
363 set_tag_key_width(crate::tags_tree_nodes::TAG_HASH_LEN);
364 }
365
366 let h0 = mvcc.read_page(HEADER_PAGE_ID_0).ok();
368 let h1 = mvcc.read_page(HEADER_PAGE_ID_1).ok();
369 {
370 let mut ws = mvcc.writer_state.lock().unwrap();
371 if let Some(p0) = h0 {
372 ws.headers[0] = (*p0).clone();
373 }
374 if let Some(p1) = h1 {
375 ws.headers[1] = (*p1).clone();
376 }
377 }
378
379 if header_latest.schema_version == 1 {
382 use crate::node::Node;
383 let tags_root = header_latest.tags_tree_root_id;
384 set_tag_key_width(crate::tags_tree_nodes::TAG_HASH_LEN);
386 let sixteen_ok = mvcc
387 .read_page(tags_root)
388 .map(|p| matches!(p.node, Node::TagsLeaf(_) | Node::TagsInternal(_)))
389 .is_ok();
390 if !sixteen_ok {
391 set_tag_key_width(8);
393 let eight_ok = mvcc
394 .read_page(tags_root)
395 .map(|p| matches!(p.node, Node::TagsLeaf(_) | Node::TagsInternal(_)))
396 .is_ok();
397 if eight_ok {
398 {
400 let mut ws = mvcc.writer_state.lock().unwrap();
401 let mut buf = vec![0u8; mvcc.page_size];
403 for hp in ws.headers.iter_mut() {
404 let header_page_id = hp.page_id;
405 if let Node::Header(ref mut hn) = hp.node {
406 let schema_version = hn.schema_version;
407 if mvcc.verbose {
408 println!(
409 "Resetting header node {header_page_id:?} schema version from {schema_version:?} to 0"
410 );
411 }
412 hn.schema_version = 0;
413 }
414 serialize_page_into(&mut buf, &hp.node, mvcc.zero_fill_pages)?;
416 mvcc.pager.write_page(hp.page_id, &buf)?;
417 }
418 }
419
420 set_tag_key_width(8);
422
423 mvcc.fsync()?;
425 } else {
426 if mvcc.verbose {
428 println!(
429 "Tags root failed to deserialize at width 16 and 8; leaving schema_version unchanged"
430 );
431 }
432 }
433 }
434 }
435 }
436 if let Ok(header_page) = mvcc.get_latest_header_page() {
437 let hid = header_page.page_id;
438 let header_latest = page_as_header_node(&header_page)?;
439 {
441 let empty_dirty: std::collections::HashMap<PageID, Page> =
442 std::collections::HashMap::new();
443 let mut last_by_tree: Option<u64> = None;
444 match read_conditional(
445 &mvcc,
446 &empty_dirty,
447 header_latest.events_tree_root_id,
448 header_latest.tags_tree_root_id,
449 DcbQuery { items: vec![] },
450 None,
451 true,
452 Some(1),
453 false,
454 None,
455 ) {
456 Ok(mut v) => {
457 if let Some(ev) = v.pop() {
458 last_by_tree = Some(ev.position);
459 }
460 }
461 Err(e) => {
462 if mvcc.verbose {
463 println!(
464 "Warning: failed to read last event from events tree: {:?}",
465 e
466 );
467 }
468 }
469 }
470 let last_pos = last_by_tree.unwrap_or(0);
471 let expected_next = Position(last_pos.saturating_add(1));
472 if expected_next != header_latest.next_position {
473 if mvcc.verbose {
474 println!(
475 "Fixing header.next_position from {} to {} based on events tree",
476 header_latest.next_position.0, expected_next.0
477 );
478 }
479 mvcc.update_header(
481 hid,
482 header_latest.tsn,
483 header_latest.free_lists_tree_root_id,
484 header_latest.events_tree_root_id,
485 header_latest.tags_tree_root_id,
486 header_latest.tracking_root_page_id,
487 header_latest.next_page_id,
488 expected_next,
489 )?;
490 mvcc.fsync()?;
491 }
492 }
493 }
494 }
495
496 if let Some(ref page_cache) = mvcc.page_cache {
498 if let Some(p0) = mvcc.read_page(HEADER_PAGE_ID_0).ok() {
499 page_cache.insert(p0.page_id, p0);
500 }
501 if let Some(p1) = mvcc.read_page(HEADER_PAGE_ID_1).ok() {
502 page_cache.insert(p1.page_id, p1);
503 }
504 }
505 if let Some(ref page_cache) = mvcc.page_cache {
507 let header_page = mvcc.get_latest_header_page()?;
508 let header_node = page_as_header_node(&header_page)?;
509 let page = mvcc.read_page(header_node.free_lists_tree_root_id)?;
510 page_cache.insert(page.page_id, page);
511
512 let page = mvcc.read_page(header_node.tags_tree_root_id)?;
513 page_cache.insert(page.page_id, page);
514
515 let page = mvcc.read_page(header_node.events_tree_root_id)?;
516 page_cache.insert(page.page_id, page);
517
518 if header_node.tracking_root_page_id != PageID(0) {
519 let page = mvcc.read_page(header_node.tracking_root_page_id)?;
520 page_cache.insert(page.page_id, page);
521 }
522 }
523
524 Ok(mvcc)
525 }
526
527 pub fn get_latest_header_page(&self) -> DcbResult<Arc<Page>> {
528 for attempt in 0..GET_LATEST_HEADER_RETRIES {
529 let h0 = self.read_page(HEADER_PAGE_ID_0);
530 let h1 = self.read_page(HEADER_PAGE_ID_1);
531
532 match (h0, h1) {
533 (Ok(page0), Ok(page1)) => {
534 let header0 = page_as_header_node(&page0)?;
535 let header1 = page_as_header_node(&page1)?;
536
537 if header1.tsn > header0.tsn {
538 return Ok(page1);
539 } else {
540 return Ok(page0);
541 }
542 }
543 (Ok(page0), Err(_)) => {
544 let _ = page_as_header_node(&page0)?;
545 return Ok(page0);
546 }
547 (Err(_), Ok(page1)) => {
548 let _ = page_as_header_node(&page1)?;
549 return Ok(page1);
550 }
551 (Err(e0), Err(e1)) => {
552 if attempt + 1 < GET_LATEST_HEADER_RETRIES {
553 if self.verbose {
554 println!(
555 "Both headers invalid on attempt {}: {:?} | {:?}. Retrying...",
556 attempt + 1,
557 e0,
558 e1
559 );
560 }
561 sleep(GET_LATEST_HEADER_DELAY);
562 continue;
563 } else {
564 return Err(DcbError::DatabaseCorrupted(format!(
565 "Both header pages appear corrupted after {} attempts: ({:?}) and ({:?})",
566 GET_LATEST_HEADER_RETRIES, e0, e1
567 )));
568 }
569 }
570 }
571 }
572 Err(DcbError::DatabaseCorrupted(
574 "Unable to read a valid header".to_string(),
575 ))
576 }
577
578 fn update_header(
579 &self,
580 page_id: PageID,
581 tsn: Tsn,
582 free_lists_tree_root_id: PageID,
583 events_tree_root_id: PageID,
584 tags_tree_root_id: PageID,
585 tracking_tree_root_id: PageID,
586 next_page_id: PageID,
587 next_position: Position,
588 ) -> DcbResult<()> {
589 let mut ws = self.writer_state.lock().unwrap();
590 let headers_idx = if page_id == HEADER_PAGE_ID_0 { 0 } else { 1 };
591 match &mut ws.headers[headers_idx].node {
592 Node::Header(node) => {
593 node.tsn = tsn;
595 node.free_lists_tree_root_id = free_lists_tree_root_id;
596 node.events_tree_root_id = events_tree_root_id;
597 node.tags_tree_root_id = tags_tree_root_id;
598 node.next_page_id = next_page_id;
599 node.next_position = next_position;
600 node.tracking_root_page_id = tracking_tree_root_id;
601 }
602 _ => panic!("Shouldn't get here: header should be a header"),
603 }
604
605 let WriterState { headers, scratch } = &mut *ws;
608 serialize_page_into(scratch, &headers[headers_idx].node, self.zero_fill_pages)?;
609 self.pager.write_page(page_id, scratch)?;
610 Ok(())
611 }
612
613 pub fn read_page(&self, page_id: PageID) -> DcbResult<Arc<Page>> {
614 if let Some(ref page_cache) = self.page_cache {
616 if let Some(page) = page_cache.get(&page_id) {
617 return Ok(page);
618 }
619 }
620
621 let page = match self.read_method {
623 ReadMethod::Mmap => {
624 let mapped = self.pager.read_page_mmap_slice(page_id)?;
625 if self.verbose {
626 println!("Read {page_id:?} from mmap, deserializing...");
627 }
628 Page::deserialize(page_id, mapped.as_slice())?
629 }
630 ReadMethod::FileIo => {
631 let page = self.pager.read_page(page_id)?;
632 if self.verbose {
633 println!("Read {page_id:?} from file, deserializing...");
634 }
635 Page::deserialize(page_id, &page)?
636 }
637 };
638 let page = Arc::new(page);
639
640 if let Some(ref page_cache) = self.page_cache {
642 page_cache.insert(page_id, Arc::clone(&page));
643 }
644
645 Ok(page)
646 }
647
648 pub fn fsync(&self) -> DcbResult<()> {
649 self.pager.fsync()?;
650 Ok(())
651 }
652
653 pub fn reader(&self) -> DcbResult<Reader> {
654 loop {
656 let header_page = self.get_latest_header_page()?;
658 let header_node = page_as_header_node(&header_page)?;
659
660 let tsn = header_node.tsn;
662 self.reader_tsns.register(tsn);
663
664 let recheck = self.get_latest_header_page()?;
667 let recheck_node = page_as_header_node(&recheck)?;
668 if recheck_node.tsn == tsn {
669 return Ok(Reader {
670 header_page_id: header_page.page_id,
671 tsn: header_node.tsn,
672 events_tree_root_id: header_node.events_tree_root_id,
673 tags_tree_root_id: header_node.tags_tree_root_id,
674 next_position: header_node.next_position,
675 tracking_tree_root_id: header_node.tracking_root_page_id,
676 reader_tsns: Arc::clone(&self.reader_tsns),
677 });
678 }
679 self.reader_tsns.unregister(tsn);
684 }
685 }
686
687 pub fn writer(&self) -> DcbResult<Writer> {
688 if self.verbose {
689 println!();
690 println!("Constructing writer...");
691 }
692
693 let header_page = self.get_latest_header_page()?;
695 let header_node = page_as_header_node(&header_page)?;
696
697 let mut writer = Writer::new(
699 header_page.page_id,
700 Tsn(header_node.tsn.0 + 1),
701 header_node.next_page_id,
702 header_node.free_lists_tree_root_id,
703 header_node.events_tree_root_id,
704 header_node.tags_tree_root_id,
705 header_node.tracking_root_page_id,
706 header_node.next_position,
707 self.verbose,
708 );
709
710 if self.verbose {
711 println!("Constructed writer with {:?}", writer.tsn);
712 }
713
714 writer.find_reusable_page_ids(self)?;
716
717 Ok(writer)
718 }
719
720 pub fn write_pages<'a, I>(&self, pages: I) -> DcbResult<usize>
723 where
724 I: IntoIterator<Item = &'a Page>,
725 {
726 let mut ws = self.writer_state.lock().unwrap();
727 let buf = &mut ws.scratch;
728 let mut count = 0usize;
729 for page in pages {
730 page.serialize_into_with_zero_fill(buf, self.zero_fill_pages)?;
731 self.pager.write_page(page.page_id, buf)?;
732 if self.verbose {
733 println!("Wrote {:?} to file", page.page_id);
734 }
735 count += 1;
736 }
737 Ok(count)
738 }
739
740 pub fn commit(&self, writer: &mut Writer) -> DcbResult<()> {
778 if self.verbose {
780 println!();
781 println!("Commiting writer with {:?}", writer.tsn);
782 }
783
784 while !writer.reused_page_ids.is_empty() || !writer.freed_page_ids.is_empty() {
785 while let Some((reused_page_id, tsn)) = writer.reused_page_ids.pop_front() {
787 writer.remove_free_page_id(self, tsn, reused_page_id)?;
789 }
790
791 while let Some(freed_page_id) = writer.freed_page_ids.pop_front() {
793 writer.dirty.remove(&freed_page_id);
795
796 writer.insert_freed_page_id(self, writer.tsn, freed_page_id)?;
798 }
799 }
800
801 if !writer.dirty.is_empty() {
803 let count = {
804 self.write_pages(writer.dirty.values())?
812 };
813 if self.verbose {
814 println!("Wrote {} dirty page(s) to file", count);
815 }
816 }
817
818 self.fsync()?;
820
821 if let Some(ref page_cache) = self.page_cache {
823 for (page_id, page) in writer.dirty.drain() {
824 page_cache.insert(page_id, Arc::new(page));
825 }
826 } else {
827 writer.dirty.clear();
828 }
829
830 let (alternate_header_page_id, alternate_header_page_idx) =
832 if writer.header_page_id == HEADER_PAGE_ID_0 {
833 (HEADER_PAGE_ID_1, 1)
834 } else {
835 (HEADER_PAGE_ID_0, 0)
836 };
837 self.update_header(
838 alternate_header_page_id,
839 writer.tsn,
840 writer.free_lists_tree_root_id,
841 writer.events_tree_root_id,
842 writer.tags_tree_root_id,
843 writer.tracking_tree_root_id,
844 writer.next_page_id,
845 writer.next_position,
846 )?;
847
848 self.fsync()?;
850
851 let header_page = self.writer_state.lock().unwrap().headers[alternate_header_page_idx].clone();
853 if let Some(ref page_cache) = self.page_cache {
854 page_cache.insert(header_page.page_id, Arc::new(header_page));
855 }
856
857 if self.verbose {
858 println!("Committed writer with {:?}", writer.tsn);
859 }
860
861 Ok(())
862 }
863}
864
865pub struct Writer {
867 pub header_page_id: PageID,
868 pub tsn: Tsn,
869 pub next_page_id: PageID,
870 pub free_lists_tree_root_id: PageID,
871 pub events_tree_root_id: PageID,
872 pub tags_tree_root_id: PageID,
873 pub tracking_tree_root_id: PageID,
874 pub next_position: Position,
875 pub reusable_page_ids: VecDeque<(PageID, Tsn)>,
876 pub freed_page_ids: VecDeque<PageID>,
877 pub deserialized: HashMap<PageID, Arc<Page>>,
878 pub dirty: HashMap<PageID, Page>,
879 pub reused_page_ids: VecDeque<(PageID, Tsn)>,
880 pub verbose: bool,
881}
882
883impl Writer {
884 pub fn new(
885 header_page_id: PageID,
886 tsn: Tsn,
887 next_page_id: PageID,
888 free_lists_tree_root_id: PageID,
889 events_tree_root_id: PageID,
890 tags_tree_root_id: PageID,
891 tracking_tree_root_id: PageID,
892 next_position: Position,
893 verbose: bool,
894 ) -> Self {
895 Self {
896 header_page_id,
897 tsn,
898 next_page_id,
899 free_lists_tree_root_id,
900 events_tree_root_id,
901 tags_tree_root_id,
902 tracking_tree_root_id,
903 next_position,
904 reusable_page_ids: VecDeque::new(),
905 freed_page_ids: VecDeque::new(),
906 deserialized: HashMap::new(),
907 dirty: HashMap::new(),
908 reused_page_ids: VecDeque::new(),
909 verbose,
910 }
911 }
912
913 pub fn issue_position(&mut self) -> Position {
919 let pos = self.next_position;
920 self.next_position = Position(self.next_position.0 + 1);
921 pos
922 }
923
924 pub fn get_page_ref(&mut self, mvcc: &Mvcc, page_id: PageID) -> DcbResult<&Page> {
938 if self.dirty.contains_key(&page_id) {
940 return Ok(self.dirty.get(&page_id).unwrap());
941 }
942
943 if self.deserialized.contains_key(&page_id) {
945 return Ok(self.deserialized.get(&page_id).unwrap());
946 }
947
948 let deserialized_page = mvcc.read_page(page_id)?;
950 self.insert_deserialized(deserialized_page);
951
952 Ok(self.deserialized.get(&page_id).unwrap())
954 }
955
956 pub fn get_mut_dirty(&mut self, page_id: PageID) -> DcbResult<&mut Page> {
957 if let Some(page) = self.dirty.get_mut(&page_id) {
958 Ok(page)
959 } else {
960 Err(DcbError::DirtyPageNotFound(page_id.0))
961 }
962 }
963
964 pub fn insert_deserialized(&mut self, page: Arc<Page>) {
965 self.deserialized.insert(page.page_id, page);
966 }
967
968 pub fn insert_dirty(&mut self, page: Page) -> DcbResult<()> {
969 if self.freed_page_ids.contains(&page.page_id) {
970 return Err(DcbError::PageAlreadyFreed(page.page_id.0));
971 }
972 if self.dirty.contains_key(&page.page_id) {
973 return Err(DcbError::PageAlreadyDirty(page.page_id.0));
974 }
975 self.dirty.insert(page.page_id, page);
976 Ok(())
977 }
978
979 pub fn alloc_page_id(&mut self) -> PageID {
980 if let Some((free_page_id, tsn)) = self.reusable_page_ids.pop_front() {
981 self.reused_page_ids.push_back((free_page_id, tsn));
982 return free_page_id;
983 }
984
985 let next_page_id = self.next_page_id;
986 self.next_page_id = PageID(next_page_id.0 + 1);
987 next_page_id
988 }
989
990 pub fn get_dirty_page_id(&mut self, page_id: PageID) -> DcbResult<PageID> {
991 let mut dirty_page_id = page_id;
992 if !self.freed_page_ids.iter().any(|&id| id == page_id) {
993 if !self.dirty.contains_key(&page_id) {
994 let old_page_id = page_id;
995 self.freed_page_ids.push_back(old_page_id);
996
997 let new_page_id = self.alloc_page_id();
998 let mut new_page = Arc::unwrap_or_clone(
999 self.deserialized.remove(&old_page_id).ok_or_else(|| {
1000 DcbError::DatabaseCorrupted(format!(
1001 "Deserialized page {:?} not found while marking dirty",
1002 old_page_id
1003 ))
1004 })?,
1005 );
1006 new_page.page_id = new_page_id;
1007
1008 self.dirty.insert(new_page_id, new_page);
1009 if self.verbose {
1010 println!(
1011 "Copied {:?} to {:?}: {:?}",
1012 old_page_id,
1013 new_page_id,
1014 self.dirty.get(&new_page_id).unwrap().node
1015 );
1016 }
1017 dirty_page_id = new_page_id;
1018 } else if self.verbose {
1019 println!("{page_id:?} is already dirty");
1020 }
1021 } else {
1022 return Err(DcbError::PageAlreadyFreed(page_id.0));
1023 }
1024 Ok(dirty_page_id)
1025 }
1026
1027 pub fn append_freed_page_id(&mut self, page_id: PageID) {
1028 let verbose = self.verbose;
1029 if !self.freed_page_ids.iter().any(|&id| id == page_id) {
1030 self.freed_page_ids.push_back(page_id);
1031 if verbose {
1032 println!("Appended {page_id:?} to freed_page_ids");
1033 }
1034 if self.dirty.contains_key(&page_id) {
1035 self.dirty.remove(&page_id);
1036 if verbose {
1037 println!("Page ID {page_id:?} was in dirty and was removed");
1038 }
1039 }
1040 if verbose && self.dirty.contains_key(&page_id) {
1041 println!("Page ID {page_id:?} is still in dirty!!!!!");
1042 }
1043 }
1044 }
1045
1046 pub fn find_reusable_page_ids(&mut self, mvcc: &Mvcc) -> DcbResult<()> {
1047 let verbose = self.verbose;
1048 let mut reusable_page_ids: VecDeque<(PageID, Tsn)> = VecDeque::new();
1049 if verbose {
1051 println!("Finding reusable page IDs for TSN {:?}...", self.tsn);
1052 }
1053
1054 let smallest_reader_tsn = mvcc.reader_tsns.min();
1056 if verbose {
1057 println!("Smallest reader TSN: {smallest_reader_tsn:?}");
1058 }
1059
1060 if verbose {
1061 println!("Root is {:?}", self.free_lists_tree_root_id);
1062 }
1063 let mut stack = vec![(self.free_lists_tree_root_id, 0)];
1065 let mut is_finished = false;
1066
1067 while let Some((page_id, idx)) = stack.pop() {
1068 if is_finished {
1069 break;
1070 }
1071 let node_owned = {
1072 match self.get_page_ref(mvcc, page_id) {
1073 Ok(p) => p.node.clone(),
1074 Err(e) => {
1075 return Err(DcbError::DatabaseCorrupted(format!(
1076 "Free list page {:?} load error: {:?}",
1077 page_id, e
1078 )));
1079 }
1080 }
1081 };
1082 match node_owned {
1083 Node::FreeListInternal(node) => {
1084 if verbose {
1085 println!("{:?} is internal node", page_id);
1086 }
1087 if idx < node.child_ids.len() {
1088 let child_page_id = node.child_ids[idx];
1089 stack.push((page_id, idx + 1));
1090 stack.push((child_page_id, 0));
1091 }
1092 }
1093 Node::FreeListLeaf(node) => {
1094 if verbose {
1095 println!("{:?} is leaf node", page_id);
1096 }
1097 for i in 0..node.keys.len() {
1098 let tsn = node.keys[i];
1099 if let Some(smallest) = smallest_reader_tsn
1100 && tsn > smallest
1101 {
1102 is_finished = true;
1103 break;
1104 }
1105
1106 let leaf_value = &node.values[i];
1107 if leaf_value.root_id == PageID(0) {
1108 for &page_id in &leaf_value.page_ids {
1109 reusable_page_ids.push_back((page_id, tsn));
1110 }
1111 } else {
1112 let mut tsn_stack: Vec<(PageID, usize)> = vec![(leaf_value.root_id, 0)];
1116 if self.verbose {
1117 println!("TSN-subtree root_id: {:?}", leaf_value.root_id);
1118 }
1119 if self.verbose {
1120 println!(
1121 "root_id in dirty? {}",
1122 self.dirty.contains_key(&leaf_value.root_id)
1123 );
1124 }
1125 while let Some((sub_id, sidx)) = tsn_stack.pop() {
1126 let sub_node = {
1127 match self.get_page_ref(mvcc, sub_id) {
1128 Ok(p) => p.node.clone(),
1129 Err(e) => {
1130 return Err(DcbError::DatabaseCorrupted(format!(
1131 "TSN subtree page {:?} load error: {:?}",
1132 sub_id, e
1133 )));
1134 }
1135 }
1136 };
1137 match sub_node {
1138 Node::FreeListTsnInternal(tsn_internal) => {
1139 if sidx < tsn_internal.child_ids.len() {
1140 let child_id = tsn_internal.child_ids[sidx];
1141 tsn_stack.push((sub_id, sidx + 1));
1142 tsn_stack.push((child_id, 0));
1143 }
1144 }
1145 Node::FreeListTsnLeaf(tsn_leaf) => {
1146 for &pid in &tsn_leaf.page_ids {
1147 reusable_page_ids.push_back((pid, tsn));
1148 }
1149 }
1150 other => {
1151 return Err(DcbError::DatabaseCorrupted(format!(
1152 "Invalid node type in TSN subtree: {}",
1153 other.type_name()
1154 )));
1155 }
1156 }
1157 }
1158 }
1159 }
1160 }
1161 _ => {
1162 return Err(DcbError::DatabaseCorrupted(
1163 "Invalid node type in free list tree".to_string(),
1164 ));
1165 }
1166 }
1167 }
1168
1169 self.reusable_page_ids = reusable_page_ids;
1170 if verbose {
1171 println!("Found reusable page IDs: {:?}", self.reusable_page_ids);
1172 }
1173 Ok(())
1174 }
1175
1176 pub fn insert_freed_page_id(
1178 &mut self,
1179 mvcc: &Mvcc,
1180 tsn: Tsn,
1181 freed_page_id: PageID,
1182 ) -> DcbResult<()> {
1183 let verbose = self.verbose;
1184 if verbose {
1185 println!("Inserting {freed_page_id:?} for {tsn:?}");
1186 println!("Root is {:?}", self.free_lists_tree_root_id);
1187 }
1188 let mut current_page_id = self.free_lists_tree_root_id;
1190
1191 let mut stack: Vec<PageID> = Vec::new();
1193 let plan: FreePageIDInsertStrategy;
1194 loop {
1195 let current_page_ref = self.get_page_ref(mvcc, current_page_id)?;
1196 if let Node::FreeListLeaf(leaf_node) = ¤t_page_ref.node {
1197 let len_keys = leaf_node.keys.len();
1198 if len_keys == 0 {
1199 if !leaf_node.would_fit_new_tsn_and_page_id(mvcc.max_node_size) {
1200 return Err(DcbError::InternalError("Page size too small".to_string()));
1201 }
1202 plan = FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf;
1203 } else {
1204 let last_idx = len_keys - 1;
1205 let last_key = leaf_node.keys[last_idx];
1206 if tsn == last_key {
1207 if leaf_node.values[last_idx].root_id != PageID(0) {
1209 plan = FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree;
1210 } else if leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
1211 plan = FreePageIDInsertStrategy::PushPageIdOntoFreeListLeaf(last_idx);
1212 } else if leaf_node.keys.len() == 1 {
1213 plan = FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree;
1214 } else {
1215 plan = FreePageIDInsertStrategy::SplitFreeListLeaf;
1216 }
1217 } else if tsn > last_key {
1218 if leaf_node.would_fit_new_tsn_and_page_id(mvcc.max_node_size) {
1220 plan = FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf;
1221 } else {
1222 plan = FreePageIDInsertStrategy::CreateAndPromoteFreeListLeaf;
1223 }
1224 } else {
1225 return Err(DcbError::InternalError(
1227 "Insertion only supported for last TSN in leaf".to_string(),
1228 ));
1229 }
1230 }
1231 break;
1232 }
1233 if let Node::FreeListInternal(internal_node) = ¤t_page_ref.node {
1234 if verbose {
1235 println!("{:?} is internal node", current_page_ref.page_id);
1236 }
1237 stack.push(current_page_id);
1238 current_page_id = *internal_node
1239 .child_ids
1240 .last()
1241 .expect("FreeListInternal node should have a child");
1242 } else {
1243 return Err(DcbError::DatabaseCorrupted(
1244 "Expected FreeListInternal node".to_string(),
1245 ));
1246 }
1247 }
1248 if verbose {
1249 println!("{current_page_id:?} is leaf node");
1250 }
1251 let dirty_leaf_page_id = { self.get_dirty_page_id(current_page_id)? };
1253 let replacement_info: Option<(PageID, PageID)> = {
1254 if dirty_leaf_page_id != current_page_id {
1255 Some((current_page_id, dirty_leaf_page_id))
1256 } else {
1257 None
1258 }
1259 };
1260 let mut split_info: Option<(Tsn, PageID)> = None;
1262
1263 match plan {
1265 FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree => {
1266 let leaf_snapshot = { self.get_page_ref(mvcc, dirty_leaf_page_id)? };
1268 let Node::FreeListLeaf(leaf_ro) = &leaf_snapshot.node else {
1269 return Err(DcbError::DatabaseCorrupted(
1270 "Expected FreeListLeaf node".to_string(),
1271 ));
1272 };
1273 let last_idx = leaf_ro.keys.len() - 1;
1274 let tsn_root_id = leaf_ro.values[last_idx].root_id;
1275 if tsn_root_id == PageID(0) {
1276 return Err(DcbError::DatabaseCorrupted(
1277 "Expected TSN-subtree root_id to be set".to_string(),
1278 ));
1279 }
1280 let new_root_id = self.tsn_subtree_insert(mvcc, tsn_root_id, freed_page_id)?;
1281 if new_root_id != tsn_root_id {
1282 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1284 let Node::FreeListLeaf(dirty_leaf_node2) = &mut dirty_leaf_page.node else {
1285 return Err(DcbError::DatabaseCorrupted(
1286 "Expected FreeListLeaf node".to_string(),
1287 ));
1288 };
1289 dirty_leaf_node2.values[last_idx].root_id = new_root_id;
1290 }
1291 }
1292 FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree => {
1293 let leaf_snapshot = { self.get_page_ref(mvcc, dirty_leaf_page_id)? };
1295 let Node::FreeListLeaf(leaf_ro) = &leaf_snapshot.node else {
1296 return Err(DcbError::DatabaseCorrupted(
1297 "Expected FreeListLeaf node".to_string(),
1298 ));
1299 };
1300 let last_idx = leaf_ro.keys.len() - 1;
1301 let mut page_ids = leaf_ro.values[last_idx].page_ids.clone();
1302 page_ids.push(freed_page_id);
1303 page_ids.sort_by_key(|pid| pid.0);
1305 page_ids.dedup();
1306 let mut initial_ids: Vec<PageID> = Vec::new();
1309 let mut tmp_leaf = FreeListTsnLeafNode {
1310 page_ids: Vec::new(),
1311 };
1312 for pid in &page_ids {
1313 let mut candidate = tmp_leaf.clone();
1314 candidate.page_ids.push(*pid);
1315 let candidate_page = Page::new(PageID(0), Node::FreeListTsnLeaf(candidate));
1316 if candidate_page.calc_serialized_size() <= mvcc.page_size {
1317 tmp_leaf.page_ids.push(*pid);
1318 initial_ids.push(*pid);
1319 } else {
1320 break;
1321 }
1322 }
1323 if initial_ids.is_empty() {
1324 return Err(DcbError::InternalError(
1325 "Page size too small for TSN-subtree leaf with one PageID".to_string(),
1326 ));
1327 }
1328 let tsn_leaf_id = self.alloc_page_id();
1329 let tsn_leaf_page = Page::new(tsn_leaf_id, Node::FreeListTsnLeaf(tmp_leaf));
1330 self.insert_dirty(tsn_leaf_page)?;
1331 let mut tsn_root_id = tsn_leaf_id;
1333 for pid in page_ids.into_iter().filter(|p| !initial_ids.contains(p)) {
1335 tsn_root_id = self.tsn_subtree_insert(mvcc, tsn_root_id, pid)?;
1336 }
1337 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1339 let Node::FreeListLeaf(dirty_leaf_node2) = &mut dirty_leaf_page.node else {
1340 return Err(DcbError::DatabaseCorrupted(
1341 "Expected FreeListLeaf node".to_string(),
1342 ));
1343 };
1344 dirty_leaf_node2.values[last_idx].page_ids.clear();
1345 dirty_leaf_node2.values[last_idx].root_id = tsn_root_id;
1346 if verbose {
1347 println!("Moved inline page IDs to TSN-subtree {:?}", tsn_root_id);
1348 }
1349 }
1350 _ => { }
1351 }
1352
1353 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1355 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1356 match plan {
1357 FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf => {
1358 dirty_leaf_node.push_new_key_and_value(tsn, freed_page_id);
1359 if verbose {
1360 println!(
1361 "Inserted first pair ({tsn:?} -> {freed_page_id:?}) in {dirty_leaf_page_id:?}: {:?}",
1362 dirty_leaf_node
1363 );
1364 }
1365 }
1366 FreePageIDInsertStrategy::PushPageIdOntoFreeListLeaf(last_idx) => {
1367 dirty_leaf_node.push_new_page_id(last_idx, freed_page_id);
1368 if verbose {
1369 println!(
1370 "Appended {freed_page_id:?} for existing last {tsn:?} in {dirty_leaf_page_id:?}: {:?}",
1371 dirty_leaf_node
1372 );
1373 }
1374 }
1375 FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree => { }
1377 FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree => { }
1378 FreePageIDInsertStrategy::SplitFreeListLeaf => {
1379 let (popped_key, mut popped_value) =
1380 dirty_leaf_node.pop_last_key_and_value()?;
1381 debug_assert_eq!(popped_key, tsn);
1382 if verbose {
1383 println!(
1384 "Split (last TSN) leaf {:?}: {:?}",
1385 dirty_leaf_page_id,
1386 dirty_leaf_node.clone()
1387 );
1388 }
1389 popped_value.page_ids.push(freed_page_id);
1391 let new_leaf_node = FreeListLeafNode {
1392 keys: vec![popped_key],
1393 values: vec![popped_value],
1394 };
1395 let new_leaf_page_id = self.alloc_page_id();
1396 let new_leaf_page =
1397 Page::new(new_leaf_page_id, Node::FreeListLeaf(new_leaf_node));
1398 if verbose {
1405 println!(
1406 "Created new leaf {:?} (moved last TSN): {:?}",
1407 new_leaf_page_id, new_leaf_page.node
1408 );
1409 }
1410 self.insert_dirty(new_leaf_page)?;
1411 split_info = Some((tsn, new_leaf_page_id));
1412 }
1413 FreePageIDInsertStrategy::CreateAndPromoteFreeListLeaf => {
1414 let new_leaf_node = FreeListLeafNode {
1416 keys: vec![tsn],
1417 values: vec![FreeListLeafValue {
1418 page_ids: vec![freed_page_id],
1419 root_id: PageID(0),
1420 }],
1421 };
1422 let new_leaf_page_id = self.alloc_page_id();
1423 let new_leaf_page =
1424 Page::new(new_leaf_page_id, Node::FreeListLeaf(new_leaf_node));
1425 if verbose {
1432 println!(
1433 "Created new leaf {:?} (new last TSN): {:?}",
1434 new_leaf_page_id, new_leaf_page.node
1435 );
1436 }
1437 self.insert_dirty(new_leaf_page)?;
1438 split_info = Some((tsn, new_leaf_page_id));
1439 }
1440 }
1441 } else {
1442 return Err(DcbError::DatabaseCorrupted(
1443 "Expected FreeListLeaf node".to_string(),
1444 ));
1445 }
1446 let mut current_replacement_info = replacement_info;
1448 while let Some(parent_page_id) = stack.pop() {
1449 let dirty_page_id = { self.get_dirty_page_id(parent_page_id)? };
1451 let parent_replacement_info: Option<(PageID, PageID)> = {
1452 if dirty_page_id != parent_page_id {
1453 Some((parent_page_id, dirty_page_id))
1454 } else {
1455 None
1456 }
1457 };
1458 let dirty_internal_page = self.get_mut_dirty(dirty_page_id)?;
1460
1461 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1462 if let Some((old_id, new_id)) = current_replacement_info {
1463 dirty_internal_node.replace_last_child_id(old_id, new_id)?;
1464 if verbose {
1465 println!(
1466 "Replaced {old_id:?} with {new_id:?} in {dirty_page_id:?}: {dirty_internal_node:?}"
1467 );
1468 }
1469 } else if verbose {
1470 println!("Nothing to replace in {dirty_page_id:?}")
1471 }
1472 } else {
1473 return Err(DcbError::DatabaseCorrupted(
1474 "Expected FreeListInternal node".to_string(),
1475 ));
1476 }
1477
1478 if let Some((promoted_key, promoted_page_id)) = split_info {
1479 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1480 dirty_internal_node
1482 .append_promoted_key_and_page_id(promoted_key, promoted_page_id)?;
1483
1484 if verbose {
1485 println!(
1486 "Appended promoted key {promoted_key:?} and child {promoted_page_id:?} in {dirty_page_id:?}: {dirty_internal_node:?}"
1487 );
1488 }
1489 } else {
1490 return Err(DcbError::DatabaseCorrupted(
1491 "Expected FreeListInternal node".to_string(),
1492 ));
1493 }
1494 }
1495
1496 if dirty_internal_page.calc_serialized_size() > mvcc.page_size {
1499 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1500 if verbose {
1501 println!("Splitting internal {dirty_page_id:?}...");
1502 }
1503 if dirty_internal_node.keys.len() < 3 || dirty_internal_node.child_ids.len() < 4
1506 {
1507 return Err(DcbError::DatabaseCorrupted(
1508 "Cannot split internal node with too few keys/children".to_string(),
1509 ));
1510 }
1511
1512 let (promoted_key, new_keys, new_child_ids) =
1514 dirty_internal_node.split_off()?;
1515
1516 assert_eq!(
1518 dirty_internal_node.keys.len() + 1,
1519 dirty_internal_node.child_ids.len()
1520 );
1521
1522 let new_internal_node = FreeListInternalNode {
1523 keys: new_keys,
1524 child_ids: new_child_ids,
1525 };
1526
1527 assert_eq!(
1529 new_internal_node.keys.len() + 1,
1530 new_internal_node.child_ids.len()
1531 );
1532
1533 let new_internal_page_id = self.alloc_page_id();
1535 let new_internal_page = Page::new(
1536 new_internal_page_id,
1537 Node::FreeListInternal(new_internal_node),
1538 );
1539 if verbose {
1540 println!(
1541 "Created internal {:?}: {:?}",
1542 new_internal_page_id, new_internal_page.node
1543 );
1544 }
1545 self.insert_dirty(new_internal_page)?;
1546
1547 split_info = Some((promoted_key, new_internal_page_id));
1548 } else {
1549 return Err(DcbError::DatabaseCorrupted(
1550 "Expected FreeListInternal node".to_string(),
1551 ));
1552 }
1553 } else {
1554 split_info = None;
1555 }
1556 current_replacement_info = parent_replacement_info;
1557 }
1558
1559 if let Some((old_id, new_id)) = current_replacement_info {
1560 if self.free_lists_tree_root_id == old_id {
1561 self.free_lists_tree_root_id = new_id;
1562 if verbose {
1563 println!("Replaced root {old_id:?} with {new_id:?}");
1564 }
1565 } else {
1566 return Err(DcbError::RootIDMismatch(old_id.0, new_id.0));
1567 }
1568 }
1569
1570 if let Some((promoted_key, promoted_page_id)) = split_info {
1571 let new_internal_node = FreeListInternalNode {
1573 keys: vec![promoted_key],
1574 child_ids: vec![self.free_lists_tree_root_id, promoted_page_id],
1575 };
1576
1577 let new_root_page_id = self.alloc_page_id();
1578 let new_root_page =
1579 Page::new(new_root_page_id, Node::FreeListInternal(new_internal_node));
1580 if verbose {
1581 println!(
1582 "Created new internal root {:?}: {:?}",
1583 new_root_page_id, new_root_page.node
1584 );
1585 }
1586 self.insert_dirty(new_root_page)?;
1587
1588 self.free_lists_tree_root_id = new_root_page_id;
1589 }
1590
1591 Ok(())
1592 }
1593
1594 fn tsn_subtree_insert(
1597 &mut self,
1598 mvcc: &Mvcc,
1599 root_id: PageID,
1600 key: PageID,
1601 ) -> DcbResult<PageID> {
1602 let verbose = self.verbose;
1603 let mut stack: Vec<(PageID, usize)> = Vec::new();
1604 let mut current_id = root_id;
1605 loop {
1606 let current_page_ref = self.get_page_ref(mvcc, current_id)?;
1607 match ¤t_page_ref.node {
1608 Node::FreeListTsnLeaf(_) => break,
1609 Node::FreeListTsnInternal(internal) => {
1610 let child_idx = match internal.keys.binary_search_by(|k| k.0.cmp(&key.0)) {
1612 Ok(idx) => idx + 1, Err(idx) => idx, };
1615 let next_id = internal.child_ids[child_idx];
1616 stack.push((current_id, child_idx));
1617 current_id = next_id;
1618 }
1619 other => {
1620 return Err(DcbError::DatabaseCorrupted(format!(
1621 "Unexpected node type in TSN-subtree during insert: {}",
1622 other.type_name()
1623 )));
1624 }
1625 }
1626 }
1627
1628 {
1631 let leaf_page = self.get_mut_dirty(current_id)?;
1632 let Node::FreeListTsnLeaf(ref mut leaf) = leaf_page.node else {
1633 return Err(DcbError::DatabaseCorrupted(
1634 "Expected TSN-subtree leaf".to_string(),
1635 ));
1636 };
1637 match leaf.page_ids.binary_search_by(|pid| pid.0.cmp(&key.0)) {
1639 Ok(_) => {
1640 if verbose {
1642 println!("Duplicate PageID {:?} ignored in TSN-subtree", key);
1643 }
1644 }
1645 Err(ins) => {
1646 leaf.page_ids.insert(ins, key);
1647 if leaf.calc_serialized_size() <= mvcc.max_node_size {
1648 return Ok(root_id);
1649 }
1650 let mid = leaf.page_ids.len() / 2; let right_ids: Vec<PageID> = leaf.page_ids.split_off(mid);
1653 let promoted_key = right_ids[0];
1654 let right_leaf_id = self.alloc_page_id();
1656 let right_leaf_node = FreeListTsnLeafNode {
1657 page_ids: right_ids,
1658 };
1659 let right_leaf_page =
1660 Page::new(right_leaf_id, Node::FreeListTsnLeaf(right_leaf_node));
1661 self.insert_dirty(right_leaf_page)?;
1662 let mut promoted: Option<(PageID, PageID)> =
1664 Some((promoted_key, right_leaf_id));
1665 for (parent_id, child_idx) in stack.into_iter().rev() {
1667 if let Some((prom_key, prom_right_id)) = promoted.take() {
1668 let parent_page = self.get_mut_dirty(parent_id)?;
1669 let Node::FreeListTsnInternal(ref mut parent_node) = parent_page.node
1670 else {
1671 return Err(DcbError::DatabaseCorrupted(
1672 "Expected TSN-subtree internal".to_string(),
1673 ));
1674 };
1675
1676 parent_node.keys.insert(child_idx, prom_key);
1678 parent_node.child_ids.insert(child_idx + 1, prom_right_id);
1679 if parent_node.calc_serialized_size() <= mvcc.max_node_size {
1680 current_id = parent_id;
1682 continue;
1683 }
1684 let total_keys = parent_node.keys.len();
1686 debug_assert!(
1687 total_keys >= 2,
1688 "splitting parent with <2 keys after insert"
1689 );
1690 let mid = total_keys / 2; let promote_up_key = parent_node.keys[mid];
1692 let left_keys: Vec<PageID> = parent_node.keys[..mid].to_vec();
1694 let left_child_ids: Vec<PageID> =
1695 parent_node.child_ids[..=mid].to_vec();
1696 let right_keys: Vec<PageID> = parent_node.keys[mid + 1..].to_vec();
1698 let right_child_ids: Vec<PageID> =
1699 parent_node.child_ids[mid + 1..].to_vec();
1700 if right_child_ids.len() != right_keys.len() + 1 {
1701 return Err(DcbError::DatabaseCorrupted(
1702 "TSN-subtree internal split produced invalid right arity"
1703 .to_string(),
1704 ));
1705 }
1706 if left_child_ids.len() != left_keys.len() + 1 {
1707 return Err(DcbError::DatabaseCorrupted(
1708 "TSN-subtree internal split produced invalid left arity"
1709 .to_string(),
1710 ));
1711 }
1712 parent_node.keys = left_keys;
1714 parent_node.child_ids = left_child_ids;
1715 let right_internal_id = self.alloc_page_id();
1717 let right_internal =
1718 crate::free_lists_tree_nodes::FreeListTsnInternalNode {
1719 keys: right_keys,
1720 child_ids: right_child_ids,
1721 };
1722 let right_internal_page = Page::new(
1723 right_internal_id,
1724 Node::FreeListTsnInternal(right_internal),
1725 );
1726 self.insert_dirty(right_internal_page)?;
1727 promoted = Some((promote_up_key, right_internal_id));
1729 }
1730 current_id = parent_id;
1731 }
1732 if let Some((prom_key, prom_right_id)) = promoted.take() {
1734 let new_root_id = self.alloc_page_id();
1735 let left_id = current_id;
1736 let new_root = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
1737 keys: vec![prom_key],
1738 child_ids: vec![left_id, prom_right_id],
1739 };
1740 let new_root_page =
1741 Page::new(new_root_id, Node::FreeListTsnInternal(new_root));
1742 self.insert_dirty(new_root_page)?;
1743 if verbose {
1744 println!("Promoted new TSN-subtree root {:?}", new_root_id);
1745 }
1746 return Ok(new_root_id);
1747 }
1748 }
1749 };
1750 Ok(root_id)
1751 }
1752 }
1753
1754 pub fn remove_free_page_id(
1755 &mut self,
1756 mvcc: &Mvcc,
1757 tsn: Tsn,
1758 used_page_id: PageID,
1759 ) -> DcbResult<()> {
1760 let verbose = self.verbose;
1761 if verbose {
1762 println!();
1763 println!("Removing {used_page_id:?} from {tsn:?}...");
1764 println!("Root is {:?}", self.free_lists_tree_root_id);
1765 }
1766 let mut current_page_id = self.free_lists_tree_root_id;
1768
1769 let mut stack: Vec<PageID> = Vec::new();
1771 let mut removed_page_ids: Vec<PageID> = Vec::new();
1772
1773 loop {
1774 let current_page_ref = self.get_page_ref(mvcc, current_page_id)?;
1775 if matches!(current_page_ref.node, Node::FreeListLeaf(_)) {
1776 break;
1777 }
1778 if let Node::FreeListInternal(internal_node) = ¤t_page_ref.node {
1779 if verbose {
1780 println!("Page {:?} is internal node", current_page_ref.page_id);
1781 }
1782 stack.push(current_page_id);
1783 current_page_id = *internal_node.child_ids.first().unwrap();
1784 } else {
1785 return Err(DcbError::DatabaseCorrupted(
1786 "Expected FreeListInternal node".to_string(),
1787 ));
1788 }
1789 }
1790 if verbose {
1791 println!("Page {current_page_id:?} is leaf node");
1792 }
1793
1794 let mut replacement_info: Option<(PageID, PageID)> = None;
1797 let mut removal_info = None;
1798
1799 let leaf_snapshot = { self.get_page_ref(mvcc, current_page_id)? };
1801 let Node::FreeListLeaf(leaf_node_ro) = &leaf_snapshot.node else {
1802 return Err(DcbError::DatabaseCorrupted(
1803 "Expected FreeListLeaf node".to_string(),
1804 ));
1805 };
1806 if leaf_node_ro.keys.is_empty() || leaf_node_ro.keys[0] != tsn {
1807 return Err(DcbError::DatabaseCorrupted(format!(
1808 "Expected TSN {} not found: {:?}",
1809 tsn.0, leaf_node_ro
1810 )));
1811 }
1812
1813 let leaf_value_root_id = leaf_node_ro.values[0].root_id;
1814 if leaf_value_root_id == PageID(0) {
1820 let dirty_page_id = { self.get_dirty_page_id(current_page_id)? };
1822 if dirty_page_id != current_page_id {
1823 replacement_info = Some((current_page_id, dirty_page_id));
1824 }
1825 let dirty_leaf_page = self.get_mut_dirty(dirty_page_id)?;
1826 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1827 let leaf_value = &mut dirty_leaf_node.values[0];
1828 if let Some(pos) = leaf_value
1829 .page_ids
1830 .iter()
1831 .position(|&id| id == used_page_id)
1832 {
1833 leaf_value.page_ids.remove(pos);
1834 } else {
1835 return Err(DcbError::DatabaseCorrupted(format!(
1836 "{used_page_id:?} not found in {tsn:?}"
1837 )));
1838 }
1839 if verbose {
1840 println!("Removed {used_page_id:?} from {tsn:?} in {dirty_page_id:?}");
1841 }
1842 if leaf_value.page_ids.is_empty() {
1843 dirty_leaf_node.keys.remove(0);
1844 dirty_leaf_node.values.remove(0);
1845 if verbose {
1846 println!("Removed {tsn:?} from {dirty_page_id:?}");
1847 }
1848 if dirty_leaf_node.keys.is_empty() {
1849 if verbose {
1850 println!("Empty leaf page {dirty_page_id:?}: {dirty_leaf_node:?}");
1851 }
1852 removal_info = Some(dirty_page_id);
1853 } else if verbose {
1854 println!("Leaf page not empty {dirty_page_id:?}: {dirty_leaf_node:?}");
1855 }
1856 } else if verbose {
1857 println!("Leaf value not empty {tsn:?}: {leaf_value:?}");
1858 }
1859 } else {
1860 return Err(DcbError::DatabaseCorrupted(
1861 "Expected FreeListLeaf node".to_string(),
1862 ));
1863 }
1864 } else {
1865 let tsn_root_id = leaf_value_root_id;
1867 let dirty_tsn_root_id = { self.get_dirty_page_id(tsn_root_id)? };
1868 let mut tsn_root_replaced: Option<PageID> = None;
1869 let dirty_tsn_root_page = self.get_mut_dirty(dirty_tsn_root_id)?;
1870 let mut tsn_leaf_became_empty = false;
1871 let mut tsn_child_leaf_became_empty = false;
1872 match &mut dirty_tsn_root_page.node {
1873 Node::FreeListTsnLeaf(tsn_leaf_node) => {
1874 if let Some(pos) = tsn_leaf_node
1875 .page_ids
1876 .iter()
1877 .position(|&id| id == used_page_id)
1878 {
1879 tsn_leaf_node.page_ids.remove(pos);
1880 } else {
1881 return Err(DcbError::DatabaseCorrupted(format!(
1882 "{used_page_id:?} not found in TSN-subtree for {tsn:?}"
1883 )));
1884 }
1885 if verbose {
1886 println!(
1887 "Removed {used_page_id:?} from TSN-subtree leaf {dirty_tsn_root_id:?} for {tsn:?}"
1888 );
1889 }
1890 if tsn_leaf_node.page_ids.is_empty() {
1891 tsn_leaf_became_empty = true;
1892 removed_page_ids.push(dirty_tsn_root_id);
1893 }
1894 }
1895 Node::FreeListTsnInternal(_) => {
1896 let mut path: Vec<(PageID, usize)> = Vec::new();
1906 let mut current_id = dirty_tsn_root_id;
1907 loop {
1908 let node_owned = { self.get_page_ref(mvcc, current_id)?.node.clone() };
1909 match node_owned {
1910 Node::FreeListTsnLeaf(_) => {
1911 break; }
1913 Node::FreeListTsnInternal(internal) => {
1914 let mut child_idx = 0usize;
1916 while child_idx < internal.keys.len()
1917 && used_page_id >= internal.keys[child_idx]
1918 {
1919 child_idx += 1;
1920 }
1921 let next_id = internal.child_ids[child_idx];
1922 path.push((current_id, child_idx));
1923 current_id = next_id;
1924 }
1925 other => {
1926 return Err(DcbError::DatabaseCorrupted(format!(
1927 "Unexpected node type in TSN-subtree during descent: {}",
1928 other.type_name()
1929 )));
1930 }
1931 }
1932 }
1933
1934 let mut dirty_child_id = { self.get_dirty_page_id(current_id)? };
1936 {
1937 let child_page = self.get_mut_dirty(dirty_child_id)?;
1938 match &mut child_page.node {
1939 Node::FreeListTsnLeaf(leaf_node) => {
1940 if let Some(pos) =
1941 leaf_node.page_ids.iter().position(|&id| id == used_page_id)
1942 {
1943 leaf_node.page_ids.remove(pos);
1944 } else {
1945 return Err(DcbError::DatabaseCorrupted(format!(
1946 "{used_page_id:?} not found in TSN-subtree for {tsn:?}"
1947 )));
1948 }
1949 if verbose {
1950 println!(
1951 "Removed {used_page_id:?} from TSN-subtree leaf {dirty_child_id:?} for {tsn:?}"
1952 );
1953 }
1954 if leaf_node.page_ids.is_empty() {
1955 tsn_child_leaf_became_empty = true;
1956 removed_page_ids.push(dirty_child_id);
1957 }
1958 }
1959 other => {
1960 return Err(DcbError::DatabaseCorrupted(format!(
1961 "Expected TSN-subtree leaf, got {}",
1962 other.type_name()
1963 )));
1964 }
1965 }
1966 }
1967
1968 let mut subtree_emptied = false;
1970 let mut new_root_id_opt: Option<PageID> = None;
1971
1972 let path_len = path.len();
1973 for (level, (parent_id, child_idx)) in path.into_iter().rev().enumerate() {
1974 let parent_dirty_id = { self.get_dirty_page_id(parent_id)? };
1976
1977 let parent_page = self.get_mut_dirty(parent_dirty_id)?;
1982 let Node::FreeListTsnInternal(ref mut parent_node) = parent_page.node
1983 else {
1984 return Err(DcbError::DatabaseCorrupted(
1985 "Expected TSN-subtree internal node".to_string(),
1986 ));
1987 };
1988
1989 if tsn_child_leaf_became_empty && level == 0 {
1990 parent_node.child_ids.remove(child_idx);
1992 if !parent_node.keys.is_empty() {
1993 let key_remove_idx = if child_idx == 0 { 0 } else { child_idx - 1 };
1994 if key_remove_idx < parent_node.keys.len() {
1995 parent_node.keys.remove(key_remove_idx);
1996 }
1997 }
1998
1999 match parent_node.child_ids.len() {
2001 0 => {
2002 subtree_emptied = true;
2004 removed_page_ids.push(parent_dirty_id);
2005 }
2006 1 => {
2007 let remaining_child = parent_node.child_ids[0];
2009 removed_page_ids.push(parent_dirty_id);
2010 dirty_child_id = remaining_child;
2012 new_root_id_opt = Some(remaining_child);
2013 }
2014 _ => {
2015 dirty_child_id = parent_dirty_id;
2017 if level == path_len - 1 {
2018 new_root_id_opt = Some(parent_dirty_id);
2020 }
2021 }
2022 }
2023 } else {
2024 if parent_node.child_ids[child_idx] != dirty_child_id {
2026 parent_node.child_ids[child_idx] = dirty_child_id;
2027 }
2028 dirty_child_id = parent_dirty_id;
2030 if level == path_len - 1 {
2031 new_root_id_opt = Some(parent_dirty_id);
2032 }
2033 }
2034 }
2035
2036 if subtree_emptied {
2039 tsn_leaf_became_empty = true;
2040 } else if let Some(new_root) = new_root_id_opt
2041 && new_root != dirty_tsn_root_id
2042 {
2043 tsn_root_replaced = Some(new_root);
2044 }
2045 }
2046 _ => {
2047 return Err(DcbError::DatabaseCorrupted(
2048 "Expected TSN-subtree node".to_string(),
2049 ));
2050 }
2051 }
2052
2053 let dirty_page_id = { self.get_dirty_page_id(current_page_id)? };
2055 if dirty_page_id != current_page_id {
2056 replacement_info = Some((current_page_id, dirty_page_id));
2057 }
2058 let dirty_leaf_page = self.get_mut_dirty(dirty_page_id)?;
2059 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
2060 if dirty_leaf_node.keys.is_empty() || dirty_leaf_node.keys[0] != tsn {
2062 return Err(DcbError::DatabaseCorrupted(format!(
2063 "Expected TSN {} not found in dirty leaf: {:?}",
2064 tsn.0, dirty_leaf_node
2065 )));
2066 }
2067 if tsn_leaf_became_empty {
2068 dirty_leaf_node.keys.remove(0);
2070 dirty_leaf_node.values.remove(0);
2071 if verbose {
2072 println!("Removed {tsn:?} from {dirty_page_id:?}");
2073 }
2074 if dirty_leaf_node.keys.is_empty() {
2075 if verbose {
2076 println!("Empty leaf page {dirty_page_id:?}: {dirty_leaf_node:?}");
2077 }
2078 removal_info = Some(dirty_page_id);
2079 } else if verbose {
2080 println!("Leaf page not empty {dirty_page_id:?}: {dirty_leaf_node:?}");
2081 }
2082 } else if let Some(new_root) = tsn_root_replaced {
2083 dirty_leaf_node.values[0].root_id = new_root;
2085 } else if dirty_tsn_root_id != tsn_root_id {
2086 dirty_leaf_node.values[0].root_id = dirty_tsn_root_id;
2088 }
2089 } else {
2090 return Err(DcbError::DatabaseCorrupted(
2091 "Expected FreeListLeaf node".to_string(),
2092 ));
2093 }
2094 }
2095
2096 let mut current_replacement_info = replacement_info;
2098
2099 while let Some(parent_page_id) = stack.pop() {
2100 let dirty_page_id = { self.get_dirty_page_id(parent_page_id)? };
2102 let parent_replacement_info: Option<(PageID, PageID)> = {
2103 if dirty_page_id != parent_page_id {
2104 Some((parent_page_id, dirty_page_id))
2105 } else {
2106 None
2107 }
2108 };
2109 let dirty_internal_page = self.get_mut_dirty(dirty_page_id)?;
2111
2112 if let Some((old_id, new_id)) = current_replacement_info {
2113 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
2114 if dirty_internal_node.child_ids[0] == old_id {
2116 dirty_internal_node.child_ids[0] = new_id;
2117 if verbose {
2118 println!(
2119 "Replaced {old_id:?} with {new_id:?} in {dirty_page_id:?}: {dirty_internal_page:?}"
2120 );
2121 }
2122 } else {
2123 return Err(DcbError::DatabaseCorrupted("Child ID mismatch".to_string()));
2124 }
2125 } else {
2126 return Err(DcbError::DatabaseCorrupted(
2127 "Expected FreeListInternal node".to_string(),
2128 ));
2129 }
2130 }
2131 current_replacement_info = parent_replacement_info;
2132
2133 if let Some(removed_page_id) = removal_info {
2134 removed_page_ids.push(removed_page_id);
2135
2136 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
2137 if dirty_internal_node.child_ids[0] != removed_page_id {
2139 return Err(DcbError::DatabaseCorrupted("Child ID mismatch".to_string()));
2140 }
2141 if dirty_internal_node.keys.is_empty() {
2142 return Err(DcbError::DatabaseCorrupted(
2143 "Empty internal node keys".to_string(),
2144 ));
2145 }
2146 dirty_internal_node.child_ids.remove(0);
2147 dirty_internal_node.keys.remove(0);
2148 if verbose {
2149 println!(
2150 "Removed {removed_page_id:?} from {dirty_page_id:?}: {dirty_internal_node:?}"
2151 );
2152 }
2153
2154 if dirty_internal_node.keys.is_empty() {
2156 if verbose {
2157 println!(
2158 "Empty internal page {dirty_page_id:?}: {dirty_internal_node:?}"
2159 );
2160 }
2161 assert_eq!(dirty_internal_node.child_ids.len(), 1);
2162 let orphaned_child_id = dirty_internal_node.child_ids[0];
2163
2164 removed_page_ids.push(dirty_page_id);
2165
2166 if let Some((old_id, _)) = parent_replacement_info {
2167 current_replacement_info = Some((old_id, orphaned_child_id));
2168 } else {
2169 current_replacement_info = Some((dirty_page_id, orphaned_child_id));
2170 }
2171 }
2172 } else {
2173 return Err(DcbError::DatabaseCorrupted(
2174 "Expected FreeListInternal node".to_string(),
2175 ));
2176 }
2177
2178 removal_info = None;
2179 }
2180 }
2181
2182 for &removed_page_id in &removed_page_ids {
2183 self.append_freed_page_id(removed_page_id);
2184 }
2185
2186 if let Some((old_id, new_id)) = current_replacement_info {
2189 if self.free_lists_tree_root_id == old_id {
2190 self.append_freed_page_id(old_id);
2191 self.free_lists_tree_root_id = new_id;
2192 if verbose {
2193 println!("Replaced root {old_id:?} with {new_id:?}");
2194 }
2195 } else {
2196 return Err(DcbError::RootIDMismatch(old_id.0, new_id.0));
2197 }
2198 }
2199
2200 Ok(())
2201 }
2202}
2203
2204enum FreePageIDInsertStrategy {
2205 PushTsnOntoFreeListLeaf,
2206 PushPageIdOntoFreeListLeaf(usize),
2207 PushPageIdOntoExistingTsnSubtree,
2208 MoveTsnToNewTsnSubtree,
2209 SplitFreeListLeaf,
2210 CreateAndPromoteFreeListLeaf,
2211}
2212
2213pub struct Reader {
2215 pub header_page_id: PageID,
2216 pub tsn: Tsn,
2217 pub events_tree_root_id: PageID,
2218 pub tags_tree_root_id: PageID,
2219 pub next_position: Position,
2220 pub tracking_tree_root_id: PageID,
2221 reader_tsns: Arc<ReaderTsnRegistry>,
2222}
2223
2224impl Drop for Reader {
2225 fn drop(&mut self) {
2226 self.reader_tsns.unregister(self.tsn);
2228 }
2229}
2230
2231#[cfg(test)]
2232mod tests {
2233 use super::*;
2234 use crate::free_lists_tree_nodes::FreeListLeafValue;
2235 use serial_test::serial;
2236 use tempfile::tempdir;
2237
2238 static VERBOSE: bool = false;
2239
2240 #[test]
2241 #[serial]
2242 fn test_mvcc_init() {
2243 let temp_dir = tempdir().unwrap();
2244 let db_path = temp_dir.path().join("mvcc-test.db");
2245
2246 {
2247 let db = Mvcc::new(
2248 VERBOSE,
2249 StorageOptions::default()
2250 .db_path(db_path.clone())
2251 .page_size(4096),
2252 )
2253 .unwrap();
2254 assert!(db.pager.is_file_new);
2255 }
2256
2257 {
2258 let db = Mvcc::new(
2259 VERBOSE,
2260 StorageOptions::default()
2261 .db_path(db_path.clone())
2262 .page_size(4096),
2263 )
2264 .unwrap();
2265 assert!(!db.pager.is_file_new);
2266 }
2267 }
2268
2269 #[test]
2270 #[serial]
2271 fn test_write_transaction_incrementing_tsn_and_alternating_header() {
2272 let temp_dir = tempdir().unwrap();
2273 let db_path = temp_dir.path().join("mvcc-test.db");
2274 let db = Mvcc::new(
2275 VERBOSE,
2276 StorageOptions::default().db_path(db_path).page_size(4096),
2277 )
2278 .unwrap();
2279
2280 {
2281 let mut writer = db.writer().unwrap();
2282 assert_eq!(Tsn(1), writer.tsn);
2283 assert_eq!(PageID(0), writer.header_page_id);
2284 db.commit(&mut writer).unwrap();
2285 }
2286
2287 {
2288 let mut writer = db.writer().unwrap();
2289 assert_eq!(Tsn(2), writer.tsn);
2290 assert_eq!(PageID(1), writer.header_page_id);
2291 db.commit(&mut writer).unwrap();
2292 }
2293
2294 {
2295 let mut writer = db.writer().unwrap();
2296 assert_eq!(Tsn(3), writer.tsn);
2297 assert_eq!(PageID(0), writer.header_page_id);
2298 db.commit(&mut writer).unwrap();
2299 }
2300
2301 {
2302 let mut writer = db.writer().unwrap();
2303 assert_eq!(Tsn(4), writer.tsn);
2304 assert_eq!(PageID(1), writer.header_page_id);
2305 db.commit(&mut writer).unwrap();
2306 }
2307
2308 {
2309 let mut writer = db.writer().unwrap();
2310 assert_eq!(Tsn(5), writer.tsn);
2311 assert_eq!(PageID(0), writer.header_page_id);
2312 db.commit(&mut writer).unwrap();
2313 }
2314 }
2315
2316 #[test]
2317 #[serial]
2318 fn test_read_transaction_header_and_tsn() {
2319 let temp_dir = tempdir().unwrap();
2320 let db_path = temp_dir.path().join("mvcc-test.db");
2321 let db = Mvcc::new(
2322 VERBOSE,
2323 StorageOptions::default().db_path(db_path).page_size(4096),
2324 )
2325 .unwrap();
2326
2327 {
2329 assert_eq!(0, db.reader_tsns.live_reader_count());
2330 let reader = db.reader().unwrap();
2331 assert_eq!(1, db.reader_tsns.live_reader_count());
2332 assert_eq!(
2333 vec![Tsn(0)],
2334 db.reader_tsns.live_tsns_sorted()
2335 );
2336 assert_eq!(PageID(0), reader.header_page_id);
2337 assert_eq!(Tsn(0), reader.tsn);
2338 }
2339 assert_eq!(0, db.reader_tsns.live_reader_count());
2340
2341 {
2343 let reader1 = db.reader().unwrap();
2344 assert_eq!(
2345 vec![Tsn(0)],
2346 db.reader_tsns.live_tsns_sorted()
2347 );
2348 assert_eq!(PageID(0), reader1.header_page_id);
2349 assert_eq!(Tsn(0), reader1.tsn);
2350
2351 {
2352 let reader2 = db.reader().unwrap();
2353 assert_eq!(
2354 vec![Tsn(0), Tsn(0)],
2355 db.reader_tsns.live_tsns_sorted()
2356 );
2357 assert_eq!(PageID(0), reader2.header_page_id);
2358 assert_eq!(Tsn(0), reader2.tsn);
2359
2360 {
2361 let reader3 = db.reader().unwrap();
2362 assert_eq!(
2363 vec![Tsn(0), Tsn(0), Tsn(0)],
2364 db.reader_tsns.live_tsns_sorted()
2365 );
2366 assert_eq!(PageID(0), reader3.header_page_id);
2367 assert_eq!(Tsn(0), reader3.tsn);
2368 }
2369 }
2370 }
2371 assert_eq!(0, db.reader_tsns.live_reader_count());
2372
2373 {
2375 let mut writer = db.writer().unwrap();
2376 assert_eq!(0, db.reader_tsns.live_reader_count());
2377 assert_eq!(Tsn(1), writer.tsn);
2378 assert_eq!(PageID(0), writer.header_page_id);
2379 db.commit(&mut writer).unwrap();
2380 }
2381
2382 {
2384 let reader = db.reader().unwrap();
2385 assert_eq!(
2386 vec![Tsn(1)],
2387 db.reader_tsns.live_tsns_sorted()
2388 );
2389 assert_eq!(PageID(1), reader.header_page_id);
2390 assert_eq!(Tsn(1), reader.tsn);
2391 }
2392 }
2393
2394 #[test]
2395 #[serial]
2396 fn test_copy_on_write_page_reuse() {
2397 let temp_dir = tempdir().unwrap();
2398 let db_path = temp_dir.path().join("mvcc-test.db");
2399 let db = Mvcc::new(
2400 VERBOSE,
2401 StorageOptions::default().db_path(db_path).page_size(4096),
2402 )
2403 .unwrap();
2404 {
2406 let mut writer = db.writer().unwrap();
2407
2408 assert_eq!(0, writer.reusable_page_ids.len());
2410
2411 assert_eq!(PageID(2), writer.free_lists_tree_root_id);
2413
2414 assert_eq!(PageID(3), writer.events_tree_root_id);
2416
2417 let free_page_id = writer.alloc_page_id();
2419 assert_eq!(PageID(5), free_page_id);
2420 writer
2421 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2422 .unwrap();
2423
2424 assert_eq!(1, writer.dirty.len());
2426 assert_eq!(PageID(6), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2427
2428 assert_eq!(1, writer.freed_page_ids.len());
2430 assert_eq!(PageID(2), writer.freed_page_ids[0]);
2431
2432 db.commit(&mut writer).unwrap();
2433 }
2434
2435 {
2437 let mut writer = db.writer().unwrap();
2438
2439 assert_eq!(2, writer.reusable_page_ids.len());
2441 assert_eq!((PageID(5), Tsn(1)), writer.reusable_page_ids[0]);
2442 assert_eq!((PageID(2), Tsn(1)), writer.reusable_page_ids[1]);
2443
2444 assert_eq!(PageID(6), writer.free_lists_tree_root_id);
2446
2447 assert_eq!(PageID(3), writer.events_tree_root_id);
2449
2450 let free_page_id = writer.alloc_page_id();
2452 assert_eq!(PageID(5), free_page_id);
2453 writer
2454 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2455 .unwrap();
2456
2457 assert_eq!(1, writer.dirty.len());
2459 assert_eq!(PageID(2), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2460
2461 assert_eq!(1, writer.freed_page_ids.len());
2463 assert_eq!(PageID(6), writer.freed_page_ids[0]);
2464
2465 db.commit(&mut writer).unwrap();
2466 }
2467
2468 {
2470 let mut writer = db.writer().unwrap();
2471
2472 assert_eq!(2, writer.reusable_page_ids.len());
2474 assert_eq!((PageID(5), Tsn(2)), writer.reusable_page_ids[0]);
2475 assert_eq!((PageID(6), Tsn(2)), writer.reusable_page_ids[1]);
2476
2477 assert_eq!(PageID(2), writer.free_lists_tree_root_id);
2479
2480 assert_eq!(PageID(3), writer.events_tree_root_id);
2482
2483 let free_page_id = writer.alloc_page_id();
2485 assert_eq!(PageID(5), free_page_id);
2486 writer
2487 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2488 .unwrap();
2489
2490 assert_eq!(1, writer.dirty.len());
2492 assert_eq!(PageID(6), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2493
2494 assert_eq!(1, writer.freed_page_ids.len());
2496 assert_eq!(PageID(2), writer.freed_page_ids[0]);
2497
2498 db.commit(&mut writer).unwrap();
2499 }
2500 }
2501
2502 mod free_list_tree_tests {
2504 use super::*;
2505 use serial_test::serial;
2506 use tempfile::tempdir;
2507
2508 fn construct_mvcc(page_size: usize) -> (tempfile::TempDir, Mvcc) {
2510 let temp_dir = tempdir().unwrap();
2511 let db_path = temp_dir.path().join("mvcc-test.db");
2512 let db = Mvcc::new(
2513 VERBOSE,
2514 StorageOptions::default()
2515 .db_path(db_path)
2516 .page_size(page_size),
2517 )
2518 .unwrap();
2519 (temp_dir, db)
2520 }
2521
2522 #[test]
2523 #[serial]
2524 fn test_find_reusable_page_ids_empty_no_entries() {
2525 let (_temp_dir, db) = construct_mvcc(64);
2526 let writer = db.writer().unwrap();
2528 assert_eq!(0, writer.reusable_page_ids.len());
2529 }
2530
2531 #[test]
2532 #[serial]
2533 fn test_find_reusable_page_ids_leaf() {
2534 let (_temp_dir, db) = construct_mvcc(64);
2535 let mut writer = db.writer().unwrap();
2536
2537 let (tsn, free_pid1, free_pid2) = build_free_list_tree_leaf(&mut writer);
2538
2539 writer.find_reusable_page_ids(&db).unwrap();
2541 assert_eq!(2, writer.reusable_page_ids.len());
2542 assert_eq!((free_pid1, tsn), writer.reusable_page_ids[0]);
2543 assert_eq!((free_pid2, tsn), writer.reusable_page_ids[1]);
2544 }
2545
2546 #[test]
2547 #[serial]
2548 fn test_find_reusable_page_ids_internal_leaf() {
2549 let (_temp_dir, db) = construct_mvcc(128);
2550 let mut writer = db.writer().unwrap();
2551
2552 let (tsn1, tsn2, pid1, pid2, pid3, pid4) =
2553 build_free_list_tree_internal_leaf(&mut writer);
2554
2555 writer.find_reusable_page_ids(&db).unwrap();
2557 assert_eq!(4, writer.reusable_page_ids.len());
2559 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
2560 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
2561 assert_eq!((pid3, tsn2), writer.reusable_page_ids[2]);
2562 assert_eq!((pid4, tsn2), writer.reusable_page_ids[3]);
2563 }
2564
2565 #[test]
2566 #[serial]
2567 fn test_find_reusable_page_ids_internal_internal_leaf() {
2568 let (_temp_dir, db) = construct_mvcc(128);
2569 let mut writer = db.writer().unwrap();
2570
2571 let (tsn1, tsn2, tsn3, tsn4, pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8) =
2572 build_free_list_tree_internal_internal_leaf(&mut writer);
2573
2574 writer.find_reusable_page_ids(&db).unwrap();
2576 assert_eq!(8, writer.reusable_page_ids.len());
2578 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
2579 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
2580 assert_eq!((pid3, tsn2), writer.reusable_page_ids[2]);
2581 assert_eq!((pid4, tsn2), writer.reusable_page_ids[3]);
2582 assert_eq!((pid5, tsn3), writer.reusable_page_ids[4]);
2583 assert_eq!((pid6, tsn3), writer.reusable_page_ids[5]);
2584 assert_eq!((pid7, tsn4), writer.reusable_page_ids[6]);
2585 assert_eq!((pid8, tsn4), writer.reusable_page_ids[7]);
2586 }
2587
2588 #[test]
2589 #[serial]
2590 fn test_find_reusable_page_ids_leaf_tsn_subtree_leaf() {
2591 let (_temp_dir, db) = construct_mvcc(64);
2592 let mut writer = db.writer().unwrap();
2593
2594 let (pid1, pid2, tsn) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
2595
2596 writer.find_reusable_page_ids(&db).unwrap();
2598 assert_eq!(2, writer.reusable_page_ids.len());
2600 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2601 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2602 }
2603
2604 #[test]
2605 #[serial]
2606 fn test_find_reusable_page_ids_leaf_tsn_subtree_internal_leaf() {
2607 let (_temp_dir, db) = construct_mvcc(64);
2608 let mut writer = db.writer().unwrap();
2609
2610 let (pid1, pid2, pid3, pid4, tsn) =
2611 build_free_list_tree_leaf_tsn_subtree_internal_leaf(&mut writer);
2612
2613 writer.find_reusable_page_ids(&db).unwrap();
2615 assert_eq!(4, writer.reusable_page_ids.len());
2617 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2618 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2619 assert_eq!((pid3, tsn), writer.reusable_page_ids[2]);
2620 assert_eq!((pid4, tsn), writer.reusable_page_ids[3]);
2621 }
2622
2623 #[test]
2624 #[serial]
2625 fn test_find_reusable_page_ids_leaf_tsn_subtree_internal_internal_leaf() {
2626 let (_temp_dir, db) = construct_mvcc(64);
2627 let mut writer = db.writer().unwrap();
2628
2629 let (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn) =
2630 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut writer);
2631
2632 writer.find_reusable_page_ids(&db).unwrap();
2634 assert_eq!(8, writer.reusable_page_ids.len());
2636 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2637 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2638 assert_eq!((pid3, tsn), writer.reusable_page_ids[2]);
2639 assert_eq!((pid4, tsn), writer.reusable_page_ids[3]);
2640 assert_eq!((pid5, tsn), writer.reusable_page_ids[4]);
2641 assert_eq!((pid6, tsn), writer.reusable_page_ids[5]);
2642 assert_eq!((pid7, tsn), writer.reusable_page_ids[6]);
2643 assert_eq!((pid8, tsn), writer.reusable_page_ids[7]);
2644 }
2645
2646 fn build_free_list_tree_leaf(writer: &mut Writer) -> (Tsn, PageID, PageID) {
2647 let tsn = Tsn(123);
2649 let free_pid1 = writer.alloc_page_id();
2650 let free_pid2 = writer.alloc_page_id();
2651 let leaf = FreeListLeafNode {
2652 keys: vec![tsn],
2653 values: vec![FreeListLeafValue {
2654 page_ids: vec![free_pid1, free_pid2],
2655 root_id: PageID(0),
2656 }],
2657 };
2658 let root_id = writer.alloc_page_id();
2659 let page = Page::new(root_id, Node::FreeListLeaf(leaf));
2660 writer.insert_dirty(page).unwrap();
2661 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2662 writer.free_lists_tree_root_id = root_id;
2663 (tsn, free_pid1, free_pid2)
2664 }
2665
2666 fn build_free_list_tree_internal_leaf(
2667 writer: &mut Writer,
2668 ) -> (Tsn, Tsn, PageID, PageID, PageID, PageID) {
2669 let tsn1 = Tsn(10);
2671 let tsn2 = Tsn(20);
2672 let pid1 = writer.alloc_page_id();
2673 let pid2 = writer.alloc_page_id();
2674 let pid3 = writer.alloc_page_id();
2675 let pid4 = writer.alloc_page_id();
2676
2677 let leaf1_id = writer.alloc_page_id();
2678 let leaf2_id = writer.alloc_page_id();
2679 let leaf1 = FreeListLeafNode {
2680 keys: vec![tsn1],
2681 values: vec![FreeListLeafValue {
2682 page_ids: vec![pid1, pid2],
2683 root_id: PageID(0),
2684 }],
2685 };
2686 let leaf2 = FreeListLeafNode {
2687 keys: vec![tsn2],
2688 values: vec![FreeListLeafValue {
2689 page_ids: vec![pid3, pid4],
2690 root_id: PageID(0),
2691 }],
2692 };
2693 writer
2694 .insert_dirty(Page::new(leaf1_id, Node::FreeListLeaf(leaf1)))
2695 .unwrap();
2696 writer
2697 .insert_dirty(Page::new(leaf2_id, Node::FreeListLeaf(leaf2)))
2698 .unwrap();
2699
2700 let internal = FreeListInternalNode {
2702 keys: vec![tsn1],
2703 child_ids: vec![leaf1_id, leaf2_id],
2704 };
2705 let root_id = writer.alloc_page_id();
2706 writer
2707 .insert_dirty(Page::new(root_id, Node::FreeListInternal(internal)))
2708 .unwrap();
2709 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2710 writer.free_lists_tree_root_id = root_id;
2711 (tsn1, tsn2, pid1, pid2, pid3, pid4)
2712 }
2713
2714 fn build_free_list_tree_internal_internal_leaf(
2715 writer: &mut Writer,
2716 ) -> (
2717 Tsn,
2718 Tsn,
2719 Tsn,
2720 Tsn,
2721 PageID,
2722 PageID,
2723 PageID,
2724 PageID,
2725 PageID,
2726 PageID,
2727 PageID,
2728 PageID,
2729 ) {
2730 let tsn1 = Tsn(10);
2732 let tsn2 = Tsn(20);
2733 let tsn3 = Tsn(30);
2734 let tsn4 = Tsn(40);
2735 let pid1 = writer.alloc_page_id();
2736 let pid2 = writer.alloc_page_id();
2737 let pid3 = writer.alloc_page_id();
2738 let pid4 = writer.alloc_page_id();
2739 let pid5 = writer.alloc_page_id();
2740 let pid6 = writer.alloc_page_id();
2741 let pid7 = writer.alloc_page_id();
2742 let pid8 = writer.alloc_page_id();
2743
2744 let leaf1_id = writer.alloc_page_id();
2745 let leaf2_id = writer.alloc_page_id();
2746 let leaf3_id = writer.alloc_page_id();
2747 let leaf4_id = writer.alloc_page_id();
2748 let leaf1 = FreeListLeafNode {
2749 keys: vec![tsn1],
2750 values: vec![FreeListLeafValue {
2751 page_ids: vec![pid1, pid2],
2752 root_id: PageID(0),
2753 }],
2754 };
2755 let leaf2 = FreeListLeafNode {
2756 keys: vec![tsn2],
2757 values: vec![FreeListLeafValue {
2758 page_ids: vec![pid3, pid4],
2759 root_id: PageID(0),
2760 }],
2761 };
2762 let leaf3 = FreeListLeafNode {
2763 keys: vec![tsn3],
2764 values: vec![FreeListLeafValue {
2765 page_ids: vec![pid5, pid6],
2766 root_id: PageID(0),
2767 }],
2768 };
2769 let leaf4 = FreeListLeafNode {
2770 keys: vec![tsn4],
2771 values: vec![FreeListLeafValue {
2772 page_ids: vec![pid7, pid8],
2773 root_id: PageID(0),
2774 }],
2775 };
2776 writer
2777 .insert_dirty(Page::new(leaf1_id, Node::FreeListLeaf(leaf1)))
2778 .unwrap();
2779 writer
2780 .insert_dirty(Page::new(leaf2_id, Node::FreeListLeaf(leaf2)))
2781 .unwrap();
2782 writer
2783 .insert_dirty(Page::new(leaf3_id, Node::FreeListLeaf(leaf3)))
2784 .unwrap();
2785 writer
2786 .insert_dirty(Page::new(leaf4_id, Node::FreeListLeaf(leaf4)))
2787 .unwrap();
2788
2789 let internal1 = FreeListInternalNode {
2791 keys: vec![tsn2],
2792 child_ids: vec![leaf1_id, leaf2_id],
2793 };
2794 let internal1_id = writer.alloc_page_id();
2795 let internal2 = FreeListInternalNode {
2796 keys: vec![tsn4],
2797 child_ids: vec![leaf3_id, leaf4_id],
2798 };
2799 let internal2_id = writer.alloc_page_id();
2800 let internal3 = FreeListInternalNode {
2801 keys: vec![tsn3],
2802 child_ids: vec![internal1_id, internal2_id],
2803 };
2804 let internal3_id = writer.alloc_page_id();
2805 writer
2806 .insert_dirty(Page::new(internal1_id, Node::FreeListInternal(internal1)))
2807 .unwrap();
2808 writer
2809 .insert_dirty(Page::new(internal2_id, Node::FreeListInternal(internal2)))
2810 .unwrap();
2811 writer
2812 .insert_dirty(Page::new(internal3_id, Node::FreeListInternal(internal3)))
2813 .unwrap();
2814 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2815 writer.free_lists_tree_root_id = internal3_id;
2816 (
2817 tsn1, tsn2, tsn3, tsn4, pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8,
2818 )
2819 }
2820
2821 fn build_free_list_tree_leaf_tsn_subtree_leaf(
2822 writer: &mut Writer,
2823 ) -> (PageID, PageID, Tsn) {
2824 let tsn_sub_leaf_id = writer.alloc_page_id();
2826 let pid1 = writer.alloc_page_id();
2827 let pid2 = writer.alloc_page_id();
2828 let tsn_sub_leaf = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2829 page_ids: vec![pid1, pid2],
2830 };
2831 writer
2832 .insert_dirty(Page::new(
2833 tsn_sub_leaf_id,
2834 Node::FreeListTsnLeaf(tsn_sub_leaf),
2835 ))
2836 .unwrap();
2837
2838 let tsn = Tsn(33);
2839 let leaf = FreeListLeafNode {
2840 keys: vec![tsn],
2841 values: vec![FreeListLeafValue {
2842 page_ids: vec![],
2843 root_id: tsn_sub_leaf_id,
2844 }],
2845 };
2846 let root_id = writer.alloc_page_id();
2847 writer
2848 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2849 .unwrap();
2850 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2851 writer.free_lists_tree_root_id = root_id;
2852 (pid1, pid2, tsn)
2853 }
2854
2855 fn build_free_list_tree_leaf_tsn_subtree_internal_leaf(
2856 writer: &mut Writer,
2857 ) -> (PageID, PageID, PageID, PageID, Tsn) {
2858 let tsn_sub_leaf_id1 = writer.alloc_page_id();
2860 let pid1 = writer.alloc_page_id();
2861 let pid2 = writer.alloc_page_id();
2862 let tsn_sub_leaf1 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2863 page_ids: vec![pid1, pid2],
2864 };
2865 writer
2866 .insert_dirty(Page::new(
2867 tsn_sub_leaf_id1,
2868 Node::FreeListTsnLeaf(tsn_sub_leaf1),
2869 ))
2870 .unwrap();
2871
2872 let tsn_sub_leaf_id2 = writer.alloc_page_id();
2874 let pid3 = writer.alloc_page_id();
2875 let pid4 = writer.alloc_page_id();
2876 let tsn_sub_leaf2 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2877 page_ids: vec![pid3, pid4],
2878 };
2879 writer
2880 .insert_dirty(Page::new(
2881 tsn_sub_leaf_id2,
2882 Node::FreeListTsnLeaf(tsn_sub_leaf2),
2883 ))
2884 .unwrap();
2885
2886 let tsn_sub_internal_id = writer.alloc_page_id();
2888 let tsn_sub_internal = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2889 keys: vec![pid3],
2890 child_ids: vec![tsn_sub_leaf_id1, tsn_sub_leaf_id2],
2891 };
2892 writer
2893 .insert_dirty(Page::new(
2894 tsn_sub_internal_id,
2895 Node::FreeListTsnInternal(tsn_sub_internal),
2896 ))
2897 .unwrap();
2898
2899 let tsn = Tsn(33);
2901 let leaf = FreeListLeafNode {
2902 keys: vec![tsn],
2903 values: vec![FreeListLeafValue {
2904 page_ids: vec![],
2905 root_id: tsn_sub_internal_id,
2906 }],
2907 };
2908 let root_id = writer.alloc_page_id();
2909 writer
2910 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2911 .unwrap();
2912 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2913 writer.free_lists_tree_root_id = root_id;
2914 (pid1, pid2, pid3, pid4, tsn)
2915 }
2916
2917 fn build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(
2918 writer: &mut Writer,
2919 ) -> (
2920 PageID,
2921 PageID,
2922 PageID,
2923 PageID,
2924 PageID,
2925 PageID,
2926 PageID,
2927 PageID,
2928 Tsn,
2929 ) {
2930 let tsn_sub_leaf_id1 = writer.alloc_page_id();
2932 let pid1 = writer.alloc_page_id();
2933 let pid2 = writer.alloc_page_id();
2934 let tsn_sub_leaf1 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2935 page_ids: vec![pid1, pid2],
2936 };
2937 writer
2938 .insert_dirty(Page::new(
2939 tsn_sub_leaf_id1,
2940 Node::FreeListTsnLeaf(tsn_sub_leaf1),
2941 ))
2942 .unwrap();
2943
2944 let tsn_sub_leaf_id2 = writer.alloc_page_id();
2946 let pid3 = writer.alloc_page_id();
2947 let pid4 = writer.alloc_page_id();
2948 let tsn_sub_leaf2 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2949 page_ids: vec![pid3, pid4],
2950 };
2951 writer
2952 .insert_dirty(Page::new(
2953 tsn_sub_leaf_id2,
2954 Node::FreeListTsnLeaf(tsn_sub_leaf2),
2955 ))
2956 .unwrap();
2957
2958 let tsn_sub_internal_id1 = writer.alloc_page_id();
2960 let tsn_sub_internal1 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2961 keys: vec![pid3],
2962 child_ids: vec![tsn_sub_leaf_id1, tsn_sub_leaf_id2],
2963 };
2964 writer
2965 .insert_dirty(Page::new(
2966 tsn_sub_internal_id1,
2967 Node::FreeListTsnInternal(tsn_sub_internal1),
2968 ))
2969 .unwrap();
2970
2971 let tsn_sub_leaf_id3 = writer.alloc_page_id();
2973 let pid5 = writer.alloc_page_id();
2974 let pid6 = writer.alloc_page_id();
2975 let tsn_sub_leaf3 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2976 page_ids: vec![pid5, pid6],
2977 };
2978 writer
2979 .insert_dirty(Page::new(
2980 tsn_sub_leaf_id3,
2981 Node::FreeListTsnLeaf(tsn_sub_leaf3),
2982 ))
2983 .unwrap();
2984
2985 let tsn_sub_leaf_id4 = writer.alloc_page_id();
2987 let pid7 = writer.alloc_page_id();
2988 let pid8 = writer.alloc_page_id();
2989 let tsn_sub_leaf4 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2990 page_ids: vec![pid7, pid8],
2991 };
2992 writer
2993 .insert_dirty(Page::new(
2994 tsn_sub_leaf_id4,
2995 Node::FreeListTsnLeaf(tsn_sub_leaf4),
2996 ))
2997 .unwrap();
2998
2999 let tsn_sub_internal_id2 = writer.alloc_page_id();
3001 let tsn_sub_internal2 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
3002 keys: vec![pid7],
3003 child_ids: vec![tsn_sub_leaf_id3, tsn_sub_leaf_id4],
3004 };
3005 writer
3006 .insert_dirty(Page::new(
3007 tsn_sub_internal_id2,
3008 Node::FreeListTsnInternal(tsn_sub_internal2),
3009 ))
3010 .unwrap();
3011
3012 let tsn_sub_internal_id3 = writer.alloc_page_id();
3014 let tsn_sub_internal3 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
3015 keys: vec![pid5],
3016 child_ids: vec![tsn_sub_internal_id1, tsn_sub_internal_id2],
3017 };
3018 writer
3019 .insert_dirty(Page::new(
3020 tsn_sub_internal_id3,
3021 Node::FreeListTsnInternal(tsn_sub_internal3),
3022 ))
3023 .unwrap();
3024
3025 let tsn = writer.tsn;
3027 let leaf = FreeListLeafNode {
3028 keys: vec![tsn],
3029 values: vec![FreeListLeafValue {
3030 page_ids: vec![],
3031 root_id: tsn_sub_internal_id3,
3032 }],
3033 };
3034 let root_id = writer.alloc_page_id();
3035 writer
3036 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
3037 .unwrap();
3038 writer.append_freed_page_id(writer.free_lists_tree_root_id);
3039 writer.free_lists_tree_root_id = root_id;
3040 (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn)
3041 }
3042
3043 #[test]
3044 #[serial]
3045 fn test_insert_freed_page_id_to_empty_leaf_root() {
3046 let (_temp_dir, mut db) = construct_mvcc(64);
3047
3048 let header_page = db.get_latest_header_page().unwrap();
3050 let header_node = header_page.as_header_node().unwrap();
3051 let header_page_id = header_page.page_id;
3052 assert_eq!(PageID(0), header_page_id);
3053
3054 assert_eq!(PageID(5), header_node.next_page_id);
3056
3057 let tsn = Tsn(1001);
3059
3060 let mut writer = Writer::new(
3061 header_page_id,
3062 tsn,
3063 header_node.next_page_id,
3064 header_node.free_lists_tree_root_id,
3065 header_node.events_tree_root_id,
3066 header_node.tags_tree_root_id,
3067 header_node.tracking_root_page_id,
3068 header_node.next_position,
3069 VERBOSE,
3070 );
3071
3072 let initial_root_id = writer.free_lists_tree_root_id;
3074 assert_eq!(PageID(2), initial_root_id);
3075
3076 let page_id = writer.alloc_page_id();
3078
3079 assert_eq!(header_node.next_page_id, page_id);
3081
3082 let current_tsn = writer.tsn;
3084 writer
3085 .insert_freed_page_id(&mut db, current_tsn, page_id)
3086 .unwrap();
3087
3088 let expected_new_root_id = PageID(header_node.next_page_id.0 + 1);
3090 assert_eq!(expected_new_root_id, writer.free_lists_tree_root_id);
3091 assert_eq!(1, writer.dirty.len());
3092 assert!(writer.dirty.contains_key(&expected_new_root_id));
3093
3094 let new_root_page = writer.dirty.get(&expected_new_root_id).unwrap();
3095 assert_eq!(expected_new_root_id, new_root_page.page_id);
3096
3097 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3098 assert_eq!(vec![initial_root_id], freed_page_ids);
3099
3100 match &new_root_page.node {
3102 Node::FreeListLeaf(node) => {
3103 let expected_keys = vec![tsn];
3104 assert_eq!(expected_keys, node.keys);
3105
3106 let expected_values = vec![FreeListLeafValue {
3107 page_ids: vec![header_node.next_page_id],
3108 root_id: PageID(0),
3109 }];
3110 assert_eq!(expected_values, node.values);
3111 }
3112 _ => panic!("Expected FreeListLeaf node"),
3113 }
3114 }
3115
3116 #[test]
3117 #[serial]
3118 fn test_remove_freed_page_id_from_root_leaf_root() {
3119 let (_temp_dir, mut db) = construct_mvcc(64);
3120
3121 let mut writer;
3123 let inserted_tsn;
3124 let inserted_page_id;
3125 let previous_root_id;
3126
3127 {
3128 writer = db.writer().unwrap();
3130
3131 previous_root_id = writer.free_lists_tree_root_id;
3133
3134 inserted_page_id = writer.alloc_page_id();
3136
3137 inserted_tsn = writer.tsn;
3139 writer
3140 .insert_freed_page_id(&mut db, inserted_tsn, inserted_page_id)
3141 .unwrap();
3142
3143 db.commit(&mut writer).unwrap();
3145 }
3146
3147 {
3149 db.reader_tsns.register(Tsn(0));
3150 }
3151
3152 {
3154 writer = db.writer().unwrap();
3155
3156 assert_eq!(0, writer.reusable_page_ids.len());
3158
3159 let initial_root_id = writer.free_lists_tree_root_id;
3161 let next_page_id = writer.next_page_id;
3162
3163 writer
3165 .remove_free_page_id(&db, inserted_tsn, inserted_page_id)
3166 .unwrap();
3167
3168 let expected_new_root_id = next_page_id;
3170 assert_eq!(expected_new_root_id, writer.free_lists_tree_root_id);
3171 assert_eq!(1, writer.dirty.len());
3172 assert!(writer.dirty.contains_key(&expected_new_root_id));
3173
3174 let new_root_page = writer.dirty.get(&expected_new_root_id).unwrap();
3175 assert_eq!(expected_new_root_id, new_root_page.page_id);
3176
3177 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3178 assert_eq!(vec![initial_root_id], freed_page_ids);
3179
3180 match &new_root_page.node {
3182 Node::FreeListLeaf(node) => {
3183 let expected_keys = vec![inserted_tsn];
3184 assert_eq!(expected_keys, node.keys);
3185
3186 let expected_values = vec![FreeListLeafValue {
3187 page_ids: vec![previous_root_id],
3188 root_id: PageID(0),
3189 }];
3190 assert_eq!(expected_values, node.values);
3191 }
3192 _ => panic!("Expected FreeListLeaf node"),
3193 }
3194 }
3195 }
3196
3197 #[test]
3198 #[serial]
3199 fn test_insert_freed_page_ids_until_split_leaf() {
3200 let (_temp_dir, mut db) = construct_mvcc(64);
3201
3202 let header_page = db.get_latest_header_page().unwrap();
3204 let header_node = header_page.as_header_node().unwrap();
3205 let header_page_id = header_page.page_id;
3206
3207 let mut tsn = Tsn(100);
3209 let mut writer = Writer::new(
3210 header_page_id,
3211 Tsn(header_node.tsn.0 + 1),
3212 header_node.next_page_id,
3213 header_node.free_lists_tree_root_id,
3214 header_node.events_tree_root_id,
3215 header_node.tags_tree_root_id,
3216 header_node.tracking_root_page_id,
3217 header_node.next_position,
3218 VERBOSE,
3219 );
3220
3221 let mut has_split_leaf = false;
3222 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3223
3224 while !has_split_leaf {
3226 let page_id1 = writer.alloc_page_id();
3228 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3229 inserted.push((tsn, page_id1));
3230
3231 let page_id2 = writer.alloc_page_id();
3233 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3234 inserted.push((tsn, page_id2));
3235
3236 tsn = Tsn(tsn.0 + 1);
3238
3239 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3241 match &root_page.node {
3242 Node::FreeListInternal(_) => {
3243 has_split_leaf = true;
3244 }
3245 _ => {}
3246 }
3247 }
3248
3249 let mut copy_inserted = inserted.clone();
3251
3252 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3254 let root_node = match &root_page.node {
3255 Node::FreeListInternal(node) => node,
3256 _ => panic!("Expected FreeListInternal node"),
3257 };
3258
3259 let mut active_page_ids = vec![
3261 HEADER_PAGE_ID_0,
3262 HEADER_PAGE_ID_1,
3263 writer.free_lists_tree_root_id,
3264 writer.events_tree_root_id,
3265 writer.tags_tree_root_id,
3266 ];
3267
3268 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3270
3271 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3273 active_page_ids.push(child_id);
3274
3275 let child_page = writer.dirty.get(&child_id).unwrap();
3276 assert_eq!(child_id, child_page.page_id);
3277
3278 let child_node = match &child_page.node {
3279 Node::FreeListLeaf(node) => node,
3280 _ => panic!("Expected FreeListLeaf node"),
3281 };
3282
3283 if i > 0 {
3285 assert_eq!(root_node.keys[i - 1], child_node.keys[0]);
3286 }
3287
3288 for (k, &key) in child_node.keys.iter().enumerate() {
3290 for &value in &child_node.values[k].page_ids {
3291 let (inserted_tsn, inserted_page_id) = copy_inserted.remove(0);
3292 assert_eq!(inserted_tsn, key);
3293 assert_eq!(inserted_page_id, value);
3294 freed_page_ids.push(value);
3295 }
3296 }
3297 }
3298
3299 assert_eq!(7, active_page_ids.len());
3301
3302 let mut all_page_ids = active_page_ids.clone();
3304 all_page_ids.extend(freed_page_ids.clone());
3305 all_page_ids.sort();
3306 all_page_ids.dedup();
3307
3308 assert_eq!(
3309 all_page_ids.len(),
3310 active_page_ids.len() + freed_page_ids.len()
3311 - active_page_ids
3312 .iter()
3313 .filter(|id| freed_page_ids.contains(id))
3314 .count()
3315 );
3316
3317 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3319 assert_eq!(expected_page_ids, all_page_ids);
3320 }
3321
3322 #[test]
3323 #[serial]
3324 fn test_insert_freed_page_ids_until_replace_internal_node_child_id() {
3325 let (_temp_dir, db) = construct_mvcc(128);
3326
3327 {
3329 db.reader_tsns.register(Tsn(0));
3330 }
3331
3332 let mut has_split_leaf = false;
3333 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3334
3335 while !has_split_leaf {
3337 let mut writer = db.writer().unwrap();
3339 let page_id1 = writer.alloc_page_id();
3341 writer
3342 .insert_freed_page_id(&db, writer.tsn, page_id1)
3343 .unwrap();
3344 inserted.push((writer.tsn, page_id1));
3345
3346 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3348 match &root_page.node {
3349 Node::FreeListInternal(_) => {
3350 has_split_leaf = true;
3351 }
3352 _ => {}
3353 }
3354
3355 if !has_split_leaf {
3356 let page_id2 = writer.alloc_page_id();
3358 writer
3359 .insert_freed_page_id(&db, writer.tsn, page_id2)
3360 .unwrap();
3361 inserted.push((writer.tsn, page_id2));
3362
3363 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3365 match &root_page.node {
3366 Node::FreeListInternal(_) => {
3367 has_split_leaf = true;
3368 }
3369 _ => {}
3370 }
3371 }
3372
3373 db.commit(&mut writer).unwrap();
3374 }
3375
3376 let mut writer = db.writer().unwrap();
3377 let page_id3 = writer.alloc_page_id();
3378 writer
3379 .insert_freed_page_id(&db, writer.tsn, page_id3)
3380 .unwrap();
3381 db.commit(&mut writer).unwrap();
3382 inserted.push((writer.tsn, page_id3));
3383
3384 writer = db.writer().unwrap();
3385 let page_id4 = writer.alloc_page_id();
3386 writer
3387 .insert_freed_page_id(&db, writer.tsn, page_id4)
3388 .unwrap();
3389 db.commit(&mut writer).unwrap();
3390 inserted.push((writer.tsn, page_id4));
3391
3392 writer = db.writer().unwrap();
3394 let root_page = db.read_page(writer.free_lists_tree_root_id).unwrap();
3395 let root_node = match &root_page.node {
3396 Node::FreeListInternal(node) => node,
3397 _ => panic!("Expected FreeListInternal node"),
3398 };
3399
3400 let mut active_page_ids = vec![
3402 HEADER_PAGE_ID_0,
3403 HEADER_PAGE_ID_1,
3404 writer.free_lists_tree_root_id,
3405 writer.events_tree_root_id,
3406 writer.tags_tree_root_id,
3407 ];
3408
3409 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3411
3412 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3414 active_page_ids.push(child_id);
3415
3416 let child_page = db.read_page(child_id).unwrap();
3418 assert_eq!(child_id, child_page.page_id);
3419
3420 let child_node = match &child_page.node {
3421 Node::FreeListLeaf(node) => node,
3422 _ => panic!("Expected FreeListLeaf node"),
3423 };
3424
3425 if i > 0 {
3427 assert_eq!(root_node.keys[i - 1], child_node.keys[0]);
3428 }
3429
3430 for child_value in child_node.values.clone() {
3432 for page_id in child_value.page_ids {
3433 freed_page_ids.push(page_id);
3434 }
3435 }
3436 }
3437
3438 assert_eq!(8, active_page_ids.len());
3440
3441 let mut all_page_ids = active_page_ids.clone();
3443 all_page_ids.extend(freed_page_ids.clone());
3444 all_page_ids.sort();
3445 all_page_ids.dedup();
3446
3447 assert_eq!(
3448 all_page_ids.len(),
3449 active_page_ids.len() + freed_page_ids.len()
3450 - active_page_ids
3451 .iter()
3452 .filter(|id| freed_page_ids.contains(id))
3453 .count()
3454 );
3455
3456 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3458 assert_eq!(expected_page_ids, all_page_ids);
3459 }
3460
3461 #[test]
3462 #[serial]
3463 fn test_remove_freed_page_ids_from_split_leaf() {
3464 let (_temp_dir, mut db) = construct_mvcc(128);
3465
3466 if VERBOSE {
3468 println!("Inserting page IDs......");
3469 }
3470 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3471 let previous_root_id;
3472 let previous_writer_tsn;
3473
3474 {
3475 let mut writer = db.writer().unwrap();
3477
3478 previous_root_id = writer.free_lists_tree_root_id;
3480
3481 let mut has_split_leaf = false;
3483 let mut tsn = writer.tsn;
3484
3485 while !has_split_leaf {
3486 tsn = Tsn(tsn.0 + 1);
3488
3489 let page_id1 = writer.alloc_page_id();
3491 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3492 inserted.push((tsn, page_id1));
3493
3494 let page_id2 = writer.alloc_page_id();
3496 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3497 inserted.push((tsn, page_id2));
3498
3499 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3501 match &root_page.node {
3502 Node::FreeListInternal(_) => {
3503 has_split_leaf = true;
3504 }
3505 _ => {}
3506 }
3507 }
3508
3509 writer.tsn = Tsn(tsn.0 + 1);
3511 previous_writer_tsn = writer.tsn;
3512
3513 db.commit(&mut writer).unwrap();
3515 }
3516
3517 if VERBOSE {
3519 println!();
3520 println!("Removing all inserted page IDs......");
3521 }
3522
3523 {
3524 let header_page = db.get_latest_header_page().unwrap();
3526 let header_node = header_page.as_header_node().unwrap();
3527 let header_page_id = header_page.page_id;
3528
3529 let mut writer = Writer::new(
3531 header_page_id,
3532 Tsn(header_node.tsn.0 + 1),
3533 header_node.next_page_id,
3534 header_node.free_lists_tree_root_id,
3535 header_node.events_tree_root_id,
3536 header_node.tags_tree_root_id,
3537 header_node.tracking_root_page_id,
3538 header_node.next_position,
3539 VERBOSE,
3540 );
3541
3542 let old_root_id = writer.free_lists_tree_root_id;
3544
3545 for (tsn, page_id) in inserted.iter() {
3547 writer.remove_free_page_id(&db, *tsn, *page_id).unwrap();
3548 if VERBOSE {
3549 println!("Dirty pages: {:?}", writer.dirty.keys());
3550 }
3551 }
3552
3553 assert_ne!(old_root_id, writer.free_lists_tree_root_id);
3555
3556 assert_eq!(1, writer.dirty.len());
3557 assert!(writer.dirty.contains_key(&writer.free_lists_tree_root_id));
3558
3559 let new_root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3560 assert_eq!(writer.free_lists_tree_root_id, new_root_page.page_id);
3561
3562 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3564 assert!(freed_page_ids.contains(&old_root_id));
3565
3566 assert_eq!(5, writer.freed_page_ids.len());
3569
3570 match &new_root_page.node {
3572 Node::FreeListLeaf(node) => {
3573 let expected_keys = vec![previous_writer_tsn];
3574 assert_eq!(expected_keys, node.keys);
3575
3576 let expected_values = vec![FreeListLeafValue {
3577 page_ids: vec![previous_root_id],
3578 root_id: PageID(0),
3579 }];
3580 assert_eq!(expected_values, node.values);
3581 }
3582 _ => panic!("Expected FreeListLeaf node"),
3583 }
3584
3585 let active_page_ids = vec![
3587 HEADER_PAGE_ID_0,
3588 HEADER_PAGE_ID_1,
3589 writer.free_lists_tree_root_id,
3590 writer.events_tree_root_id,
3591 writer.tags_tree_root_id,
3592 ];
3593
3594 let mut all_freed_page_ids = freed_page_ids.clone();
3596
3597 match &new_root_page.node {
3599 Node::FreeListLeaf(node) => {
3600 for (_, value) in node.keys.iter().zip(node.values.iter()) {
3601 all_freed_page_ids.extend(value.page_ids.clone());
3602 }
3603 }
3604 _ => panic!("Expected FreeListLeaf node"),
3605 }
3606
3607 let inserted_page_ids: Vec<PageID> = inserted.iter().map(|(_, id)| *id).collect();
3609
3610 let mut all_page_ids = active_page_ids.clone();
3612 all_page_ids.extend(all_freed_page_ids.clone());
3613 all_page_ids.extend(inserted_page_ids.clone());
3614 all_page_ids.sort();
3615 all_page_ids.dedup();
3616
3617 let expected_page_ids: Vec<PageID> =
3619 (0..writer.next_page_id.0).map(PageID).collect();
3620 assert_eq!(expected_page_ids, all_page_ids);
3621 }
3622 }
3623
3624 #[test]
3625 #[serial]
3626 fn test_insert_freed_page_ids_until_split_internal() {
3627 let (_temp_dir, mut db) = construct_mvcc(64);
3628
3629 let header_page = db.get_latest_header_page().unwrap();
3631 let header_node = header_page.as_header_node().unwrap();
3632 let header_page_id = header_page.page_id;
3633
3634 let mut writer = Writer::new(
3636 header_page_id,
3637 Tsn(header_node.tsn.0 + 1),
3638 header_node.next_page_id,
3639 header_node.free_lists_tree_root_id,
3640 header_node.events_tree_root_id,
3641 header_node.tags_tree_root_id,
3642 header_node.tracking_root_page_id,
3643 header_node.next_position,
3644 VERBOSE,
3645 );
3646
3647 let mut tsn = Tsn(100);
3649 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3650 let mut has_split_internal = false;
3651
3652 while !has_split_internal {
3654 let page_id1 = writer.alloc_page_id();
3656 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3657 inserted.push((tsn, page_id1));
3658
3659 let page_id2 = writer.alloc_page_id();
3661 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3662 inserted.push((tsn, page_id2));
3663
3664 tsn = Tsn(tsn.0 + 1);
3666
3667 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3669 match &root_page.node {
3670 Node::FreeListInternal(root_node) => {
3671 if !root_node.child_ids.is_empty() {
3673 let child_id = root_node.child_ids[0];
3674 if let Some(child_page) = writer.dirty.get(&child_id) {
3675 match &child_page.node {
3676 Node::FreeListInternal(_) => {
3677 has_split_internal = true;
3678 }
3679 _ => {}
3680 }
3681 }
3682 }
3683 }
3684 _ => {}
3685 }
3686 if inserted.len() > 100 {
3687 panic!("Too many inserted page IDs");
3688 }
3689 }
3690
3691 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3693 let root_node = match &root_page.node {
3694 Node::FreeListInternal(node) => node,
3695 _ => panic!("Expected FreeListInternal node"),
3696 };
3697
3698 let mut active_page_ids = vec![
3700 HEADER_PAGE_ID_0,
3701 HEADER_PAGE_ID_1,
3702 writer.free_lists_tree_root_id,
3703 writer.events_tree_root_id,
3704 writer.tags_tree_root_id,
3705 ];
3706
3707 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3709
3710 let mut previous_child: Option<&FreeListInternalNode> = None;
3712
3713 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3715 active_page_ids.push(child_id);
3716
3717 let child_page = writer.dirty.get(&child_id).unwrap();
3718 assert_eq!(child_id, child_page.page_id);
3719
3720 let child_node = match &child_page.node {
3721 Node::FreeListInternal(node) => node,
3722 _ => panic!("Expected FreeListInternal node"),
3723 };
3724
3725 if i > 0 {
3727 assert!(root_node.keys[i - 1] < child_node.keys[0]);
3728
3729 if let Some(prev_child) = previous_child {
3731 assert!(root_node.keys[i - 1] > *prev_child.keys.last().unwrap());
3732 }
3733 }
3734
3735 previous_child = Some(child_node);
3736
3737 for (j, &grand_child_id) in child_node.child_ids.iter().enumerate() {
3739 active_page_ids.push(grand_child_id);
3740
3741 let grand_child_page = writer.dirty.get(&grand_child_id).unwrap();
3742 assert_eq!(grand_child_id, grand_child_page.page_id);
3743
3744 let grand_child_node = match &grand_child_page.node {
3745 Node::FreeListLeaf(node) => node,
3746 _ => panic!("Expected FreeListLeaf node"),
3747 };
3748
3749 if j > 0 {
3751 assert_eq!(child_node.keys[j - 1], grand_child_node.keys[0]);
3752 }
3753
3754 for (k, &key) in grand_child_node.keys.iter().enumerate() {
3756 for &value in &grand_child_node.values[k].page_ids {
3757 let pos = inserted.iter().position(|&(t, p)| t == key && p == value);
3759 if let Some(idx) = pos {
3760 inserted.remove(idx);
3761 }
3762 freed_page_ids.push(value);
3763 }
3764 }
3765 }
3766 }
3767
3768 assert!(inserted.is_empty());
3770
3771 assert_eq!(11, active_page_ids.len());
3773
3774 let mut all_page_ids = active_page_ids.clone();
3776 all_page_ids.extend(freed_page_ids.clone());
3777 all_page_ids.sort();
3778 all_page_ids.dedup();
3779
3780 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3782 assert_eq!(expected_page_ids, all_page_ids);
3783 }
3784
3785 #[test]
3786 #[serial]
3787 fn test_remove_freed_page_ids_from_split_internal() {
3788 let (_temp_dir, mut db) = construct_mvcc(64);
3789
3790 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3792 let previous_root_id;
3793 let previous_writer_tsn;
3794
3795 {
3796 let mut writer = db.writer().unwrap();
3798
3799 previous_root_id = writer.free_lists_tree_root_id;
3801
3802 let mut has_split_internal = false;
3804 let mut tsn = writer.tsn;
3805
3806 while !has_split_internal {
3807 tsn = Tsn(tsn.0 + 1);
3809 writer.tsn = tsn;
3810
3811 let page_id1 = writer.alloc_page_id();
3813 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3814 inserted.push((tsn, page_id1));
3815
3816 let page_id2 = writer.alloc_page_id();
3818 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3819 inserted.push((tsn, page_id2));
3820
3821 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3823 match &root_page.node {
3824 Node::FreeListInternal(root_node) => {
3825 if !root_node.child_ids.is_empty() {
3827 let child_id = root_node.child_ids[0];
3828 if let Some(child_page) = writer.dirty.get(&child_id) {
3829 match &child_page.node {
3830 Node::FreeListInternal(_) => {
3831 has_split_internal = true;
3832 }
3833 _ => {}
3834 }
3835 }
3836 }
3837 }
3838 _ => {}
3839 }
3840 }
3841
3842 previous_writer_tsn = writer.tsn;
3844
3845 db.commit(&mut writer).unwrap();
3847 }
3848
3849 {
3851 let header_page = db.get_latest_header_page().unwrap();
3853 let header_node = header_page.as_header_node().unwrap();
3854 let header_page_id = header_page.page_id;
3855
3856 let mut writer = Writer::new(
3858 header_page_id,
3859 Tsn(header_node.tsn.0 + 1),
3860 header_node.next_page_id,
3861 header_node.free_lists_tree_root_id,
3862 header_node.events_tree_root_id,
3863 header_node.tags_tree_root_id,
3864 header_node.tracking_root_page_id,
3865 header_node.next_position,
3866 VERBOSE,
3867 );
3868
3869 let old_root_id = writer.free_lists_tree_root_id;
3871
3872 for (tsn, page_id) in inserted.iter() {
3874 writer.remove_free_page_id(&db, *tsn, *page_id).unwrap();
3875 }
3876
3877 assert_ne!(old_root_id, writer.free_lists_tree_root_id);
3879 assert_eq!(1, writer.dirty.len());
3880 assert!(writer.dirty.contains_key(&writer.free_lists_tree_root_id));
3881
3882 let new_root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3883 assert_eq!(writer.free_lists_tree_root_id, new_root_page.page_id);
3884
3885 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3887 assert!(freed_page_ids.contains(&old_root_id));
3888
3889 assert_eq!(13, writer.freed_page_ids.len());
3892
3893 match &new_root_page.node {
3895 Node::FreeListLeaf(node) => {
3896 let expected_keys = vec![previous_writer_tsn];
3897 assert_eq!(expected_keys, node.keys);
3898
3899 let expected_values = vec![FreeListLeafValue {
3900 page_ids: vec![previous_root_id],
3901 root_id: PageID(0),
3902 }];
3903 assert_eq!(expected_values, node.values);
3904 }
3905 _ => panic!("Expected FreeListLeaf node"),
3906 }
3907
3908 let active_page_ids = vec![
3910 HEADER_PAGE_ID_0,
3911 HEADER_PAGE_ID_1,
3912 writer.free_lists_tree_root_id,
3913 writer.events_tree_root_id,
3914 writer.tags_tree_root_id,
3915 ];
3916
3917 let mut all_freed_page_ids = freed_page_ids.clone();
3919
3920 match &new_root_page.node {
3922 Node::FreeListLeaf(node) => {
3923 for (_, value) in node.keys.iter().zip(node.values.iter()) {
3924 all_freed_page_ids.extend(value.page_ids.clone());
3925 }
3926 }
3927 _ => panic!("Expected FreeListLeaf node"),
3928 }
3929
3930 let inserted_page_ids: Vec<PageID> = inserted.iter().map(|(_, id)| *id).collect();
3932
3933 let mut all_page_ids = active_page_ids.clone();
3935 all_page_ids.extend(all_freed_page_ids.clone());
3936 all_page_ids.extend(inserted_page_ids.clone());
3937 all_page_ids.sort();
3938 all_page_ids.dedup();
3939
3940 let expected_page_ids: Vec<PageID> =
3942 (0..writer.next_page_id.0).map(PageID).collect();
3943 assert_eq!(expected_page_ids, all_page_ids);
3944 }
3945 }
3946
3947 #[test]
3948 #[serial]
3949 fn test_remove_freed_page_ids_until_replace_old_id_with_orphaned_child_id() {
3950 let (_temp_dir, mut db) = construct_mvcc(128);
3961
3962 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3964
3965 {
3966 let mut writer = db.writer().unwrap();
3968
3969 let mut has_split_internal = false;
3971 let mut tsn = writer.tsn;
3972
3973 while !has_split_internal {
3974 tsn = Tsn(tsn.0 + 1);
3976 writer.tsn = tsn;
3977
3978 let page_id1 = writer.alloc_page_id();
3980 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3981 inserted.push((tsn, page_id1));
3982
3983 let page_id2 = writer.alloc_page_id();
3985 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3986 inserted.push((tsn, page_id2));
3987
3988 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3990 match &root_page.node {
3991 Node::FreeListInternal(root_node) => {
3992 if !root_node.child_ids.is_empty() {
3994 let child_id = root_node.child_ids[0];
3995 if let Some(child_page) = writer.dirty.get(&child_id) {
3996 match &child_page.node {
3997 Node::FreeListInternal(_) => {
3998 has_split_internal = true;
3999 }
4000 _ => {}
4001 }
4002 }
4003 }
4004 }
4005 _ => {}
4006 }
4007 }
4008
4009 db.commit(&mut writer).unwrap();
4011 }
4012
4013 db.reader_tsns.unregister(Tsn(0));
4015 let writer = db.writer().unwrap();
4016 let reusable_page_ids = writer.reusable_page_ids.clone();
4017
4018 db.reader_tsns.register(Tsn(0));
4020
4021 for (page_id, tsn) in reusable_page_ids {
4023 let mut writer = db.writer().unwrap();
4024 writer.remove_free_page_id(&db, tsn, page_id).unwrap();
4025 db.commit(&mut writer).unwrap();
4026 }
4027 }
4028
4029 #[test]
4030 #[serial]
4031 fn test_insert_freed_page_ids_overflow_single_key_moves_to_tsn_subtree() {
4032 let page_size = 64;
4034 let (_temp_dir, mut mvcc) = construct_mvcc(page_size);
4035
4036 let mut writer = mvcc.writer().unwrap();
4038 let tsn = writer.tsn;
4039
4040 let mut inserted_count: usize = 0;
4043 loop {
4044 let pid = writer.alloc_page_id();
4045 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
4046 inserted_count += 1;
4047 let dirty_page_id = {
4048 let mut keys = writer.dirty.keys();
4049 assert_eq!(keys.len(), 1);
4050 *keys.next().unwrap()
4051 };
4052 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
4053 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
4054 if !leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
4055 break;
4056 }
4057 } else {
4058 panic!("Expected leaf node")
4059 }
4060 }
4061
4062 let pid5 = writer.alloc_page_id();
4064 writer.insert_freed_page_id(&mut mvcc, tsn, pid5).unwrap();
4065 inserted_count += 1;
4066
4067 let pid6 = writer.alloc_page_id();
4069 writer.insert_freed_page_id(&mut mvcc, tsn, pid6).unwrap();
4070 inserted_count += 1;
4071
4072 let pid7 = writer.alloc_page_id();
4073 writer.insert_freed_page_id(&mut mvcc, tsn, pid7).unwrap();
4074 inserted_count += 1;
4075
4076 let dirty_ids: Vec<PageID> = { writer.dirty.keys().cloned().collect() };
4078 let mut tsn_root_id = PageID(0);
4079 for dirty_page_id in dirty_ids {
4080 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
4081 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
4082 assert_eq!(1, leaf_node.keys.len());
4083 assert_eq!(tsn, leaf_node.keys[0]);
4084 let val = &leaf_node.values[0];
4085 assert_eq!(0, val.page_ids.len());
4086 assert_ne!(PageID(0), val.root_id);
4087 tsn_root_id = val.root_id;
4088 break;
4089 }
4090 }
4091 assert_ne!(PageID(0), tsn_root_id);
4092 let tsn_root_page = writer.get_page_ref(&mvcc, tsn_root_id).unwrap();
4095 match &tsn_root_page.node {
4096 Node::FreeListTsnInternal(internal) => {
4097 assert_eq!(2, internal.child_ids.len());
4098 assert_eq!(1, internal.keys.len());
4099 }
4100 other => panic!(
4101 "Expected TSN-subtree internal node, got {:?}",
4102 other.type_name()
4103 ),
4104 }
4105
4106 let mut extra_inserts = 0usize;
4108 let mut guard = 0usize;
4109 loop {
4110 guard += 1;
4111 assert!(
4112 guard < 200,
4113 "guard hit while waiting for TSN-subtree internal split"
4114 );
4115 let pid = writer.alloc_page_id();
4116 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
4117 extra_inserts += 1;
4118
4119 let dirty_ids: Vec<PageID> = writer.dirty.keys().cloned().collect();
4121 tsn_root_id = PageID(0);
4122 for dirty_page_id in dirty_ids {
4123 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
4124 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
4125 assert_eq!(1, leaf_node.keys.len());
4126 assert_eq!(tsn, leaf_node.keys[0]);
4127 let val = &leaf_node.values[0];
4128 assert_eq!(0, val.page_ids.len());
4129 assert_ne!(PageID(0), val.root_id);
4130 tsn_root_id = val.root_id;
4131 break;
4132 }
4133 }
4134 assert_ne!(PageID(0), tsn_root_id);
4135
4136 let root_node_owned = {
4137 writer
4138 .get_page_ref(&mvcc, tsn_root_id)
4139 .unwrap()
4140 .node
4141 .clone()
4142 };
4143 match root_node_owned {
4144 Node::FreeListTsnInternal(internal_root) => {
4145 let first_child_id = internal_root.child_ids[0];
4147 let first_child_node = {
4148 writer
4149 .get_page_ref(&mvcc, first_child_id)
4150 .unwrap()
4151 .node
4152 .clone()
4153 };
4154 if matches!(first_child_node, Node::FreeListTsnInternal(_)) {
4155 assert_eq!(1, internal_root.keys.len());
4158 assert_eq!(2, internal_root.child_ids.len());
4159 break;
4160 }
4161 }
4162 other => panic!(
4163 "Expected TSN-subtree internal node, got {:?}",
4164 other.type_name()
4165 ),
4166 }
4167 }
4168
4169 let pid = writer.alloc_page_id();
4171 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
4172 extra_inserts += 1;
4173
4174 writer.find_reusable_page_ids(&mvcc).unwrap();
4176 assert_eq!(
4177 inserted_count + extra_inserts,
4178 writer.reusable_page_ids.len()
4179 );
4180 for &(_pid, _tsn) in writer.reusable_page_ids.iter() {
4181 assert_eq!(tsn, _tsn);
4182 }
4183 }
4184
4185 #[test]
4186 #[serial]
4187 fn test_remove_freed_page_id_from_tsn_subtree_leaf_pid1() {
4188 let (_temp_dir, db) = construct_mvcc(128);
4189 let mut writer = db.writer().unwrap();
4190
4191 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4192
4193 writer.find_reusable_page_ids(&db).unwrap();
4194 assert_eq!(2, writer.reusable_page_ids.len());
4195 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
4196 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
4197
4198 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4199 writer.find_reusable_page_ids(&db).unwrap();
4200 assert_eq!(1, writer.reusable_page_ids.len());
4201 assert_eq!((pid2, tsn1), writer.reusable_page_ids[0]);
4202 }
4203
4204 #[test]
4205 #[serial]
4206 fn test_remove_freed_page_id_from_tsn_subtree_leaf_pid2() {
4207 let (_temp_dir, db) = construct_mvcc(128);
4208 let mut writer = db.writer().unwrap();
4209
4210 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4211
4212 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4213 writer.find_reusable_page_ids(&db).unwrap();
4214 assert_eq!(1, writer.reusable_page_ids.len());
4215 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
4216 }
4217
4218 #[test]
4219 #[serial]
4220 fn test_tsn_subtree_random_order_insert_and_ordering() {
4221 let page_size = 256;
4223 let (_temp_dir, mut db) = construct_mvcc(page_size);
4224 let mut writer = db.writer().unwrap();
4225 let tsn = writer.tsn;
4226
4227 let n = 100usize;
4229 let mut pids: Vec<PageID> = Vec::with_capacity(n);
4230 for _ in 0..n {
4231 pids.push(writer.alloc_page_id());
4232 }
4233 for i in 0..pids.len() {
4235 let j = (i * 37 + 13) % pids.len();
4236 pids.swap(i, j);
4237 }
4238 for pid in &pids {
4240 writer.insert_freed_page_id(&mut db, tsn, *pid).unwrap();
4241 }
4242 let dup = pids[0];
4244 writer.insert_freed_page_id(&mut db, tsn, dup).unwrap();
4245
4246 writer.find_reusable_page_ids(&db).unwrap();
4248 assert_eq!(n, writer.reusable_page_ids.len());
4249 for &(_pid, _tsn) in writer.reusable_page_ids.iter() {
4250 assert_eq!(tsn, _tsn);
4251 }
4252
4253 let mut tsn_root_id = PageID(0);
4256 for page_id in writer.dirty.keys().cloned().collect::<Vec<_>>() {
4257 let page = writer.get_mut_dirty(page_id).unwrap();
4258 if let Node::FreeListLeaf(leaf_node) = &page.node {
4259 if !leaf_node.keys.is_empty() && leaf_node.keys[0] == tsn {
4260 tsn_root_id = leaf_node.values[0].root_id;
4261 break;
4262 }
4263 }
4264 }
4265 assert_ne!(PageID(0), tsn_root_id);
4266 let root_node_owned = { writer.get_page_ref(&db, tsn_root_id).unwrap().node.clone() };
4267 match root_node_owned {
4268 Node::FreeListTsnInternal(internal_root) => {
4269 let first_child_id = internal_root.child_ids[0];
4271 let first_child_node = {
4272 writer
4273 .get_page_ref(&db, first_child_id)
4274 .unwrap()
4275 .node
4276 .clone()
4277 };
4278 if let Node::FreeListTsnInternal(_in2) = first_child_node {
4279 }
4281 }
4282 Node::FreeListTsnLeaf(_) => {
4283 }
4285 other => panic!("Unexpected node type: {}", other.type_name()),
4286 }
4287 }
4288
4289 #[test]
4290 #[serial]
4291 fn test_upgrade_inline_to_tsn_subtree_with_random_inserts_and_duplicates() {
4292 let page_size = 96;
4294 let (_temp_dir, mut mvcc) = construct_mvcc(page_size);
4295 let mut writer = mvcc.writer().unwrap();
4296 let tsn = writer.tsn;
4297
4298 let mut inline_ids = Vec::new();
4300 loop {
4301 let pid = writer.alloc_page_id();
4302 let res = writer.insert_freed_page_id(&mut mvcc, tsn, pid);
4303 if res.is_err() {
4304 panic!("unexpected error inserting into inline");
4305 }
4306 inline_ids.push(pid);
4307 let dirty_id = writer.dirty.keys().cloned().next().unwrap();
4309 if let Node::FreeListLeaf(leaf_node) = &writer.get_mut_dirty(dirty_id).unwrap().node
4310 {
4311 if !leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
4312 break;
4313 }
4314 }
4315 }
4316
4317 let mut extra_ids = Vec::new();
4319 for _ in 0..30 {
4320 extra_ids.push(writer.alloc_page_id());
4321 }
4322 for i in 0..extra_ids.len() {
4324 let j = (i * 29 + 7) % extra_ids.len();
4325 extra_ids.swap(i, j);
4326 }
4327 if !inline_ids.is_empty() {
4329 extra_ids.push(inline_ids[0]);
4330 }
4331 if inline_ids.len() > 1 {
4332 extra_ids.push(inline_ids[1]);
4333 }
4334
4335 for pid in &extra_ids {
4336 writer.insert_freed_page_id(&mut mvcc, tsn, *pid).unwrap();
4337 }
4338
4339 writer.find_reusable_page_ids(&mvcc).unwrap();
4341 let mut expected: Vec<PageID> = inline_ids.clone();
4342 for p in extra_ids {
4343 if !expected.contains(&p) {
4344 expected.push(p);
4345 }
4346 }
4347 expected.sort_by_key(|p| p.0);
4348 expected.dedup();
4349 let mut actual: Vec<PageID> =
4350 writer.reusable_page_ids.iter().map(|(p, _)| *p).collect();
4351 actual.sort_by_key(|p| p.0);
4352 assert_eq!(expected, actual);
4353 }
4354
4355 #[test]
4356 #[serial]
4357 fn test_remove_freed_page_id_from_tsn_subtree_leaf_all() {
4358 let (_temp_dir, db) = construct_mvcc(128);
4359 let mut writer = db.writer().unwrap();
4360
4361 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4362
4363 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4364 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4365 writer.find_reusable_page_ids(&db).unwrap();
4366 assert_eq!(0, writer.reusable_page_ids.len());
4367
4368 assert_eq!(2, writer.freed_page_ids.len());
4369 }
4370
4371 #[test]
4372 #[serial]
4373 fn test_remove_freed_page_id_from_tsn_subtree_internal_leaf_pid1() {
4374 let (_temp_dir, db) = construct_mvcc(128);
4375 let mut writer = db.writer().unwrap();
4376
4377 let (pid1, pid2, pid3, pid4, tsn1) =
4378 build_free_list_tree_leaf_tsn_subtree_internal_leaf(&mut writer);
4379
4380 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4381 writer.find_reusable_page_ids(&db).unwrap();
4382 assert_eq!(3, writer.reusable_page_ids.len());
4383 assert_eq!((pid2, tsn1), writer.reusable_page_ids[0]);
4384 assert_eq!((pid3, tsn1), writer.reusable_page_ids[1]);
4385 assert_eq!((pid4, tsn1), writer.reusable_page_ids[2]);
4386
4387 assert_eq!(1, writer.freed_page_ids.len());
4388
4389 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4390 writer.find_reusable_page_ids(&db).unwrap();
4391 assert_eq!(2, writer.reusable_page_ids.len());
4392 assert_eq!((pid3, tsn1), writer.reusable_page_ids[0]);
4393 assert_eq!((pid4, tsn1), writer.reusable_page_ids[1]);
4394
4395 assert_eq!(3, writer.freed_page_ids.len());
4396
4397 writer.remove_free_page_id(&db, tsn1, pid3).unwrap();
4398 writer.find_reusable_page_ids(&db).unwrap();
4399 assert_eq!(1, writer.reusable_page_ids.len());
4400 assert_eq!((pid4, tsn1), writer.reusable_page_ids[0]);
4401
4402 assert_eq!(3, writer.freed_page_ids.len());
4403
4404 writer.remove_free_page_id(&db, tsn1, pid4).unwrap();
4405 writer.find_reusable_page_ids(&db).unwrap();
4406 assert_eq!(0, writer.reusable_page_ids.len());
4407
4408 assert_eq!(4, writer.freed_page_ids.len());
4409 }
4410
4411 #[test]
4412 #[serial]
4413 fn test_remove_freed_page_id_from_tsn_subtree_internal_internal_leaf_pid1() {
4414 let (_temp_dir, db) = construct_mvcc(128);
4415 let mut writer = db.writer().unwrap();
4416
4417 let (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn) =
4418 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut writer);
4419
4420 writer.remove_free_page_id(&db, tsn, pid1).unwrap();
4421 writer.find_reusable_page_ids(&db).unwrap();
4422 assert_eq!(7, writer.reusable_page_ids.len());
4423 assert_eq!((pid2, tsn), writer.reusable_page_ids[0]);
4424 assert_eq!((pid3, tsn), writer.reusable_page_ids[1]);
4425 assert_eq!((pid4, tsn), writer.reusable_page_ids[2]);
4426 assert_eq!((pid5, tsn), writer.reusable_page_ids[3]);
4427 assert_eq!((pid6, tsn), writer.reusable_page_ids[4]);
4428 assert_eq!((pid7, tsn), writer.reusable_page_ids[5]);
4429 assert_eq!((pid8, tsn), writer.reusable_page_ids[6]);
4430
4431 assert_eq!(1, writer.freed_page_ids.len());
4432 }
4433
4434 #[test]
4435 #[serial]
4436 fn test_remove_freed_page_id_cow_does_not_leak_ids_in_tsn_subtree() {
4437 let (_temp_dir, db) = construct_mvcc(128);
4440
4441 let w1_tsn = {
4443 let mut w1 = db.writer().unwrap();
4444 let (_pid1, _pid2, _pid3, _pid4, _pid5, _pid6, _pid7, _pid8, tsn) =
4445 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut w1);
4446 db.commit(&mut w1).unwrap();
4448 tsn
4450 };
4451
4452 let mut w2 = db.writer().unwrap();
4454 w2.find_reusable_page_ids(&db).unwrap();
4456 assert!(w2.reusable_page_ids.len() >= 1);
4457 let (remove_pid, remove_tsn) = w2.reusable_page_ids[0];
4458 assert_eq!(remove_tsn, w1_tsn); w2.remove_free_page_id(&db, remove_tsn, remove_pid).unwrap();
4460
4461 let mut active: Vec<PageID> = Vec::new();
4464 let mut freed_tree: Vec<PageID> = Vec::new();
4465 active.push(HEADER_PAGE_ID_0);
4467 active.push(HEADER_PAGE_ID_1);
4468 active.push(w2.free_lists_tree_root_id);
4469 active.push(w2.events_tree_root_id);
4470 active.push(w2.tags_tree_root_id);
4471
4472 let mut stack: Vec<PageID> = vec![w2.free_lists_tree_root_id];
4474 while let Some(pid) = stack.pop() {
4475 if active.contains(&pid) == false {
4476 active.push(pid);
4477 }
4478 let node_owned = { w2.get_page_ref(&db, pid).unwrap().node.clone() };
4479 match node_owned {
4480 Node::FreeListInternal(internal) => {
4481 for child in internal.child_ids {
4482 stack.push(child);
4483 }
4484 }
4485 Node::FreeListLeaf(leaf) => {
4486 for val in &leaf.values {
4488 for &p in &val.page_ids {
4489 freed_tree.push(p);
4490 }
4491 }
4492 for val in leaf.values {
4494 if val.root_id != PageID(0) {
4495 let mut tsn_stack: Vec<PageID> = vec![val.root_id];
4496 while let Some(tid) = tsn_stack.pop() {
4497 if active.contains(&tid) == false {
4498 active.push(tid);
4499 }
4500 let tnode = { w2.get_page_ref(&db, tid).unwrap().node.clone() };
4501 match tnode {
4502 Node::FreeListTsnInternal(tint) => {
4503 for c in tint.child_ids {
4504 tsn_stack.push(c);
4505 }
4506 }
4507 Node::FreeListTsnLeaf(tleaf) => {
4508 for &p in &tleaf.page_ids {
4509 freed_tree.push(p);
4510 }
4511 }
4512 _ => panic!("Unexpected node type in TSN-subtree"),
4513 }
4514 }
4515 }
4516 }
4517 }
4518 _ => panic!("Unexpected node type in free list tree"),
4519 }
4520 }
4521
4522 for (&pid, _page) in w2.dirty.iter() {
4524 if !active.contains(&pid) {
4525 active.push(pid);
4526 }
4527 }
4528 for (&pid, _page) in w2.deserialized.iter() {
4530 if !active.contains(&pid) {
4531 active.push(pid);
4532 }
4533 }
4534
4535 let mut freed: Vec<PageID> = w2.freed_page_ids.iter().cloned().collect();
4537 freed.extend(freed_tree);
4539 for (pid, _tsn) in w2.reusable_page_ids.iter() {
4541 freed.push(*pid);
4542 }
4543
4544 active.sort_by_key(|p| p.0);
4546 active.dedup();
4547 freed.sort_by_key(|p| p.0);
4548 freed.dedup();
4549 let mut union = active.clone();
4550 for id in &freed {
4551 if !union.contains(id) {
4552 union.push(*id);
4553 }
4554 }
4555 union.sort_by_key(|p| p.0);
4556
4557 let expected: Vec<PageID> = (0..w2.next_page_id.0).map(PageID).collect();
4558 if expected != union {
4559 let mut missing: Vec<PageID> = expected
4560 .iter()
4561 .cloned()
4562 .filter(|p| !union.contains(p))
4563 .collect();
4564 let mut unexpected: Vec<PageID> = union
4565 .iter()
4566 .cloned()
4567 .filter(|p| !expected.contains(p))
4568 .collect();
4569 missing.sort_by_key(|p| p.0);
4570 unexpected.sort_by_key(|p| p.0);
4571 panic!(
4572 "Missing: {:?} Unexpected: {:?}\nactive: {:?}\nfreed: {:?}",
4573 missing, unexpected, active, freed
4574 );
4575 }
4576 assert_eq!(
4577 expected, union,
4578 "All page IDs must be accounted for (active or freed)"
4579 );
4580 }
4581 }
4582
4583 #[cfg(test)]
4584 mod schema_version_tests {
4585 use super::*;
4586 use serial_test::serial;
4587 use tempfile::tempdir;
4588
4589 #[test]
4590 #[serial]
4591 fn opening_db_with_newer_schema_version_errors() {
4592 let temp_dir = tempdir().unwrap();
4594 let db_path = temp_dir.path().join("schema-guard.db");
4595 let page_size = 64usize; let verbose = false;
4597
4598 let mvcc = Mvcc::new(
4599 verbose,
4600 StorageOptions::default()
4601 .db_path(db_path.clone())
4602 .page_size(page_size),
4603 )
4604 .expect("must create new db");
4605
4606 let h0 = mvcc.read_page(HEADER_PAGE_ID_0).expect("read header 0");
4608 let mut h0 = match &h0.node {
4609 Node::Header(node) => node.clone(),
4610 _ => panic!("Page 0 is not a header"),
4611 };
4612 h0.schema_version = DB_SCHEMA_VERSION + 1;
4613
4614 let page = Page::new(HEADER_PAGE_ID_0, Node::Header(h0));
4616 {
4617 let mut ws = mvcc.writer_state.lock().unwrap();
4618 serialize_page_into(&mut ws.scratch, &page.node, mvcc.zero_fill_pages)
4619 .expect("serialize header page");
4620 mvcc.pager
4621 .write_page(page.page_id, &ws.scratch)
4622 .expect("write modified header 0");
4623 }
4624
4625 mvcc.fsync().ok();
4627
4628 drop(mvcc);
4630
4631 match Mvcc::new(
4633 verbose,
4634 StorageOptions::default()
4635 .db_path(db_path)
4636 .page_size(page_size),
4637 ) {
4638 Ok(_) => panic!("opening should have failed due to newer on-disk schema"),
4639 Err(DcbError::InternalError(msg)) => {
4640 assert!(
4641 msg.contains("Software version is too old"),
4642 "unexpected error message: {msg}"
4643 );
4644 }
4645 Err(other) => panic!("unexpected error type: {other:?}"),
4646 }
4647 }
4648 }
4649
4650 #[cfg(test)]
4651 mod page_cache_tests {
4652 use super::*;
4653 use serial_test::serial;
4654 use tempfile::tempdir;
4655
4656 #[test]
4657 #[serial]
4658 fn test_mvcc_page_cache_max_pages() {
4659 let temp_dir = tempdir().unwrap();
4660 let db_path = temp_dir.path().join("cache-pages.db");
4661 let options = StorageOptions::default()
4662 .db_path(&db_path)
4663 .page_cache_max_pages(10);
4664
4665 let mvcc = Mvcc::new(false, options).expect("mvcc new");
4666 assert!(mvcc.page_cache.is_some());
4667
4668 let mut writer = mvcc.writer().expect("writer");
4670 let p1 = writer.alloc_page_id();
4671 let page = Page::new(
4672 p1,
4673 Node::FreeListLeaf(crate::free_lists_tree_nodes::FreeListLeafNode::default()),
4674 );
4675 writer.insert_dirty(page).expect("insert dirty");
4676 mvcc.commit(&mut writer).expect("commit");
4677
4678 {
4680 let cache = mvcc.page_cache.as_ref().unwrap();
4681 assert!(cache.get(&p1).is_some());
4682 assert!(
4683 cache.get(&HEADER_PAGE_ID_0).is_some()
4684 || cache.get(&HEADER_PAGE_ID_1).is_some()
4685 );
4686 }
4687
4688 let _read_page = mvcc.read_page(p1).expect("read page");
4690 }
4691
4692 #[test]
4693 #[serial]
4694 fn test_mvcc_page_cache_max_mb() {
4695 let temp_dir = tempdir().unwrap();
4696 let db_path = temp_dir.path().join("cache-mb.db");
4697 let options = StorageOptions::default()
4698 .db_path(&db_path)
4699 .page_cache_max_mb(1); let mvcc = Mvcc::new(false, options).expect("mvcc new");
4702 assert!(mvcc.page_cache.is_some());
4703
4704 let mut writer = mvcc.writer().expect("writer");
4706 let p1 = writer.alloc_page_id();
4707 let page = Page::new(
4708 p1,
4709 Node::FreeListLeaf(crate::free_lists_tree_nodes::FreeListLeafNode::default()),
4710 );
4711 writer.insert_dirty(page).expect("insert dirty");
4712 mvcc.commit(&mut writer).expect("commit");
4713
4714 {
4716 let cache = mvcc.page_cache.as_ref().unwrap();
4717 assert!(cache.get(&p1).is_some());
4718 }
4719
4720 let _read_page = mvcc.read_page(p1).expect("read page");
4722 }
4723
4724 #[test]
4725 #[serial]
4726 fn test_mvcc_page_cache_eviction() {
4727 let temp_dir = tempdir().unwrap();
4728 let db_path = temp_dir.path().join("cache-eviction.db");
4729 let options = StorageOptions::default()
4730 .db_path(&db_path)
4731 .page_cache_max_pages(2);
4732
4733 let mvcc = Mvcc::new(false, options).expect("mvcc new");
4734
4735 let mut writer = mvcc.writer().expect("writer");
4738 let p1 = writer.alloc_page_id();
4739 let p2 = writer.alloc_page_id();
4740 let p3 = writer.alloc_page_id();
4741
4742 writer
4743 .insert_dirty(Page::new(p1, Node::FreeListLeaf(Default::default())))
4744 .unwrap();
4745 writer
4746 .insert_dirty(Page::new(p2, Node::FreeListLeaf(Default::default())))
4747 .unwrap();
4748 writer
4749 .insert_dirty(Page::new(p3, Node::FreeListLeaf(Default::default())))
4750 .unwrap();
4751
4752 mvcc.commit(&mut writer).expect("commit");
4753
4754 let cache = mvcc.page_cache.as_ref().unwrap();
4757 cache.run_pending_tasks();
4760 assert!(cache.entry_count() <= 2);
4761 }
4762 }
4763}
4764
4765#[cfg(test)]
4766mod high_level_mvcc_tests {
4767 use super::*;
4768 use crate::db::UmaDb;
4769 use serial_test::serial;
4770 use std::sync::Arc;
4771 use tempfile::tempdir;
4772 use umadb_dcb::{DcbError, DcbEvent, DcbEventStoreSync};
4773
4774 #[test]
4775 #[serial]
4776 fn test_append_event_with_tag_larger_than_page_size_via_mvcc() {
4777 let temp_dir = tempdir().unwrap();
4783 let db_path = temp_dir.path().join("mvcc-high-level-tag.db");
4784 let page_size = 512;
4785
4786 let options = StorageOptions::default()
4787 .db_path(&db_path)
4788 .page_size(page_size);
4789
4790 let mvcc = Mvcc::new(true, options).expect("mvcc new");
4791 let uma = UmaDb::from_arc(Arc::new(mvcc));
4792
4793 let header_before = uma.mvcc.get_latest_header_page().expect("header before");
4794 let header_before = header_before.as_header_node().expect("header before node");
4795 let free_root_before = uma
4796 .mvcc
4797 .read_page(header_before.free_lists_tree_root_id)
4798 .expect("free root before");
4799 let events_root_before = uma
4800 .mvcc
4801 .read_page(header_before.events_tree_root_id)
4802 .expect("events root before");
4803 let tags_root_before = uma
4804 .mvcc
4805 .read_page(header_before.tags_tree_root_id)
4806 .expect("tags root before");
4807
4808 let tsn_before = header_before.tsn;
4809 let next_page_id_before = header_before.next_page_id;
4810 let next_position_before = header_before.next_position;
4811 let free_lists_tree_root_id_before = header_before.free_lists_tree_root_id;
4812 let events_tree_root_id_before = header_before.events_tree_root_id;
4813 let tags_tree_root_id_before = header_before.tags_tree_root_id;
4814 let tracking_root_page_id_before = header_before.tracking_root_page_id;
4815
4816 let free_root_node_before = free_root_before.node.clone();
4817 let events_root_node_before = events_root_before.node.clone();
4818 let tags_root_node_before = tags_root_before.node.clone();
4819
4820 let large_tag = "t".repeat(600);
4821 let event = DcbEvent::new()
4822 .event_type("Type")
4823 .data(vec![1, 2, 3])
4824 .tags(vec![large_tag]);
4825
4826 println!(
4827 "Attempting to append event with tag of size 600 using UmaDb/Mvcc (page size 512)"
4828 );
4829 let result = uma.append(vec![event], None, None);
4830 match result {
4831 Err(DcbError::InvalidArgument(_)) => {}
4832 other => panic!("Expected InvalidArgument, got {other:?}"),
4833 }
4834
4835 let header_after = uma.mvcc.get_latest_header_page().expect("header after");
4836 let header_after = header_after.as_header_node().expect("header after node");
4837
4838 assert_eq!(tsn_before, header_after.tsn);
4839 assert_eq!(next_page_id_before, header_after.next_page_id);
4840 assert_eq!(next_position_before, header_after.next_position);
4841 assert_eq!(
4842 free_lists_tree_root_id_before,
4843 header_after.free_lists_tree_root_id
4844 );
4845 assert_eq!(events_tree_root_id_before, header_after.events_tree_root_id);
4846 assert_eq!(tags_tree_root_id_before, header_after.tags_tree_root_id);
4847 assert_eq!(
4848 tracking_root_page_id_before,
4849 header_after.tracking_root_page_id
4850 );
4851
4852 let free_root_after = uma
4853 .mvcc
4854 .read_page(header_after.free_lists_tree_root_id)
4855 .expect("free root after");
4856 let events_root_after = uma
4857 .mvcc
4858 .read_page(header_after.events_tree_root_id)
4859 .expect("events root after");
4860 let tags_root_after = uma
4861 .mvcc
4862 .read_page(header_after.tags_tree_root_id)
4863 .expect("tags root after");
4864
4865 assert_eq!(free_root_node_before, free_root_after.node);
4866 assert_eq!(events_root_node_before, events_root_after.node);
4867 assert_eq!(tags_root_node_before, tags_root_after.node);
4868 }
4869}