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 dashmap::DashMap;
18use std::collections::HashMap;
19use std::collections::VecDeque;
20use std::path::{Path, PathBuf};
21use std::str::FromStr;
22use std::sync::atomic::{AtomicUsize, Ordering};
23use std::sync::{Arc, Mutex};
24use std::thread::sleep;
25use std::time::Duration;
26use moka::sync::Cache;
27use umadb_dcb::DcbError::InternalError;
28use umadb_dcb::{DcbQuery, DcbResult, DcbError};
29
30const GET_LATEST_HEADER_RETRIES: usize = 5;
31const GET_LATEST_HEADER_DELAY: Duration = Duration::from_millis(10);
32const HEADER_PAGE_ID_0: PageID = PageID(0);
33const HEADER_PAGE_ID_1: PageID = PageID(1);
34
35pub const DEFAULT_PAGE_SIZE: usize = 4096;
36pub const DEFAULT_DB_FILENAME: &str = "uma.db";
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39pub enum ReadMethod {
40 #[default]
41 FileIo,
42 Mmap,
43}
44
45impl FromStr for ReadMethod {
46 type Err = String;
47
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 match s.to_lowercase().as_str() {
50 "fileio" => Ok(ReadMethod::FileIo),
51 "mmap" => Ok(ReadMethod::Mmap),
52 _ => Err(format!("Unknown read method: {}", s)),
53 }
54 }
55}
56
57
58#[derive(Debug, Clone)]
59pub struct StorageOptions {
60 pub db_path: PathBuf,
61 pub page_size: usize,
62 pub read_method: ReadMethod,
63 pub page_cache_max_pages: usize,
64 pub page_cache_max_mb: usize,
65 pub zero_fill_pages: bool,
66}
67
68impl Default for StorageOptions {
69 fn default() -> Self {
70 Self {
71 db_path: DEFAULT_DB_FILENAME.into(),
72 page_size: DEFAULT_PAGE_SIZE,
73 read_method: ReadMethod::default(),
74 page_cache_max_pages: 0,
75 page_cache_max_mb: 0,
76 zero_fill_pages: true,
77 }
78 }
79}
80
81impl StorageOptions {
82 pub fn db_path<P: AsRef<Path>>(self, db_path: P) -> Self {
83 let p = db_path.as_ref();
84 let db_path = if p.is_dir() {
85 p.join(DEFAULT_DB_FILENAME)
86 } else {
87 p.to_path_buf()
88 };
89 Self {
90 db_path,
91 ..self
92 }
93 }
94
95 pub fn page_size(self, page_size: usize) -> Self {
96 Self {
97 page_size,
98 ..self
99 }
100 }
101
102 pub fn read_method(self, read_method: ReadMethod) -> Self {
103 Self {
104 read_method,
105 ..self
106 }
107 }
108
109 pub fn page_cache_max_pages(self, page_cache_max_pages: usize) -> Self {
110 Self {
111 page_cache_max_pages,
112 ..self
113 }
114 }
115
116 pub fn page_cache_max_mb(self, page_cache_max_mb: usize) -> Self {
117 Self {
118 page_cache_max_mb,
119 ..self
120 }
121 }
122
123 pub fn zero_fill_pages(self, zero_fill_pages: bool) -> Self {
124 Self {
125 zero_fill_pages,
126 ..self
127 }
128 }
129}
130
131pub struct Mvcc {
133 pub pager: Pager,
134 pub reader_tsns: Arc<DashMap<usize, Tsn>>,
135 pub writer_lock: Mutex<()>,
136 pub page_size: usize,
137 pub max_node_size: usize,
138 pub headers: Mutex<Vec<Page>>,
140 pub header_page_buf: Mutex<Vec<u8>>,
142 pub page_buf: Mutex<Vec<u8>>,
144 reader_id_counter: AtomicUsize,
145 pub verbose: bool,
146 pub zero_fill_pages: bool,
147 read_method: ReadMethod,
148 page_cache: Option<Cache<PageID, Arc<Page>>>,
149}
150
151impl Mvcc {
152 pub fn new(
153 verbose: bool,
154 options: StorageOptions,
155 ) -> DcbResult<Self> {
156 let pager = Pager::new(&options.db_path, options.page_size)?;
157 println!("UmaDB opened file {}", options.db_path.canonicalize()?.display());
158
159 println!(
160 "UmaDB reading with {}",
161 match options.read_method {
162 ReadMethod::Mmap => "memory map",
163 ReadMethod::FileIo => "file I/O",
164 }
165 );
166
167 println!(
168 "UmaDB zero-fill pages: {}",
169 if options.zero_fill_pages { "true" } else { "false" }
170 );
171
172 let page_cache = if options.page_cache_max_mb > 0 {
173 println!(
174 "UmaDB page cache max MB: {}",
175 options.page_cache_max_mb
176 );
177 Some(
178 Cache::builder()
179 .max_capacity(1000000 * options.page_cache_max_mb as u64)
180 .weigher(|_page_id, page: &Arc<Page>| {
181 page_approx_deserialized_bytes(page.as_ref()).min(u32::MAX as usize) as u32
182 })
183 .build(),
184 )
185 } else if options.page_cache_max_pages > 0 {
186 println!("UmaDB page cache max pages: {}", options.page_cache_max_pages);
187 Some(Cache::new(options.page_cache_max_pages as u64))
188 } else {
189 println!("UmaDB page cache not enabled");
190 None
191 };
192
193 let mvcc = Self {
194 pager,
195 reader_tsns: Arc::new(DashMap::new()),
196 writer_lock: Mutex::new(()),
197 page_size: options.page_size,
198 max_node_size: options.page_size - PAGE_HEADER_SIZE,
199 headers: Mutex::new(vec![
200 Page {
201 page_id: PageID(0),
202 node: Node::Header(HeaderNode::default()),
203 },
204 Page {
205 page_id: PageID(1),
206 node: Node::Header(HeaderNode::default()),
207 },
208 ]),
209 header_page_buf: Mutex::new(vec![0u8; options.page_size]),
210 page_buf: Mutex::new(vec![0u8; options.page_size]),
211 reader_id_counter: AtomicUsize::new(0),
212 verbose,
213 zero_fill_pages: options.zero_fill_pages,
214 read_method: options.read_method,
215 page_cache
216 };
217
218 if mvcc.pager.is_file_new {
219 set_tag_key_width(crate::tags_tree_nodes::TAG_HASH_LEN);
221
222 let initial_tsn = Tsn(0);
224 let initial_free_lists_tree_root_id = PageID(2);
225 let initial_events_tree_root_id = PageID(3);
226 let initial_tags_tree_root_id = PageID(4);
227 let initial_next_page_id = PageID(5);
228 let initial_next_position = Position(1);
229 mvcc.update_header(
230 HEADER_PAGE_ID_0,
231 initial_tsn,
232 initial_free_lists_tree_root_id,
233 initial_events_tree_root_id,
234 initial_tags_tree_root_id,
235 PageID(0),
236 initial_next_page_id,
237 initial_next_position,
238 )?;
239 mvcc.update_header(
240 HEADER_PAGE_ID_1,
241 initial_tsn,
242 initial_free_lists_tree_root_id,
243 initial_events_tree_root_id,
244 initial_tags_tree_root_id,
245 PageID(0),
246 initial_next_page_id,
247 initial_next_position,
248 )?;
249
250 let free_list_leaf = FreeListLeafNode {
252 keys: Vec::new(),
253 values: Vec::new(),
254 };
255 let free_list_page = Page::new(
256 initial_free_lists_tree_root_id,
257 Node::FreeListLeaf(free_list_leaf),
258 );
259
260 let event_leaf = EventLeafNode {
262 keys: Vec::new(),
263 values: Vec::new(),
264 };
265 let position_page = Page::new(initial_events_tree_root_id, Node::EventLeaf(event_leaf));
266
267 let tags_leaf = TagsLeafNode {
269 keys: Vec::new(),
270 values: Vec::new(),
271 };
272 let tags_page = Page::new(initial_tags_tree_root_id, Node::TagsLeaf(tags_leaf));
273
274 let _ = mvcc.write_pages([&free_list_page, &position_page, &tags_page].into_iter())?;
276
277 mvcc.fsync()?;
279
280 } else {
281 if let Ok(header_page) = mvcc.get_latest_header_page() {
283 let header_latest = page_as_header_node(&header_page)?;
284 if header_latest.schema_version > DB_SCHEMA_VERSION {
286 let schema_version = header_latest.schema_version;
287 return Err(InternalError(format!(
288 "Software version is too old. This software supports schema version {DB_SCHEMA_VERSION} but the database file has schema version {schema_version}."
289 )));
290 }
291
292 if header_latest.schema_version == 0 {
294 set_tag_key_width(8);
295 } else {
296 set_tag_key_width(crate::tags_tree_nodes::TAG_HASH_LEN);
297 }
298
299 let h0 = mvcc.read_page(HEADER_PAGE_ID_0).ok();
301 let h1 = mvcc.read_page(HEADER_PAGE_ID_1).ok();
302 {
303 let mut headers = mvcc.headers.lock().unwrap();
304 if let Some(p0) = h0 {
305 headers[0] = (*p0).clone();
306 }
307 if let Some(p1) = h1 {
308 headers[1] = (*p1).clone();
309 }
310 }
311
312 if header_latest.schema_version == 1 {
315 use crate::node::Node;
316 let tags_root = header_latest.tags_tree_root_id;
317 set_tag_key_width(crate::tags_tree_nodes::TAG_HASH_LEN);
319 let sixteen_ok = mvcc
320 .read_page(tags_root)
321 .map(|p| matches!(p.node, Node::TagsLeaf(_) | Node::TagsInternal(_)))
322 .is_ok();
323 if !sixteen_ok {
324 set_tag_key_width(8);
326 let eight_ok = mvcc
327 .read_page(tags_root)
328 .map(|p| matches!(p.node, Node::TagsLeaf(_) | Node::TagsInternal(_)))
329 .is_ok();
330 if eight_ok {
331 {
333 let mut headers = mvcc.headers.lock().unwrap();
334 for hp in headers.iter_mut() {
335 let header_page_id = hp.page_id;
336 if let Node::Header(ref mut hn) = hp.node {
337 let schema_version = hn.schema_version;
338 if mvcc.verbose {
339 println!(
340 "Resetting header node {header_page_id:?} schema version from {schema_version:?} to 0"
341 );
342 }
343 hn.schema_version = 0;
344 }
345 let mut buf = mvcc.page_buf.lock().unwrap();
347 serialize_page_into(
348 &mut buf,
349 &hp.node,
350 mvcc.zero_fill_pages,
351 )?;
352 mvcc.pager.write_page(hp.page_id, &buf)?;
353 }
354 }
355
356 set_tag_key_width(8);
358
359 mvcc.fsync()?;
361 } else {
362 if mvcc.verbose {
364 println!(
365 "Tags root failed to deserialize at width 16 and 8; leaving schema_version unchanged"
366 );
367 }
368 }
369 }
370 }
371 }
372 if let Ok(header_page) = mvcc.get_latest_header_page() {
373 let hid = header_page.page_id;
374 let header_latest = page_as_header_node(&header_page)?;
375 {
377 let empty_dirty: std::collections::HashMap<PageID, Page> =
378 std::collections::HashMap::new();
379 let mut last_by_tree: Option<u64> = None;
380 match read_conditional(
381 &mvcc,
382 &empty_dirty,
383 header_latest.events_tree_root_id,
384 header_latest.tags_tree_root_id,
385 DcbQuery { items: vec![] },
386 None,
387 true,
388 Some(1),
389 false,
390 None,
391 ) {
392 Ok(mut v) => {
393 if let Some(ev) = v.pop() {
394 last_by_tree = Some(ev.position);
395 }
396 }
397 Err(e) => {
398 if mvcc.verbose {
399 println!(
400 "Warning: failed to read last event from events tree: {:?}",
401 e
402 );
403 }
404 }
405 }
406 let last_pos = last_by_tree.unwrap_or(0);
407 let expected_next = Position(last_pos.saturating_add(1));
408 if expected_next != header_latest.next_position {
409 if mvcc.verbose {
410 println!(
411 "Fixing header.next_position from {} to {} based on events tree",
412 header_latest.next_position.0, expected_next.0
413 );
414 }
415 mvcc.update_header(
417 hid,
418 header_latest.tsn,
419 header_latest.free_lists_tree_root_id,
420 header_latest.events_tree_root_id,
421 header_latest.tags_tree_root_id,
422 header_latest.tracking_root_page_id,
423 header_latest.next_page_id,
424 expected_next,
425 )?;
426 mvcc.fsync()?;
427 }
428 }
429 }
430 }
431
432 if let Some(ref page_cache) = mvcc.page_cache {
434 if let Some(p0) = mvcc.read_page(HEADER_PAGE_ID_0).ok() {
435 page_cache.insert(p0.page_id, p0);
436 }
437 if let Some(p1) = mvcc.read_page(HEADER_PAGE_ID_1).ok() {
438 page_cache.insert(p1.page_id, p1);
439 }
440 }
441 if let Some(ref page_cache) = mvcc.page_cache {
443 let header_page = mvcc.get_latest_header_page()?;
444 let header_node = page_as_header_node(&header_page)?;
445 let page = mvcc.read_page(header_node.free_lists_tree_root_id)?;
446 page_cache.insert(page.page_id, page);
447
448 let page = mvcc.read_page(header_node.tags_tree_root_id)?;
449 page_cache.insert(page.page_id, page);
450
451 let page = mvcc.read_page(header_node.events_tree_root_id)?;
452 page_cache.insert(page.page_id, page);
453
454 if header_node.tracking_root_page_id != PageID(0) {
455 let page = mvcc.read_page(header_node.tracking_root_page_id)?;
456 page_cache.insert(page.page_id, page);
457 }
458 }
459
460 Ok(mvcc)
461 }
462
463 pub fn get_latest_header_page(&self) -> DcbResult<Arc<Page>> {
464 for attempt in 0..GET_LATEST_HEADER_RETRIES {
465 let h0 = self.read_page(HEADER_PAGE_ID_0);
466 let h1 = self.read_page(HEADER_PAGE_ID_1);
467
468 match (h0, h1) {
469 (Ok(page0), Ok(page1)) => {
470 let header0 = page_as_header_node(&page0)?;
471 let header1 = page_as_header_node(&page1)?;
472
473 if header1.tsn > header0.tsn {
474 return Ok(page1);
475 } else {
476 return Ok(page0);
477 }
478 }
479 (Ok(page0), Err(_)) => {
480 let _ = page_as_header_node(&page0)?;
481 return Ok(page0);
482 }
483 (Err(_), Ok(page1)) => {
484 let _ = page_as_header_node(&page1)?;
485 return Ok(page1);
486 }
487 (Err(e0), Err(e1)) => {
488 if attempt + 1 < GET_LATEST_HEADER_RETRIES {
489 if self.verbose {
490 println!(
491 "Both headers invalid on attempt {}: {:?} | {:?}. Retrying...",
492 attempt + 1,
493 e0,
494 e1
495 );
496 }
497 sleep(GET_LATEST_HEADER_DELAY);
498 continue;
499 } else {
500 return Err(DcbError::DatabaseCorrupted(format!(
501 "Both header pages appear corrupted after {} attempts: ({:?}) and ({:?})",
502 GET_LATEST_HEADER_RETRIES, e0, e1
503 )));
504 }
505 }
506 }
507 }
508 Err(DcbError::DatabaseCorrupted(
510 "Unable to read a valid header".to_string(),
511 ))
512 }
513
514 fn update_header(
515 &self,
516 page_id: PageID,
517 tsn: Tsn,
518 free_lists_tree_root_id: PageID,
519 events_tree_root_id: PageID,
520 tags_tree_root_id: PageID,
521 tracking_tree_root_id: PageID,
522 next_page_id: PageID,
523 next_position: Position,
524 ) -> DcbResult<()> {
525 let mut headers = self.headers.lock().unwrap();
526 let headers_idx = { if page_id == HEADER_PAGE_ID_0 { 0 } else { 1 } };
527 let header = &mut headers[headers_idx];
528 match &mut header.node {
529 Node::Header(node) => {
530 node.tsn = tsn;
532 node.free_lists_tree_root_id = free_lists_tree_root_id;
533 node.events_tree_root_id = events_tree_root_id;
534 node.tags_tree_root_id = tags_tree_root_id;
535 node.next_page_id = next_page_id;
536 node.next_position = next_position;
537 node.tracking_root_page_id = tracking_tree_root_id;
538
539 let mut buf = self.page_buf.lock().unwrap();
541 serialize_page_into(
542 &mut buf,
543 &header.node,
544 self.zero_fill_pages,
545 )?;
546 self.pager.write_page(page_id, &buf)?;
547 Ok(())
548 }
549 _ => panic!("Shouldn't get here: header should be a header"),
550 }
551 }
552
553 pub fn read_page(&self, page_id: PageID) -> DcbResult<Arc<Page>> {
554 if let Some(ref page_cache) = self.page_cache {
556 if let Some(page) = page_cache.get(&page_id) {
557 return Ok(page);
558 }
559 }
560
561 let page = match self.read_method {
563 ReadMethod::Mmap => {
564 let mapped = self.pager.read_page_mmap_slice(page_id)?;
565 if self.verbose {
566 println!("Read {page_id:?} from mmap, deserializing...");
567 }
568 Page::deserialize(page_id, mapped.as_slice())?
569 },
570 ReadMethod::FileIo => {
571 let page = self.pager.read_page(page_id)?;
572 if self.verbose {
573 println!("Read {page_id:?} from file, deserializing...");
574 }
575 Page::deserialize(page_id, &page)?
576 }
577 };
578 let page = Arc::new(page);
579
580 if let Some(ref page_cache) = self.page_cache {
582 page_cache.insert(page_id, Arc::clone(&page));
583 }
584
585 Ok(page)
586 }
587
588 pub fn fsync(&self) -> DcbResult<()> {
589 self.pager.fsync()?;
590 Ok(())
591 }
592
593 pub fn reader(&self) -> DcbResult<Reader> {
594 let header_page = self.get_latest_header_page()?;
595 let header_node = page_as_header_node(&header_page)?;
596
597 let reader_id = self.reader_id_counter.fetch_add(1, Ordering::Relaxed) + 1;
599
600 self.reader_tsns.insert(reader_id, header_node.tsn);
602
603 let reader = Reader {
605 header_page_id: header_page.page_id,
606 tsn: header_node.tsn,
607 events_tree_root_id: header_node.events_tree_root_id,
608 tags_tree_root_id: header_node.tags_tree_root_id,
609 next_position: header_node.next_position,
610 tracking_tree_root_id: header_node.tracking_root_page_id,
611 reader_id,
612 reader_tsns: Arc::clone(&self.reader_tsns),
613 };
614
615 Ok(reader)
616 }
617
618 pub fn writer(&self) -> DcbResult<Writer> {
619 if self.verbose {
620 println!();
621 println!("Constructing writer...");
622 }
623
624 let header_page = self.get_latest_header_page()?;
626 let header_node = page_as_header_node(&header_page)?;
627
628 let mut writer = Writer::new(
630 header_page.page_id,
631 Tsn(header_node.tsn.0 + 1),
632 header_node.next_page_id,
633 header_node.free_lists_tree_root_id,
634 header_node.events_tree_root_id,
635 header_node.tags_tree_root_id,
636 header_node.tracking_root_page_id,
637 header_node.next_position,
638 self.verbose,
639 );
640
641 if self.verbose {
642 println!("Constructed writer with {:?}", writer.tsn);
643 }
644
645 writer.find_reusable_page_ids(self)?;
647
648 Ok(writer)
649 }
650
651 pub fn write_pages<'a, I>(&self, pages: I) -> DcbResult<usize>
654 where
655 I: IntoIterator<Item = &'a Page>,
656 {
657 let mut buf = self.page_buf.lock().unwrap();
658 let mut count = 0usize;
659 for page in pages {
660 page.serialize_into_with_zero_fill(&mut buf, self.zero_fill_pages)?;
661 self.pager.write_page(page.page_id, &buf)?;
662 if self.verbose {
663 println!("Wrote {:?} to file", page.page_id);
664 }
665 count += 1;
666 }
667 Ok(count)
668 }
669
670 pub fn commit(&self, writer: &mut Writer) -> DcbResult<()> {
708 if self.verbose {
710 println!();
711 println!("Commiting writer with {:?}", writer.tsn);
712 }
713
714 while !writer.reused_page_ids.is_empty() || !writer.freed_page_ids.is_empty() {
715 while let Some((reused_page_id, tsn)) = writer.reused_page_ids.pop_front() {
717 writer.remove_free_page_id(self, tsn, reused_page_id)?;
719 }
720
721 while let Some(freed_page_id) = writer.freed_page_ids.pop_front() {
723 writer.dirty.remove(&freed_page_id);
725
726 writer.insert_freed_page_id(self, writer.tsn, freed_page_id)?;
728 }
729 }
730
731 if !writer.dirty.is_empty() {
733 let count = {
734 self.write_pages(writer.dirty.values())?
742 };
743 if self.verbose {
744 println!("Wrote {} dirty page(s) to file", count);
745 }
746 }
747
748 self.fsync()?;
750
751 if let Some(ref page_cache) = self.page_cache {
753 for (page_id, page) in writer.dirty.drain() {
754 page_cache.insert(page_id, Arc::new(page));
755 }
756 } else {
757 writer.dirty.clear();
758 }
759
760 let (alternate_header_page_id, alternate_header_page_idx) = if writer.header_page_id == HEADER_PAGE_ID_0 {
762 (HEADER_PAGE_ID_1, 1)
763 } else {
764 (HEADER_PAGE_ID_0, 0)
765 };
766 self.update_header(
767 alternate_header_page_id,
768 writer.tsn,
769 writer.free_lists_tree_root_id,
770 writer.events_tree_root_id,
771 writer.tags_tree_root_id,
772 writer.tracking_tree_root_id,
773 writer.next_page_id,
774 writer.next_position,
775 )?;
776
777 self.fsync()?;
779
780 let header_page = self.headers.lock().unwrap()[alternate_header_page_idx].clone();
782 if let Some(ref page_cache) = self.page_cache {
783 page_cache.insert(header_page.page_id, Arc::new(header_page));
784 }
785
786 if self.verbose {
787 println!("Committed writer with {:?}", writer.tsn);
788 }
789
790 Ok(())
791 }
792}
793
794pub struct Writer {
796 pub header_page_id: PageID,
797 pub tsn: Tsn,
798 pub next_page_id: PageID,
799 pub free_lists_tree_root_id: PageID,
800 pub events_tree_root_id: PageID,
801 pub tags_tree_root_id: PageID,
802 pub tracking_tree_root_id: PageID,
803 pub next_position: Position,
804 pub reusable_page_ids: VecDeque<(PageID, Tsn)>,
805 pub freed_page_ids: VecDeque<PageID>,
806 pub deserialized: HashMap<PageID, Arc<Page>>,
807 pub dirty: HashMap<PageID, Page>,
808 pub reused_page_ids: VecDeque<(PageID, Tsn)>,
809 pub verbose: bool,
810}
811
812impl Writer {
813 pub fn new(
814 header_page_id: PageID,
815 tsn: Tsn,
816 next_page_id: PageID,
817 free_lists_tree_root_id: PageID,
818 events_tree_root_id: PageID,
819 tags_tree_root_id: PageID,
820 tracking_tree_root_id: PageID,
821 next_position: Position,
822 verbose: bool,
823 ) -> Self {
824 Self {
825 header_page_id,
826 tsn,
827 next_page_id,
828 free_lists_tree_root_id,
829 events_tree_root_id,
830 tags_tree_root_id,
831 tracking_tree_root_id,
832 next_position,
833 reusable_page_ids: VecDeque::new(),
834 freed_page_ids: VecDeque::new(),
835 deserialized: HashMap::new(),
836 dirty: HashMap::new(),
837 reused_page_ids: VecDeque::new(),
838 verbose,
839 }
840 }
841
842 pub fn issue_position(&mut self) -> Position {
848 let pos = self.next_position;
849 self.next_position = Position(self.next_position.0 + 1);
850 pos
851 }
852
853 pub fn get_page_ref(&mut self, mvcc: &Mvcc, page_id: PageID) -> DcbResult<&Page> {
867 if self.dirty.contains_key(&page_id) {
869 return Ok(self.dirty.get(&page_id).unwrap());
870 }
871
872 if self.deserialized.contains_key(&page_id) {
874 return Ok(self.deserialized.get(&page_id).unwrap());
875 }
876
877 let deserialized_page = mvcc.read_page(page_id)?;
879 self.insert_deserialized(deserialized_page);
880
881 Ok(self.deserialized.get(&page_id).unwrap())
883 }
884
885 pub fn get_mut_dirty(&mut self, page_id: PageID) -> DcbResult<&mut Page> {
886 if let Some(page) = self.dirty.get_mut(&page_id) {
887 Ok(page)
888 } else {
889 Err(DcbError::DirtyPageNotFound(page_id.0))
890 }
891 }
892
893 pub fn insert_deserialized(&mut self, page: Arc<Page>) {
894 self.deserialized.insert(page.page_id, page);
895 }
896
897 pub fn insert_dirty(&mut self, page: Page) -> DcbResult<()> {
898 if self.freed_page_ids.contains(&page.page_id) {
899 return Err(DcbError::PageAlreadyFreed(page.page_id.0));
900 }
901 if self.dirty.contains_key(&page.page_id) {
902 return Err(DcbError::PageAlreadyDirty(page.page_id.0));
903 }
904 self.dirty.insert(page.page_id, page);
905 Ok(())
906 }
907
908 pub fn alloc_page_id(&mut self) -> PageID {
909 if let Some((free_page_id, tsn)) = self.reusable_page_ids.pop_front() {
910 self.reused_page_ids.push_back((free_page_id, tsn));
911 return free_page_id;
912 }
913
914 let next_page_id = self.next_page_id;
915 self.next_page_id = PageID(next_page_id.0 + 1);
916 next_page_id
917 }
918
919 pub fn get_dirty_page_id(&mut self, page_id: PageID) -> DcbResult<PageID> {
920 let mut dirty_page_id = page_id;
921 if !self.freed_page_ids.iter().any(|&id| id == page_id) {
922 if !self.dirty.contains_key(&page_id) {
923 let old_page_id = page_id;
924 self.freed_page_ids.push_back(old_page_id);
925
926 let new_page_id = self.alloc_page_id();
927 let mut new_page = Arc::unwrap_or_clone(
928 self.deserialized.remove(&old_page_id).ok_or_else(|| {
929 DcbError::DatabaseCorrupted(format!(
930 "Deserialized page {:?} not found while marking dirty",
931 old_page_id
932 ))
933 })?,
934 );
935 new_page.page_id = new_page_id;
936
937 self.dirty.insert(new_page_id, new_page);
938 if self.verbose {
939 println!(
940 "Copied {:?} to {:?}: {:?}",
941 old_page_id,
942 new_page_id,
943 self.dirty.get(&new_page_id).unwrap().node
944 );
945 }
946 dirty_page_id = new_page_id;
947 } else if self.verbose {
948 println!("{page_id:?} is already dirty");
949 }
950 } else {
951 return Err(DcbError::PageAlreadyFreed(page_id.0));
952 }
953 Ok(dirty_page_id)
954 }
955
956 pub fn append_freed_page_id(&mut self, page_id: PageID) {
957 let verbose = self.verbose;
958 if !self.freed_page_ids.iter().any(|&id| id == page_id) {
959 self.freed_page_ids.push_back(page_id);
960 if verbose {
961 println!("Appended {page_id:?} to freed_page_ids");
962 }
963 if self.dirty.contains_key(&page_id) {
964 self.dirty.remove(&page_id);
965 if verbose {
966 println!("Page ID {page_id:?} was in dirty and was removed");
967 }
968 }
969 if verbose && self.dirty.contains_key(&page_id) {
970 println!("Page ID {page_id:?} is still in dirty!!!!!");
971 }
972 }
973 }
974
975 pub fn find_reusable_page_ids(&mut self, mvcc: &Mvcc) -> DcbResult<()> {
976 let verbose = self.verbose;
977 let mut reusable_page_ids: VecDeque<(PageID, Tsn)> = VecDeque::new();
978 if verbose {
980 println!("Finding reusable page IDs for TSN {:?}...", self.tsn);
981 }
982
983 let smallest_reader_tsn = mvcc.reader_tsns.iter().map(|r| *r.value()).min();
985 if verbose {
986 println!("Smallest reader TSN: {smallest_reader_tsn:?}");
987 }
988
989 if verbose {
990 println!("Root is {:?}", self.free_lists_tree_root_id);
991 }
992 let mut stack = vec![(self.free_lists_tree_root_id, 0)];
994 let mut is_finished = false;
995
996 while let Some((page_id, idx)) = stack.pop() {
997 if is_finished {
998 break;
999 }
1000 let node_owned = {
1001 match self.get_page_ref(mvcc, page_id) {
1002 Ok(p) => p.node.clone(),
1003 Err(e) => {
1004 return Err(DcbError::DatabaseCorrupted(format!(
1005 "Free list page {:?} load error: {:?}",
1006 page_id, e
1007 )));
1008 }
1009 }
1010 };
1011 match node_owned {
1012 Node::FreeListInternal(node) => {
1013 if verbose {
1014 println!("{:?} is internal node", page_id);
1015 }
1016 if idx < node.child_ids.len() {
1017 let child_page_id = node.child_ids[idx];
1018 stack.push((page_id, idx + 1));
1019 stack.push((child_page_id, 0));
1020 }
1021 }
1022 Node::FreeListLeaf(node) => {
1023 if verbose {
1024 println!("{:?} is leaf node", page_id);
1025 }
1026 for i in 0..node.keys.len() {
1027 let tsn = node.keys[i];
1028 if let Some(smallest) = smallest_reader_tsn
1029 && tsn > smallest
1030 {
1031 is_finished = true;
1032 break;
1033 }
1034
1035 let leaf_value = &node.values[i];
1036 if leaf_value.root_id == PageID(0) {
1037 for &page_id in &leaf_value.page_ids {
1038 reusable_page_ids.push_back((page_id, tsn));
1039 }
1040 } else {
1041 let mut tsn_stack: Vec<(PageID, usize)> = vec![(leaf_value.root_id, 0)];
1045 if self.verbose {
1046 println!("TSN-subtree root_id: {:?}", leaf_value.root_id);
1047 }
1048 if self.verbose {
1049 println!(
1050 "root_id in dirty? {}",
1051 self.dirty.contains_key(&leaf_value.root_id)
1052 );
1053 }
1054 while let Some((sub_id, sidx)) = tsn_stack.pop() {
1055 let sub_node = {
1056 match self.get_page_ref(mvcc, sub_id) {
1057 Ok(p) => p.node.clone(),
1058 Err(e) => {
1059 return Err(DcbError::DatabaseCorrupted(format!(
1060 "TSN subtree page {:?} load error: {:?}",
1061 sub_id, e
1062 )));
1063 }
1064 }
1065 };
1066 match sub_node {
1067 Node::FreeListTsnInternal(tsn_internal) => {
1068 if sidx < tsn_internal.child_ids.len() {
1069 let child_id = tsn_internal.child_ids[sidx];
1070 tsn_stack.push((sub_id, sidx + 1));
1071 tsn_stack.push((child_id, 0));
1072 }
1073 }
1074 Node::FreeListTsnLeaf(tsn_leaf) => {
1075 for &pid in &tsn_leaf.page_ids {
1076 reusable_page_ids.push_back((pid, tsn));
1077 }
1078 }
1079 other => {
1080 return Err(DcbError::DatabaseCorrupted(format!(
1081 "Invalid node type in TSN subtree: {}",
1082 other.type_name()
1083 )));
1084 }
1085 }
1086 }
1087 }
1088 }
1089 }
1090 _ => {
1091 return Err(DcbError::DatabaseCorrupted(
1092 "Invalid node type in free list tree".to_string(),
1093 ));
1094 }
1095 }
1096 }
1097
1098 self.reusable_page_ids = reusable_page_ids;
1099 if verbose {
1100 println!("Found reusable page IDs: {:?}", self.reusable_page_ids);
1101 }
1102 Ok(())
1103 }
1104
1105 pub fn insert_freed_page_id(
1107 &mut self,
1108 mvcc: &Mvcc,
1109 tsn: Tsn,
1110 freed_page_id: PageID,
1111 ) -> DcbResult<()> {
1112 let verbose = self.verbose;
1113 if verbose {
1114 println!("Inserting {freed_page_id:?} for {tsn:?}");
1115 println!("Root is {:?}", self.free_lists_tree_root_id);
1116 }
1117 let mut current_page_id = self.free_lists_tree_root_id;
1119
1120 let mut stack: Vec<PageID> = Vec::new();
1122 let plan: FreePageIDInsertStrategy;
1123 loop {
1124 let current_page_ref = self.get_page_ref(mvcc, current_page_id)?;
1125 if let Node::FreeListLeaf(leaf_node) = ¤t_page_ref.node {
1126 let len_keys = leaf_node.keys.len();
1127 if len_keys == 0 {
1128 if !leaf_node.would_fit_new_tsn_and_page_id(mvcc.max_node_size) {
1129 return Err(DcbError::InternalError("Page size too small".to_string()));
1130 }
1131 plan = FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf;
1132 } else {
1133 let last_idx = len_keys - 1;
1134 let last_key = leaf_node.keys[last_idx];
1135 if tsn == last_key {
1136 if leaf_node.values[last_idx].root_id != PageID(0) {
1138 plan = FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree;
1139 } else if leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
1140 plan = FreePageIDInsertStrategy::PushPageIdOntoFreeListLeaf(last_idx);
1141 } else if leaf_node.keys.len() == 1 {
1142 plan = FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree;
1143 } else {
1144 plan = FreePageIDInsertStrategy::SplitFreeListLeaf;
1145 }
1146 } else if tsn > last_key {
1147 if leaf_node.would_fit_new_tsn_and_page_id(mvcc.max_node_size) {
1149 plan = FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf;
1150 } else {
1151 plan = FreePageIDInsertStrategy::CreateAndPromoteFreeListLeaf;
1152 }
1153 } else {
1154 return Err(DcbError::InternalError(
1156 "Insertion only supported for last TSN in leaf".to_string(),
1157 ));
1158 }
1159 }
1160 break;
1161 }
1162 if let Node::FreeListInternal(internal_node) = ¤t_page_ref.node {
1163 if verbose {
1164 println!("{:?} is internal node", current_page_ref.page_id);
1165 }
1166 stack.push(current_page_id);
1167 current_page_id = *internal_node
1168 .child_ids
1169 .last()
1170 .expect("FreeListInternal node should have a child");
1171 } else {
1172 return Err(DcbError::DatabaseCorrupted(
1173 "Expected FreeListInternal node".to_string(),
1174 ));
1175 }
1176 }
1177 if verbose {
1178 println!("{current_page_id:?} is leaf node");
1179 }
1180 let dirty_leaf_page_id = { self.get_dirty_page_id(current_page_id)? };
1182 let replacement_info: Option<(PageID, PageID)> = {
1183 if dirty_leaf_page_id != current_page_id {
1184 Some((current_page_id, dirty_leaf_page_id))
1185 } else {
1186 None
1187 }
1188 };
1189 let mut split_info: Option<(Tsn, PageID)> = None;
1191
1192 match plan {
1194 FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree => {
1195 let leaf_snapshot = { self.get_page_ref(mvcc, dirty_leaf_page_id)? };
1197 let Node::FreeListLeaf(leaf_ro) = &leaf_snapshot.node else {
1198 return Err(DcbError::DatabaseCorrupted(
1199 "Expected FreeListLeaf node".to_string(),
1200 ));
1201 };
1202 let last_idx = leaf_ro.keys.len() - 1;
1203 let tsn_root_id = leaf_ro.values[last_idx].root_id;
1204 if tsn_root_id == PageID(0) {
1205 return Err(DcbError::DatabaseCorrupted(
1206 "Expected TSN-subtree root_id to be set".to_string(),
1207 ));
1208 }
1209 let new_root_id = self.tsn_subtree_insert(mvcc, tsn_root_id, freed_page_id)?;
1210 if new_root_id != tsn_root_id {
1211 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1213 let Node::FreeListLeaf(dirty_leaf_node2) = &mut dirty_leaf_page.node else {
1214 return Err(DcbError::DatabaseCorrupted(
1215 "Expected FreeListLeaf node".to_string(),
1216 ));
1217 };
1218 dirty_leaf_node2.values[last_idx].root_id = new_root_id;
1219 }
1220 }
1221 FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree => {
1222 let leaf_snapshot = { self.get_page_ref(mvcc, dirty_leaf_page_id)? };
1224 let Node::FreeListLeaf(leaf_ro) = &leaf_snapshot.node else {
1225 return Err(DcbError::DatabaseCorrupted(
1226 "Expected FreeListLeaf node".to_string(),
1227 ));
1228 };
1229 let last_idx = leaf_ro.keys.len() - 1;
1230 let mut page_ids = leaf_ro.values[last_idx].page_ids.clone();
1231 page_ids.push(freed_page_id);
1232 page_ids.sort_by_key(|pid| pid.0);
1234 page_ids.dedup();
1235 let mut initial_ids: Vec<PageID> = Vec::new();
1238 let mut tmp_leaf = FreeListTsnLeafNode {
1239 page_ids: Vec::new(),
1240 };
1241 for pid in &page_ids {
1242 let mut candidate = tmp_leaf.clone();
1243 candidate.page_ids.push(*pid);
1244 let candidate_page = Page::new(PageID(0), Node::FreeListTsnLeaf(candidate));
1245 if candidate_page.calc_serialized_size() <= mvcc.page_size {
1246 tmp_leaf.page_ids.push(*pid);
1247 initial_ids.push(*pid);
1248 } else {
1249 break;
1250 }
1251 }
1252 if initial_ids.is_empty() {
1253 return Err(DcbError::InternalError(
1254 "Page size too small for TSN-subtree leaf with one PageID".to_string(),
1255 ));
1256 }
1257 let tsn_leaf_id = self.alloc_page_id();
1258 let tsn_leaf_page = Page::new(tsn_leaf_id, Node::FreeListTsnLeaf(tmp_leaf));
1259 self.insert_dirty(tsn_leaf_page)?;
1260 let mut tsn_root_id = tsn_leaf_id;
1262 for pid in page_ids.into_iter().filter(|p| !initial_ids.contains(p)) {
1264 tsn_root_id = self.tsn_subtree_insert(mvcc, tsn_root_id, pid)?;
1265 }
1266 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1268 let Node::FreeListLeaf(dirty_leaf_node2) = &mut dirty_leaf_page.node else {
1269 return Err(DcbError::DatabaseCorrupted(
1270 "Expected FreeListLeaf node".to_string(),
1271 ));
1272 };
1273 dirty_leaf_node2.values[last_idx].page_ids.clear();
1274 dirty_leaf_node2.values[last_idx].root_id = tsn_root_id;
1275 if verbose {
1276 println!("Moved inline page IDs to TSN-subtree {:?}", tsn_root_id);
1277 }
1278 }
1279 _ => { }
1280 }
1281
1282 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1284 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1285 match plan {
1286 FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf => {
1287 dirty_leaf_node.push_new_key_and_value(tsn, freed_page_id);
1288 if verbose {
1289 println!(
1290 "Inserted first pair ({tsn:?} -> {freed_page_id:?}) in {dirty_leaf_page_id:?}: {:?}",
1291 dirty_leaf_node
1292 );
1293 }
1294 }
1295 FreePageIDInsertStrategy::PushPageIdOntoFreeListLeaf(last_idx) => {
1296 dirty_leaf_node.push_new_page_id(last_idx, freed_page_id);
1297 if verbose {
1298 println!(
1299 "Appended {freed_page_id:?} for existing last {tsn:?} in {dirty_leaf_page_id:?}: {:?}",
1300 dirty_leaf_node
1301 );
1302 }
1303 }
1304 FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree => { }
1306 FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree => { }
1307 FreePageIDInsertStrategy::SplitFreeListLeaf => {
1308 let (popped_key, mut popped_value) =
1309 dirty_leaf_node.pop_last_key_and_value()?;
1310 debug_assert_eq!(popped_key, tsn);
1311 if verbose {
1312 println!(
1313 "Split (last TSN) leaf {:?}: {:?}",
1314 dirty_leaf_page_id,
1315 dirty_leaf_node.clone()
1316 );
1317 }
1318 popped_value.page_ids.push(freed_page_id);
1320 let new_leaf_node = FreeListLeafNode {
1321 keys: vec![popped_key],
1322 values: vec![popped_value],
1323 };
1324 let new_leaf_page_id = self.alloc_page_id();
1325 let new_leaf_page =
1326 Page::new(new_leaf_page_id, Node::FreeListLeaf(new_leaf_node));
1327 if verbose {
1334 println!(
1335 "Created new leaf {:?} (moved last TSN): {:?}",
1336 new_leaf_page_id, new_leaf_page.node
1337 );
1338 }
1339 self.insert_dirty(new_leaf_page)?;
1340 split_info = Some((tsn, new_leaf_page_id));
1341 }
1342 FreePageIDInsertStrategy::CreateAndPromoteFreeListLeaf => {
1343 let new_leaf_node = FreeListLeafNode {
1345 keys: vec![tsn],
1346 values: vec![FreeListLeafValue {
1347 page_ids: vec![freed_page_id],
1348 root_id: PageID(0),
1349 }],
1350 };
1351 let new_leaf_page_id = self.alloc_page_id();
1352 let new_leaf_page =
1353 Page::new(new_leaf_page_id, Node::FreeListLeaf(new_leaf_node));
1354 if verbose {
1361 println!(
1362 "Created new leaf {:?} (new last TSN): {:?}",
1363 new_leaf_page_id, new_leaf_page.node
1364 );
1365 }
1366 self.insert_dirty(new_leaf_page)?;
1367 split_info = Some((tsn, new_leaf_page_id));
1368 }
1369 }
1370 } else {
1371 return Err(DcbError::DatabaseCorrupted(
1372 "Expected FreeListLeaf node".to_string(),
1373 ));
1374 }
1375 let mut current_replacement_info = replacement_info;
1377 while let Some(parent_page_id) = stack.pop() {
1378 let dirty_page_id = { self.get_dirty_page_id(parent_page_id)? };
1380 let parent_replacement_info: Option<(PageID, PageID)> = {
1381 if dirty_page_id != parent_page_id {
1382 Some((parent_page_id, dirty_page_id))
1383 } else {
1384 None
1385 }
1386 };
1387 let dirty_internal_page = self.get_mut_dirty(dirty_page_id)?;
1389
1390 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1391 if let Some((old_id, new_id)) = current_replacement_info {
1392 dirty_internal_node.replace_last_child_id(old_id, new_id)?;
1393 if verbose {
1394 println!(
1395 "Replaced {old_id:?} with {new_id:?} in {dirty_page_id:?}: {dirty_internal_node:?}"
1396 );
1397 }
1398 } else if verbose {
1399 println!("Nothing to replace in {dirty_page_id:?}")
1400 }
1401 } else {
1402 return Err(DcbError::DatabaseCorrupted(
1403 "Expected FreeListInternal node".to_string(),
1404 ));
1405 }
1406
1407 if let Some((promoted_key, promoted_page_id)) = split_info {
1408 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1409 dirty_internal_node
1411 .append_promoted_key_and_page_id(promoted_key, promoted_page_id)?;
1412
1413 if verbose {
1414 println!(
1415 "Appended promoted key {promoted_key:?} and child {promoted_page_id:?} in {dirty_page_id:?}: {dirty_internal_node:?}"
1416 );
1417 }
1418 } else {
1419 return Err(DcbError::DatabaseCorrupted(
1420 "Expected FreeListInternal node".to_string(),
1421 ));
1422 }
1423 }
1424
1425 if dirty_internal_page.calc_serialized_size() > mvcc.page_size {
1428 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1429 if verbose {
1430 println!("Splitting internal {dirty_page_id:?}...");
1431 }
1432 if dirty_internal_node.keys.len() < 3 || dirty_internal_node.child_ids.len() < 4
1435 {
1436 return Err(DcbError::DatabaseCorrupted(
1437 "Cannot split internal node with too few keys/children".to_string(),
1438 ));
1439 }
1440
1441 let (promoted_key, new_keys, new_child_ids) =
1443 dirty_internal_node.split_off()?;
1444
1445 assert_eq!(
1447 dirty_internal_node.keys.len() + 1,
1448 dirty_internal_node.child_ids.len()
1449 );
1450
1451 let new_internal_node = FreeListInternalNode {
1452 keys: new_keys,
1453 child_ids: new_child_ids,
1454 };
1455
1456 assert_eq!(
1458 new_internal_node.keys.len() + 1,
1459 new_internal_node.child_ids.len()
1460 );
1461
1462 let new_internal_page_id = self.alloc_page_id();
1464 let new_internal_page = Page::new(
1465 new_internal_page_id,
1466 Node::FreeListInternal(new_internal_node),
1467 );
1468 if verbose {
1469 println!(
1470 "Created internal {:?}: {:?}",
1471 new_internal_page_id, new_internal_page.node
1472 );
1473 }
1474 self.insert_dirty(new_internal_page)?;
1475
1476 split_info = Some((promoted_key, new_internal_page_id));
1477 } else {
1478 return Err(DcbError::DatabaseCorrupted(
1479 "Expected FreeListInternal node".to_string(),
1480 ));
1481 }
1482 } else {
1483 split_info = None;
1484 }
1485 current_replacement_info = parent_replacement_info;
1486 }
1487
1488 if let Some((old_id, new_id)) = current_replacement_info {
1489 if self.free_lists_tree_root_id == old_id {
1490 self.free_lists_tree_root_id = new_id;
1491 if verbose {
1492 println!("Replaced root {old_id:?} with {new_id:?}");
1493 }
1494 } else {
1495 return Err(DcbError::RootIDMismatch(old_id.0, new_id.0));
1496 }
1497 }
1498
1499 if let Some((promoted_key, promoted_page_id)) = split_info {
1500 let new_internal_node = FreeListInternalNode {
1502 keys: vec![promoted_key],
1503 child_ids: vec![self.free_lists_tree_root_id, promoted_page_id],
1504 };
1505
1506 let new_root_page_id = self.alloc_page_id();
1507 let new_root_page =
1508 Page::new(new_root_page_id, Node::FreeListInternal(new_internal_node));
1509 if verbose {
1510 println!(
1511 "Created new internal root {:?}: {:?}",
1512 new_root_page_id, new_root_page.node
1513 );
1514 }
1515 self.insert_dirty(new_root_page)?;
1516
1517 self.free_lists_tree_root_id = new_root_page_id;
1518 }
1519
1520 Ok(())
1521 }
1522
1523 fn tsn_subtree_insert(
1526 &mut self,
1527 mvcc: &Mvcc,
1528 root_id: PageID,
1529 key: PageID,
1530 ) -> DcbResult<PageID> {
1531 let verbose = self.verbose;
1532 let mut stack: Vec<(PageID, usize)> = Vec::new();
1533 let mut current_id = root_id;
1534 loop {
1535 let current_page_ref = self.get_page_ref(mvcc, current_id)?;
1536 match ¤t_page_ref.node {
1537 Node::FreeListTsnLeaf(_) => break,
1538 Node::FreeListTsnInternal(internal) => {
1539 let child_idx = match internal.keys.binary_search_by(|k| k.0.cmp(&key.0)) {
1541 Ok(idx) => idx + 1, Err(idx) => idx, };
1544 let next_id = internal.child_ids[child_idx];
1545 stack.push((current_id, child_idx));
1546 current_id = next_id;
1547 }
1548 other => {
1549 return Err(DcbError::DatabaseCorrupted(format!(
1550 "Unexpected node type in TSN-subtree during insert: {}",
1551 other.type_name()
1552 )));
1553 }
1554 }
1555 }
1556
1557 {
1560 let leaf_page = self.get_mut_dirty(current_id)?;
1561 let Node::FreeListTsnLeaf(ref mut leaf) = leaf_page.node else {
1562 return Err(DcbError::DatabaseCorrupted(
1563 "Expected TSN-subtree leaf".to_string(),
1564 ));
1565 };
1566 match leaf.page_ids.binary_search_by(|pid| pid.0.cmp(&key.0)) {
1568 Ok(_) => {
1569 if verbose {
1571 println!("Duplicate PageID {:?} ignored in TSN-subtree", key);
1572 }
1573 }
1574 Err(ins) => {
1575 leaf.page_ids.insert(ins, key);
1576 if leaf.calc_serialized_size() <= mvcc.max_node_size {
1577 return Ok(root_id);
1578 }
1579 let mid = leaf.page_ids.len() / 2; let right_ids: Vec<PageID> = leaf.page_ids.split_off(mid);
1582 let promoted_key = right_ids[0];
1583 let right_leaf_id = self.alloc_page_id();
1585 let right_leaf_node = FreeListTsnLeafNode {
1586 page_ids: right_ids,
1587 };
1588 let right_leaf_page =
1589 Page::new(right_leaf_id, Node::FreeListTsnLeaf(right_leaf_node));
1590 self.insert_dirty(right_leaf_page)?;
1591 let mut promoted: Option<(PageID, PageID)> =
1593 Some((promoted_key, right_leaf_id));
1594 for (parent_id, child_idx) in stack.into_iter().rev() {
1596 if let Some((prom_key, prom_right_id)) = promoted.take() {
1597 let parent_page = self.get_mut_dirty(parent_id)?;
1598 let Node::FreeListTsnInternal(ref mut parent_node) = parent_page.node
1599 else {
1600 return Err(DcbError::DatabaseCorrupted(
1601 "Expected TSN-subtree internal".to_string(),
1602 ));
1603 };
1604
1605 parent_node.keys.insert(child_idx, prom_key);
1607 parent_node.child_ids.insert(child_idx + 1, prom_right_id);
1608 if parent_node.calc_serialized_size() <= mvcc.max_node_size {
1609 current_id = parent_id;
1611 continue;
1612 }
1613 let total_keys = parent_node.keys.len();
1615 debug_assert!(
1616 total_keys >= 2,
1617 "splitting parent with <2 keys after insert"
1618 );
1619 let mid = total_keys / 2; let promote_up_key = parent_node.keys[mid];
1621 let left_keys: Vec<PageID> = parent_node.keys[..mid].to_vec();
1623 let left_child_ids: Vec<PageID> =
1624 parent_node.child_ids[..=mid].to_vec();
1625 let right_keys: Vec<PageID> = parent_node.keys[mid + 1..].to_vec();
1627 let right_child_ids: Vec<PageID> =
1628 parent_node.child_ids[mid + 1..].to_vec();
1629 if right_child_ids.len() != right_keys.len() + 1 {
1630 return Err(DcbError::DatabaseCorrupted(
1631 "TSN-subtree internal split produced invalid right arity"
1632 .to_string(),
1633 ));
1634 }
1635 if left_child_ids.len() != left_keys.len() + 1 {
1636 return Err(DcbError::DatabaseCorrupted(
1637 "TSN-subtree internal split produced invalid left arity"
1638 .to_string(),
1639 ));
1640 }
1641 parent_node.keys = left_keys;
1643 parent_node.child_ids = left_child_ids;
1644 let right_internal_id = self.alloc_page_id();
1646 let right_internal =
1647 crate::free_lists_tree_nodes::FreeListTsnInternalNode {
1648 keys: right_keys,
1649 child_ids: right_child_ids,
1650 };
1651 let right_internal_page = Page::new(
1652 right_internal_id,
1653 Node::FreeListTsnInternal(right_internal),
1654 );
1655 self.insert_dirty(right_internal_page)?;
1656 promoted = Some((promote_up_key, right_internal_id));
1658 }
1659 current_id = parent_id;
1660 }
1661 if let Some((prom_key, prom_right_id)) = promoted.take() {
1663 let new_root_id = self.alloc_page_id();
1664 let left_id = current_id;
1665 let new_root = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
1666 keys: vec![prom_key],
1667 child_ids: vec![left_id, prom_right_id],
1668 };
1669 let new_root_page =
1670 Page::new(new_root_id, Node::FreeListTsnInternal(new_root));
1671 self.insert_dirty(new_root_page)?;
1672 if verbose {
1673 println!("Promoted new TSN-subtree root {:?}", new_root_id);
1674 }
1675 return Ok(new_root_id);
1676 }
1677 }
1678 };
1679 Ok(root_id)
1680 }
1681 }
1682
1683 pub fn remove_free_page_id(
1684 &mut self,
1685 mvcc: &Mvcc,
1686 tsn: Tsn,
1687 used_page_id: PageID,
1688 ) -> DcbResult<()> {
1689 let verbose = self.verbose;
1690 if verbose {
1691 println!();
1692 println!("Removing {used_page_id:?} from {tsn:?}...");
1693 println!("Root is {:?}", self.free_lists_tree_root_id);
1694 }
1695 let mut current_page_id = self.free_lists_tree_root_id;
1697
1698 let mut stack: Vec<PageID> = Vec::new();
1700 let mut removed_page_ids: Vec<PageID> = Vec::new();
1701
1702 loop {
1703 let current_page_ref = self.get_page_ref(mvcc, current_page_id)?;
1704 if matches!(current_page_ref.node, Node::FreeListLeaf(_)) {
1705 break;
1706 }
1707 if let Node::FreeListInternal(internal_node) = ¤t_page_ref.node {
1708 if verbose {
1709 println!("Page {:?} is internal node", current_page_ref.page_id);
1710 }
1711 stack.push(current_page_id);
1712 current_page_id = *internal_node.child_ids.first().unwrap();
1713 } else {
1714 return Err(DcbError::DatabaseCorrupted(
1715 "Expected FreeListInternal node".to_string(),
1716 ));
1717 }
1718 }
1719 if verbose {
1720 println!("Page {current_page_id:?} is leaf node");
1721 }
1722
1723 let mut replacement_info: Option<(PageID, PageID)> = None;
1726 let mut removal_info = None;
1727
1728 let leaf_snapshot = { self.get_page_ref(mvcc, current_page_id)? };
1730 let Node::FreeListLeaf(leaf_node_ro) = &leaf_snapshot.node else {
1731 return Err(DcbError::DatabaseCorrupted(
1732 "Expected FreeListLeaf node".to_string(),
1733 ));
1734 };
1735 if leaf_node_ro.keys.is_empty() || leaf_node_ro.keys[0] != tsn {
1736 return Err(DcbError::DatabaseCorrupted(format!(
1737 "Expected TSN {} not found: {:?}",
1738 tsn.0, leaf_node_ro
1739 )));
1740 }
1741
1742 let leaf_value_root_id = leaf_node_ro.values[0].root_id;
1743 if leaf_value_root_id == PageID(0) {
1749 let dirty_page_id = { self.get_dirty_page_id(current_page_id)? };
1751 if dirty_page_id != current_page_id {
1752 replacement_info = Some((current_page_id, dirty_page_id));
1753 }
1754 let dirty_leaf_page = self.get_mut_dirty(dirty_page_id)?;
1755 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1756 let leaf_value = &mut dirty_leaf_node.values[0];
1757 if let Some(pos) = leaf_value
1758 .page_ids
1759 .iter()
1760 .position(|&id| id == used_page_id)
1761 {
1762 leaf_value.page_ids.remove(pos);
1763 } else {
1764 return Err(DcbError::DatabaseCorrupted(format!(
1765 "{used_page_id:?} not found in {tsn:?}"
1766 )));
1767 }
1768 if verbose {
1769 println!("Removed {used_page_id:?} from {tsn:?} in {dirty_page_id:?}");
1770 }
1771 if leaf_value.page_ids.is_empty() {
1772 dirty_leaf_node.keys.remove(0);
1773 dirty_leaf_node.values.remove(0);
1774 if verbose {
1775 println!("Removed {tsn:?} from {dirty_page_id:?}");
1776 }
1777 if dirty_leaf_node.keys.is_empty() {
1778 if verbose {
1779 println!("Empty leaf page {dirty_page_id:?}: {dirty_leaf_node:?}");
1780 }
1781 removal_info = Some(dirty_page_id);
1782 } else if verbose {
1783 println!("Leaf page not empty {dirty_page_id:?}: {dirty_leaf_node:?}");
1784 }
1785 } else if verbose {
1786 println!("Leaf value not empty {tsn:?}: {leaf_value:?}");
1787 }
1788 } else {
1789 return Err(DcbError::DatabaseCorrupted(
1790 "Expected FreeListLeaf node".to_string(),
1791 ));
1792 }
1793 } else {
1794 let tsn_root_id = leaf_value_root_id;
1796 let dirty_tsn_root_id = { self.get_dirty_page_id(tsn_root_id)? };
1797 let mut tsn_root_replaced: Option<PageID> = None;
1798 let dirty_tsn_root_page = self.get_mut_dirty(dirty_tsn_root_id)?;
1799 let mut tsn_leaf_became_empty = false;
1800 let mut tsn_child_leaf_became_empty = false;
1801 match &mut dirty_tsn_root_page.node {
1802 Node::FreeListTsnLeaf(tsn_leaf_node) => {
1803 if let Some(pos) = tsn_leaf_node
1804 .page_ids
1805 .iter()
1806 .position(|&id| id == used_page_id)
1807 {
1808 tsn_leaf_node.page_ids.remove(pos);
1809 } else {
1810 return Err(DcbError::DatabaseCorrupted(format!(
1811 "{used_page_id:?} not found in TSN-subtree for {tsn:?}"
1812 )));
1813 }
1814 if verbose {
1815 println!(
1816 "Removed {used_page_id:?} from TSN-subtree leaf {dirty_tsn_root_id:?} for {tsn:?}"
1817 );
1818 }
1819 if tsn_leaf_node.page_ids.is_empty() {
1820 tsn_leaf_became_empty = true;
1821 removed_page_ids.push(dirty_tsn_root_id);
1822 }
1823 }
1824 Node::FreeListTsnInternal(_) => {
1825 let mut path: Vec<(PageID, usize)> = Vec::new();
1835 let mut current_id = dirty_tsn_root_id;
1836 loop {
1837 let node_owned = { self.get_page_ref(mvcc, current_id)?.node.clone() };
1838 match node_owned {
1839 Node::FreeListTsnLeaf(_) => {
1840 break; }
1842 Node::FreeListTsnInternal(internal) => {
1843 let mut child_idx = 0usize;
1845 while child_idx < internal.keys.len()
1846 && used_page_id >= internal.keys[child_idx]
1847 {
1848 child_idx += 1;
1849 }
1850 let next_id = internal.child_ids[child_idx];
1851 path.push((current_id, child_idx));
1852 current_id = next_id;
1853 }
1854 other => {
1855 return Err(DcbError::DatabaseCorrupted(format!(
1856 "Unexpected node type in TSN-subtree during descent: {}",
1857 other.type_name()
1858 )));
1859 }
1860 }
1861 }
1862
1863 let mut dirty_child_id = { self.get_dirty_page_id(current_id)? };
1865 {
1866 let child_page = self.get_mut_dirty(dirty_child_id)?;
1867 match &mut child_page.node {
1868 Node::FreeListTsnLeaf(leaf_node) => {
1869 if let Some(pos) =
1870 leaf_node.page_ids.iter().position(|&id| id == used_page_id)
1871 {
1872 leaf_node.page_ids.remove(pos);
1873 } else {
1874 return Err(DcbError::DatabaseCorrupted(format!(
1875 "{used_page_id:?} not found in TSN-subtree for {tsn:?}"
1876 )));
1877 }
1878 if verbose {
1879 println!(
1880 "Removed {used_page_id:?} from TSN-subtree leaf {dirty_child_id:?} for {tsn:?}"
1881 );
1882 }
1883 if leaf_node.page_ids.is_empty() {
1884 tsn_child_leaf_became_empty = true;
1885 removed_page_ids.push(dirty_child_id);
1886 }
1887 }
1888 other => {
1889 return Err(DcbError::DatabaseCorrupted(format!(
1890 "Expected TSN-subtree leaf, got {}",
1891 other.type_name()
1892 )));
1893 }
1894 }
1895 }
1896
1897 let mut subtree_emptied = false;
1899 let mut new_root_id_opt: Option<PageID> = None;
1900
1901 let path_len = path.len();
1902 for (level, (parent_id, child_idx)) in path.into_iter().rev().enumerate() {
1903 let parent_dirty_id = { self.get_dirty_page_id(parent_id)? };
1905
1906 let parent_page = self.get_mut_dirty(parent_dirty_id)?;
1911 let Node::FreeListTsnInternal(ref mut parent_node) = parent_page.node
1912 else {
1913 return Err(DcbError::DatabaseCorrupted(
1914 "Expected TSN-subtree internal node".to_string(),
1915 ));
1916 };
1917
1918 if tsn_child_leaf_became_empty && level == 0 {
1919 parent_node.child_ids.remove(child_idx);
1921 if !parent_node.keys.is_empty() {
1922 let key_remove_idx = if child_idx == 0 { 0 } else { child_idx - 1 };
1923 if key_remove_idx < parent_node.keys.len() {
1924 parent_node.keys.remove(key_remove_idx);
1925 }
1926 }
1927
1928 match parent_node.child_ids.len() {
1930 0 => {
1931 subtree_emptied = true;
1933 removed_page_ids.push(parent_dirty_id);
1934 }
1935 1 => {
1936 let remaining_child = parent_node.child_ids[0];
1938 removed_page_ids.push(parent_dirty_id);
1939 dirty_child_id = remaining_child;
1941 new_root_id_opt = Some(remaining_child);
1942 }
1943 _ => {
1944 dirty_child_id = parent_dirty_id;
1946 if level == path_len - 1 {
1947 new_root_id_opt = Some(parent_dirty_id);
1949 }
1950 }
1951 }
1952 } else {
1953 if parent_node.child_ids[child_idx] != dirty_child_id {
1955 parent_node.child_ids[child_idx] = dirty_child_id;
1956 }
1957 dirty_child_id = parent_dirty_id;
1959 if level == path_len - 1 {
1960 new_root_id_opt = Some(parent_dirty_id);
1961 }
1962 }
1963 }
1964
1965 if subtree_emptied {
1968 tsn_leaf_became_empty = true;
1969 } else if let Some(new_root) = new_root_id_opt
1970 && new_root != dirty_tsn_root_id
1971 {
1972 tsn_root_replaced = Some(new_root);
1973 }
1974 }
1975 _ => {
1976 return Err(DcbError::DatabaseCorrupted(
1977 "Expected TSN-subtree node".to_string(),
1978 ));
1979 }
1980 }
1981
1982 let dirty_page_id = { self.get_dirty_page_id(current_page_id)? };
1984 if dirty_page_id != current_page_id {
1985 replacement_info = Some((current_page_id, dirty_page_id));
1986 }
1987 let dirty_leaf_page = self.get_mut_dirty(dirty_page_id)?;
1988 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1989 if dirty_leaf_node.keys.is_empty() || dirty_leaf_node.keys[0] != tsn {
1991 return Err(DcbError::DatabaseCorrupted(format!(
1992 "Expected TSN {} not found in dirty leaf: {:?}",
1993 tsn.0, dirty_leaf_node
1994 )));
1995 }
1996 if tsn_leaf_became_empty {
1997 dirty_leaf_node.keys.remove(0);
1999 dirty_leaf_node.values.remove(0);
2000 if verbose {
2001 println!("Removed {tsn:?} from {dirty_page_id:?}");
2002 }
2003 if dirty_leaf_node.keys.is_empty() {
2004 if verbose {
2005 println!("Empty leaf page {dirty_page_id:?}: {dirty_leaf_node:?}");
2006 }
2007 removal_info = Some(dirty_page_id);
2008 } else if verbose {
2009 println!("Leaf page not empty {dirty_page_id:?}: {dirty_leaf_node:?}");
2010 }
2011 } else if let Some(new_root) = tsn_root_replaced {
2012 dirty_leaf_node.values[0].root_id = new_root;
2014 } else if dirty_tsn_root_id != tsn_root_id {
2015 dirty_leaf_node.values[0].root_id = dirty_tsn_root_id;
2017 }
2018 } else {
2019 return Err(DcbError::DatabaseCorrupted(
2020 "Expected FreeListLeaf node".to_string(),
2021 ));
2022 }
2023 }
2024
2025 let mut current_replacement_info = replacement_info;
2027
2028 while let Some(parent_page_id) = stack.pop() {
2029 let dirty_page_id = { self.get_dirty_page_id(parent_page_id)? };
2031 let parent_replacement_info: Option<(PageID, PageID)> = {
2032 if dirty_page_id != parent_page_id {
2033 Some((parent_page_id, dirty_page_id))
2034 } else {
2035 None
2036 }
2037 };
2038 let dirty_internal_page = self.get_mut_dirty(dirty_page_id)?;
2040
2041 if let Some((old_id, new_id)) = current_replacement_info {
2042 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
2043 if dirty_internal_node.child_ids[0] == old_id {
2045 dirty_internal_node.child_ids[0] = new_id;
2046 if verbose {
2047 println!(
2048 "Replaced {old_id:?} with {new_id:?} in {dirty_page_id:?}: {dirty_internal_page:?}"
2049 );
2050 }
2051 } else {
2052 return Err(DcbError::DatabaseCorrupted("Child ID mismatch".to_string()));
2053 }
2054 } else {
2055 return Err(DcbError::DatabaseCorrupted(
2056 "Expected FreeListInternal node".to_string(),
2057 ));
2058 }
2059 }
2060 current_replacement_info = parent_replacement_info;
2061
2062 if let Some(removed_page_id) = removal_info {
2063 removed_page_ids.push(removed_page_id);
2064
2065 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
2066 if dirty_internal_node.child_ids[0] != removed_page_id {
2068 return Err(DcbError::DatabaseCorrupted("Child ID mismatch".to_string()));
2069 }
2070 if dirty_internal_node.keys.is_empty() {
2071 return Err(DcbError::DatabaseCorrupted(
2072 "Empty internal node keys".to_string(),
2073 ));
2074 }
2075 dirty_internal_node.child_ids.remove(0);
2076 dirty_internal_node.keys.remove(0);
2077 if verbose {
2078 println!(
2079 "Removed {removed_page_id:?} from {dirty_page_id:?}: {dirty_internal_node:?}"
2080 );
2081 }
2082
2083 if dirty_internal_node.keys.is_empty() {
2085 if verbose {
2086 println!(
2087 "Empty internal page {dirty_page_id:?}: {dirty_internal_node:?}"
2088 );
2089 }
2090 assert_eq!(dirty_internal_node.child_ids.len(), 1);
2091 let orphaned_child_id = dirty_internal_node.child_ids[0];
2092
2093 removed_page_ids.push(dirty_page_id);
2094
2095 if let Some((old_id, _)) = parent_replacement_info {
2096 current_replacement_info = Some((old_id, orphaned_child_id));
2097 } else {
2098 current_replacement_info = Some((dirty_page_id, orphaned_child_id));
2099 }
2100 }
2101 } else {
2102 return Err(DcbError::DatabaseCorrupted(
2103 "Expected FreeListInternal node".to_string(),
2104 ));
2105 }
2106
2107 removal_info = None;
2108 }
2109 }
2110
2111 for &removed_page_id in &removed_page_ids {
2112 self.append_freed_page_id(removed_page_id);
2113 }
2114
2115 if let Some((old_id, new_id)) = current_replacement_info {
2118 if self.free_lists_tree_root_id == old_id {
2119 self.append_freed_page_id(old_id);
2120 self.free_lists_tree_root_id = new_id;
2121 if verbose {
2122 println!("Replaced root {old_id:?} with {new_id:?}");
2123 }
2124 } else {
2125 return Err(DcbError::RootIDMismatch(old_id.0, new_id.0));
2126 }
2127 }
2128
2129 Ok(())
2130 }
2131}
2132
2133enum FreePageIDInsertStrategy {
2134 PushTsnOntoFreeListLeaf,
2135 PushPageIdOntoFreeListLeaf(usize),
2136 PushPageIdOntoExistingTsnSubtree,
2137 MoveTsnToNewTsnSubtree,
2138 SplitFreeListLeaf,
2139 CreateAndPromoteFreeListLeaf,
2140}
2141
2142pub struct Reader {
2144 pub header_page_id: PageID,
2145 pub tsn: Tsn,
2146 pub events_tree_root_id: PageID,
2147 pub tags_tree_root_id: PageID,
2148 pub next_position: Position,
2149 pub tracking_tree_root_id: PageID,
2150 reader_id: usize,
2151 reader_tsns: Arc<DashMap<usize, Tsn>>,
2152}
2153
2154impl Drop for Reader {
2155 fn drop(&mut self) {
2156 self.reader_tsns.remove(&self.reader_id);
2158 }
2159}
2160
2161#[cfg(test)]
2162mod tests {
2163 use super::*;
2164 use crate::free_lists_tree_nodes::FreeListLeafValue;
2165 use serial_test::serial;
2166 use tempfile::tempdir;
2167
2168 static VERBOSE: bool = false;
2169
2170 #[test]
2171 #[serial]
2172 fn test_mvcc_init() {
2173 let temp_dir = tempdir().unwrap();
2174 let db_path = temp_dir.path().join("mvcc-test.db");
2175
2176 {
2177 let db = Mvcc::new(
2178 VERBOSE,
2179 StorageOptions::default()
2180 .db_path(db_path.clone())
2181 .page_size(4096),
2182 )
2183 .unwrap();
2184 assert!(db.pager.is_file_new);
2185 }
2186
2187 {
2188 let db = Mvcc::new(
2189 VERBOSE,
2190 StorageOptions::default()
2191 .db_path(db_path.clone())
2192 .page_size(4096),
2193 )
2194 .unwrap();
2195 assert!(!db.pager.is_file_new);
2196 }
2197 }
2198
2199 #[test]
2200 #[serial]
2201 fn test_write_transaction_incrementing_tsn_and_alternating_header() {
2202 let temp_dir = tempdir().unwrap();
2203 let db_path = temp_dir.path().join("mvcc-test.db");
2204 let db = Mvcc::new(
2205 VERBOSE,
2206 StorageOptions::default().
2207 db_path(db_path).
2208 page_size(4096),
2209 )
2210 .unwrap();
2211
2212 {
2213 let mut writer = db.writer().unwrap();
2214 assert_eq!(Tsn(1), writer.tsn);
2215 assert_eq!(PageID(0), writer.header_page_id);
2216 db.commit(&mut writer).unwrap();
2217 }
2218
2219 {
2220 let mut writer = db.writer().unwrap();
2221 assert_eq!(Tsn(2), writer.tsn);
2222 assert_eq!(PageID(1), writer.header_page_id);
2223 db.commit(&mut writer).unwrap();
2224 }
2225
2226 {
2227 let mut writer = db.writer().unwrap();
2228 assert_eq!(Tsn(3), writer.tsn);
2229 assert_eq!(PageID(0), writer.header_page_id);
2230 db.commit(&mut writer).unwrap();
2231 }
2232
2233 {
2234 let mut writer = db.writer().unwrap();
2235 assert_eq!(Tsn(4), writer.tsn);
2236 assert_eq!(PageID(1), writer.header_page_id);
2237 db.commit(&mut writer).unwrap();
2238 }
2239
2240 {
2241 let mut writer = db.writer().unwrap();
2242 assert_eq!(Tsn(5), writer.tsn);
2243 assert_eq!(PageID(0), writer.header_page_id);
2244 db.commit(&mut writer).unwrap();
2245 }
2246 }
2247
2248 #[test]
2249 #[serial]
2250 fn test_read_transaction_header_and_tsn() {
2251 let temp_dir = tempdir().unwrap();
2252 let db_path = temp_dir.path().join("mvcc-test.db");
2253 let db = Mvcc::new(
2254 VERBOSE,
2255 StorageOptions::default().db_path(db_path).page_size(4096),
2256 )
2257 .unwrap();
2258
2259 {
2261 assert_eq!(0, db.reader_tsns.len());
2262 let reader = db.reader().unwrap();
2263 assert_eq!(1, db.reader_tsns.len());
2264 assert_eq!(
2265 vec![Tsn(0)],
2266 db.reader_tsns
2267 .iter()
2268 .map(|r| *r.value())
2269 .collect::<Vec<_>>()
2270 );
2271 assert_eq!(PageID(0), reader.header_page_id);
2272 assert_eq!(Tsn(0), reader.tsn);
2273 }
2274 assert_eq!(0, db.reader_tsns.len());
2275
2276 {
2278 let reader1 = db.reader().unwrap();
2279 assert_eq!(
2280 vec![Tsn(0)],
2281 db.reader_tsns
2282 .iter()
2283 .map(|r| *r.value())
2284 .collect::<Vec<_>>()
2285 );
2286 assert_eq!(PageID(0), reader1.header_page_id);
2287 assert_eq!(Tsn(0), reader1.tsn);
2288
2289 {
2290 let reader2 = db.reader().unwrap();
2291 assert_eq!(
2292 vec![Tsn(0), Tsn(0)],
2293 db.reader_tsns
2294 .iter()
2295 .map(|r| *r.value())
2296 .collect::<Vec<_>>()
2297 );
2298 assert_eq!(PageID(0), reader2.header_page_id);
2299 assert_eq!(Tsn(0), reader2.tsn);
2300
2301 {
2302 let reader3 = db.reader().unwrap();
2303 assert_eq!(
2304 vec![Tsn(0), Tsn(0), Tsn(0)],
2305 db.reader_tsns
2306 .iter()
2307 .map(|r| *r.value())
2308 .collect::<Vec<_>>()
2309 );
2310 assert_eq!(PageID(0), reader3.header_page_id);
2311 assert_eq!(Tsn(0), reader3.tsn);
2312 }
2313 }
2314 }
2315 assert_eq!(0, db.reader_tsns.len());
2316
2317 {
2319 let mut writer = db.writer().unwrap();
2320 assert_eq!(0, db.reader_tsns.len());
2321 assert_eq!(Tsn(1), writer.tsn);
2322 assert_eq!(PageID(0), writer.header_page_id);
2323 db.commit(&mut writer).unwrap();
2324 }
2325
2326 {
2328 let reader = db.reader().unwrap();
2329 assert_eq!(
2330 vec![Tsn(1)],
2331 db.reader_tsns
2332 .iter()
2333 .map(|r| *r.value())
2334 .collect::<Vec<_>>()
2335 );
2336 assert_eq!(PageID(1), reader.header_page_id);
2337 assert_eq!(Tsn(1), reader.tsn);
2338 }
2339 }
2340
2341 #[test]
2342 #[serial]
2343 fn test_copy_on_write_page_reuse() {
2344 let temp_dir = tempdir().unwrap();
2345 let db_path = temp_dir.path().join("mvcc-test.db");
2346 let db = Mvcc::new(
2347 VERBOSE,
2348 StorageOptions::default().db_path(db_path).page_size(4096),
2349 )
2350 .unwrap();
2351 {
2353 let mut writer = db.writer().unwrap();
2354
2355 assert_eq!(0, writer.reusable_page_ids.len());
2357
2358 assert_eq!(PageID(2), writer.free_lists_tree_root_id);
2360
2361 assert_eq!(PageID(3), writer.events_tree_root_id);
2363
2364 let free_page_id = writer.alloc_page_id();
2366 assert_eq!(PageID(5), free_page_id);
2367 writer
2368 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2369 .unwrap();
2370
2371 assert_eq!(1, writer.dirty.len());
2373 assert_eq!(PageID(6), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2374
2375 assert_eq!(1, writer.freed_page_ids.len());
2377 assert_eq!(PageID(2), writer.freed_page_ids[0]);
2378
2379 db.commit(&mut writer).unwrap();
2380 }
2381
2382 {
2384 let mut writer = db.writer().unwrap();
2385
2386 assert_eq!(2, writer.reusable_page_ids.len());
2388 assert_eq!((PageID(5), Tsn(1)), writer.reusable_page_ids[0]);
2389 assert_eq!((PageID(2), Tsn(1)), writer.reusable_page_ids[1]);
2390
2391 assert_eq!(PageID(6), writer.free_lists_tree_root_id);
2393
2394 assert_eq!(PageID(3), writer.events_tree_root_id);
2396
2397 let free_page_id = writer.alloc_page_id();
2399 assert_eq!(PageID(5), free_page_id);
2400 writer
2401 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2402 .unwrap();
2403
2404 assert_eq!(1, writer.dirty.len());
2406 assert_eq!(PageID(2), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2407
2408 assert_eq!(1, writer.freed_page_ids.len());
2410 assert_eq!(PageID(6), writer.freed_page_ids[0]);
2411
2412 db.commit(&mut writer).unwrap();
2413 }
2414
2415 {
2417 let mut writer = db.writer().unwrap();
2418
2419 assert_eq!(2, writer.reusable_page_ids.len());
2421 assert_eq!((PageID(5), Tsn(2)), writer.reusable_page_ids[0]);
2422 assert_eq!((PageID(6), Tsn(2)), writer.reusable_page_ids[1]);
2423
2424 assert_eq!(PageID(2), writer.free_lists_tree_root_id);
2426
2427 assert_eq!(PageID(3), writer.events_tree_root_id);
2429
2430 let free_page_id = writer.alloc_page_id();
2432 assert_eq!(PageID(5), free_page_id);
2433 writer
2434 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2435 .unwrap();
2436
2437 assert_eq!(1, writer.dirty.len());
2439 assert_eq!(PageID(6), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2440
2441 assert_eq!(1, writer.freed_page_ids.len());
2443 assert_eq!(PageID(2), writer.freed_page_ids[0]);
2444
2445 db.commit(&mut writer).unwrap();
2446 }
2447 }
2448
2449 mod free_list_tree_tests {
2451 use super::*;
2452 use serial_test::serial;
2453 use tempfile::tempdir;
2454
2455 fn construct_mvcc(page_size: usize) -> (tempfile::TempDir, Mvcc) {
2457 let temp_dir = tempdir().unwrap();
2458 let db_path = temp_dir.path().join("mvcc-test.db");
2459 let db = Mvcc::new(
2460 VERBOSE,
2461 StorageOptions::default().db_path(db_path).page_size(page_size),
2462 )
2463 .unwrap();
2464 (temp_dir, db)
2465 }
2466
2467 #[test]
2468 #[serial]
2469 fn test_find_reusable_page_ids_empty_no_entries() {
2470 let (_temp_dir, db) = construct_mvcc(64);
2471 let writer = db.writer().unwrap();
2473 assert_eq!(0, writer.reusable_page_ids.len());
2474 }
2475
2476 #[test]
2477 #[serial]
2478 fn test_find_reusable_page_ids_leaf() {
2479 let (_temp_dir, db) = construct_mvcc(64);
2480 let mut writer = db.writer().unwrap();
2481
2482 let (tsn, free_pid1, free_pid2) = build_free_list_tree_leaf(&mut writer);
2483
2484 writer.find_reusable_page_ids(&db).unwrap();
2486 assert_eq!(2, writer.reusable_page_ids.len());
2487 assert_eq!((free_pid1, tsn), writer.reusable_page_ids[0]);
2488 assert_eq!((free_pid2, tsn), writer.reusable_page_ids[1]);
2489 }
2490
2491 #[test]
2492 #[serial]
2493 fn test_find_reusable_page_ids_internal_leaf() {
2494 let (_temp_dir, db) = construct_mvcc(128);
2495 let mut writer = db.writer().unwrap();
2496
2497 let (tsn1, tsn2, pid1, pid2, pid3, pid4) =
2498 build_free_list_tree_internal_leaf(&mut writer);
2499
2500 writer.find_reusable_page_ids(&db).unwrap();
2502 assert_eq!(4, writer.reusable_page_ids.len());
2504 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
2505 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
2506 assert_eq!((pid3, tsn2), writer.reusable_page_ids[2]);
2507 assert_eq!((pid4, tsn2), writer.reusable_page_ids[3]);
2508 }
2509
2510 #[test]
2511 #[serial]
2512 fn test_find_reusable_page_ids_internal_internal_leaf() {
2513 let (_temp_dir, db) = construct_mvcc(128);
2514 let mut writer = db.writer().unwrap();
2515
2516 let (tsn1, tsn2, tsn3, tsn4, pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8) =
2517 build_free_list_tree_internal_internal_leaf(&mut writer);
2518
2519 writer.find_reusable_page_ids(&db).unwrap();
2521 assert_eq!(8, writer.reusable_page_ids.len());
2523 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
2524 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
2525 assert_eq!((pid3, tsn2), writer.reusable_page_ids[2]);
2526 assert_eq!((pid4, tsn2), writer.reusable_page_ids[3]);
2527 assert_eq!((pid5, tsn3), writer.reusable_page_ids[4]);
2528 assert_eq!((pid6, tsn3), writer.reusable_page_ids[5]);
2529 assert_eq!((pid7, tsn4), writer.reusable_page_ids[6]);
2530 assert_eq!((pid8, tsn4), writer.reusable_page_ids[7]);
2531 }
2532
2533 #[test]
2534 #[serial]
2535 fn test_find_reusable_page_ids_leaf_tsn_subtree_leaf() {
2536 let (_temp_dir, db) = construct_mvcc(64);
2537 let mut writer = db.writer().unwrap();
2538
2539 let (pid1, pid2, tsn) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
2540
2541 writer.find_reusable_page_ids(&db).unwrap();
2543 assert_eq!(2, writer.reusable_page_ids.len());
2545 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2546 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2547 }
2548
2549 #[test]
2550 #[serial]
2551 fn test_find_reusable_page_ids_leaf_tsn_subtree_internal_leaf() {
2552 let (_temp_dir, db) = construct_mvcc(64);
2553 let mut writer = db.writer().unwrap();
2554
2555 let (pid1, pid2, pid3, pid4, tsn) =
2556 build_free_list_tree_leaf_tsn_subtree_internal_leaf(&mut writer);
2557
2558 writer.find_reusable_page_ids(&db).unwrap();
2560 assert_eq!(4, writer.reusable_page_ids.len());
2562 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2563 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2564 assert_eq!((pid3, tsn), writer.reusable_page_ids[2]);
2565 assert_eq!((pid4, tsn), writer.reusable_page_ids[3]);
2566 }
2567
2568 #[test]
2569 #[serial]
2570 fn test_find_reusable_page_ids_leaf_tsn_subtree_internal_internal_leaf() {
2571 let (_temp_dir, db) = construct_mvcc(64);
2572 let mut writer = db.writer().unwrap();
2573
2574 let (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn) =
2575 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut writer);
2576
2577 writer.find_reusable_page_ids(&db).unwrap();
2579 assert_eq!(8, writer.reusable_page_ids.len());
2581 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2582 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2583 assert_eq!((pid3, tsn), writer.reusable_page_ids[2]);
2584 assert_eq!((pid4, tsn), writer.reusable_page_ids[3]);
2585 assert_eq!((pid5, tsn), writer.reusable_page_ids[4]);
2586 assert_eq!((pid6, tsn), writer.reusable_page_ids[5]);
2587 assert_eq!((pid7, tsn), writer.reusable_page_ids[6]);
2588 assert_eq!((pid8, tsn), writer.reusable_page_ids[7]);
2589 }
2590
2591 fn build_free_list_tree_leaf(writer: &mut Writer) -> (Tsn, PageID, PageID) {
2592 let tsn = Tsn(123);
2594 let free_pid1 = writer.alloc_page_id();
2595 let free_pid2 = writer.alloc_page_id();
2596 let leaf = FreeListLeafNode {
2597 keys: vec![tsn],
2598 values: vec![FreeListLeafValue {
2599 page_ids: vec![free_pid1, free_pid2],
2600 root_id: PageID(0),
2601 }],
2602 };
2603 let root_id = writer.alloc_page_id();
2604 let page = Page::new(root_id, Node::FreeListLeaf(leaf));
2605 writer.insert_dirty(page).unwrap();
2606 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2607 writer.free_lists_tree_root_id = root_id;
2608 (tsn, free_pid1, free_pid2)
2609 }
2610
2611 fn build_free_list_tree_internal_leaf(
2612 writer: &mut Writer,
2613 ) -> (Tsn, Tsn, PageID, PageID, PageID, PageID) {
2614 let tsn1 = Tsn(10);
2616 let tsn2 = Tsn(20);
2617 let pid1 = writer.alloc_page_id();
2618 let pid2 = writer.alloc_page_id();
2619 let pid3 = writer.alloc_page_id();
2620 let pid4 = writer.alloc_page_id();
2621
2622 let leaf1_id = writer.alloc_page_id();
2623 let leaf2_id = writer.alloc_page_id();
2624 let leaf1 = FreeListLeafNode {
2625 keys: vec![tsn1],
2626 values: vec![FreeListLeafValue {
2627 page_ids: vec![pid1, pid2],
2628 root_id: PageID(0),
2629 }],
2630 };
2631 let leaf2 = FreeListLeafNode {
2632 keys: vec![tsn2],
2633 values: vec![FreeListLeafValue {
2634 page_ids: vec![pid3, pid4],
2635 root_id: PageID(0),
2636 }],
2637 };
2638 writer
2639 .insert_dirty(Page::new(leaf1_id, Node::FreeListLeaf(leaf1)))
2640 .unwrap();
2641 writer
2642 .insert_dirty(Page::new(leaf2_id, Node::FreeListLeaf(leaf2)))
2643 .unwrap();
2644
2645 let internal = FreeListInternalNode {
2647 keys: vec![tsn1],
2648 child_ids: vec![leaf1_id, leaf2_id],
2649 };
2650 let root_id = writer.alloc_page_id();
2651 writer
2652 .insert_dirty(Page::new(root_id, Node::FreeListInternal(internal)))
2653 .unwrap();
2654 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2655 writer.free_lists_tree_root_id = root_id;
2656 (tsn1, tsn2, pid1, pid2, pid3, pid4)
2657 }
2658
2659 fn build_free_list_tree_internal_internal_leaf(
2660 writer: &mut Writer,
2661 ) -> (
2662 Tsn,
2663 Tsn,
2664 Tsn,
2665 Tsn,
2666 PageID,
2667 PageID,
2668 PageID,
2669 PageID,
2670 PageID,
2671 PageID,
2672 PageID,
2673 PageID,
2674 ) {
2675 let tsn1 = Tsn(10);
2677 let tsn2 = Tsn(20);
2678 let tsn3 = Tsn(30);
2679 let tsn4 = Tsn(40);
2680 let pid1 = writer.alloc_page_id();
2681 let pid2 = writer.alloc_page_id();
2682 let pid3 = writer.alloc_page_id();
2683 let pid4 = writer.alloc_page_id();
2684 let pid5 = writer.alloc_page_id();
2685 let pid6 = writer.alloc_page_id();
2686 let pid7 = writer.alloc_page_id();
2687 let pid8 = writer.alloc_page_id();
2688
2689 let leaf1_id = writer.alloc_page_id();
2690 let leaf2_id = writer.alloc_page_id();
2691 let leaf3_id = writer.alloc_page_id();
2692 let leaf4_id = writer.alloc_page_id();
2693 let leaf1 = FreeListLeafNode {
2694 keys: vec![tsn1],
2695 values: vec![FreeListLeafValue {
2696 page_ids: vec![pid1, pid2],
2697 root_id: PageID(0),
2698 }],
2699 };
2700 let leaf2 = FreeListLeafNode {
2701 keys: vec![tsn2],
2702 values: vec![FreeListLeafValue {
2703 page_ids: vec![pid3, pid4],
2704 root_id: PageID(0),
2705 }],
2706 };
2707 let leaf3 = FreeListLeafNode {
2708 keys: vec![tsn3],
2709 values: vec![FreeListLeafValue {
2710 page_ids: vec![pid5, pid6],
2711 root_id: PageID(0),
2712 }],
2713 };
2714 let leaf4 = FreeListLeafNode {
2715 keys: vec![tsn4],
2716 values: vec![FreeListLeafValue {
2717 page_ids: vec![pid7, pid8],
2718 root_id: PageID(0),
2719 }],
2720 };
2721 writer
2722 .insert_dirty(Page::new(leaf1_id, Node::FreeListLeaf(leaf1)))
2723 .unwrap();
2724 writer
2725 .insert_dirty(Page::new(leaf2_id, Node::FreeListLeaf(leaf2)))
2726 .unwrap();
2727 writer
2728 .insert_dirty(Page::new(leaf3_id, Node::FreeListLeaf(leaf3)))
2729 .unwrap();
2730 writer
2731 .insert_dirty(Page::new(leaf4_id, Node::FreeListLeaf(leaf4)))
2732 .unwrap();
2733
2734 let internal1 = FreeListInternalNode {
2736 keys: vec![tsn2],
2737 child_ids: vec![leaf1_id, leaf2_id],
2738 };
2739 let internal1_id = writer.alloc_page_id();
2740 let internal2 = FreeListInternalNode {
2741 keys: vec![tsn4],
2742 child_ids: vec![leaf3_id, leaf4_id],
2743 };
2744 let internal2_id = writer.alloc_page_id();
2745 let internal3 = FreeListInternalNode {
2746 keys: vec![tsn3],
2747 child_ids: vec![internal1_id, internal2_id],
2748 };
2749 let internal3_id = writer.alloc_page_id();
2750 writer
2751 .insert_dirty(Page::new(internal1_id, Node::FreeListInternal(internal1)))
2752 .unwrap();
2753 writer
2754 .insert_dirty(Page::new(internal2_id, Node::FreeListInternal(internal2)))
2755 .unwrap();
2756 writer
2757 .insert_dirty(Page::new(internal3_id, Node::FreeListInternal(internal3)))
2758 .unwrap();
2759 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2760 writer.free_lists_tree_root_id = internal3_id;
2761 (
2762 tsn1, tsn2, tsn3, tsn4, pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8,
2763 )
2764 }
2765
2766 fn build_free_list_tree_leaf_tsn_subtree_leaf(
2767 writer: &mut Writer,
2768 ) -> (PageID, PageID, Tsn) {
2769 let tsn_sub_leaf_id = writer.alloc_page_id();
2771 let pid1 = writer.alloc_page_id();
2772 let pid2 = writer.alloc_page_id();
2773 let tsn_sub_leaf = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2774 page_ids: vec![pid1, pid2],
2775 };
2776 writer
2777 .insert_dirty(Page::new(
2778 tsn_sub_leaf_id,
2779 Node::FreeListTsnLeaf(tsn_sub_leaf),
2780 ))
2781 .unwrap();
2782
2783 let tsn = Tsn(33);
2784 let leaf = FreeListLeafNode {
2785 keys: vec![tsn],
2786 values: vec![FreeListLeafValue {
2787 page_ids: vec![],
2788 root_id: tsn_sub_leaf_id,
2789 }],
2790 };
2791 let root_id = writer.alloc_page_id();
2792 writer
2793 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2794 .unwrap();
2795 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2796 writer.free_lists_tree_root_id = root_id;
2797 (pid1, pid2, tsn)
2798 }
2799
2800 fn build_free_list_tree_leaf_tsn_subtree_internal_leaf(
2801 writer: &mut Writer,
2802 ) -> (PageID, PageID, PageID, PageID, Tsn) {
2803 let tsn_sub_leaf_id1 = writer.alloc_page_id();
2805 let pid1 = writer.alloc_page_id();
2806 let pid2 = writer.alloc_page_id();
2807 let tsn_sub_leaf1 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2808 page_ids: vec![pid1, pid2],
2809 };
2810 writer
2811 .insert_dirty(Page::new(
2812 tsn_sub_leaf_id1,
2813 Node::FreeListTsnLeaf(tsn_sub_leaf1),
2814 ))
2815 .unwrap();
2816
2817 let tsn_sub_leaf_id2 = writer.alloc_page_id();
2819 let pid3 = writer.alloc_page_id();
2820 let pid4 = writer.alloc_page_id();
2821 let tsn_sub_leaf2 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2822 page_ids: vec![pid3, pid4],
2823 };
2824 writer
2825 .insert_dirty(Page::new(
2826 tsn_sub_leaf_id2,
2827 Node::FreeListTsnLeaf(tsn_sub_leaf2),
2828 ))
2829 .unwrap();
2830
2831 let tsn_sub_internal_id = writer.alloc_page_id();
2833 let tsn_sub_internal = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2834 keys: vec![pid3],
2835 child_ids: vec![tsn_sub_leaf_id1, tsn_sub_leaf_id2],
2836 };
2837 writer
2838 .insert_dirty(Page::new(
2839 tsn_sub_internal_id,
2840 Node::FreeListTsnInternal(tsn_sub_internal),
2841 ))
2842 .unwrap();
2843
2844 let tsn = Tsn(33);
2846 let leaf = FreeListLeafNode {
2847 keys: vec![tsn],
2848 values: vec![FreeListLeafValue {
2849 page_ids: vec![],
2850 root_id: tsn_sub_internal_id,
2851 }],
2852 };
2853 let root_id = writer.alloc_page_id();
2854 writer
2855 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2856 .unwrap();
2857 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2858 writer.free_lists_tree_root_id = root_id;
2859 (pid1, pid2, pid3, pid4, tsn)
2860 }
2861
2862 fn build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(
2863 writer: &mut Writer,
2864 ) -> (
2865 PageID,
2866 PageID,
2867 PageID,
2868 PageID,
2869 PageID,
2870 PageID,
2871 PageID,
2872 PageID,
2873 Tsn,
2874 ) {
2875 let tsn_sub_leaf_id1 = writer.alloc_page_id();
2877 let pid1 = writer.alloc_page_id();
2878 let pid2 = writer.alloc_page_id();
2879 let tsn_sub_leaf1 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2880 page_ids: vec![pid1, pid2],
2881 };
2882 writer
2883 .insert_dirty(Page::new(
2884 tsn_sub_leaf_id1,
2885 Node::FreeListTsnLeaf(tsn_sub_leaf1),
2886 ))
2887 .unwrap();
2888
2889 let tsn_sub_leaf_id2 = writer.alloc_page_id();
2891 let pid3 = writer.alloc_page_id();
2892 let pid4 = writer.alloc_page_id();
2893 let tsn_sub_leaf2 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2894 page_ids: vec![pid3, pid4],
2895 };
2896 writer
2897 .insert_dirty(Page::new(
2898 tsn_sub_leaf_id2,
2899 Node::FreeListTsnLeaf(tsn_sub_leaf2),
2900 ))
2901 .unwrap();
2902
2903 let tsn_sub_internal_id1 = writer.alloc_page_id();
2905 let tsn_sub_internal1 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2906 keys: vec![pid3],
2907 child_ids: vec![tsn_sub_leaf_id1, tsn_sub_leaf_id2],
2908 };
2909 writer
2910 .insert_dirty(Page::new(
2911 tsn_sub_internal_id1,
2912 Node::FreeListTsnInternal(tsn_sub_internal1),
2913 ))
2914 .unwrap();
2915
2916 let tsn_sub_leaf_id3 = writer.alloc_page_id();
2918 let pid5 = writer.alloc_page_id();
2919 let pid6 = writer.alloc_page_id();
2920 let tsn_sub_leaf3 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2921 page_ids: vec![pid5, pid6],
2922 };
2923 writer
2924 .insert_dirty(Page::new(
2925 tsn_sub_leaf_id3,
2926 Node::FreeListTsnLeaf(tsn_sub_leaf3),
2927 ))
2928 .unwrap();
2929
2930 let tsn_sub_leaf_id4 = writer.alloc_page_id();
2932 let pid7 = writer.alloc_page_id();
2933 let pid8 = writer.alloc_page_id();
2934 let tsn_sub_leaf4 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2935 page_ids: vec![pid7, pid8],
2936 };
2937 writer
2938 .insert_dirty(Page::new(
2939 tsn_sub_leaf_id4,
2940 Node::FreeListTsnLeaf(tsn_sub_leaf4),
2941 ))
2942 .unwrap();
2943
2944 let tsn_sub_internal_id2 = writer.alloc_page_id();
2946 let tsn_sub_internal2 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2947 keys: vec![pid7],
2948 child_ids: vec![tsn_sub_leaf_id3, tsn_sub_leaf_id4],
2949 };
2950 writer
2951 .insert_dirty(Page::new(
2952 tsn_sub_internal_id2,
2953 Node::FreeListTsnInternal(tsn_sub_internal2),
2954 ))
2955 .unwrap();
2956
2957 let tsn_sub_internal_id3 = writer.alloc_page_id();
2959 let tsn_sub_internal3 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2960 keys: vec![pid5],
2961 child_ids: vec![tsn_sub_internal_id1, tsn_sub_internal_id2],
2962 };
2963 writer
2964 .insert_dirty(Page::new(
2965 tsn_sub_internal_id3,
2966 Node::FreeListTsnInternal(tsn_sub_internal3),
2967 ))
2968 .unwrap();
2969
2970 let tsn = writer.tsn;
2972 let leaf = FreeListLeafNode {
2973 keys: vec![tsn],
2974 values: vec![FreeListLeafValue {
2975 page_ids: vec![],
2976 root_id: tsn_sub_internal_id3,
2977 }],
2978 };
2979 let root_id = writer.alloc_page_id();
2980 writer
2981 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2982 .unwrap();
2983 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2984 writer.free_lists_tree_root_id = root_id;
2985 (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn)
2986 }
2987
2988 #[test]
2989 #[serial]
2990 fn test_insert_freed_page_id_to_empty_leaf_root() {
2991 let (_temp_dir, mut db) = construct_mvcc(64);
2992
2993 let header_page = db.get_latest_header_page().unwrap();
2995 let header_node = header_page.as_header_node().unwrap();
2996 let header_page_id = header_page.page_id;
2997 assert_eq!(PageID(0), header_page_id);
2998
2999 assert_eq!(PageID(5), header_node.next_page_id);
3001
3002 let tsn = Tsn(1001);
3004
3005 let mut writer = Writer::new(
3006 header_page_id,
3007 tsn,
3008 header_node.next_page_id,
3009 header_node.free_lists_tree_root_id,
3010 header_node.events_tree_root_id,
3011 header_node.tags_tree_root_id,
3012 header_node.tracking_root_page_id,
3013 header_node.next_position,
3014 VERBOSE,
3015 );
3016
3017 let initial_root_id = writer.free_lists_tree_root_id;
3019 assert_eq!(PageID(2), initial_root_id);
3020
3021 let page_id = writer.alloc_page_id();
3023
3024 assert_eq!(header_node.next_page_id, page_id);
3026
3027 let current_tsn = writer.tsn;
3029 writer
3030 .insert_freed_page_id(&mut db, current_tsn, page_id)
3031 .unwrap();
3032
3033 let expected_new_root_id = PageID(header_node.next_page_id.0 + 1);
3035 assert_eq!(expected_new_root_id, writer.free_lists_tree_root_id);
3036 assert_eq!(1, writer.dirty.len());
3037 assert!(writer.dirty.contains_key(&expected_new_root_id));
3038
3039 let new_root_page = writer.dirty.get(&expected_new_root_id).unwrap();
3040 assert_eq!(expected_new_root_id, new_root_page.page_id);
3041
3042 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3043 assert_eq!(vec![initial_root_id], freed_page_ids);
3044
3045 match &new_root_page.node {
3047 Node::FreeListLeaf(node) => {
3048 let expected_keys = vec![tsn];
3049 assert_eq!(expected_keys, node.keys);
3050
3051 let expected_values = vec![FreeListLeafValue {
3052 page_ids: vec![header_node.next_page_id],
3053 root_id: PageID(0),
3054 }];
3055 assert_eq!(expected_values, node.values);
3056 }
3057 _ => panic!("Expected FreeListLeaf node"),
3058 }
3059 }
3060
3061 #[test]
3062 #[serial]
3063 fn test_remove_freed_page_id_from_root_leaf_root() {
3064 let (_temp_dir, mut db) = construct_mvcc(64);
3065
3066 let mut writer;
3068 let inserted_tsn;
3069 let inserted_page_id;
3070 let previous_root_id;
3071
3072 {
3073 writer = db.writer().unwrap();
3075
3076 previous_root_id = writer.free_lists_tree_root_id;
3078
3079 inserted_page_id = writer.alloc_page_id();
3081
3082 inserted_tsn = writer.tsn;
3084 writer
3085 .insert_freed_page_id(&mut db, inserted_tsn, inserted_page_id)
3086 .unwrap();
3087
3088 db.commit(&mut writer).unwrap();
3090 }
3091
3092 {
3094 db.reader_tsns.insert(0, Tsn(0));
3095 }
3096
3097 {
3099 writer = db.writer().unwrap();
3100
3101 assert_eq!(0, writer.reusable_page_ids.len());
3103
3104 let initial_root_id = writer.free_lists_tree_root_id;
3106 let next_page_id = writer.next_page_id;
3107
3108 writer
3110 .remove_free_page_id(&db, inserted_tsn, inserted_page_id)
3111 .unwrap();
3112
3113 let expected_new_root_id = next_page_id;
3115 assert_eq!(expected_new_root_id, writer.free_lists_tree_root_id);
3116 assert_eq!(1, writer.dirty.len());
3117 assert!(writer.dirty.contains_key(&expected_new_root_id));
3118
3119 let new_root_page = writer.dirty.get(&expected_new_root_id).unwrap();
3120 assert_eq!(expected_new_root_id, new_root_page.page_id);
3121
3122 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3123 assert_eq!(vec![initial_root_id], freed_page_ids);
3124
3125 match &new_root_page.node {
3127 Node::FreeListLeaf(node) => {
3128 let expected_keys = vec![inserted_tsn];
3129 assert_eq!(expected_keys, node.keys);
3130
3131 let expected_values = vec![FreeListLeafValue {
3132 page_ids: vec![previous_root_id],
3133 root_id: PageID(0),
3134 }];
3135 assert_eq!(expected_values, node.values);
3136 }
3137 _ => panic!("Expected FreeListLeaf node"),
3138 }
3139 }
3140 }
3141
3142 #[test]
3143 #[serial]
3144 fn test_insert_freed_page_ids_until_split_leaf() {
3145 let (_temp_dir, mut db) = construct_mvcc(64);
3146
3147 let header_page = db.get_latest_header_page().unwrap();
3149 let header_node = header_page.as_header_node().unwrap();
3150 let header_page_id = header_page.page_id;
3151
3152 let mut tsn = Tsn(100);
3154 let mut writer = Writer::new(
3155 header_page_id,
3156 Tsn(header_node.tsn.0 + 1),
3157 header_node.next_page_id,
3158 header_node.free_lists_tree_root_id,
3159 header_node.events_tree_root_id,
3160 header_node.tags_tree_root_id,
3161 header_node.tracking_root_page_id,
3162 header_node.next_position,
3163 VERBOSE,
3164 );
3165
3166 let mut has_split_leaf = false;
3167 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3168
3169 while !has_split_leaf {
3171 let page_id1 = writer.alloc_page_id();
3173 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3174 inserted.push((tsn, page_id1));
3175
3176 let page_id2 = writer.alloc_page_id();
3178 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3179 inserted.push((tsn, page_id2));
3180
3181 tsn = Tsn(tsn.0 + 1);
3183
3184 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3186 match &root_page.node {
3187 Node::FreeListInternal(_) => {
3188 has_split_leaf = true;
3189 }
3190 _ => {}
3191 }
3192 }
3193
3194 let mut copy_inserted = inserted.clone();
3196
3197 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3199 let root_node = match &root_page.node {
3200 Node::FreeListInternal(node) => node,
3201 _ => panic!("Expected FreeListInternal node"),
3202 };
3203
3204 let mut active_page_ids = vec![
3206 HEADER_PAGE_ID_0,
3207 HEADER_PAGE_ID_1,
3208 writer.free_lists_tree_root_id,
3209 writer.events_tree_root_id,
3210 writer.tags_tree_root_id,
3211 ];
3212
3213 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3215
3216 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3218 active_page_ids.push(child_id);
3219
3220 let child_page = writer.dirty.get(&child_id).unwrap();
3221 assert_eq!(child_id, child_page.page_id);
3222
3223 let child_node = match &child_page.node {
3224 Node::FreeListLeaf(node) => node,
3225 _ => panic!("Expected FreeListLeaf node"),
3226 };
3227
3228 if i > 0 {
3230 assert_eq!(root_node.keys[i - 1], child_node.keys[0]);
3231 }
3232
3233 for (k, &key) in child_node.keys.iter().enumerate() {
3235 for &value in &child_node.values[k].page_ids {
3236 let (inserted_tsn, inserted_page_id) = copy_inserted.remove(0);
3237 assert_eq!(inserted_tsn, key);
3238 assert_eq!(inserted_page_id, value);
3239 freed_page_ids.push(value);
3240 }
3241 }
3242 }
3243
3244 assert_eq!(7, active_page_ids.len());
3246
3247 let mut all_page_ids = active_page_ids.clone();
3249 all_page_ids.extend(freed_page_ids.clone());
3250 all_page_ids.sort();
3251 all_page_ids.dedup();
3252
3253 assert_eq!(
3254 all_page_ids.len(),
3255 active_page_ids.len() + freed_page_ids.len()
3256 - active_page_ids
3257 .iter()
3258 .filter(|id| freed_page_ids.contains(id))
3259 .count()
3260 );
3261
3262 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3264 assert_eq!(expected_page_ids, all_page_ids);
3265 }
3266
3267 #[test]
3268 #[serial]
3269 fn test_insert_freed_page_ids_until_replace_internal_node_child_id() {
3270 let (_temp_dir, db) = construct_mvcc(128);
3271
3272 {
3274 db.reader_tsns.insert(0, Tsn(0));
3275 }
3276
3277 let mut has_split_leaf = false;
3278 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3279
3280 while !has_split_leaf {
3282 let mut writer = db.writer().unwrap();
3284 let page_id1 = writer.alloc_page_id();
3286 writer
3287 .insert_freed_page_id(&db, writer.tsn, page_id1)
3288 .unwrap();
3289 inserted.push((writer.tsn, page_id1));
3290
3291 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3293 match &root_page.node {
3294 Node::FreeListInternal(_) => {
3295 has_split_leaf = true;
3296 }
3297 _ => {}
3298 }
3299
3300 if !has_split_leaf {
3301 let page_id2 = writer.alloc_page_id();
3303 writer
3304 .insert_freed_page_id(&db, writer.tsn, page_id2)
3305 .unwrap();
3306 inserted.push((writer.tsn, page_id2));
3307
3308 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3310 match &root_page.node {
3311 Node::FreeListInternal(_) => {
3312 has_split_leaf = true;
3313 }
3314 _ => {}
3315 }
3316 }
3317
3318 db.commit(&mut writer).unwrap();
3319 }
3320
3321 let mut writer = db.writer().unwrap();
3322 let page_id3 = writer.alloc_page_id();
3323 writer
3324 .insert_freed_page_id(&db, writer.tsn, page_id3)
3325 .unwrap();
3326 db.commit(&mut writer).unwrap();
3327 inserted.push((writer.tsn, page_id3));
3328
3329 writer = db.writer().unwrap();
3330 let page_id4 = writer.alloc_page_id();
3331 writer
3332 .insert_freed_page_id(&db, writer.tsn, page_id4)
3333 .unwrap();
3334 db.commit(&mut writer).unwrap();
3335 inserted.push((writer.tsn, page_id4));
3336
3337 writer = db.writer().unwrap();
3339 let root_page = db.read_page(writer.free_lists_tree_root_id).unwrap();
3340 let root_node = match &root_page.node {
3341 Node::FreeListInternal(node) => node,
3342 _ => panic!("Expected FreeListInternal node"),
3343 };
3344
3345 let mut active_page_ids = vec![
3347 HEADER_PAGE_ID_0,
3348 HEADER_PAGE_ID_1,
3349 writer.free_lists_tree_root_id,
3350 writer.events_tree_root_id,
3351 writer.tags_tree_root_id,
3352 ];
3353
3354 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3356
3357 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3359 active_page_ids.push(child_id);
3360
3361 let child_page = db.read_page(child_id).unwrap();
3363 assert_eq!(child_id, child_page.page_id);
3364
3365 let child_node = match &child_page.node {
3366 Node::FreeListLeaf(node) => node,
3367 _ => panic!("Expected FreeListLeaf node"),
3368 };
3369
3370 if i > 0 {
3372 assert_eq!(root_node.keys[i - 1], child_node.keys[0]);
3373 }
3374
3375 for child_value in child_node.values.clone() {
3377 for page_id in child_value.page_ids {
3378 freed_page_ids.push(page_id);
3379 }
3380 }
3381 }
3382
3383 assert_eq!(8, active_page_ids.len());
3385
3386 let mut all_page_ids = active_page_ids.clone();
3388 all_page_ids.extend(freed_page_ids.clone());
3389 all_page_ids.sort();
3390 all_page_ids.dedup();
3391
3392 assert_eq!(
3393 all_page_ids.len(),
3394 active_page_ids.len() + freed_page_ids.len()
3395 - active_page_ids
3396 .iter()
3397 .filter(|id| freed_page_ids.contains(id))
3398 .count()
3399 );
3400
3401 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3403 assert_eq!(expected_page_ids, all_page_ids);
3404 }
3405
3406 #[test]
3407 #[serial]
3408 fn test_remove_freed_page_ids_from_split_leaf() {
3409 let (_temp_dir, mut db) = construct_mvcc(128);
3410
3411 if VERBOSE {
3413 println!("Inserting page IDs......");
3414 }
3415 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3416 let previous_root_id;
3417 let previous_writer_tsn;
3418
3419 {
3420 let mut writer = db.writer().unwrap();
3422
3423 previous_root_id = writer.free_lists_tree_root_id;
3425
3426 let mut has_split_leaf = false;
3428 let mut tsn = writer.tsn;
3429
3430 while !has_split_leaf {
3431 tsn = Tsn(tsn.0 + 1);
3433
3434 let page_id1 = writer.alloc_page_id();
3436 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3437 inserted.push((tsn, page_id1));
3438
3439 let page_id2 = writer.alloc_page_id();
3441 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3442 inserted.push((tsn, page_id2));
3443
3444 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3446 match &root_page.node {
3447 Node::FreeListInternal(_) => {
3448 has_split_leaf = true;
3449 }
3450 _ => {}
3451 }
3452 }
3453
3454 writer.tsn = Tsn(tsn.0 + 1);
3456 previous_writer_tsn = writer.tsn;
3457
3458 db.commit(&mut writer).unwrap();
3460 }
3461
3462 if VERBOSE {
3464 println!();
3465 println!("Removing all inserted page IDs......");
3466 }
3467
3468 {
3469 let header_page = db.get_latest_header_page().unwrap();
3471 let header_node = header_page.as_header_node().unwrap();
3472 let header_page_id = header_page.page_id;
3473
3474 let mut writer = Writer::new(
3476 header_page_id,
3477 Tsn(header_node.tsn.0 + 1),
3478 header_node.next_page_id,
3479 header_node.free_lists_tree_root_id,
3480 header_node.events_tree_root_id,
3481 header_node.tags_tree_root_id,
3482 header_node.tracking_root_page_id,
3483 header_node.next_position,
3484 VERBOSE,
3485 );
3486
3487 let old_root_id = writer.free_lists_tree_root_id;
3489
3490 for (tsn, page_id) in inserted.iter() {
3492 writer.remove_free_page_id(&db, *tsn, *page_id).unwrap();
3493 if VERBOSE {
3494 println!("Dirty pages: {:?}", writer.dirty.keys());
3495 }
3496 }
3497
3498 assert_ne!(old_root_id, writer.free_lists_tree_root_id);
3500
3501 assert_eq!(1, writer.dirty.len());
3502 assert!(writer.dirty.contains_key(&writer.free_lists_tree_root_id));
3503
3504 let new_root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3505 assert_eq!(writer.free_lists_tree_root_id, new_root_page.page_id);
3506
3507 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3509 assert!(freed_page_ids.contains(&old_root_id));
3510
3511 assert_eq!(5, writer.freed_page_ids.len());
3514
3515 match &new_root_page.node {
3517 Node::FreeListLeaf(node) => {
3518 let expected_keys = vec![previous_writer_tsn];
3519 assert_eq!(expected_keys, node.keys);
3520
3521 let expected_values = vec![FreeListLeafValue {
3522 page_ids: vec![previous_root_id],
3523 root_id: PageID(0),
3524 }];
3525 assert_eq!(expected_values, node.values);
3526 }
3527 _ => panic!("Expected FreeListLeaf node"),
3528 }
3529
3530 let active_page_ids = vec![
3532 HEADER_PAGE_ID_0,
3533 HEADER_PAGE_ID_1,
3534 writer.free_lists_tree_root_id,
3535 writer.events_tree_root_id,
3536 writer.tags_tree_root_id,
3537 ];
3538
3539 let mut all_freed_page_ids = freed_page_ids.clone();
3541
3542 match &new_root_page.node {
3544 Node::FreeListLeaf(node) => {
3545 for (_, value) in node.keys.iter().zip(node.values.iter()) {
3546 all_freed_page_ids.extend(value.page_ids.clone());
3547 }
3548 }
3549 _ => panic!("Expected FreeListLeaf node"),
3550 }
3551
3552 let inserted_page_ids: Vec<PageID> = inserted.iter().map(|(_, id)| *id).collect();
3554
3555 let mut all_page_ids = active_page_ids.clone();
3557 all_page_ids.extend(all_freed_page_ids.clone());
3558 all_page_ids.extend(inserted_page_ids.clone());
3559 all_page_ids.sort();
3560 all_page_ids.dedup();
3561
3562 let expected_page_ids: Vec<PageID> =
3564 (0..writer.next_page_id.0).map(PageID).collect();
3565 assert_eq!(expected_page_ids, all_page_ids);
3566 }
3567 }
3568
3569 #[test]
3570 #[serial]
3571 fn test_insert_freed_page_ids_until_split_internal() {
3572 let (_temp_dir, mut db) = construct_mvcc(64);
3573
3574 let header_page = db.get_latest_header_page().unwrap();
3576 let header_node = header_page.as_header_node().unwrap();
3577 let header_page_id = header_page.page_id;
3578
3579 let mut writer = Writer::new(
3581 header_page_id,
3582 Tsn(header_node.tsn.0 + 1),
3583 header_node.next_page_id,
3584 header_node.free_lists_tree_root_id,
3585 header_node.events_tree_root_id,
3586 header_node.tags_tree_root_id,
3587 header_node.tracking_root_page_id,
3588 header_node.next_position,
3589 VERBOSE,
3590 );
3591
3592 let mut tsn = Tsn(100);
3594 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3595 let mut has_split_internal = false;
3596
3597 while !has_split_internal {
3599 let page_id1 = writer.alloc_page_id();
3601 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3602 inserted.push((tsn, page_id1));
3603
3604 let page_id2 = writer.alloc_page_id();
3606 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3607 inserted.push((tsn, page_id2));
3608
3609 tsn = Tsn(tsn.0 + 1);
3611
3612 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3614 match &root_page.node {
3615 Node::FreeListInternal(root_node) => {
3616 if !root_node.child_ids.is_empty() {
3618 let child_id = root_node.child_ids[0];
3619 if let Some(child_page) = writer.dirty.get(&child_id) {
3620 match &child_page.node {
3621 Node::FreeListInternal(_) => {
3622 has_split_internal = true;
3623 }
3624 _ => {}
3625 }
3626 }
3627 }
3628 }
3629 _ => {}
3630 }
3631 if inserted.len() > 100 {
3632 panic!("Too many inserted page IDs");
3633 }
3634 }
3635
3636 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3638 let root_node = match &root_page.node {
3639 Node::FreeListInternal(node) => node,
3640 _ => panic!("Expected FreeListInternal node"),
3641 };
3642
3643 let mut active_page_ids = vec![
3645 HEADER_PAGE_ID_0,
3646 HEADER_PAGE_ID_1,
3647 writer.free_lists_tree_root_id,
3648 writer.events_tree_root_id,
3649 writer.tags_tree_root_id,
3650 ];
3651
3652 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3654
3655 let mut previous_child: Option<&FreeListInternalNode> = None;
3657
3658 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3660 active_page_ids.push(child_id);
3661
3662 let child_page = writer.dirty.get(&child_id).unwrap();
3663 assert_eq!(child_id, child_page.page_id);
3664
3665 let child_node = match &child_page.node {
3666 Node::FreeListInternal(node) => node,
3667 _ => panic!("Expected FreeListInternal node"),
3668 };
3669
3670 if i > 0 {
3672 assert!(root_node.keys[i - 1] < child_node.keys[0]);
3673
3674 if let Some(prev_child) = previous_child {
3676 assert!(root_node.keys[i - 1] > *prev_child.keys.last().unwrap());
3677 }
3678 }
3679
3680 previous_child = Some(child_node);
3681
3682 for (j, &grand_child_id) in child_node.child_ids.iter().enumerate() {
3684 active_page_ids.push(grand_child_id);
3685
3686 let grand_child_page = writer.dirty.get(&grand_child_id).unwrap();
3687 assert_eq!(grand_child_id, grand_child_page.page_id);
3688
3689 let grand_child_node = match &grand_child_page.node {
3690 Node::FreeListLeaf(node) => node,
3691 _ => panic!("Expected FreeListLeaf node"),
3692 };
3693
3694 if j > 0 {
3696 assert_eq!(child_node.keys[j - 1], grand_child_node.keys[0]);
3697 }
3698
3699 for (k, &key) in grand_child_node.keys.iter().enumerate() {
3701 for &value in &grand_child_node.values[k].page_ids {
3702 let pos = inserted.iter().position(|&(t, p)| t == key && p == value);
3704 if let Some(idx) = pos {
3705 inserted.remove(idx);
3706 }
3707 freed_page_ids.push(value);
3708 }
3709 }
3710 }
3711 }
3712
3713 assert!(inserted.is_empty());
3715
3716 assert_eq!(11, active_page_ids.len());
3718
3719 let mut all_page_ids = active_page_ids.clone();
3721 all_page_ids.extend(freed_page_ids.clone());
3722 all_page_ids.sort();
3723 all_page_ids.dedup();
3724
3725 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3727 assert_eq!(expected_page_ids, all_page_ids);
3728 }
3729
3730 #[test]
3731 #[serial]
3732 fn test_remove_freed_page_ids_from_split_internal() {
3733 let (_temp_dir, mut db) = construct_mvcc(64);
3734
3735 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3737 let previous_root_id;
3738 let previous_writer_tsn;
3739
3740 {
3741 let mut writer = db.writer().unwrap();
3743
3744 previous_root_id = writer.free_lists_tree_root_id;
3746
3747 let mut has_split_internal = false;
3749 let mut tsn = writer.tsn;
3750
3751 while !has_split_internal {
3752 tsn = Tsn(tsn.0 + 1);
3754 writer.tsn = tsn;
3755
3756 let page_id1 = writer.alloc_page_id();
3758 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3759 inserted.push((tsn, page_id1));
3760
3761 let page_id2 = writer.alloc_page_id();
3763 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3764 inserted.push((tsn, page_id2));
3765
3766 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3768 match &root_page.node {
3769 Node::FreeListInternal(root_node) => {
3770 if !root_node.child_ids.is_empty() {
3772 let child_id = root_node.child_ids[0];
3773 if let Some(child_page) = writer.dirty.get(&child_id) {
3774 match &child_page.node {
3775 Node::FreeListInternal(_) => {
3776 has_split_internal = true;
3777 }
3778 _ => {}
3779 }
3780 }
3781 }
3782 }
3783 _ => {}
3784 }
3785 }
3786
3787 previous_writer_tsn = writer.tsn;
3789
3790 db.commit(&mut writer).unwrap();
3792 }
3793
3794 {
3796 let header_page = db.get_latest_header_page().unwrap();
3798 let header_node = header_page.as_header_node().unwrap();
3799 let header_page_id = header_page.page_id;
3800
3801 let mut writer = Writer::new(
3803 header_page_id,
3804 Tsn(header_node.tsn.0 + 1),
3805 header_node.next_page_id,
3806 header_node.free_lists_tree_root_id,
3807 header_node.events_tree_root_id,
3808 header_node.tags_tree_root_id,
3809 header_node.tracking_root_page_id,
3810 header_node.next_position,
3811 VERBOSE,
3812 );
3813
3814 let old_root_id = writer.free_lists_tree_root_id;
3816
3817 for (tsn, page_id) in inserted.iter() {
3819 writer.remove_free_page_id(&db, *tsn, *page_id).unwrap();
3820 }
3821
3822 assert_ne!(old_root_id, writer.free_lists_tree_root_id);
3824 assert_eq!(1, writer.dirty.len());
3825 assert!(writer.dirty.contains_key(&writer.free_lists_tree_root_id));
3826
3827 let new_root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3828 assert_eq!(writer.free_lists_tree_root_id, new_root_page.page_id);
3829
3830 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3832 assert!(freed_page_ids.contains(&old_root_id));
3833
3834 assert_eq!(13, writer.freed_page_ids.len());
3837
3838 match &new_root_page.node {
3840 Node::FreeListLeaf(node) => {
3841 let expected_keys = vec![previous_writer_tsn];
3842 assert_eq!(expected_keys, node.keys);
3843
3844 let expected_values = vec![FreeListLeafValue {
3845 page_ids: vec![previous_root_id],
3846 root_id: PageID(0),
3847 }];
3848 assert_eq!(expected_values, node.values);
3849 }
3850 _ => panic!("Expected FreeListLeaf node"),
3851 }
3852
3853 let active_page_ids = vec![
3855 HEADER_PAGE_ID_0,
3856 HEADER_PAGE_ID_1,
3857 writer.free_lists_tree_root_id,
3858 writer.events_tree_root_id,
3859 writer.tags_tree_root_id,
3860 ];
3861
3862 let mut all_freed_page_ids = freed_page_ids.clone();
3864
3865 match &new_root_page.node {
3867 Node::FreeListLeaf(node) => {
3868 for (_, value) in node.keys.iter().zip(node.values.iter()) {
3869 all_freed_page_ids.extend(value.page_ids.clone());
3870 }
3871 }
3872 _ => panic!("Expected FreeListLeaf node"),
3873 }
3874
3875 let inserted_page_ids: Vec<PageID> = inserted.iter().map(|(_, id)| *id).collect();
3877
3878 let mut all_page_ids = active_page_ids.clone();
3880 all_page_ids.extend(all_freed_page_ids.clone());
3881 all_page_ids.extend(inserted_page_ids.clone());
3882 all_page_ids.sort();
3883 all_page_ids.dedup();
3884
3885 let expected_page_ids: Vec<PageID> =
3887 (0..writer.next_page_id.0).map(PageID).collect();
3888 assert_eq!(expected_page_ids, all_page_ids);
3889 }
3890 }
3891
3892 #[test]
3893 #[serial]
3894 fn test_remove_freed_page_ids_until_replace_old_id_with_orphaned_child_id() {
3895 let (_temp_dir, mut db) = construct_mvcc(128);
3906
3907 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3909
3910 {
3911 let mut writer = db.writer().unwrap();
3913
3914 let mut has_split_internal = false;
3916 let mut tsn = writer.tsn;
3917
3918 while !has_split_internal {
3919 tsn = Tsn(tsn.0 + 1);
3921 writer.tsn = tsn;
3922
3923 let page_id1 = writer.alloc_page_id();
3925 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3926 inserted.push((tsn, page_id1));
3927
3928 let page_id2 = writer.alloc_page_id();
3930 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3931 inserted.push((tsn, page_id2));
3932
3933 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3935 match &root_page.node {
3936 Node::FreeListInternal(root_node) => {
3937 if !root_node.child_ids.is_empty() {
3939 let child_id = root_node.child_ids[0];
3940 if let Some(child_page) = writer.dirty.get(&child_id) {
3941 match &child_page.node {
3942 Node::FreeListInternal(_) => {
3943 has_split_internal = true;
3944 }
3945 _ => {}
3946 }
3947 }
3948 }
3949 }
3950 _ => {}
3951 }
3952 }
3953
3954 db.commit(&mut writer).unwrap();
3956 }
3957
3958 db.reader_tsns.remove(&0);
3960 let writer = db.writer().unwrap();
3961 let reusable_page_ids = writer.reusable_page_ids.clone();
3962
3963 db.reader_tsns.insert(0, Tsn(0));
3965
3966 for (page_id, tsn) in reusable_page_ids {
3968 let mut writer = db.writer().unwrap();
3969 writer.remove_free_page_id(&db, tsn, page_id).unwrap();
3970 db.commit(&mut writer).unwrap();
3971 }
3972 }
3973
3974 #[test]
3975 #[serial]
3976 fn test_insert_freed_page_ids_overflow_single_key_moves_to_tsn_subtree() {
3977 let page_size = 64;
3979 let (_temp_dir, mut mvcc) = construct_mvcc(page_size);
3980
3981 let mut writer = mvcc.writer().unwrap();
3983 let tsn = writer.tsn;
3984
3985 let mut inserted_count: usize = 0;
3988 loop {
3989 let pid = writer.alloc_page_id();
3990 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
3991 inserted_count += 1;
3992 let dirty_page_id = {
3993 let mut keys = writer.dirty.keys();
3994 assert_eq!(keys.len(), 1);
3995 *keys.next().unwrap()
3996 };
3997 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
3998 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
3999 if !leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
4000 break;
4001 }
4002 } else {
4003 panic!("Expected leaf node")
4004 }
4005 }
4006
4007 let pid5 = writer.alloc_page_id();
4009 writer.insert_freed_page_id(&mut mvcc, tsn, pid5).unwrap();
4010 inserted_count += 1;
4011
4012 let pid6 = writer.alloc_page_id();
4014 writer.insert_freed_page_id(&mut mvcc, tsn, pid6).unwrap();
4015 inserted_count += 1;
4016
4017 let pid7 = writer.alloc_page_id();
4018 writer.insert_freed_page_id(&mut mvcc, tsn, pid7).unwrap();
4019 inserted_count += 1;
4020
4021 let dirty_ids: Vec<PageID> = { writer.dirty.keys().cloned().collect() };
4023 let mut tsn_root_id = PageID(0);
4024 for dirty_page_id in dirty_ids {
4025 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
4026 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
4027 assert_eq!(1, leaf_node.keys.len());
4028 assert_eq!(tsn, leaf_node.keys[0]);
4029 let val = &leaf_node.values[0];
4030 assert_eq!(0, val.page_ids.len());
4031 assert_ne!(PageID(0), val.root_id);
4032 tsn_root_id = val.root_id;
4033 break;
4034 }
4035 }
4036 assert_ne!(PageID(0), tsn_root_id);
4037 let tsn_root_page = writer.get_page_ref(&mvcc, tsn_root_id).unwrap();
4040 match &tsn_root_page.node {
4041 Node::FreeListTsnInternal(internal) => {
4042 assert_eq!(2, internal.child_ids.len());
4043 assert_eq!(1, internal.keys.len());
4044 }
4045 other => panic!(
4046 "Expected TSN-subtree internal node, got {:?}",
4047 other.type_name()
4048 ),
4049 }
4050
4051 let mut extra_inserts = 0usize;
4053 let mut guard = 0usize;
4054 loop {
4055 guard += 1;
4056 assert!(
4057 guard < 200,
4058 "guard hit while waiting for TSN-subtree internal split"
4059 );
4060 let pid = writer.alloc_page_id();
4061 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
4062 extra_inserts += 1;
4063
4064 let dirty_ids: Vec<PageID> = writer.dirty.keys().cloned().collect();
4066 tsn_root_id = PageID(0);
4067 for dirty_page_id in dirty_ids {
4068 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
4069 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
4070 assert_eq!(1, leaf_node.keys.len());
4071 assert_eq!(tsn, leaf_node.keys[0]);
4072 let val = &leaf_node.values[0];
4073 assert_eq!(0, val.page_ids.len());
4074 assert_ne!(PageID(0), val.root_id);
4075 tsn_root_id = val.root_id;
4076 break;
4077 }
4078 }
4079 assert_ne!(PageID(0), tsn_root_id);
4080
4081 let root_node_owned = {
4082 writer
4083 .get_page_ref(&mvcc, tsn_root_id)
4084 .unwrap()
4085 .node
4086 .clone()
4087 };
4088 match root_node_owned {
4089 Node::FreeListTsnInternal(internal_root) => {
4090 let first_child_id = internal_root.child_ids[0];
4092 let first_child_node = {
4093 writer
4094 .get_page_ref(&mvcc, first_child_id)
4095 .unwrap()
4096 .node
4097 .clone()
4098 };
4099 if matches!(first_child_node, Node::FreeListTsnInternal(_)) {
4100 assert_eq!(1, internal_root.keys.len());
4103 assert_eq!(2, internal_root.child_ids.len());
4104 break;
4105 }
4106 }
4107 other => panic!(
4108 "Expected TSN-subtree internal node, got {:?}",
4109 other.type_name()
4110 ),
4111 }
4112 }
4113
4114 let pid = writer.alloc_page_id();
4116 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
4117 extra_inserts += 1;
4118
4119 writer.find_reusable_page_ids(&mvcc).unwrap();
4121 assert_eq!(
4122 inserted_count + extra_inserts,
4123 writer.reusable_page_ids.len()
4124 );
4125 for &(_pid, _tsn) in writer.reusable_page_ids.iter() {
4126 assert_eq!(tsn, _tsn);
4127 }
4128 }
4129
4130 #[test]
4131 #[serial]
4132 fn test_remove_freed_page_id_from_tsn_subtree_leaf_pid1() {
4133 let (_temp_dir, db) = construct_mvcc(128);
4134 let mut writer = db.writer().unwrap();
4135
4136 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4137
4138 writer.find_reusable_page_ids(&db).unwrap();
4139 assert_eq!(2, writer.reusable_page_ids.len());
4140 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
4141 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
4142
4143 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4144 writer.find_reusable_page_ids(&db).unwrap();
4145 assert_eq!(1, writer.reusable_page_ids.len());
4146 assert_eq!((pid2, tsn1), writer.reusable_page_ids[0]);
4147 }
4148
4149 #[test]
4150 #[serial]
4151 fn test_remove_freed_page_id_from_tsn_subtree_leaf_pid2() {
4152 let (_temp_dir, db) = construct_mvcc(128);
4153 let mut writer = db.writer().unwrap();
4154
4155 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4156
4157 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4158 writer.find_reusable_page_ids(&db).unwrap();
4159 assert_eq!(1, writer.reusable_page_ids.len());
4160 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
4161 }
4162
4163 #[test]
4164 #[serial]
4165 fn test_tsn_subtree_random_order_insert_and_ordering() {
4166 let page_size = 256;
4168 let (_temp_dir, mut db) = construct_mvcc(page_size);
4169 let mut writer = db.writer().unwrap();
4170 let tsn = writer.tsn;
4171
4172 let n = 100usize;
4174 let mut pids: Vec<PageID> = Vec::with_capacity(n);
4175 for _ in 0..n {
4176 pids.push(writer.alloc_page_id());
4177 }
4178 for i in 0..pids.len() {
4180 let j = (i * 37 + 13) % pids.len();
4181 pids.swap(i, j);
4182 }
4183 for pid in &pids {
4185 writer.insert_freed_page_id(&mut db, tsn, *pid).unwrap();
4186 }
4187 let dup = pids[0];
4189 writer.insert_freed_page_id(&mut db, tsn, dup).unwrap();
4190
4191 writer.find_reusable_page_ids(&db).unwrap();
4193 assert_eq!(n, writer.reusable_page_ids.len());
4194 for &(_pid, _tsn) in writer.reusable_page_ids.iter() {
4195 assert_eq!(tsn, _tsn);
4196 }
4197
4198 let mut tsn_root_id = PageID(0);
4201 for page_id in writer.dirty.keys().cloned().collect::<Vec<_>>() {
4202 let page = writer.get_mut_dirty(page_id).unwrap();
4203 if let Node::FreeListLeaf(leaf_node) = &page.node {
4204 if !leaf_node.keys.is_empty() && leaf_node.keys[0] == tsn {
4205 tsn_root_id = leaf_node.values[0].root_id;
4206 break;
4207 }
4208 }
4209 }
4210 assert_ne!(PageID(0), tsn_root_id);
4211 let root_node_owned = { writer.get_page_ref(&db, tsn_root_id).unwrap().node.clone() };
4212 match root_node_owned {
4213 Node::FreeListTsnInternal(internal_root) => {
4214 let first_child_id = internal_root.child_ids[0];
4216 let first_child_node = {
4217 writer
4218 .get_page_ref(&db, first_child_id)
4219 .unwrap()
4220 .node
4221 .clone()
4222 };
4223 if let Node::FreeListTsnInternal(_in2) = first_child_node {
4224 }
4226 }
4227 Node::FreeListTsnLeaf(_) => {
4228 }
4230 other => panic!("Unexpected node type: {}", other.type_name()),
4231 }
4232 }
4233
4234 #[test]
4235 #[serial]
4236 fn test_upgrade_inline_to_tsn_subtree_with_random_inserts_and_duplicates() {
4237 let page_size = 96;
4239 let (_temp_dir, mut mvcc) = construct_mvcc(page_size);
4240 let mut writer = mvcc.writer().unwrap();
4241 let tsn = writer.tsn;
4242
4243 let mut inline_ids = Vec::new();
4245 loop {
4246 let pid = writer.alloc_page_id();
4247 let res = writer.insert_freed_page_id(&mut mvcc, tsn, pid);
4248 if res.is_err() {
4249 panic!("unexpected error inserting into inline");
4250 }
4251 inline_ids.push(pid);
4252 let dirty_id = writer.dirty.keys().cloned().next().unwrap();
4254 if let Node::FreeListLeaf(leaf_node) = &writer.get_mut_dirty(dirty_id).unwrap().node
4255 {
4256 if !leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
4257 break;
4258 }
4259 }
4260 }
4261
4262 let mut extra_ids = Vec::new();
4264 for _ in 0..30 {
4265 extra_ids.push(writer.alloc_page_id());
4266 }
4267 for i in 0..extra_ids.len() {
4269 let j = (i * 29 + 7) % extra_ids.len();
4270 extra_ids.swap(i, j);
4271 }
4272 if !inline_ids.is_empty() {
4274 extra_ids.push(inline_ids[0]);
4275 }
4276 if inline_ids.len() > 1 {
4277 extra_ids.push(inline_ids[1]);
4278 }
4279
4280 for pid in &extra_ids {
4281 writer.insert_freed_page_id(&mut mvcc, tsn, *pid).unwrap();
4282 }
4283
4284 writer.find_reusable_page_ids(&mvcc).unwrap();
4286 let mut expected: Vec<PageID> = inline_ids.clone();
4287 for p in extra_ids {
4288 if !expected.contains(&p) {
4289 expected.push(p);
4290 }
4291 }
4292 expected.sort_by_key(|p| p.0);
4293 expected.dedup();
4294 let mut actual: Vec<PageID> =
4295 writer.reusable_page_ids.iter().map(|(p, _)| *p).collect();
4296 actual.sort_by_key(|p| p.0);
4297 assert_eq!(expected, actual);
4298 }
4299
4300 #[test]
4301 #[serial]
4302 fn test_remove_freed_page_id_from_tsn_subtree_leaf_all() {
4303 let (_temp_dir, db) = construct_mvcc(128);
4304 let mut writer = db.writer().unwrap();
4305
4306 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4307
4308 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4309 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4310 writer.find_reusable_page_ids(&db).unwrap();
4311 assert_eq!(0, writer.reusable_page_ids.len());
4312
4313 assert_eq!(2, writer.freed_page_ids.len());
4314 }
4315
4316 #[test]
4317 #[serial]
4318 fn test_remove_freed_page_id_from_tsn_subtree_internal_leaf_pid1() {
4319 let (_temp_dir, db) = construct_mvcc(128);
4320 let mut writer = db.writer().unwrap();
4321
4322 let (pid1, pid2, pid3, pid4, tsn1) =
4323 build_free_list_tree_leaf_tsn_subtree_internal_leaf(&mut writer);
4324
4325 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4326 writer.find_reusable_page_ids(&db).unwrap();
4327 assert_eq!(3, writer.reusable_page_ids.len());
4328 assert_eq!((pid2, tsn1), writer.reusable_page_ids[0]);
4329 assert_eq!((pid3, tsn1), writer.reusable_page_ids[1]);
4330 assert_eq!((pid4, tsn1), writer.reusable_page_ids[2]);
4331
4332 assert_eq!(1, writer.freed_page_ids.len());
4333
4334 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4335 writer.find_reusable_page_ids(&db).unwrap();
4336 assert_eq!(2, writer.reusable_page_ids.len());
4337 assert_eq!((pid3, tsn1), writer.reusable_page_ids[0]);
4338 assert_eq!((pid4, tsn1), writer.reusable_page_ids[1]);
4339
4340 assert_eq!(3, writer.freed_page_ids.len());
4341
4342 writer.remove_free_page_id(&db, tsn1, pid3).unwrap();
4343 writer.find_reusable_page_ids(&db).unwrap();
4344 assert_eq!(1, writer.reusable_page_ids.len());
4345 assert_eq!((pid4, tsn1), writer.reusable_page_ids[0]);
4346
4347 assert_eq!(3, writer.freed_page_ids.len());
4348
4349 writer.remove_free_page_id(&db, tsn1, pid4).unwrap();
4350 writer.find_reusable_page_ids(&db).unwrap();
4351 assert_eq!(0, writer.reusable_page_ids.len());
4352
4353 assert_eq!(4, writer.freed_page_ids.len());
4354 }
4355
4356 #[test]
4357 #[serial]
4358 fn test_remove_freed_page_id_from_tsn_subtree_internal_internal_leaf_pid1() {
4359 let (_temp_dir, db) = construct_mvcc(128);
4360 let mut writer = db.writer().unwrap();
4361
4362 let (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn) =
4363 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut writer);
4364
4365 writer.remove_free_page_id(&db, tsn, pid1).unwrap();
4366 writer.find_reusable_page_ids(&db).unwrap();
4367 assert_eq!(7, writer.reusable_page_ids.len());
4368 assert_eq!((pid2, tsn), writer.reusable_page_ids[0]);
4369 assert_eq!((pid3, tsn), writer.reusable_page_ids[1]);
4370 assert_eq!((pid4, tsn), writer.reusable_page_ids[2]);
4371 assert_eq!((pid5, tsn), writer.reusable_page_ids[3]);
4372 assert_eq!((pid6, tsn), writer.reusable_page_ids[4]);
4373 assert_eq!((pid7, tsn), writer.reusable_page_ids[5]);
4374 assert_eq!((pid8, tsn), writer.reusable_page_ids[6]);
4375
4376 assert_eq!(1, writer.freed_page_ids.len());
4377 }
4378
4379 #[test]
4380 #[serial]
4381 fn test_remove_freed_page_id_cow_does_not_leak_ids_in_tsn_subtree() {
4382 let (_temp_dir, db) = construct_mvcc(128);
4385
4386 let w1_tsn = {
4388 let mut w1 = db.writer().unwrap();
4389 let (_pid1, _pid2, _pid3, _pid4, _pid5, _pid6, _pid7, _pid8, tsn) =
4390 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut w1);
4391 db.commit(&mut w1).unwrap();
4393 tsn
4395 };
4396
4397 let mut w2 = db.writer().unwrap();
4399 w2.find_reusable_page_ids(&db).unwrap();
4401 assert!(w2.reusable_page_ids.len() >= 1);
4402 let (remove_pid, remove_tsn) = w2.reusable_page_ids[0];
4403 assert_eq!(remove_tsn, w1_tsn); w2.remove_free_page_id(&db, remove_tsn, remove_pid).unwrap();
4405
4406 let mut active: Vec<PageID> = Vec::new();
4409 let mut freed_tree: Vec<PageID> = Vec::new();
4410 active.push(HEADER_PAGE_ID_0);
4412 active.push(HEADER_PAGE_ID_1);
4413 active.push(w2.free_lists_tree_root_id);
4414 active.push(w2.events_tree_root_id);
4415 active.push(w2.tags_tree_root_id);
4416
4417 let mut stack: Vec<PageID> = vec![w2.free_lists_tree_root_id];
4419 while let Some(pid) = stack.pop() {
4420 if active.contains(&pid) == false {
4421 active.push(pid);
4422 }
4423 let node_owned = { w2.get_page_ref(&db, pid).unwrap().node.clone() };
4424 match node_owned {
4425 Node::FreeListInternal(internal) => {
4426 for child in internal.child_ids {
4427 stack.push(child);
4428 }
4429 }
4430 Node::FreeListLeaf(leaf) => {
4431 for val in &leaf.values {
4433 for &p in &val.page_ids {
4434 freed_tree.push(p);
4435 }
4436 }
4437 for val in leaf.values {
4439 if val.root_id != PageID(0) {
4440 let mut tsn_stack: Vec<PageID> = vec![val.root_id];
4441 while let Some(tid) = tsn_stack.pop() {
4442 if active.contains(&tid) == false {
4443 active.push(tid);
4444 }
4445 let tnode = { w2.get_page_ref(&db, tid).unwrap().node.clone() };
4446 match tnode {
4447 Node::FreeListTsnInternal(tint) => {
4448 for c in tint.child_ids {
4449 tsn_stack.push(c);
4450 }
4451 }
4452 Node::FreeListTsnLeaf(tleaf) => {
4453 for &p in &tleaf.page_ids {
4454 freed_tree.push(p);
4455 }
4456 }
4457 _ => panic!("Unexpected node type in TSN-subtree"),
4458 }
4459 }
4460 }
4461 }
4462 }
4463 _ => panic!("Unexpected node type in free list tree"),
4464 }
4465 }
4466
4467 for (&pid, _page) in w2.dirty.iter() {
4469 if !active.contains(&pid) {
4470 active.push(pid);
4471 }
4472 }
4473 for (&pid, _page) in w2.deserialized.iter() {
4475 if !active.contains(&pid) {
4476 active.push(pid);
4477 }
4478 }
4479
4480 let mut freed: Vec<PageID> = w2.freed_page_ids.iter().cloned().collect();
4482 freed.extend(freed_tree);
4484 for (pid, _tsn) in w2.reusable_page_ids.iter() {
4486 freed.push(*pid);
4487 }
4488
4489 active.sort_by_key(|p| p.0);
4491 active.dedup();
4492 freed.sort_by_key(|p| p.0);
4493 freed.dedup();
4494 let mut union = active.clone();
4495 for id in &freed {
4496 if !union.contains(id) {
4497 union.push(*id);
4498 }
4499 }
4500 union.sort_by_key(|p| p.0);
4501
4502 let expected: Vec<PageID> = (0..w2.next_page_id.0).map(PageID).collect();
4503 if expected != union {
4504 let mut missing: Vec<PageID> = expected
4505 .iter()
4506 .cloned()
4507 .filter(|p| !union.contains(p))
4508 .collect();
4509 let mut unexpected: Vec<PageID> = union
4510 .iter()
4511 .cloned()
4512 .filter(|p| !expected.contains(p))
4513 .collect();
4514 missing.sort_by_key(|p| p.0);
4515 unexpected.sort_by_key(|p| p.0);
4516 panic!(
4517 "Missing: {:?} Unexpected: {:?}\nactive: {:?}\nfreed: {:?}",
4518 missing, unexpected, active, freed
4519 );
4520 }
4521 assert_eq!(
4522 expected, union,
4523 "All page IDs must be accounted for (active or freed)"
4524 );
4525 }
4526 }
4527
4528 #[cfg(test)]
4529 mod schema_version_tests {
4530 use super::*;
4531 use serial_test::serial;
4532 use tempfile::tempdir;
4533
4534 #[test]
4535 #[serial]
4536 fn opening_db_with_newer_schema_version_errors() {
4537 let temp_dir = tempdir().unwrap();
4539 let db_path = temp_dir.path().join("schema-guard.db");
4540 let page_size = 64usize; let verbose = false;
4542
4543 let mvcc = Mvcc::new(
4544 verbose,
4545 StorageOptions::default().db_path(db_path.clone()).page_size(page_size),
4546 )
4547 .expect("must create new db");
4548
4549 let h0 = mvcc.read_page(HEADER_PAGE_ID_0).expect("read header 0");
4551 let mut h0 = match &h0.node {
4552 Node::Header(node) => node.clone(),
4553 _ => panic!("Page 0 is not a header"),
4554 };
4555 h0.schema_version = DB_SCHEMA_VERSION + 1;
4556
4557 let page = Page::new(HEADER_PAGE_ID_0, Node::Header(h0));
4559 {
4560 let mut buf = mvcc.page_buf.lock().unwrap();
4561 serialize_page_into(&mut buf, &page.node, mvcc.zero_fill_pages)
4562 .expect("serialize header page");
4563 mvcc.pager
4564 .write_page(page.page_id, &buf)
4565 .expect("write modified header 0");
4566 }
4567
4568 mvcc.fsync().ok();
4570
4571 drop(mvcc);
4573
4574 match Mvcc::new(
4576 verbose,
4577 StorageOptions::default().db_path(db_path).page_size(page_size),
4578 ) {
4579 Ok(_) => panic!("opening should have failed due to newer on-disk schema"),
4580 Err(DcbError::InternalError(msg)) => {
4581 assert!(
4582 msg.contains("Software version is too old"),
4583 "unexpected error message: {msg}"
4584 );
4585 }
4586 Err(other) => panic!("unexpected error type: {other:?}"),
4587 }
4588 }
4589 }
4590
4591 #[cfg(test)]
4592 mod page_cache_tests {
4593 use super::*;
4594 use serial_test::serial;
4595 use tempfile::tempdir;
4596
4597 #[test]
4598 #[serial]
4599 fn test_mvcc_page_cache_max_pages() {
4600 let temp_dir = tempdir().unwrap();
4601 let db_path = temp_dir.path().join("cache-pages.db");
4602 let options = StorageOptions::default()
4603 .db_path(&db_path)
4604 .page_cache_max_pages(10);
4605
4606 let mvcc = Mvcc::new(false, options).expect("mvcc new");
4607 assert!(mvcc.page_cache.is_some());
4608
4609 let mut writer = mvcc.writer().expect("writer");
4611 let p1 = writer.alloc_page_id();
4612 let page = Page::new(p1, Node::FreeListLeaf(crate::free_lists_tree_nodes::FreeListLeafNode::default()));
4613 writer.insert_dirty(page).expect("insert dirty");
4614 mvcc.commit(&mut writer).expect("commit");
4615
4616 {
4618 let cache = mvcc.page_cache.as_ref().unwrap();
4619 assert!(cache.get(&p1).is_some());
4620 assert!(cache.get(&HEADER_PAGE_ID_0).is_some() || cache.get(&HEADER_PAGE_ID_1).is_some());
4621 }
4622
4623 let _read_page = mvcc.read_page(p1).expect("read page");
4625 }
4626
4627 #[test]
4628 #[serial]
4629 fn test_mvcc_page_cache_max_mb() {
4630 let temp_dir = tempdir().unwrap();
4631 let db_path = temp_dir.path().join("cache-mb.db");
4632 let options = StorageOptions::default()
4633 .db_path(&db_path)
4634 .page_cache_max_mb(1); let mvcc = Mvcc::new(false, options).expect("mvcc new");
4637 assert!(mvcc.page_cache.is_some());
4638
4639 let mut writer = mvcc.writer().expect("writer");
4641 let p1 = writer.alloc_page_id();
4642 let page = Page::new(p1, Node::FreeListLeaf(crate::free_lists_tree_nodes::FreeListLeafNode::default()));
4643 writer.insert_dirty(page).expect("insert dirty");
4644 mvcc.commit(&mut writer).expect("commit");
4645
4646 {
4648 let cache = mvcc.page_cache.as_ref().unwrap();
4649 assert!(cache.get(&p1).is_some());
4650 }
4651
4652 let _read_page = mvcc.read_page(p1).expect("read page");
4654 }
4655
4656 #[test]
4657 #[serial]
4658 fn test_mvcc_page_cache_eviction() {
4659 let temp_dir = tempdir().unwrap();
4660 let db_path = temp_dir.path().join("cache-eviction.db");
4661 let options = StorageOptions::default()
4662 .db_path(&db_path)
4663 .page_cache_max_pages(2);
4664
4665 let mvcc = Mvcc::new(false, options).expect("mvcc new");
4666
4667 let mut writer = mvcc.writer().expect("writer");
4670 let p1 = writer.alloc_page_id();
4671 let p2 = writer.alloc_page_id();
4672 let p3 = writer.alloc_page_id();
4673
4674 writer.insert_dirty(Page::new(p1, Node::FreeListLeaf(Default::default()))).unwrap();
4675 writer.insert_dirty(Page::new(p2, Node::FreeListLeaf(Default::default()))).unwrap();
4676 writer.insert_dirty(Page::new(p3, Node::FreeListLeaf(Default::default()))).unwrap();
4677
4678 mvcc.commit(&mut writer).expect("commit");
4679
4680 let cache = mvcc.page_cache.as_ref().unwrap();
4683 cache.run_pending_tasks();
4686 assert!(cache.entry_count() <= 2);
4687 }
4688 }
4689}