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