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 header_page = self.get_latest_header_page()?;
583 let header_node = page_as_header_node(&header_page)?;
584
585 let reader_id = self.reader_id_counter.fetch_add(1, Ordering::Relaxed) + 1;
587
588 self.reader_tsns.insert(reader_id, header_node.tsn);
590
591 let reader = Reader {
593 header_page_id: header_page.page_id,
594 tsn: header_node.tsn,
595 events_tree_root_id: header_node.events_tree_root_id,
596 tags_tree_root_id: header_node.tags_tree_root_id,
597 next_position: header_node.next_position,
598 tracking_tree_root_id: header_node.tracking_root_page_id,
599 reader_id,
600 reader_tsns: Arc::clone(&self.reader_tsns),
601 };
602
603 Ok(reader)
604 }
605
606 pub fn writer(&self) -> DcbResult<Writer> {
607 if self.verbose {
608 println!();
609 println!("Constructing writer...");
610 }
611
612 let header_page = self.get_latest_header_page()?;
614 let header_node = page_as_header_node(&header_page)?;
615
616 let mut writer = Writer::new(
618 header_page.page_id,
619 Tsn(header_node.tsn.0 + 1),
620 header_node.next_page_id,
621 header_node.free_lists_tree_root_id,
622 header_node.events_tree_root_id,
623 header_node.tags_tree_root_id,
624 header_node.tracking_root_page_id,
625 header_node.next_position,
626 self.verbose,
627 );
628
629 if self.verbose {
630 println!("Constructed writer with {:?}", writer.tsn);
631 }
632
633 writer.find_reusable_page_ids(self)?;
635
636 Ok(writer)
637 }
638
639 pub fn write_pages<'a, I>(&self, pages: I) -> DcbResult<usize>
642 where
643 I: IntoIterator<Item = &'a Page>,
644 {
645 let mut buf = self.page_buf.lock().unwrap();
646 let mut count = 0usize;
647 for page in pages {
648 page.serialize_into_with_zero_fill(&mut buf, self.zero_fill_pages)?;
649 self.pager.write_page(page.page_id, &buf)?;
650 if self.verbose {
651 println!("Wrote {:?} to file", page.page_id);
652 }
653 count += 1;
654 }
655 Ok(count)
656 }
657
658 pub fn commit(&self, writer: &mut Writer) -> DcbResult<()> {
696 if self.verbose {
698 println!();
699 println!("Commiting writer with {:?}", writer.tsn);
700 }
701
702 while !writer.reused_page_ids.is_empty() || !writer.freed_page_ids.is_empty() {
703 while let Some((reused_page_id, tsn)) = writer.reused_page_ids.pop_front() {
705 writer.remove_free_page_id(self, tsn, reused_page_id)?;
707 }
708
709 while let Some(freed_page_id) = writer.freed_page_ids.pop_front() {
711 writer.dirty.remove(&freed_page_id);
713
714 writer.insert_freed_page_id(self, writer.tsn, freed_page_id)?;
716 }
717 }
718
719 if !writer.dirty.is_empty() {
721 let count = {
722 self.write_pages(writer.dirty.values())?
730 };
731 if self.verbose {
732 println!("Wrote {} dirty page(s) to file", count);
733 }
734 }
735
736 self.fsync()?;
738
739 if let Some(ref page_cache) = self.page_cache {
741 for (page_id, page) in writer.dirty.drain() {
742 page_cache.insert(page_id, Arc::new(page));
743 }
744 } else {
745 writer.dirty.clear();
746 }
747
748 let (alternate_header_page_id, alternate_header_page_idx) =
750 if writer.header_page_id == HEADER_PAGE_ID_0 {
751 (HEADER_PAGE_ID_1, 1)
752 } else {
753 (HEADER_PAGE_ID_0, 0)
754 };
755 self.update_header(
756 alternate_header_page_id,
757 writer.tsn,
758 writer.free_lists_tree_root_id,
759 writer.events_tree_root_id,
760 writer.tags_tree_root_id,
761 writer.tracking_tree_root_id,
762 writer.next_page_id,
763 writer.next_position,
764 )?;
765
766 self.fsync()?;
768
769 let header_page = self.headers.lock().unwrap()[alternate_header_page_idx].clone();
771 if let Some(ref page_cache) = self.page_cache {
772 page_cache.insert(header_page.page_id, Arc::new(header_page));
773 }
774
775 if self.verbose {
776 println!("Committed writer with {:?}", writer.tsn);
777 }
778
779 Ok(())
780 }
781}
782
783pub struct Writer {
785 pub header_page_id: PageID,
786 pub tsn: Tsn,
787 pub next_page_id: PageID,
788 pub free_lists_tree_root_id: PageID,
789 pub events_tree_root_id: PageID,
790 pub tags_tree_root_id: PageID,
791 pub tracking_tree_root_id: PageID,
792 pub next_position: Position,
793 pub reusable_page_ids: VecDeque<(PageID, Tsn)>,
794 pub freed_page_ids: VecDeque<PageID>,
795 pub deserialized: HashMap<PageID, Arc<Page>>,
796 pub dirty: HashMap<PageID, Page>,
797 pub reused_page_ids: VecDeque<(PageID, Tsn)>,
798 pub verbose: bool,
799}
800
801impl Writer {
802 pub fn new(
803 header_page_id: PageID,
804 tsn: Tsn,
805 next_page_id: PageID,
806 free_lists_tree_root_id: PageID,
807 events_tree_root_id: PageID,
808 tags_tree_root_id: PageID,
809 tracking_tree_root_id: PageID,
810 next_position: Position,
811 verbose: bool,
812 ) -> Self {
813 Self {
814 header_page_id,
815 tsn,
816 next_page_id,
817 free_lists_tree_root_id,
818 events_tree_root_id,
819 tags_tree_root_id,
820 tracking_tree_root_id,
821 next_position,
822 reusable_page_ids: VecDeque::new(),
823 freed_page_ids: VecDeque::new(),
824 deserialized: HashMap::new(),
825 dirty: HashMap::new(),
826 reused_page_ids: VecDeque::new(),
827 verbose,
828 }
829 }
830
831 pub fn issue_position(&mut self) -> Position {
837 let pos = self.next_position;
838 self.next_position = Position(self.next_position.0 + 1);
839 pos
840 }
841
842 pub fn get_page_ref(&mut self, mvcc: &Mvcc, page_id: PageID) -> DcbResult<&Page> {
856 if self.dirty.contains_key(&page_id) {
858 return Ok(self.dirty.get(&page_id).unwrap());
859 }
860
861 if self.deserialized.contains_key(&page_id) {
863 return Ok(self.deserialized.get(&page_id).unwrap());
864 }
865
866 let deserialized_page = mvcc.read_page(page_id)?;
868 self.insert_deserialized(deserialized_page);
869
870 Ok(self.deserialized.get(&page_id).unwrap())
872 }
873
874 pub fn get_mut_dirty(&mut self, page_id: PageID) -> DcbResult<&mut Page> {
875 if let Some(page) = self.dirty.get_mut(&page_id) {
876 Ok(page)
877 } else {
878 Err(DcbError::DirtyPageNotFound(page_id.0))
879 }
880 }
881
882 pub fn insert_deserialized(&mut self, page: Arc<Page>) {
883 self.deserialized.insert(page.page_id, page);
884 }
885
886 pub fn insert_dirty(&mut self, page: Page) -> DcbResult<()> {
887 if self.freed_page_ids.contains(&page.page_id) {
888 return Err(DcbError::PageAlreadyFreed(page.page_id.0));
889 }
890 if self.dirty.contains_key(&page.page_id) {
891 return Err(DcbError::PageAlreadyDirty(page.page_id.0));
892 }
893 self.dirty.insert(page.page_id, page);
894 Ok(())
895 }
896
897 pub fn alloc_page_id(&mut self) -> PageID {
898 if let Some((free_page_id, tsn)) = self.reusable_page_ids.pop_front() {
899 self.reused_page_ids.push_back((free_page_id, tsn));
900 return free_page_id;
901 }
902
903 let next_page_id = self.next_page_id;
904 self.next_page_id = PageID(next_page_id.0 + 1);
905 next_page_id
906 }
907
908 pub fn get_dirty_page_id(&mut self, page_id: PageID) -> DcbResult<PageID> {
909 let mut dirty_page_id = page_id;
910 if !self.freed_page_ids.iter().any(|&id| id == page_id) {
911 if !self.dirty.contains_key(&page_id) {
912 let old_page_id = page_id;
913 self.freed_page_ids.push_back(old_page_id);
914
915 let new_page_id = self.alloc_page_id();
916 let mut new_page = Arc::unwrap_or_clone(
917 self.deserialized.remove(&old_page_id).ok_or_else(|| {
918 DcbError::DatabaseCorrupted(format!(
919 "Deserialized page {:?} not found while marking dirty",
920 old_page_id
921 ))
922 })?,
923 );
924 new_page.page_id = new_page_id;
925
926 self.dirty.insert(new_page_id, new_page);
927 if self.verbose {
928 println!(
929 "Copied {:?} to {:?}: {:?}",
930 old_page_id,
931 new_page_id,
932 self.dirty.get(&new_page_id).unwrap().node
933 );
934 }
935 dirty_page_id = new_page_id;
936 } else if self.verbose {
937 println!("{page_id:?} is already dirty");
938 }
939 } else {
940 return Err(DcbError::PageAlreadyFreed(page_id.0));
941 }
942 Ok(dirty_page_id)
943 }
944
945 pub fn append_freed_page_id(&mut self, page_id: PageID) {
946 let verbose = self.verbose;
947 if !self.freed_page_ids.iter().any(|&id| id == page_id) {
948 self.freed_page_ids.push_back(page_id);
949 if verbose {
950 println!("Appended {page_id:?} to freed_page_ids");
951 }
952 if self.dirty.contains_key(&page_id) {
953 self.dirty.remove(&page_id);
954 if verbose {
955 println!("Page ID {page_id:?} was in dirty and was removed");
956 }
957 }
958 if verbose && self.dirty.contains_key(&page_id) {
959 println!("Page ID {page_id:?} is still in dirty!!!!!");
960 }
961 }
962 }
963
964 pub fn find_reusable_page_ids(&mut self, mvcc: &Mvcc) -> DcbResult<()> {
965 let verbose = self.verbose;
966 let mut reusable_page_ids: VecDeque<(PageID, Tsn)> = VecDeque::new();
967 if verbose {
969 println!("Finding reusable page IDs for TSN {:?}...", self.tsn);
970 }
971
972 let smallest_reader_tsn = mvcc.reader_tsns.iter().map(|r| *r.value()).min();
974 if verbose {
975 println!("Smallest reader TSN: {smallest_reader_tsn:?}");
976 }
977
978 if verbose {
979 println!("Root is {:?}", self.free_lists_tree_root_id);
980 }
981 let mut stack = vec![(self.free_lists_tree_root_id, 0)];
983 let mut is_finished = false;
984
985 while let Some((page_id, idx)) = stack.pop() {
986 if is_finished {
987 break;
988 }
989 let node_owned = {
990 match self.get_page_ref(mvcc, page_id) {
991 Ok(p) => p.node.clone(),
992 Err(e) => {
993 return Err(DcbError::DatabaseCorrupted(format!(
994 "Free list page {:?} load error: {:?}",
995 page_id, e
996 )));
997 }
998 }
999 };
1000 match node_owned {
1001 Node::FreeListInternal(node) => {
1002 if verbose {
1003 println!("{:?} is internal node", page_id);
1004 }
1005 if idx < node.child_ids.len() {
1006 let child_page_id = node.child_ids[idx];
1007 stack.push((page_id, idx + 1));
1008 stack.push((child_page_id, 0));
1009 }
1010 }
1011 Node::FreeListLeaf(node) => {
1012 if verbose {
1013 println!("{:?} is leaf node", page_id);
1014 }
1015 for i in 0..node.keys.len() {
1016 let tsn = node.keys[i];
1017 if let Some(smallest) = smallest_reader_tsn
1018 && tsn > smallest
1019 {
1020 is_finished = true;
1021 break;
1022 }
1023
1024 let leaf_value = &node.values[i];
1025 if leaf_value.root_id == PageID(0) {
1026 for &page_id in &leaf_value.page_ids {
1027 reusable_page_ids.push_back((page_id, tsn));
1028 }
1029 } else {
1030 let mut tsn_stack: Vec<(PageID, usize)> = vec![(leaf_value.root_id, 0)];
1034 if self.verbose {
1035 println!("TSN-subtree root_id: {:?}", leaf_value.root_id);
1036 }
1037 if self.verbose {
1038 println!(
1039 "root_id in dirty? {}",
1040 self.dirty.contains_key(&leaf_value.root_id)
1041 );
1042 }
1043 while let Some((sub_id, sidx)) = tsn_stack.pop() {
1044 let sub_node = {
1045 match self.get_page_ref(mvcc, sub_id) {
1046 Ok(p) => p.node.clone(),
1047 Err(e) => {
1048 return Err(DcbError::DatabaseCorrupted(format!(
1049 "TSN subtree page {:?} load error: {:?}",
1050 sub_id, e
1051 )));
1052 }
1053 }
1054 };
1055 match sub_node {
1056 Node::FreeListTsnInternal(tsn_internal) => {
1057 if sidx < tsn_internal.child_ids.len() {
1058 let child_id = tsn_internal.child_ids[sidx];
1059 tsn_stack.push((sub_id, sidx + 1));
1060 tsn_stack.push((child_id, 0));
1061 }
1062 }
1063 Node::FreeListTsnLeaf(tsn_leaf) => {
1064 for &pid in &tsn_leaf.page_ids {
1065 reusable_page_ids.push_back((pid, tsn));
1066 }
1067 }
1068 other => {
1069 return Err(DcbError::DatabaseCorrupted(format!(
1070 "Invalid node type in TSN subtree: {}",
1071 other.type_name()
1072 )));
1073 }
1074 }
1075 }
1076 }
1077 }
1078 }
1079 _ => {
1080 return Err(DcbError::DatabaseCorrupted(
1081 "Invalid node type in free list tree".to_string(),
1082 ));
1083 }
1084 }
1085 }
1086
1087 self.reusable_page_ids = reusable_page_ids;
1088 if verbose {
1089 println!("Found reusable page IDs: {:?}", self.reusable_page_ids);
1090 }
1091 Ok(())
1092 }
1093
1094 pub fn insert_freed_page_id(
1096 &mut self,
1097 mvcc: &Mvcc,
1098 tsn: Tsn,
1099 freed_page_id: PageID,
1100 ) -> DcbResult<()> {
1101 let verbose = self.verbose;
1102 if verbose {
1103 println!("Inserting {freed_page_id:?} for {tsn:?}");
1104 println!("Root is {:?}", self.free_lists_tree_root_id);
1105 }
1106 let mut current_page_id = self.free_lists_tree_root_id;
1108
1109 let mut stack: Vec<PageID> = Vec::new();
1111 let plan: FreePageIDInsertStrategy;
1112 loop {
1113 let current_page_ref = self.get_page_ref(mvcc, current_page_id)?;
1114 if let Node::FreeListLeaf(leaf_node) = ¤t_page_ref.node {
1115 let len_keys = leaf_node.keys.len();
1116 if len_keys == 0 {
1117 if !leaf_node.would_fit_new_tsn_and_page_id(mvcc.max_node_size) {
1118 return Err(DcbError::InternalError("Page size too small".to_string()));
1119 }
1120 plan = FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf;
1121 } else {
1122 let last_idx = len_keys - 1;
1123 let last_key = leaf_node.keys[last_idx];
1124 if tsn == last_key {
1125 if leaf_node.values[last_idx].root_id != PageID(0) {
1127 plan = FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree;
1128 } else if leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
1129 plan = FreePageIDInsertStrategy::PushPageIdOntoFreeListLeaf(last_idx);
1130 } else if leaf_node.keys.len() == 1 {
1131 plan = FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree;
1132 } else {
1133 plan = FreePageIDInsertStrategy::SplitFreeListLeaf;
1134 }
1135 } else if tsn > last_key {
1136 if leaf_node.would_fit_new_tsn_and_page_id(mvcc.max_node_size) {
1138 plan = FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf;
1139 } else {
1140 plan = FreePageIDInsertStrategy::CreateAndPromoteFreeListLeaf;
1141 }
1142 } else {
1143 return Err(DcbError::InternalError(
1145 "Insertion only supported for last TSN in leaf".to_string(),
1146 ));
1147 }
1148 }
1149 break;
1150 }
1151 if let Node::FreeListInternal(internal_node) = ¤t_page_ref.node {
1152 if verbose {
1153 println!("{:?} is internal node", current_page_ref.page_id);
1154 }
1155 stack.push(current_page_id);
1156 current_page_id = *internal_node
1157 .child_ids
1158 .last()
1159 .expect("FreeListInternal node should have a child");
1160 } else {
1161 return Err(DcbError::DatabaseCorrupted(
1162 "Expected FreeListInternal node".to_string(),
1163 ));
1164 }
1165 }
1166 if verbose {
1167 println!("{current_page_id:?} is leaf node");
1168 }
1169 let dirty_leaf_page_id = { self.get_dirty_page_id(current_page_id)? };
1171 let replacement_info: Option<(PageID, PageID)> = {
1172 if dirty_leaf_page_id != current_page_id {
1173 Some((current_page_id, dirty_leaf_page_id))
1174 } else {
1175 None
1176 }
1177 };
1178 let mut split_info: Option<(Tsn, PageID)> = None;
1180
1181 match plan {
1183 FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree => {
1184 let leaf_snapshot = { self.get_page_ref(mvcc, dirty_leaf_page_id)? };
1186 let Node::FreeListLeaf(leaf_ro) = &leaf_snapshot.node else {
1187 return Err(DcbError::DatabaseCorrupted(
1188 "Expected FreeListLeaf node".to_string(),
1189 ));
1190 };
1191 let last_idx = leaf_ro.keys.len() - 1;
1192 let tsn_root_id = leaf_ro.values[last_idx].root_id;
1193 if tsn_root_id == PageID(0) {
1194 return Err(DcbError::DatabaseCorrupted(
1195 "Expected TSN-subtree root_id to be set".to_string(),
1196 ));
1197 }
1198 let new_root_id = self.tsn_subtree_insert(mvcc, tsn_root_id, freed_page_id)?;
1199 if new_root_id != tsn_root_id {
1200 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1202 let Node::FreeListLeaf(dirty_leaf_node2) = &mut dirty_leaf_page.node else {
1203 return Err(DcbError::DatabaseCorrupted(
1204 "Expected FreeListLeaf node".to_string(),
1205 ));
1206 };
1207 dirty_leaf_node2.values[last_idx].root_id = new_root_id;
1208 }
1209 }
1210 FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree => {
1211 let leaf_snapshot = { self.get_page_ref(mvcc, dirty_leaf_page_id)? };
1213 let Node::FreeListLeaf(leaf_ro) = &leaf_snapshot.node else {
1214 return Err(DcbError::DatabaseCorrupted(
1215 "Expected FreeListLeaf node".to_string(),
1216 ));
1217 };
1218 let last_idx = leaf_ro.keys.len() - 1;
1219 let mut page_ids = leaf_ro.values[last_idx].page_ids.clone();
1220 page_ids.push(freed_page_id);
1221 page_ids.sort_by_key(|pid| pid.0);
1223 page_ids.dedup();
1224 let mut initial_ids: Vec<PageID> = Vec::new();
1227 let mut tmp_leaf = FreeListTsnLeafNode {
1228 page_ids: Vec::new(),
1229 };
1230 for pid in &page_ids {
1231 let mut candidate = tmp_leaf.clone();
1232 candidate.page_ids.push(*pid);
1233 let candidate_page = Page::new(PageID(0), Node::FreeListTsnLeaf(candidate));
1234 if candidate_page.calc_serialized_size() <= mvcc.page_size {
1235 tmp_leaf.page_ids.push(*pid);
1236 initial_ids.push(*pid);
1237 } else {
1238 break;
1239 }
1240 }
1241 if initial_ids.is_empty() {
1242 return Err(DcbError::InternalError(
1243 "Page size too small for TSN-subtree leaf with one PageID".to_string(),
1244 ));
1245 }
1246 let tsn_leaf_id = self.alloc_page_id();
1247 let tsn_leaf_page = Page::new(tsn_leaf_id, Node::FreeListTsnLeaf(tmp_leaf));
1248 self.insert_dirty(tsn_leaf_page)?;
1249 let mut tsn_root_id = tsn_leaf_id;
1251 for pid in page_ids.into_iter().filter(|p| !initial_ids.contains(p)) {
1253 tsn_root_id = self.tsn_subtree_insert(mvcc, tsn_root_id, pid)?;
1254 }
1255 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1257 let Node::FreeListLeaf(dirty_leaf_node2) = &mut dirty_leaf_page.node else {
1258 return Err(DcbError::DatabaseCorrupted(
1259 "Expected FreeListLeaf node".to_string(),
1260 ));
1261 };
1262 dirty_leaf_node2.values[last_idx].page_ids.clear();
1263 dirty_leaf_node2.values[last_idx].root_id = tsn_root_id;
1264 if verbose {
1265 println!("Moved inline page IDs to TSN-subtree {:?}", tsn_root_id);
1266 }
1267 }
1268 _ => { }
1269 }
1270
1271 let dirty_leaf_page = self.get_mut_dirty(dirty_leaf_page_id)?;
1273 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1274 match plan {
1275 FreePageIDInsertStrategy::PushTsnOntoFreeListLeaf => {
1276 dirty_leaf_node.push_new_key_and_value(tsn, freed_page_id);
1277 if verbose {
1278 println!(
1279 "Inserted first pair ({tsn:?} -> {freed_page_id:?}) in {dirty_leaf_page_id:?}: {:?}",
1280 dirty_leaf_node
1281 );
1282 }
1283 }
1284 FreePageIDInsertStrategy::PushPageIdOntoFreeListLeaf(last_idx) => {
1285 dirty_leaf_node.push_new_page_id(last_idx, freed_page_id);
1286 if verbose {
1287 println!(
1288 "Appended {freed_page_id:?} for existing last {tsn:?} in {dirty_leaf_page_id:?}: {:?}",
1289 dirty_leaf_node
1290 );
1291 }
1292 }
1293 FreePageIDInsertStrategy::PushPageIdOntoExistingTsnSubtree => { }
1295 FreePageIDInsertStrategy::MoveTsnToNewTsnSubtree => { }
1296 FreePageIDInsertStrategy::SplitFreeListLeaf => {
1297 let (popped_key, mut popped_value) =
1298 dirty_leaf_node.pop_last_key_and_value()?;
1299 debug_assert_eq!(popped_key, tsn);
1300 if verbose {
1301 println!(
1302 "Split (last TSN) leaf {:?}: {:?}",
1303 dirty_leaf_page_id,
1304 dirty_leaf_node.clone()
1305 );
1306 }
1307 popped_value.page_ids.push(freed_page_id);
1309 let new_leaf_node = FreeListLeafNode {
1310 keys: vec![popped_key],
1311 values: vec![popped_value],
1312 };
1313 let new_leaf_page_id = self.alloc_page_id();
1314 let new_leaf_page =
1315 Page::new(new_leaf_page_id, Node::FreeListLeaf(new_leaf_node));
1316 if verbose {
1323 println!(
1324 "Created new leaf {:?} (moved last TSN): {:?}",
1325 new_leaf_page_id, new_leaf_page.node
1326 );
1327 }
1328 self.insert_dirty(new_leaf_page)?;
1329 split_info = Some((tsn, new_leaf_page_id));
1330 }
1331 FreePageIDInsertStrategy::CreateAndPromoteFreeListLeaf => {
1332 let new_leaf_node = FreeListLeafNode {
1334 keys: vec![tsn],
1335 values: vec![FreeListLeafValue {
1336 page_ids: vec![freed_page_id],
1337 root_id: PageID(0),
1338 }],
1339 };
1340 let new_leaf_page_id = self.alloc_page_id();
1341 let new_leaf_page =
1342 Page::new(new_leaf_page_id, Node::FreeListLeaf(new_leaf_node));
1343 if verbose {
1350 println!(
1351 "Created new leaf {:?} (new last TSN): {:?}",
1352 new_leaf_page_id, new_leaf_page.node
1353 );
1354 }
1355 self.insert_dirty(new_leaf_page)?;
1356 split_info = Some((tsn, new_leaf_page_id));
1357 }
1358 }
1359 } else {
1360 return Err(DcbError::DatabaseCorrupted(
1361 "Expected FreeListLeaf node".to_string(),
1362 ));
1363 }
1364 let mut current_replacement_info = replacement_info;
1366 while let Some(parent_page_id) = stack.pop() {
1367 let dirty_page_id = { self.get_dirty_page_id(parent_page_id)? };
1369 let parent_replacement_info: Option<(PageID, PageID)> = {
1370 if dirty_page_id != parent_page_id {
1371 Some((parent_page_id, dirty_page_id))
1372 } else {
1373 None
1374 }
1375 };
1376 let dirty_internal_page = self.get_mut_dirty(dirty_page_id)?;
1378
1379 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1380 if let Some((old_id, new_id)) = current_replacement_info {
1381 dirty_internal_node.replace_last_child_id(old_id, new_id)?;
1382 if verbose {
1383 println!(
1384 "Replaced {old_id:?} with {new_id:?} in {dirty_page_id:?}: {dirty_internal_node:?}"
1385 );
1386 }
1387 } else if verbose {
1388 println!("Nothing to replace in {dirty_page_id:?}")
1389 }
1390 } else {
1391 return Err(DcbError::DatabaseCorrupted(
1392 "Expected FreeListInternal node".to_string(),
1393 ));
1394 }
1395
1396 if let Some((promoted_key, promoted_page_id)) = split_info {
1397 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1398 dirty_internal_node
1400 .append_promoted_key_and_page_id(promoted_key, promoted_page_id)?;
1401
1402 if verbose {
1403 println!(
1404 "Appended promoted key {promoted_key:?} and child {promoted_page_id:?} in {dirty_page_id:?}: {dirty_internal_node:?}"
1405 );
1406 }
1407 } else {
1408 return Err(DcbError::DatabaseCorrupted(
1409 "Expected FreeListInternal node".to_string(),
1410 ));
1411 }
1412 }
1413
1414 if dirty_internal_page.calc_serialized_size() > mvcc.page_size {
1417 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
1418 if verbose {
1419 println!("Splitting internal {dirty_page_id:?}...");
1420 }
1421 if dirty_internal_node.keys.len() < 3 || dirty_internal_node.child_ids.len() < 4
1424 {
1425 return Err(DcbError::DatabaseCorrupted(
1426 "Cannot split internal node with too few keys/children".to_string(),
1427 ));
1428 }
1429
1430 let (promoted_key, new_keys, new_child_ids) =
1432 dirty_internal_node.split_off()?;
1433
1434 assert_eq!(
1436 dirty_internal_node.keys.len() + 1,
1437 dirty_internal_node.child_ids.len()
1438 );
1439
1440 let new_internal_node = FreeListInternalNode {
1441 keys: new_keys,
1442 child_ids: new_child_ids,
1443 };
1444
1445 assert_eq!(
1447 new_internal_node.keys.len() + 1,
1448 new_internal_node.child_ids.len()
1449 );
1450
1451 let new_internal_page_id = self.alloc_page_id();
1453 let new_internal_page = Page::new(
1454 new_internal_page_id,
1455 Node::FreeListInternal(new_internal_node),
1456 );
1457 if verbose {
1458 println!(
1459 "Created internal {:?}: {:?}",
1460 new_internal_page_id, new_internal_page.node
1461 );
1462 }
1463 self.insert_dirty(new_internal_page)?;
1464
1465 split_info = Some((promoted_key, new_internal_page_id));
1466 } else {
1467 return Err(DcbError::DatabaseCorrupted(
1468 "Expected FreeListInternal node".to_string(),
1469 ));
1470 }
1471 } else {
1472 split_info = None;
1473 }
1474 current_replacement_info = parent_replacement_info;
1475 }
1476
1477 if let Some((old_id, new_id)) = current_replacement_info {
1478 if self.free_lists_tree_root_id == old_id {
1479 self.free_lists_tree_root_id = new_id;
1480 if verbose {
1481 println!("Replaced root {old_id:?} with {new_id:?}");
1482 }
1483 } else {
1484 return Err(DcbError::RootIDMismatch(old_id.0, new_id.0));
1485 }
1486 }
1487
1488 if let Some((promoted_key, promoted_page_id)) = split_info {
1489 let new_internal_node = FreeListInternalNode {
1491 keys: vec![promoted_key],
1492 child_ids: vec![self.free_lists_tree_root_id, promoted_page_id],
1493 };
1494
1495 let new_root_page_id = self.alloc_page_id();
1496 let new_root_page =
1497 Page::new(new_root_page_id, Node::FreeListInternal(new_internal_node));
1498 if verbose {
1499 println!(
1500 "Created new internal root {:?}: {:?}",
1501 new_root_page_id, new_root_page.node
1502 );
1503 }
1504 self.insert_dirty(new_root_page)?;
1505
1506 self.free_lists_tree_root_id = new_root_page_id;
1507 }
1508
1509 Ok(())
1510 }
1511
1512 fn tsn_subtree_insert(
1515 &mut self,
1516 mvcc: &Mvcc,
1517 root_id: PageID,
1518 key: PageID,
1519 ) -> DcbResult<PageID> {
1520 let verbose = self.verbose;
1521 let mut stack: Vec<(PageID, usize)> = Vec::new();
1522 let mut current_id = root_id;
1523 loop {
1524 let current_page_ref = self.get_page_ref(mvcc, current_id)?;
1525 match ¤t_page_ref.node {
1526 Node::FreeListTsnLeaf(_) => break,
1527 Node::FreeListTsnInternal(internal) => {
1528 let child_idx = match internal.keys.binary_search_by(|k| k.0.cmp(&key.0)) {
1530 Ok(idx) => idx + 1, Err(idx) => idx, };
1533 let next_id = internal.child_ids[child_idx];
1534 stack.push((current_id, child_idx));
1535 current_id = next_id;
1536 }
1537 other => {
1538 return Err(DcbError::DatabaseCorrupted(format!(
1539 "Unexpected node type in TSN-subtree during insert: {}",
1540 other.type_name()
1541 )));
1542 }
1543 }
1544 }
1545
1546 {
1549 let leaf_page = self.get_mut_dirty(current_id)?;
1550 let Node::FreeListTsnLeaf(ref mut leaf) = leaf_page.node else {
1551 return Err(DcbError::DatabaseCorrupted(
1552 "Expected TSN-subtree leaf".to_string(),
1553 ));
1554 };
1555 match leaf.page_ids.binary_search_by(|pid| pid.0.cmp(&key.0)) {
1557 Ok(_) => {
1558 if verbose {
1560 println!("Duplicate PageID {:?} ignored in TSN-subtree", key);
1561 }
1562 }
1563 Err(ins) => {
1564 leaf.page_ids.insert(ins, key);
1565 if leaf.calc_serialized_size() <= mvcc.max_node_size {
1566 return Ok(root_id);
1567 }
1568 let mid = leaf.page_ids.len() / 2; let right_ids: Vec<PageID> = leaf.page_ids.split_off(mid);
1571 let promoted_key = right_ids[0];
1572 let right_leaf_id = self.alloc_page_id();
1574 let right_leaf_node = FreeListTsnLeafNode {
1575 page_ids: right_ids,
1576 };
1577 let right_leaf_page =
1578 Page::new(right_leaf_id, Node::FreeListTsnLeaf(right_leaf_node));
1579 self.insert_dirty(right_leaf_page)?;
1580 let mut promoted: Option<(PageID, PageID)> =
1582 Some((promoted_key, right_leaf_id));
1583 for (parent_id, child_idx) in stack.into_iter().rev() {
1585 if let Some((prom_key, prom_right_id)) = promoted.take() {
1586 let parent_page = self.get_mut_dirty(parent_id)?;
1587 let Node::FreeListTsnInternal(ref mut parent_node) = parent_page.node
1588 else {
1589 return Err(DcbError::DatabaseCorrupted(
1590 "Expected TSN-subtree internal".to_string(),
1591 ));
1592 };
1593
1594 parent_node.keys.insert(child_idx, prom_key);
1596 parent_node.child_ids.insert(child_idx + 1, prom_right_id);
1597 if parent_node.calc_serialized_size() <= mvcc.max_node_size {
1598 current_id = parent_id;
1600 continue;
1601 }
1602 let total_keys = parent_node.keys.len();
1604 debug_assert!(
1605 total_keys >= 2,
1606 "splitting parent with <2 keys after insert"
1607 );
1608 let mid = total_keys / 2; let promote_up_key = parent_node.keys[mid];
1610 let left_keys: Vec<PageID> = parent_node.keys[..mid].to_vec();
1612 let left_child_ids: Vec<PageID> =
1613 parent_node.child_ids[..=mid].to_vec();
1614 let right_keys: Vec<PageID> = parent_node.keys[mid + 1..].to_vec();
1616 let right_child_ids: Vec<PageID> =
1617 parent_node.child_ids[mid + 1..].to_vec();
1618 if right_child_ids.len() != right_keys.len() + 1 {
1619 return Err(DcbError::DatabaseCorrupted(
1620 "TSN-subtree internal split produced invalid right arity"
1621 .to_string(),
1622 ));
1623 }
1624 if left_child_ids.len() != left_keys.len() + 1 {
1625 return Err(DcbError::DatabaseCorrupted(
1626 "TSN-subtree internal split produced invalid left arity"
1627 .to_string(),
1628 ));
1629 }
1630 parent_node.keys = left_keys;
1632 parent_node.child_ids = left_child_ids;
1633 let right_internal_id = self.alloc_page_id();
1635 let right_internal =
1636 crate::free_lists_tree_nodes::FreeListTsnInternalNode {
1637 keys: right_keys,
1638 child_ids: right_child_ids,
1639 };
1640 let right_internal_page = Page::new(
1641 right_internal_id,
1642 Node::FreeListTsnInternal(right_internal),
1643 );
1644 self.insert_dirty(right_internal_page)?;
1645 promoted = Some((promote_up_key, right_internal_id));
1647 }
1648 current_id = parent_id;
1649 }
1650 if let Some((prom_key, prom_right_id)) = promoted.take() {
1652 let new_root_id = self.alloc_page_id();
1653 let left_id = current_id;
1654 let new_root = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
1655 keys: vec![prom_key],
1656 child_ids: vec![left_id, prom_right_id],
1657 };
1658 let new_root_page =
1659 Page::new(new_root_id, Node::FreeListTsnInternal(new_root));
1660 self.insert_dirty(new_root_page)?;
1661 if verbose {
1662 println!("Promoted new TSN-subtree root {:?}", new_root_id);
1663 }
1664 return Ok(new_root_id);
1665 }
1666 }
1667 };
1668 Ok(root_id)
1669 }
1670 }
1671
1672 pub fn remove_free_page_id(
1673 &mut self,
1674 mvcc: &Mvcc,
1675 tsn: Tsn,
1676 used_page_id: PageID,
1677 ) -> DcbResult<()> {
1678 let verbose = self.verbose;
1679 if verbose {
1680 println!();
1681 println!("Removing {used_page_id:?} from {tsn:?}...");
1682 println!("Root is {:?}", self.free_lists_tree_root_id);
1683 }
1684 let mut current_page_id = self.free_lists_tree_root_id;
1686
1687 let mut stack: Vec<PageID> = Vec::new();
1689 let mut removed_page_ids: Vec<PageID> = Vec::new();
1690
1691 loop {
1692 let current_page_ref = self.get_page_ref(mvcc, current_page_id)?;
1693 if matches!(current_page_ref.node, Node::FreeListLeaf(_)) {
1694 break;
1695 }
1696 if let Node::FreeListInternal(internal_node) = ¤t_page_ref.node {
1697 if verbose {
1698 println!("Page {:?} is internal node", current_page_ref.page_id);
1699 }
1700 stack.push(current_page_id);
1701 current_page_id = *internal_node.child_ids.first().unwrap();
1702 } else {
1703 return Err(DcbError::DatabaseCorrupted(
1704 "Expected FreeListInternal node".to_string(),
1705 ));
1706 }
1707 }
1708 if verbose {
1709 println!("Page {current_page_id:?} is leaf node");
1710 }
1711
1712 let mut replacement_info: Option<(PageID, PageID)> = None;
1715 let mut removal_info = None;
1716
1717 let leaf_snapshot = { self.get_page_ref(mvcc, current_page_id)? };
1719 let Node::FreeListLeaf(leaf_node_ro) = &leaf_snapshot.node else {
1720 return Err(DcbError::DatabaseCorrupted(
1721 "Expected FreeListLeaf node".to_string(),
1722 ));
1723 };
1724 if leaf_node_ro.keys.is_empty() || leaf_node_ro.keys[0] != tsn {
1725 return Err(DcbError::DatabaseCorrupted(format!(
1726 "Expected TSN {} not found: {:?}",
1727 tsn.0, leaf_node_ro
1728 )));
1729 }
1730
1731 let leaf_value_root_id = leaf_node_ro.values[0].root_id;
1732 if leaf_value_root_id == PageID(0) {
1738 let dirty_page_id = { self.get_dirty_page_id(current_page_id)? };
1740 if dirty_page_id != current_page_id {
1741 replacement_info = Some((current_page_id, dirty_page_id));
1742 }
1743 let dirty_leaf_page = self.get_mut_dirty(dirty_page_id)?;
1744 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1745 let leaf_value = &mut dirty_leaf_node.values[0];
1746 if let Some(pos) = leaf_value
1747 .page_ids
1748 .iter()
1749 .position(|&id| id == used_page_id)
1750 {
1751 leaf_value.page_ids.remove(pos);
1752 } else {
1753 return Err(DcbError::DatabaseCorrupted(format!(
1754 "{used_page_id:?} not found in {tsn:?}"
1755 )));
1756 }
1757 if verbose {
1758 println!("Removed {used_page_id:?} from {tsn:?} in {dirty_page_id:?}");
1759 }
1760 if leaf_value.page_ids.is_empty() {
1761 dirty_leaf_node.keys.remove(0);
1762 dirty_leaf_node.values.remove(0);
1763 if verbose {
1764 println!("Removed {tsn:?} from {dirty_page_id:?}");
1765 }
1766 if dirty_leaf_node.keys.is_empty() {
1767 if verbose {
1768 println!("Empty leaf page {dirty_page_id:?}: {dirty_leaf_node:?}");
1769 }
1770 removal_info = Some(dirty_page_id);
1771 } else if verbose {
1772 println!("Leaf page not empty {dirty_page_id:?}: {dirty_leaf_node:?}");
1773 }
1774 } else if verbose {
1775 println!("Leaf value not empty {tsn:?}: {leaf_value:?}");
1776 }
1777 } else {
1778 return Err(DcbError::DatabaseCorrupted(
1779 "Expected FreeListLeaf node".to_string(),
1780 ));
1781 }
1782 } else {
1783 let tsn_root_id = leaf_value_root_id;
1785 let dirty_tsn_root_id = { self.get_dirty_page_id(tsn_root_id)? };
1786 let mut tsn_root_replaced: Option<PageID> = None;
1787 let dirty_tsn_root_page = self.get_mut_dirty(dirty_tsn_root_id)?;
1788 let mut tsn_leaf_became_empty = false;
1789 let mut tsn_child_leaf_became_empty = false;
1790 match &mut dirty_tsn_root_page.node {
1791 Node::FreeListTsnLeaf(tsn_leaf_node) => {
1792 if let Some(pos) = tsn_leaf_node
1793 .page_ids
1794 .iter()
1795 .position(|&id| id == used_page_id)
1796 {
1797 tsn_leaf_node.page_ids.remove(pos);
1798 } else {
1799 return Err(DcbError::DatabaseCorrupted(format!(
1800 "{used_page_id:?} not found in TSN-subtree for {tsn:?}"
1801 )));
1802 }
1803 if verbose {
1804 println!(
1805 "Removed {used_page_id:?} from TSN-subtree leaf {dirty_tsn_root_id:?} for {tsn:?}"
1806 );
1807 }
1808 if tsn_leaf_node.page_ids.is_empty() {
1809 tsn_leaf_became_empty = true;
1810 removed_page_ids.push(dirty_tsn_root_id);
1811 }
1812 }
1813 Node::FreeListTsnInternal(_) => {
1814 let mut path: Vec<(PageID, usize)> = Vec::new();
1824 let mut current_id = dirty_tsn_root_id;
1825 loop {
1826 let node_owned = { self.get_page_ref(mvcc, current_id)?.node.clone() };
1827 match node_owned {
1828 Node::FreeListTsnLeaf(_) => {
1829 break; }
1831 Node::FreeListTsnInternal(internal) => {
1832 let mut child_idx = 0usize;
1834 while child_idx < internal.keys.len()
1835 && used_page_id >= internal.keys[child_idx]
1836 {
1837 child_idx += 1;
1838 }
1839 let next_id = internal.child_ids[child_idx];
1840 path.push((current_id, child_idx));
1841 current_id = next_id;
1842 }
1843 other => {
1844 return Err(DcbError::DatabaseCorrupted(format!(
1845 "Unexpected node type in TSN-subtree during descent: {}",
1846 other.type_name()
1847 )));
1848 }
1849 }
1850 }
1851
1852 let mut dirty_child_id = { self.get_dirty_page_id(current_id)? };
1854 {
1855 let child_page = self.get_mut_dirty(dirty_child_id)?;
1856 match &mut child_page.node {
1857 Node::FreeListTsnLeaf(leaf_node) => {
1858 if let Some(pos) =
1859 leaf_node.page_ids.iter().position(|&id| id == used_page_id)
1860 {
1861 leaf_node.page_ids.remove(pos);
1862 } else {
1863 return Err(DcbError::DatabaseCorrupted(format!(
1864 "{used_page_id:?} not found in TSN-subtree for {tsn:?}"
1865 )));
1866 }
1867 if verbose {
1868 println!(
1869 "Removed {used_page_id:?} from TSN-subtree leaf {dirty_child_id:?} for {tsn:?}"
1870 );
1871 }
1872 if leaf_node.page_ids.is_empty() {
1873 tsn_child_leaf_became_empty = true;
1874 removed_page_ids.push(dirty_child_id);
1875 }
1876 }
1877 other => {
1878 return Err(DcbError::DatabaseCorrupted(format!(
1879 "Expected TSN-subtree leaf, got {}",
1880 other.type_name()
1881 )));
1882 }
1883 }
1884 }
1885
1886 let mut subtree_emptied = false;
1888 let mut new_root_id_opt: Option<PageID> = None;
1889
1890 let path_len = path.len();
1891 for (level, (parent_id, child_idx)) in path.into_iter().rev().enumerate() {
1892 let parent_dirty_id = { self.get_dirty_page_id(parent_id)? };
1894
1895 let parent_page = self.get_mut_dirty(parent_dirty_id)?;
1900 let Node::FreeListTsnInternal(ref mut parent_node) = parent_page.node
1901 else {
1902 return Err(DcbError::DatabaseCorrupted(
1903 "Expected TSN-subtree internal node".to_string(),
1904 ));
1905 };
1906
1907 if tsn_child_leaf_became_empty && level == 0 {
1908 parent_node.child_ids.remove(child_idx);
1910 if !parent_node.keys.is_empty() {
1911 let key_remove_idx = if child_idx == 0 { 0 } else { child_idx - 1 };
1912 if key_remove_idx < parent_node.keys.len() {
1913 parent_node.keys.remove(key_remove_idx);
1914 }
1915 }
1916
1917 match parent_node.child_ids.len() {
1919 0 => {
1920 subtree_emptied = true;
1922 removed_page_ids.push(parent_dirty_id);
1923 }
1924 1 => {
1925 let remaining_child = parent_node.child_ids[0];
1927 removed_page_ids.push(parent_dirty_id);
1928 dirty_child_id = remaining_child;
1930 new_root_id_opt = Some(remaining_child);
1931 }
1932 _ => {
1933 dirty_child_id = parent_dirty_id;
1935 if level == path_len - 1 {
1936 new_root_id_opt = Some(parent_dirty_id);
1938 }
1939 }
1940 }
1941 } else {
1942 if parent_node.child_ids[child_idx] != dirty_child_id {
1944 parent_node.child_ids[child_idx] = dirty_child_id;
1945 }
1946 dirty_child_id = parent_dirty_id;
1948 if level == path_len - 1 {
1949 new_root_id_opt = Some(parent_dirty_id);
1950 }
1951 }
1952 }
1953
1954 if subtree_emptied {
1957 tsn_leaf_became_empty = true;
1958 } else if let Some(new_root) = new_root_id_opt
1959 && new_root != dirty_tsn_root_id
1960 {
1961 tsn_root_replaced = Some(new_root);
1962 }
1963 }
1964 _ => {
1965 return Err(DcbError::DatabaseCorrupted(
1966 "Expected TSN-subtree node".to_string(),
1967 ));
1968 }
1969 }
1970
1971 let dirty_page_id = { self.get_dirty_page_id(current_page_id)? };
1973 if dirty_page_id != current_page_id {
1974 replacement_info = Some((current_page_id, dirty_page_id));
1975 }
1976 let dirty_leaf_page = self.get_mut_dirty(dirty_page_id)?;
1977 if let Node::FreeListLeaf(dirty_leaf_node) = &mut dirty_leaf_page.node {
1978 if dirty_leaf_node.keys.is_empty() || dirty_leaf_node.keys[0] != tsn {
1980 return Err(DcbError::DatabaseCorrupted(format!(
1981 "Expected TSN {} not found in dirty leaf: {:?}",
1982 tsn.0, dirty_leaf_node
1983 )));
1984 }
1985 if tsn_leaf_became_empty {
1986 dirty_leaf_node.keys.remove(0);
1988 dirty_leaf_node.values.remove(0);
1989 if verbose {
1990 println!("Removed {tsn:?} from {dirty_page_id:?}");
1991 }
1992 if dirty_leaf_node.keys.is_empty() {
1993 if verbose {
1994 println!("Empty leaf page {dirty_page_id:?}: {dirty_leaf_node:?}");
1995 }
1996 removal_info = Some(dirty_page_id);
1997 } else if verbose {
1998 println!("Leaf page not empty {dirty_page_id:?}: {dirty_leaf_node:?}");
1999 }
2000 } else if let Some(new_root) = tsn_root_replaced {
2001 dirty_leaf_node.values[0].root_id = new_root;
2003 } else if dirty_tsn_root_id != tsn_root_id {
2004 dirty_leaf_node.values[0].root_id = dirty_tsn_root_id;
2006 }
2007 } else {
2008 return Err(DcbError::DatabaseCorrupted(
2009 "Expected FreeListLeaf node".to_string(),
2010 ));
2011 }
2012 }
2013
2014 let mut current_replacement_info = replacement_info;
2016
2017 while let Some(parent_page_id) = stack.pop() {
2018 let dirty_page_id = { self.get_dirty_page_id(parent_page_id)? };
2020 let parent_replacement_info: Option<(PageID, PageID)> = {
2021 if dirty_page_id != parent_page_id {
2022 Some((parent_page_id, dirty_page_id))
2023 } else {
2024 None
2025 }
2026 };
2027 let dirty_internal_page = self.get_mut_dirty(dirty_page_id)?;
2029
2030 if let Some((old_id, new_id)) = current_replacement_info {
2031 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
2032 if dirty_internal_node.child_ids[0] == old_id {
2034 dirty_internal_node.child_ids[0] = new_id;
2035 if verbose {
2036 println!(
2037 "Replaced {old_id:?} with {new_id:?} in {dirty_page_id:?}: {dirty_internal_page:?}"
2038 );
2039 }
2040 } else {
2041 return Err(DcbError::DatabaseCorrupted("Child ID mismatch".to_string()));
2042 }
2043 } else {
2044 return Err(DcbError::DatabaseCorrupted(
2045 "Expected FreeListInternal node".to_string(),
2046 ));
2047 }
2048 }
2049 current_replacement_info = parent_replacement_info;
2050
2051 if let Some(removed_page_id) = removal_info {
2052 removed_page_ids.push(removed_page_id);
2053
2054 if let Node::FreeListInternal(dirty_internal_node) = &mut dirty_internal_page.node {
2055 if dirty_internal_node.child_ids[0] != removed_page_id {
2057 return Err(DcbError::DatabaseCorrupted("Child ID mismatch".to_string()));
2058 }
2059 if dirty_internal_node.keys.is_empty() {
2060 return Err(DcbError::DatabaseCorrupted(
2061 "Empty internal node keys".to_string(),
2062 ));
2063 }
2064 dirty_internal_node.child_ids.remove(0);
2065 dirty_internal_node.keys.remove(0);
2066 if verbose {
2067 println!(
2068 "Removed {removed_page_id:?} from {dirty_page_id:?}: {dirty_internal_node:?}"
2069 );
2070 }
2071
2072 if dirty_internal_node.keys.is_empty() {
2074 if verbose {
2075 println!(
2076 "Empty internal page {dirty_page_id:?}: {dirty_internal_node:?}"
2077 );
2078 }
2079 assert_eq!(dirty_internal_node.child_ids.len(), 1);
2080 let orphaned_child_id = dirty_internal_node.child_ids[0];
2081
2082 removed_page_ids.push(dirty_page_id);
2083
2084 if let Some((old_id, _)) = parent_replacement_info {
2085 current_replacement_info = Some((old_id, orphaned_child_id));
2086 } else {
2087 current_replacement_info = Some((dirty_page_id, orphaned_child_id));
2088 }
2089 }
2090 } else {
2091 return Err(DcbError::DatabaseCorrupted(
2092 "Expected FreeListInternal node".to_string(),
2093 ));
2094 }
2095
2096 removal_info = None;
2097 }
2098 }
2099
2100 for &removed_page_id in &removed_page_ids {
2101 self.append_freed_page_id(removed_page_id);
2102 }
2103
2104 if let Some((old_id, new_id)) = current_replacement_info {
2107 if self.free_lists_tree_root_id == old_id {
2108 self.append_freed_page_id(old_id);
2109 self.free_lists_tree_root_id = new_id;
2110 if verbose {
2111 println!("Replaced root {old_id:?} with {new_id:?}");
2112 }
2113 } else {
2114 return Err(DcbError::RootIDMismatch(old_id.0, new_id.0));
2115 }
2116 }
2117
2118 Ok(())
2119 }
2120}
2121
2122enum FreePageIDInsertStrategy {
2123 PushTsnOntoFreeListLeaf,
2124 PushPageIdOntoFreeListLeaf(usize),
2125 PushPageIdOntoExistingTsnSubtree,
2126 MoveTsnToNewTsnSubtree,
2127 SplitFreeListLeaf,
2128 CreateAndPromoteFreeListLeaf,
2129}
2130
2131pub struct Reader {
2133 pub header_page_id: PageID,
2134 pub tsn: Tsn,
2135 pub events_tree_root_id: PageID,
2136 pub tags_tree_root_id: PageID,
2137 pub next_position: Position,
2138 pub tracking_tree_root_id: PageID,
2139 reader_id: usize,
2140 reader_tsns: Arc<DashMap<usize, Tsn>>,
2141}
2142
2143impl Drop for Reader {
2144 fn drop(&mut self) {
2145 self.reader_tsns.remove(&self.reader_id);
2147 }
2148}
2149
2150#[cfg(test)]
2151mod tests {
2152 use super::*;
2153 use crate::free_lists_tree_nodes::FreeListLeafValue;
2154 use serial_test::serial;
2155 use tempfile::tempdir;
2156
2157 static VERBOSE: bool = false;
2158
2159 #[test]
2160 #[serial]
2161 fn test_mvcc_init() {
2162 let temp_dir = tempdir().unwrap();
2163 let db_path = temp_dir.path().join("mvcc-test.db");
2164
2165 {
2166 let db = Mvcc::new(
2167 VERBOSE,
2168 StorageOptions::default()
2169 .db_path(db_path.clone())
2170 .page_size(4096),
2171 )
2172 .unwrap();
2173 assert!(db.pager.is_file_new);
2174 }
2175
2176 {
2177 let db = Mvcc::new(
2178 VERBOSE,
2179 StorageOptions::default()
2180 .db_path(db_path.clone())
2181 .page_size(4096),
2182 )
2183 .unwrap();
2184 assert!(!db.pager.is_file_new);
2185 }
2186 }
2187
2188 #[test]
2189 #[serial]
2190 fn test_write_transaction_incrementing_tsn_and_alternating_header() {
2191 let temp_dir = tempdir().unwrap();
2192 let db_path = temp_dir.path().join("mvcc-test.db");
2193 let db = Mvcc::new(
2194 VERBOSE,
2195 StorageOptions::default().db_path(db_path).page_size(4096),
2196 )
2197 .unwrap();
2198
2199 {
2200 let mut writer = db.writer().unwrap();
2201 assert_eq!(Tsn(1), writer.tsn);
2202 assert_eq!(PageID(0), writer.header_page_id);
2203 db.commit(&mut writer).unwrap();
2204 }
2205
2206 {
2207 let mut writer = db.writer().unwrap();
2208 assert_eq!(Tsn(2), writer.tsn);
2209 assert_eq!(PageID(1), writer.header_page_id);
2210 db.commit(&mut writer).unwrap();
2211 }
2212
2213 {
2214 let mut writer = db.writer().unwrap();
2215 assert_eq!(Tsn(3), writer.tsn);
2216 assert_eq!(PageID(0), writer.header_page_id);
2217 db.commit(&mut writer).unwrap();
2218 }
2219
2220 {
2221 let mut writer = db.writer().unwrap();
2222 assert_eq!(Tsn(4), writer.tsn);
2223 assert_eq!(PageID(1), writer.header_page_id);
2224 db.commit(&mut writer).unwrap();
2225 }
2226
2227 {
2228 let mut writer = db.writer().unwrap();
2229 assert_eq!(Tsn(5), writer.tsn);
2230 assert_eq!(PageID(0), writer.header_page_id);
2231 db.commit(&mut writer).unwrap();
2232 }
2233 }
2234
2235 #[test]
2236 #[serial]
2237 fn test_read_transaction_header_and_tsn() {
2238 let temp_dir = tempdir().unwrap();
2239 let db_path = temp_dir.path().join("mvcc-test.db");
2240 let db = Mvcc::new(
2241 VERBOSE,
2242 StorageOptions::default().db_path(db_path).page_size(4096),
2243 )
2244 .unwrap();
2245
2246 {
2248 assert_eq!(0, db.reader_tsns.len());
2249 let reader = db.reader().unwrap();
2250 assert_eq!(1, db.reader_tsns.len());
2251 assert_eq!(
2252 vec![Tsn(0)],
2253 db.reader_tsns
2254 .iter()
2255 .map(|r| *r.value())
2256 .collect::<Vec<_>>()
2257 );
2258 assert_eq!(PageID(0), reader.header_page_id);
2259 assert_eq!(Tsn(0), reader.tsn);
2260 }
2261 assert_eq!(0, db.reader_tsns.len());
2262
2263 {
2265 let reader1 = db.reader().unwrap();
2266 assert_eq!(
2267 vec![Tsn(0)],
2268 db.reader_tsns
2269 .iter()
2270 .map(|r| *r.value())
2271 .collect::<Vec<_>>()
2272 );
2273 assert_eq!(PageID(0), reader1.header_page_id);
2274 assert_eq!(Tsn(0), reader1.tsn);
2275
2276 {
2277 let reader2 = db.reader().unwrap();
2278 assert_eq!(
2279 vec![Tsn(0), Tsn(0)],
2280 db.reader_tsns
2281 .iter()
2282 .map(|r| *r.value())
2283 .collect::<Vec<_>>()
2284 );
2285 assert_eq!(PageID(0), reader2.header_page_id);
2286 assert_eq!(Tsn(0), reader2.tsn);
2287
2288 {
2289 let reader3 = db.reader().unwrap();
2290 assert_eq!(
2291 vec![Tsn(0), Tsn(0), Tsn(0)],
2292 db.reader_tsns
2293 .iter()
2294 .map(|r| *r.value())
2295 .collect::<Vec<_>>()
2296 );
2297 assert_eq!(PageID(0), reader3.header_page_id);
2298 assert_eq!(Tsn(0), reader3.tsn);
2299 }
2300 }
2301 }
2302 assert_eq!(0, db.reader_tsns.len());
2303
2304 {
2306 let mut writer = db.writer().unwrap();
2307 assert_eq!(0, db.reader_tsns.len());
2308 assert_eq!(Tsn(1), writer.tsn);
2309 assert_eq!(PageID(0), writer.header_page_id);
2310 db.commit(&mut writer).unwrap();
2311 }
2312
2313 {
2315 let reader = db.reader().unwrap();
2316 assert_eq!(
2317 vec![Tsn(1)],
2318 db.reader_tsns
2319 .iter()
2320 .map(|r| *r.value())
2321 .collect::<Vec<_>>()
2322 );
2323 assert_eq!(PageID(1), reader.header_page_id);
2324 assert_eq!(Tsn(1), reader.tsn);
2325 }
2326 }
2327
2328 #[test]
2329 #[serial]
2330 fn test_copy_on_write_page_reuse() {
2331 let temp_dir = tempdir().unwrap();
2332 let db_path = temp_dir.path().join("mvcc-test.db");
2333 let db = Mvcc::new(
2334 VERBOSE,
2335 StorageOptions::default().db_path(db_path).page_size(4096),
2336 )
2337 .unwrap();
2338 {
2340 let mut writer = db.writer().unwrap();
2341
2342 assert_eq!(0, writer.reusable_page_ids.len());
2344
2345 assert_eq!(PageID(2), writer.free_lists_tree_root_id);
2347
2348 assert_eq!(PageID(3), writer.events_tree_root_id);
2350
2351 let free_page_id = writer.alloc_page_id();
2353 assert_eq!(PageID(5), free_page_id);
2354 writer
2355 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2356 .unwrap();
2357
2358 assert_eq!(1, writer.dirty.len());
2360 assert_eq!(PageID(6), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2361
2362 assert_eq!(1, writer.freed_page_ids.len());
2364 assert_eq!(PageID(2), writer.freed_page_ids[0]);
2365
2366 db.commit(&mut writer).unwrap();
2367 }
2368
2369 {
2371 let mut writer = db.writer().unwrap();
2372
2373 assert_eq!(2, writer.reusable_page_ids.len());
2375 assert_eq!((PageID(5), Tsn(1)), writer.reusable_page_ids[0]);
2376 assert_eq!((PageID(2), Tsn(1)), writer.reusable_page_ids[1]);
2377
2378 assert_eq!(PageID(6), writer.free_lists_tree_root_id);
2380
2381 assert_eq!(PageID(3), writer.events_tree_root_id);
2383
2384 let free_page_id = writer.alloc_page_id();
2386 assert_eq!(PageID(5), free_page_id);
2387 writer
2388 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2389 .unwrap();
2390
2391 assert_eq!(1, writer.dirty.len());
2393 assert_eq!(PageID(2), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2394
2395 assert_eq!(1, writer.freed_page_ids.len());
2397 assert_eq!(PageID(6), writer.freed_page_ids[0]);
2398
2399 db.commit(&mut writer).unwrap();
2400 }
2401
2402 {
2404 let mut writer = db.writer().unwrap();
2405
2406 assert_eq!(2, writer.reusable_page_ids.len());
2408 assert_eq!((PageID(5), Tsn(2)), writer.reusable_page_ids[0]);
2409 assert_eq!((PageID(6), Tsn(2)), writer.reusable_page_ids[1]);
2410
2411 assert_eq!(PageID(2), writer.free_lists_tree_root_id);
2413
2414 assert_eq!(PageID(3), writer.events_tree_root_id);
2416
2417 let free_page_id = writer.alloc_page_id();
2419 assert_eq!(PageID(5), free_page_id);
2420 writer
2421 .insert_freed_page_id(&db, writer.tsn, free_page_id)
2422 .unwrap();
2423
2424 assert_eq!(1, writer.dirty.len());
2426 assert_eq!(PageID(6), *writer.dirty.keys().collect::<Vec<_>>()[0]);
2427
2428 assert_eq!(1, writer.freed_page_ids.len());
2430 assert_eq!(PageID(2), writer.freed_page_ids[0]);
2431
2432 db.commit(&mut writer).unwrap();
2433 }
2434 }
2435
2436 mod free_list_tree_tests {
2438 use super::*;
2439 use serial_test::serial;
2440 use tempfile::tempdir;
2441
2442 fn construct_mvcc(page_size: usize) -> (tempfile::TempDir, Mvcc) {
2444 let temp_dir = tempdir().unwrap();
2445 let db_path = temp_dir.path().join("mvcc-test.db");
2446 let db = Mvcc::new(
2447 VERBOSE,
2448 StorageOptions::default()
2449 .db_path(db_path)
2450 .page_size(page_size),
2451 )
2452 .unwrap();
2453 (temp_dir, db)
2454 }
2455
2456 #[test]
2457 #[serial]
2458 fn test_find_reusable_page_ids_empty_no_entries() {
2459 let (_temp_dir, db) = construct_mvcc(64);
2460 let writer = db.writer().unwrap();
2462 assert_eq!(0, writer.reusable_page_ids.len());
2463 }
2464
2465 #[test]
2466 #[serial]
2467 fn test_find_reusable_page_ids_leaf() {
2468 let (_temp_dir, db) = construct_mvcc(64);
2469 let mut writer = db.writer().unwrap();
2470
2471 let (tsn, free_pid1, free_pid2) = build_free_list_tree_leaf(&mut writer);
2472
2473 writer.find_reusable_page_ids(&db).unwrap();
2475 assert_eq!(2, writer.reusable_page_ids.len());
2476 assert_eq!((free_pid1, tsn), writer.reusable_page_ids[0]);
2477 assert_eq!((free_pid2, tsn), writer.reusable_page_ids[1]);
2478 }
2479
2480 #[test]
2481 #[serial]
2482 fn test_find_reusable_page_ids_internal_leaf() {
2483 let (_temp_dir, db) = construct_mvcc(128);
2484 let mut writer = db.writer().unwrap();
2485
2486 let (tsn1, tsn2, pid1, pid2, pid3, pid4) =
2487 build_free_list_tree_internal_leaf(&mut writer);
2488
2489 writer.find_reusable_page_ids(&db).unwrap();
2491 assert_eq!(4, writer.reusable_page_ids.len());
2493 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
2494 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
2495 assert_eq!((pid3, tsn2), writer.reusable_page_ids[2]);
2496 assert_eq!((pid4, tsn2), writer.reusable_page_ids[3]);
2497 }
2498
2499 #[test]
2500 #[serial]
2501 fn test_find_reusable_page_ids_internal_internal_leaf() {
2502 let (_temp_dir, db) = construct_mvcc(128);
2503 let mut writer = db.writer().unwrap();
2504
2505 let (tsn1, tsn2, tsn3, tsn4, pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8) =
2506 build_free_list_tree_internal_internal_leaf(&mut writer);
2507
2508 writer.find_reusable_page_ids(&db).unwrap();
2510 assert_eq!(8, writer.reusable_page_ids.len());
2512 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
2513 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
2514 assert_eq!((pid3, tsn2), writer.reusable_page_ids[2]);
2515 assert_eq!((pid4, tsn2), writer.reusable_page_ids[3]);
2516 assert_eq!((pid5, tsn3), writer.reusable_page_ids[4]);
2517 assert_eq!((pid6, tsn3), writer.reusable_page_ids[5]);
2518 assert_eq!((pid7, tsn4), writer.reusable_page_ids[6]);
2519 assert_eq!((pid8, tsn4), writer.reusable_page_ids[7]);
2520 }
2521
2522 #[test]
2523 #[serial]
2524 fn test_find_reusable_page_ids_leaf_tsn_subtree_leaf() {
2525 let (_temp_dir, db) = construct_mvcc(64);
2526 let mut writer = db.writer().unwrap();
2527
2528 let (pid1, pid2, tsn) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
2529
2530 writer.find_reusable_page_ids(&db).unwrap();
2532 assert_eq!(2, writer.reusable_page_ids.len());
2534 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2535 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2536 }
2537
2538 #[test]
2539 #[serial]
2540 fn test_find_reusable_page_ids_leaf_tsn_subtree_internal_leaf() {
2541 let (_temp_dir, db) = construct_mvcc(64);
2542 let mut writer = db.writer().unwrap();
2543
2544 let (pid1, pid2, pid3, pid4, tsn) =
2545 build_free_list_tree_leaf_tsn_subtree_internal_leaf(&mut writer);
2546
2547 writer.find_reusable_page_ids(&db).unwrap();
2549 assert_eq!(4, writer.reusable_page_ids.len());
2551 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2552 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2553 assert_eq!((pid3, tsn), writer.reusable_page_ids[2]);
2554 assert_eq!((pid4, tsn), writer.reusable_page_ids[3]);
2555 }
2556
2557 #[test]
2558 #[serial]
2559 fn test_find_reusable_page_ids_leaf_tsn_subtree_internal_internal_leaf() {
2560 let (_temp_dir, db) = construct_mvcc(64);
2561 let mut writer = db.writer().unwrap();
2562
2563 let (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn) =
2564 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut writer);
2565
2566 writer.find_reusable_page_ids(&db).unwrap();
2568 assert_eq!(8, writer.reusable_page_ids.len());
2570 assert_eq!((pid1, tsn), writer.reusable_page_ids[0]);
2571 assert_eq!((pid2, tsn), writer.reusable_page_ids[1]);
2572 assert_eq!((pid3, tsn), writer.reusable_page_ids[2]);
2573 assert_eq!((pid4, tsn), writer.reusable_page_ids[3]);
2574 assert_eq!((pid5, tsn), writer.reusable_page_ids[4]);
2575 assert_eq!((pid6, tsn), writer.reusable_page_ids[5]);
2576 assert_eq!((pid7, tsn), writer.reusable_page_ids[6]);
2577 assert_eq!((pid8, tsn), writer.reusable_page_ids[7]);
2578 }
2579
2580 fn build_free_list_tree_leaf(writer: &mut Writer) -> (Tsn, PageID, PageID) {
2581 let tsn = Tsn(123);
2583 let free_pid1 = writer.alloc_page_id();
2584 let free_pid2 = writer.alloc_page_id();
2585 let leaf = FreeListLeafNode {
2586 keys: vec![tsn],
2587 values: vec![FreeListLeafValue {
2588 page_ids: vec![free_pid1, free_pid2],
2589 root_id: PageID(0),
2590 }],
2591 };
2592 let root_id = writer.alloc_page_id();
2593 let page = Page::new(root_id, Node::FreeListLeaf(leaf));
2594 writer.insert_dirty(page).unwrap();
2595 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2596 writer.free_lists_tree_root_id = root_id;
2597 (tsn, free_pid1, free_pid2)
2598 }
2599
2600 fn build_free_list_tree_internal_leaf(
2601 writer: &mut Writer,
2602 ) -> (Tsn, Tsn, PageID, PageID, PageID, PageID) {
2603 let tsn1 = Tsn(10);
2605 let tsn2 = Tsn(20);
2606 let pid1 = writer.alloc_page_id();
2607 let pid2 = writer.alloc_page_id();
2608 let pid3 = writer.alloc_page_id();
2609 let pid4 = writer.alloc_page_id();
2610
2611 let leaf1_id = writer.alloc_page_id();
2612 let leaf2_id = writer.alloc_page_id();
2613 let leaf1 = FreeListLeafNode {
2614 keys: vec![tsn1],
2615 values: vec![FreeListLeafValue {
2616 page_ids: vec![pid1, pid2],
2617 root_id: PageID(0),
2618 }],
2619 };
2620 let leaf2 = FreeListLeafNode {
2621 keys: vec![tsn2],
2622 values: vec![FreeListLeafValue {
2623 page_ids: vec![pid3, pid4],
2624 root_id: PageID(0),
2625 }],
2626 };
2627 writer
2628 .insert_dirty(Page::new(leaf1_id, Node::FreeListLeaf(leaf1)))
2629 .unwrap();
2630 writer
2631 .insert_dirty(Page::new(leaf2_id, Node::FreeListLeaf(leaf2)))
2632 .unwrap();
2633
2634 let internal = FreeListInternalNode {
2636 keys: vec![tsn1],
2637 child_ids: vec![leaf1_id, leaf2_id],
2638 };
2639 let root_id = writer.alloc_page_id();
2640 writer
2641 .insert_dirty(Page::new(root_id, Node::FreeListInternal(internal)))
2642 .unwrap();
2643 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2644 writer.free_lists_tree_root_id = root_id;
2645 (tsn1, tsn2, pid1, pid2, pid3, pid4)
2646 }
2647
2648 fn build_free_list_tree_internal_internal_leaf(
2649 writer: &mut Writer,
2650 ) -> (
2651 Tsn,
2652 Tsn,
2653 Tsn,
2654 Tsn,
2655 PageID,
2656 PageID,
2657 PageID,
2658 PageID,
2659 PageID,
2660 PageID,
2661 PageID,
2662 PageID,
2663 ) {
2664 let tsn1 = Tsn(10);
2666 let tsn2 = Tsn(20);
2667 let tsn3 = Tsn(30);
2668 let tsn4 = Tsn(40);
2669 let pid1 = writer.alloc_page_id();
2670 let pid2 = writer.alloc_page_id();
2671 let pid3 = writer.alloc_page_id();
2672 let pid4 = writer.alloc_page_id();
2673 let pid5 = writer.alloc_page_id();
2674 let pid6 = writer.alloc_page_id();
2675 let pid7 = writer.alloc_page_id();
2676 let pid8 = writer.alloc_page_id();
2677
2678 let leaf1_id = writer.alloc_page_id();
2679 let leaf2_id = writer.alloc_page_id();
2680 let leaf3_id = writer.alloc_page_id();
2681 let leaf4_id = writer.alloc_page_id();
2682 let leaf1 = FreeListLeafNode {
2683 keys: vec![tsn1],
2684 values: vec![FreeListLeafValue {
2685 page_ids: vec![pid1, pid2],
2686 root_id: PageID(0),
2687 }],
2688 };
2689 let leaf2 = FreeListLeafNode {
2690 keys: vec![tsn2],
2691 values: vec![FreeListLeafValue {
2692 page_ids: vec![pid3, pid4],
2693 root_id: PageID(0),
2694 }],
2695 };
2696 let leaf3 = FreeListLeafNode {
2697 keys: vec![tsn3],
2698 values: vec![FreeListLeafValue {
2699 page_ids: vec![pid5, pid6],
2700 root_id: PageID(0),
2701 }],
2702 };
2703 let leaf4 = FreeListLeafNode {
2704 keys: vec![tsn4],
2705 values: vec![FreeListLeafValue {
2706 page_ids: vec![pid7, pid8],
2707 root_id: PageID(0),
2708 }],
2709 };
2710 writer
2711 .insert_dirty(Page::new(leaf1_id, Node::FreeListLeaf(leaf1)))
2712 .unwrap();
2713 writer
2714 .insert_dirty(Page::new(leaf2_id, Node::FreeListLeaf(leaf2)))
2715 .unwrap();
2716 writer
2717 .insert_dirty(Page::new(leaf3_id, Node::FreeListLeaf(leaf3)))
2718 .unwrap();
2719 writer
2720 .insert_dirty(Page::new(leaf4_id, Node::FreeListLeaf(leaf4)))
2721 .unwrap();
2722
2723 let internal1 = FreeListInternalNode {
2725 keys: vec![tsn2],
2726 child_ids: vec![leaf1_id, leaf2_id],
2727 };
2728 let internal1_id = writer.alloc_page_id();
2729 let internal2 = FreeListInternalNode {
2730 keys: vec![tsn4],
2731 child_ids: vec![leaf3_id, leaf4_id],
2732 };
2733 let internal2_id = writer.alloc_page_id();
2734 let internal3 = FreeListInternalNode {
2735 keys: vec![tsn3],
2736 child_ids: vec![internal1_id, internal2_id],
2737 };
2738 let internal3_id = writer.alloc_page_id();
2739 writer
2740 .insert_dirty(Page::new(internal1_id, Node::FreeListInternal(internal1)))
2741 .unwrap();
2742 writer
2743 .insert_dirty(Page::new(internal2_id, Node::FreeListInternal(internal2)))
2744 .unwrap();
2745 writer
2746 .insert_dirty(Page::new(internal3_id, Node::FreeListInternal(internal3)))
2747 .unwrap();
2748 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2749 writer.free_lists_tree_root_id = internal3_id;
2750 (
2751 tsn1, tsn2, tsn3, tsn4, pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8,
2752 )
2753 }
2754
2755 fn build_free_list_tree_leaf_tsn_subtree_leaf(
2756 writer: &mut Writer,
2757 ) -> (PageID, PageID, Tsn) {
2758 let tsn_sub_leaf_id = writer.alloc_page_id();
2760 let pid1 = writer.alloc_page_id();
2761 let pid2 = writer.alloc_page_id();
2762 let tsn_sub_leaf = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2763 page_ids: vec![pid1, pid2],
2764 };
2765 writer
2766 .insert_dirty(Page::new(
2767 tsn_sub_leaf_id,
2768 Node::FreeListTsnLeaf(tsn_sub_leaf),
2769 ))
2770 .unwrap();
2771
2772 let tsn = Tsn(33);
2773 let leaf = FreeListLeafNode {
2774 keys: vec![tsn],
2775 values: vec![FreeListLeafValue {
2776 page_ids: vec![],
2777 root_id: tsn_sub_leaf_id,
2778 }],
2779 };
2780 let root_id = writer.alloc_page_id();
2781 writer
2782 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2783 .unwrap();
2784 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2785 writer.free_lists_tree_root_id = root_id;
2786 (pid1, pid2, tsn)
2787 }
2788
2789 fn build_free_list_tree_leaf_tsn_subtree_internal_leaf(
2790 writer: &mut Writer,
2791 ) -> (PageID, PageID, PageID, PageID, Tsn) {
2792 let tsn_sub_leaf_id1 = writer.alloc_page_id();
2794 let pid1 = writer.alloc_page_id();
2795 let pid2 = writer.alloc_page_id();
2796 let tsn_sub_leaf1 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2797 page_ids: vec![pid1, pid2],
2798 };
2799 writer
2800 .insert_dirty(Page::new(
2801 tsn_sub_leaf_id1,
2802 Node::FreeListTsnLeaf(tsn_sub_leaf1),
2803 ))
2804 .unwrap();
2805
2806 let tsn_sub_leaf_id2 = writer.alloc_page_id();
2808 let pid3 = writer.alloc_page_id();
2809 let pid4 = writer.alloc_page_id();
2810 let tsn_sub_leaf2 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2811 page_ids: vec![pid3, pid4],
2812 };
2813 writer
2814 .insert_dirty(Page::new(
2815 tsn_sub_leaf_id2,
2816 Node::FreeListTsnLeaf(tsn_sub_leaf2),
2817 ))
2818 .unwrap();
2819
2820 let tsn_sub_internal_id = writer.alloc_page_id();
2822 let tsn_sub_internal = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2823 keys: vec![pid3],
2824 child_ids: vec![tsn_sub_leaf_id1, tsn_sub_leaf_id2],
2825 };
2826 writer
2827 .insert_dirty(Page::new(
2828 tsn_sub_internal_id,
2829 Node::FreeListTsnInternal(tsn_sub_internal),
2830 ))
2831 .unwrap();
2832
2833 let tsn = Tsn(33);
2835 let leaf = FreeListLeafNode {
2836 keys: vec![tsn],
2837 values: vec![FreeListLeafValue {
2838 page_ids: vec![],
2839 root_id: tsn_sub_internal_id,
2840 }],
2841 };
2842 let root_id = writer.alloc_page_id();
2843 writer
2844 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2845 .unwrap();
2846 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2847 writer.free_lists_tree_root_id = root_id;
2848 (pid1, pid2, pid3, pid4, tsn)
2849 }
2850
2851 fn build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(
2852 writer: &mut Writer,
2853 ) -> (
2854 PageID,
2855 PageID,
2856 PageID,
2857 PageID,
2858 PageID,
2859 PageID,
2860 PageID,
2861 PageID,
2862 Tsn,
2863 ) {
2864 let tsn_sub_leaf_id1 = writer.alloc_page_id();
2866 let pid1 = writer.alloc_page_id();
2867 let pid2 = writer.alloc_page_id();
2868 let tsn_sub_leaf1 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2869 page_ids: vec![pid1, pid2],
2870 };
2871 writer
2872 .insert_dirty(Page::new(
2873 tsn_sub_leaf_id1,
2874 Node::FreeListTsnLeaf(tsn_sub_leaf1),
2875 ))
2876 .unwrap();
2877
2878 let tsn_sub_leaf_id2 = writer.alloc_page_id();
2880 let pid3 = writer.alloc_page_id();
2881 let pid4 = writer.alloc_page_id();
2882 let tsn_sub_leaf2 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2883 page_ids: vec![pid3, pid4],
2884 };
2885 writer
2886 .insert_dirty(Page::new(
2887 tsn_sub_leaf_id2,
2888 Node::FreeListTsnLeaf(tsn_sub_leaf2),
2889 ))
2890 .unwrap();
2891
2892 let tsn_sub_internal_id1 = writer.alloc_page_id();
2894 let tsn_sub_internal1 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2895 keys: vec![pid3],
2896 child_ids: vec![tsn_sub_leaf_id1, tsn_sub_leaf_id2],
2897 };
2898 writer
2899 .insert_dirty(Page::new(
2900 tsn_sub_internal_id1,
2901 Node::FreeListTsnInternal(tsn_sub_internal1),
2902 ))
2903 .unwrap();
2904
2905 let tsn_sub_leaf_id3 = writer.alloc_page_id();
2907 let pid5 = writer.alloc_page_id();
2908 let pid6 = writer.alloc_page_id();
2909 let tsn_sub_leaf3 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2910 page_ids: vec![pid5, pid6],
2911 };
2912 writer
2913 .insert_dirty(Page::new(
2914 tsn_sub_leaf_id3,
2915 Node::FreeListTsnLeaf(tsn_sub_leaf3),
2916 ))
2917 .unwrap();
2918
2919 let tsn_sub_leaf_id4 = writer.alloc_page_id();
2921 let pid7 = writer.alloc_page_id();
2922 let pid8 = writer.alloc_page_id();
2923 let tsn_sub_leaf4 = crate::free_lists_tree_nodes::FreeListTsnLeafNode {
2924 page_ids: vec![pid7, pid8],
2925 };
2926 writer
2927 .insert_dirty(Page::new(
2928 tsn_sub_leaf_id4,
2929 Node::FreeListTsnLeaf(tsn_sub_leaf4),
2930 ))
2931 .unwrap();
2932
2933 let tsn_sub_internal_id2 = writer.alloc_page_id();
2935 let tsn_sub_internal2 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2936 keys: vec![pid7],
2937 child_ids: vec![tsn_sub_leaf_id3, tsn_sub_leaf_id4],
2938 };
2939 writer
2940 .insert_dirty(Page::new(
2941 tsn_sub_internal_id2,
2942 Node::FreeListTsnInternal(tsn_sub_internal2),
2943 ))
2944 .unwrap();
2945
2946 let tsn_sub_internal_id3 = writer.alloc_page_id();
2948 let tsn_sub_internal3 = crate::free_lists_tree_nodes::FreeListTsnInternalNode {
2949 keys: vec![pid5],
2950 child_ids: vec![tsn_sub_internal_id1, tsn_sub_internal_id2],
2951 };
2952 writer
2953 .insert_dirty(Page::new(
2954 tsn_sub_internal_id3,
2955 Node::FreeListTsnInternal(tsn_sub_internal3),
2956 ))
2957 .unwrap();
2958
2959 let tsn = writer.tsn;
2961 let leaf = FreeListLeafNode {
2962 keys: vec![tsn],
2963 values: vec![FreeListLeafValue {
2964 page_ids: vec![],
2965 root_id: tsn_sub_internal_id3,
2966 }],
2967 };
2968 let root_id = writer.alloc_page_id();
2969 writer
2970 .insert_dirty(Page::new(root_id, Node::FreeListLeaf(leaf)))
2971 .unwrap();
2972 writer.append_freed_page_id(writer.free_lists_tree_root_id);
2973 writer.free_lists_tree_root_id = root_id;
2974 (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn)
2975 }
2976
2977 #[test]
2978 #[serial]
2979 fn test_insert_freed_page_id_to_empty_leaf_root() {
2980 let (_temp_dir, mut db) = construct_mvcc(64);
2981
2982 let header_page = db.get_latest_header_page().unwrap();
2984 let header_node = header_page.as_header_node().unwrap();
2985 let header_page_id = header_page.page_id;
2986 assert_eq!(PageID(0), header_page_id);
2987
2988 assert_eq!(PageID(5), header_node.next_page_id);
2990
2991 let tsn = Tsn(1001);
2993
2994 let mut writer = Writer::new(
2995 header_page_id,
2996 tsn,
2997 header_node.next_page_id,
2998 header_node.free_lists_tree_root_id,
2999 header_node.events_tree_root_id,
3000 header_node.tags_tree_root_id,
3001 header_node.tracking_root_page_id,
3002 header_node.next_position,
3003 VERBOSE,
3004 );
3005
3006 let initial_root_id = writer.free_lists_tree_root_id;
3008 assert_eq!(PageID(2), initial_root_id);
3009
3010 let page_id = writer.alloc_page_id();
3012
3013 assert_eq!(header_node.next_page_id, page_id);
3015
3016 let current_tsn = writer.tsn;
3018 writer
3019 .insert_freed_page_id(&mut db, current_tsn, page_id)
3020 .unwrap();
3021
3022 let expected_new_root_id = PageID(header_node.next_page_id.0 + 1);
3024 assert_eq!(expected_new_root_id, writer.free_lists_tree_root_id);
3025 assert_eq!(1, writer.dirty.len());
3026 assert!(writer.dirty.contains_key(&expected_new_root_id));
3027
3028 let new_root_page = writer.dirty.get(&expected_new_root_id).unwrap();
3029 assert_eq!(expected_new_root_id, new_root_page.page_id);
3030
3031 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3032 assert_eq!(vec![initial_root_id], freed_page_ids);
3033
3034 match &new_root_page.node {
3036 Node::FreeListLeaf(node) => {
3037 let expected_keys = vec![tsn];
3038 assert_eq!(expected_keys, node.keys);
3039
3040 let expected_values = vec![FreeListLeafValue {
3041 page_ids: vec![header_node.next_page_id],
3042 root_id: PageID(0),
3043 }];
3044 assert_eq!(expected_values, node.values);
3045 }
3046 _ => panic!("Expected FreeListLeaf node"),
3047 }
3048 }
3049
3050 #[test]
3051 #[serial]
3052 fn test_remove_freed_page_id_from_root_leaf_root() {
3053 let (_temp_dir, mut db) = construct_mvcc(64);
3054
3055 let mut writer;
3057 let inserted_tsn;
3058 let inserted_page_id;
3059 let previous_root_id;
3060
3061 {
3062 writer = db.writer().unwrap();
3064
3065 previous_root_id = writer.free_lists_tree_root_id;
3067
3068 inserted_page_id = writer.alloc_page_id();
3070
3071 inserted_tsn = writer.tsn;
3073 writer
3074 .insert_freed_page_id(&mut db, inserted_tsn, inserted_page_id)
3075 .unwrap();
3076
3077 db.commit(&mut writer).unwrap();
3079 }
3080
3081 {
3083 db.reader_tsns.insert(0, Tsn(0));
3084 }
3085
3086 {
3088 writer = db.writer().unwrap();
3089
3090 assert_eq!(0, writer.reusable_page_ids.len());
3092
3093 let initial_root_id = writer.free_lists_tree_root_id;
3095 let next_page_id = writer.next_page_id;
3096
3097 writer
3099 .remove_free_page_id(&db, inserted_tsn, inserted_page_id)
3100 .unwrap();
3101
3102 let expected_new_root_id = next_page_id;
3104 assert_eq!(expected_new_root_id, writer.free_lists_tree_root_id);
3105 assert_eq!(1, writer.dirty.len());
3106 assert!(writer.dirty.contains_key(&expected_new_root_id));
3107
3108 let new_root_page = writer.dirty.get(&expected_new_root_id).unwrap();
3109 assert_eq!(expected_new_root_id, new_root_page.page_id);
3110
3111 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3112 assert_eq!(vec![initial_root_id], freed_page_ids);
3113
3114 match &new_root_page.node {
3116 Node::FreeListLeaf(node) => {
3117 let expected_keys = vec![inserted_tsn];
3118 assert_eq!(expected_keys, node.keys);
3119
3120 let expected_values = vec![FreeListLeafValue {
3121 page_ids: vec![previous_root_id],
3122 root_id: PageID(0),
3123 }];
3124 assert_eq!(expected_values, node.values);
3125 }
3126 _ => panic!("Expected FreeListLeaf node"),
3127 }
3128 }
3129 }
3130
3131 #[test]
3132 #[serial]
3133 fn test_insert_freed_page_ids_until_split_leaf() {
3134 let (_temp_dir, mut db) = construct_mvcc(64);
3135
3136 let header_page = db.get_latest_header_page().unwrap();
3138 let header_node = header_page.as_header_node().unwrap();
3139 let header_page_id = header_page.page_id;
3140
3141 let mut tsn = Tsn(100);
3143 let mut writer = Writer::new(
3144 header_page_id,
3145 Tsn(header_node.tsn.0 + 1),
3146 header_node.next_page_id,
3147 header_node.free_lists_tree_root_id,
3148 header_node.events_tree_root_id,
3149 header_node.tags_tree_root_id,
3150 header_node.tracking_root_page_id,
3151 header_node.next_position,
3152 VERBOSE,
3153 );
3154
3155 let mut has_split_leaf = false;
3156 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3157
3158 while !has_split_leaf {
3160 let page_id1 = writer.alloc_page_id();
3162 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3163 inserted.push((tsn, page_id1));
3164
3165 let page_id2 = writer.alloc_page_id();
3167 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3168 inserted.push((tsn, page_id2));
3169
3170 tsn = Tsn(tsn.0 + 1);
3172
3173 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3175 match &root_page.node {
3176 Node::FreeListInternal(_) => {
3177 has_split_leaf = true;
3178 }
3179 _ => {}
3180 }
3181 }
3182
3183 let mut copy_inserted = inserted.clone();
3185
3186 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3188 let root_node = match &root_page.node {
3189 Node::FreeListInternal(node) => node,
3190 _ => panic!("Expected FreeListInternal node"),
3191 };
3192
3193 let mut active_page_ids = vec![
3195 HEADER_PAGE_ID_0,
3196 HEADER_PAGE_ID_1,
3197 writer.free_lists_tree_root_id,
3198 writer.events_tree_root_id,
3199 writer.tags_tree_root_id,
3200 ];
3201
3202 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3204
3205 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3207 active_page_ids.push(child_id);
3208
3209 let child_page = writer.dirty.get(&child_id).unwrap();
3210 assert_eq!(child_id, child_page.page_id);
3211
3212 let child_node = match &child_page.node {
3213 Node::FreeListLeaf(node) => node,
3214 _ => panic!("Expected FreeListLeaf node"),
3215 };
3216
3217 if i > 0 {
3219 assert_eq!(root_node.keys[i - 1], child_node.keys[0]);
3220 }
3221
3222 for (k, &key) in child_node.keys.iter().enumerate() {
3224 for &value in &child_node.values[k].page_ids {
3225 let (inserted_tsn, inserted_page_id) = copy_inserted.remove(0);
3226 assert_eq!(inserted_tsn, key);
3227 assert_eq!(inserted_page_id, value);
3228 freed_page_ids.push(value);
3229 }
3230 }
3231 }
3232
3233 assert_eq!(7, active_page_ids.len());
3235
3236 let mut all_page_ids = active_page_ids.clone();
3238 all_page_ids.extend(freed_page_ids.clone());
3239 all_page_ids.sort();
3240 all_page_ids.dedup();
3241
3242 assert_eq!(
3243 all_page_ids.len(),
3244 active_page_ids.len() + freed_page_ids.len()
3245 - active_page_ids
3246 .iter()
3247 .filter(|id| freed_page_ids.contains(id))
3248 .count()
3249 );
3250
3251 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3253 assert_eq!(expected_page_ids, all_page_ids);
3254 }
3255
3256 #[test]
3257 #[serial]
3258 fn test_insert_freed_page_ids_until_replace_internal_node_child_id() {
3259 let (_temp_dir, db) = construct_mvcc(128);
3260
3261 {
3263 db.reader_tsns.insert(0, Tsn(0));
3264 }
3265
3266 let mut has_split_leaf = false;
3267 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3268
3269 while !has_split_leaf {
3271 let mut writer = db.writer().unwrap();
3273 let page_id1 = writer.alloc_page_id();
3275 writer
3276 .insert_freed_page_id(&db, writer.tsn, page_id1)
3277 .unwrap();
3278 inserted.push((writer.tsn, page_id1));
3279
3280 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3282 match &root_page.node {
3283 Node::FreeListInternal(_) => {
3284 has_split_leaf = true;
3285 }
3286 _ => {}
3287 }
3288
3289 if !has_split_leaf {
3290 let page_id2 = writer.alloc_page_id();
3292 writer
3293 .insert_freed_page_id(&db, writer.tsn, page_id2)
3294 .unwrap();
3295 inserted.push((writer.tsn, page_id2));
3296
3297 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3299 match &root_page.node {
3300 Node::FreeListInternal(_) => {
3301 has_split_leaf = true;
3302 }
3303 _ => {}
3304 }
3305 }
3306
3307 db.commit(&mut writer).unwrap();
3308 }
3309
3310 let mut writer = db.writer().unwrap();
3311 let page_id3 = writer.alloc_page_id();
3312 writer
3313 .insert_freed_page_id(&db, writer.tsn, page_id3)
3314 .unwrap();
3315 db.commit(&mut writer).unwrap();
3316 inserted.push((writer.tsn, page_id3));
3317
3318 writer = db.writer().unwrap();
3319 let page_id4 = writer.alloc_page_id();
3320 writer
3321 .insert_freed_page_id(&db, writer.tsn, page_id4)
3322 .unwrap();
3323 db.commit(&mut writer).unwrap();
3324 inserted.push((writer.tsn, page_id4));
3325
3326 writer = db.writer().unwrap();
3328 let root_page = db.read_page(writer.free_lists_tree_root_id).unwrap();
3329 let root_node = match &root_page.node {
3330 Node::FreeListInternal(node) => node,
3331 _ => panic!("Expected FreeListInternal node"),
3332 };
3333
3334 let mut active_page_ids = vec![
3336 HEADER_PAGE_ID_0,
3337 HEADER_PAGE_ID_1,
3338 writer.free_lists_tree_root_id,
3339 writer.events_tree_root_id,
3340 writer.tags_tree_root_id,
3341 ];
3342
3343 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3345
3346 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3348 active_page_ids.push(child_id);
3349
3350 let child_page = db.read_page(child_id).unwrap();
3352 assert_eq!(child_id, child_page.page_id);
3353
3354 let child_node = match &child_page.node {
3355 Node::FreeListLeaf(node) => node,
3356 _ => panic!("Expected FreeListLeaf node"),
3357 };
3358
3359 if i > 0 {
3361 assert_eq!(root_node.keys[i - 1], child_node.keys[0]);
3362 }
3363
3364 for child_value in child_node.values.clone() {
3366 for page_id in child_value.page_ids {
3367 freed_page_ids.push(page_id);
3368 }
3369 }
3370 }
3371
3372 assert_eq!(8, active_page_ids.len());
3374
3375 let mut all_page_ids = active_page_ids.clone();
3377 all_page_ids.extend(freed_page_ids.clone());
3378 all_page_ids.sort();
3379 all_page_ids.dedup();
3380
3381 assert_eq!(
3382 all_page_ids.len(),
3383 active_page_ids.len() + freed_page_ids.len()
3384 - active_page_ids
3385 .iter()
3386 .filter(|id| freed_page_ids.contains(id))
3387 .count()
3388 );
3389
3390 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3392 assert_eq!(expected_page_ids, all_page_ids);
3393 }
3394
3395 #[test]
3396 #[serial]
3397 fn test_remove_freed_page_ids_from_split_leaf() {
3398 let (_temp_dir, mut db) = construct_mvcc(128);
3399
3400 if VERBOSE {
3402 println!("Inserting page IDs......");
3403 }
3404 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3405 let previous_root_id;
3406 let previous_writer_tsn;
3407
3408 {
3409 let mut writer = db.writer().unwrap();
3411
3412 previous_root_id = writer.free_lists_tree_root_id;
3414
3415 let mut has_split_leaf = false;
3417 let mut tsn = writer.tsn;
3418
3419 while !has_split_leaf {
3420 tsn = Tsn(tsn.0 + 1);
3422
3423 let page_id1 = writer.alloc_page_id();
3425 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3426 inserted.push((tsn, page_id1));
3427
3428 let page_id2 = writer.alloc_page_id();
3430 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3431 inserted.push((tsn, page_id2));
3432
3433 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3435 match &root_page.node {
3436 Node::FreeListInternal(_) => {
3437 has_split_leaf = true;
3438 }
3439 _ => {}
3440 }
3441 }
3442
3443 writer.tsn = Tsn(tsn.0 + 1);
3445 previous_writer_tsn = writer.tsn;
3446
3447 db.commit(&mut writer).unwrap();
3449 }
3450
3451 if VERBOSE {
3453 println!();
3454 println!("Removing all inserted page IDs......");
3455 }
3456
3457 {
3458 let header_page = db.get_latest_header_page().unwrap();
3460 let header_node = header_page.as_header_node().unwrap();
3461 let header_page_id = header_page.page_id;
3462
3463 let mut writer = Writer::new(
3465 header_page_id,
3466 Tsn(header_node.tsn.0 + 1),
3467 header_node.next_page_id,
3468 header_node.free_lists_tree_root_id,
3469 header_node.events_tree_root_id,
3470 header_node.tags_tree_root_id,
3471 header_node.tracking_root_page_id,
3472 header_node.next_position,
3473 VERBOSE,
3474 );
3475
3476 let old_root_id = writer.free_lists_tree_root_id;
3478
3479 for (tsn, page_id) in inserted.iter() {
3481 writer.remove_free_page_id(&db, *tsn, *page_id).unwrap();
3482 if VERBOSE {
3483 println!("Dirty pages: {:?}", writer.dirty.keys());
3484 }
3485 }
3486
3487 assert_ne!(old_root_id, writer.free_lists_tree_root_id);
3489
3490 assert_eq!(1, writer.dirty.len());
3491 assert!(writer.dirty.contains_key(&writer.free_lists_tree_root_id));
3492
3493 let new_root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3494 assert_eq!(writer.free_lists_tree_root_id, new_root_page.page_id);
3495
3496 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3498 assert!(freed_page_ids.contains(&old_root_id));
3499
3500 assert_eq!(5, writer.freed_page_ids.len());
3503
3504 match &new_root_page.node {
3506 Node::FreeListLeaf(node) => {
3507 let expected_keys = vec![previous_writer_tsn];
3508 assert_eq!(expected_keys, node.keys);
3509
3510 let expected_values = vec![FreeListLeafValue {
3511 page_ids: vec![previous_root_id],
3512 root_id: PageID(0),
3513 }];
3514 assert_eq!(expected_values, node.values);
3515 }
3516 _ => panic!("Expected FreeListLeaf node"),
3517 }
3518
3519 let active_page_ids = vec![
3521 HEADER_PAGE_ID_0,
3522 HEADER_PAGE_ID_1,
3523 writer.free_lists_tree_root_id,
3524 writer.events_tree_root_id,
3525 writer.tags_tree_root_id,
3526 ];
3527
3528 let mut all_freed_page_ids = freed_page_ids.clone();
3530
3531 match &new_root_page.node {
3533 Node::FreeListLeaf(node) => {
3534 for (_, value) in node.keys.iter().zip(node.values.iter()) {
3535 all_freed_page_ids.extend(value.page_ids.clone());
3536 }
3537 }
3538 _ => panic!("Expected FreeListLeaf node"),
3539 }
3540
3541 let inserted_page_ids: Vec<PageID> = inserted.iter().map(|(_, id)| *id).collect();
3543
3544 let mut all_page_ids = active_page_ids.clone();
3546 all_page_ids.extend(all_freed_page_ids.clone());
3547 all_page_ids.extend(inserted_page_ids.clone());
3548 all_page_ids.sort();
3549 all_page_ids.dedup();
3550
3551 let expected_page_ids: Vec<PageID> =
3553 (0..writer.next_page_id.0).map(PageID).collect();
3554 assert_eq!(expected_page_ids, all_page_ids);
3555 }
3556 }
3557
3558 #[test]
3559 #[serial]
3560 fn test_insert_freed_page_ids_until_split_internal() {
3561 let (_temp_dir, mut db) = construct_mvcc(64);
3562
3563 let header_page = db.get_latest_header_page().unwrap();
3565 let header_node = header_page.as_header_node().unwrap();
3566 let header_page_id = header_page.page_id;
3567
3568 let mut writer = Writer::new(
3570 header_page_id,
3571 Tsn(header_node.tsn.0 + 1),
3572 header_node.next_page_id,
3573 header_node.free_lists_tree_root_id,
3574 header_node.events_tree_root_id,
3575 header_node.tags_tree_root_id,
3576 header_node.tracking_root_page_id,
3577 header_node.next_position,
3578 VERBOSE,
3579 );
3580
3581 let mut tsn = Tsn(100);
3583 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3584 let mut has_split_internal = false;
3585
3586 while !has_split_internal {
3588 let page_id1 = writer.alloc_page_id();
3590 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3591 inserted.push((tsn, page_id1));
3592
3593 let page_id2 = writer.alloc_page_id();
3595 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3596 inserted.push((tsn, page_id2));
3597
3598 tsn = Tsn(tsn.0 + 1);
3600
3601 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3603 match &root_page.node {
3604 Node::FreeListInternal(root_node) => {
3605 if !root_node.child_ids.is_empty() {
3607 let child_id = root_node.child_ids[0];
3608 if let Some(child_page) = writer.dirty.get(&child_id) {
3609 match &child_page.node {
3610 Node::FreeListInternal(_) => {
3611 has_split_internal = true;
3612 }
3613 _ => {}
3614 }
3615 }
3616 }
3617 }
3618 _ => {}
3619 }
3620 if inserted.len() > 100 {
3621 panic!("Too many inserted page IDs");
3622 }
3623 }
3624
3625 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3627 let root_node = match &root_page.node {
3628 Node::FreeListInternal(node) => node,
3629 _ => panic!("Expected FreeListInternal node"),
3630 };
3631
3632 let mut active_page_ids = vec![
3634 HEADER_PAGE_ID_0,
3635 HEADER_PAGE_ID_1,
3636 writer.free_lists_tree_root_id,
3637 writer.events_tree_root_id,
3638 writer.tags_tree_root_id,
3639 ];
3640
3641 let mut freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3643
3644 let mut previous_child: Option<&FreeListInternalNode> = None;
3646
3647 for (i, &child_id) in root_node.child_ids.iter().enumerate() {
3649 active_page_ids.push(child_id);
3650
3651 let child_page = writer.dirty.get(&child_id).unwrap();
3652 assert_eq!(child_id, child_page.page_id);
3653
3654 let child_node = match &child_page.node {
3655 Node::FreeListInternal(node) => node,
3656 _ => panic!("Expected FreeListInternal node"),
3657 };
3658
3659 if i > 0 {
3661 assert!(root_node.keys[i - 1] < child_node.keys[0]);
3662
3663 if let Some(prev_child) = previous_child {
3665 assert!(root_node.keys[i - 1] > *prev_child.keys.last().unwrap());
3666 }
3667 }
3668
3669 previous_child = Some(child_node);
3670
3671 for (j, &grand_child_id) in child_node.child_ids.iter().enumerate() {
3673 active_page_ids.push(grand_child_id);
3674
3675 let grand_child_page = writer.dirty.get(&grand_child_id).unwrap();
3676 assert_eq!(grand_child_id, grand_child_page.page_id);
3677
3678 let grand_child_node = match &grand_child_page.node {
3679 Node::FreeListLeaf(node) => node,
3680 _ => panic!("Expected FreeListLeaf node"),
3681 };
3682
3683 if j > 0 {
3685 assert_eq!(child_node.keys[j - 1], grand_child_node.keys[0]);
3686 }
3687
3688 for (k, &key) in grand_child_node.keys.iter().enumerate() {
3690 for &value in &grand_child_node.values[k].page_ids {
3691 let pos = inserted.iter().position(|&(t, p)| t == key && p == value);
3693 if let Some(idx) = pos {
3694 inserted.remove(idx);
3695 }
3696 freed_page_ids.push(value);
3697 }
3698 }
3699 }
3700 }
3701
3702 assert!(inserted.is_empty());
3704
3705 assert_eq!(11, active_page_ids.len());
3707
3708 let mut all_page_ids = active_page_ids.clone();
3710 all_page_ids.extend(freed_page_ids.clone());
3711 all_page_ids.sort();
3712 all_page_ids.dedup();
3713
3714 let expected_page_ids: Vec<PageID> = (0..writer.next_page_id.0).map(PageID).collect();
3716 assert_eq!(expected_page_ids, all_page_ids);
3717 }
3718
3719 #[test]
3720 #[serial]
3721 fn test_remove_freed_page_ids_from_split_internal() {
3722 let (_temp_dir, mut db) = construct_mvcc(64);
3723
3724 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3726 let previous_root_id;
3727 let previous_writer_tsn;
3728
3729 {
3730 let mut writer = db.writer().unwrap();
3732
3733 previous_root_id = writer.free_lists_tree_root_id;
3735
3736 let mut has_split_internal = false;
3738 let mut tsn = writer.tsn;
3739
3740 while !has_split_internal {
3741 tsn = Tsn(tsn.0 + 1);
3743 writer.tsn = tsn;
3744
3745 let page_id1 = writer.alloc_page_id();
3747 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3748 inserted.push((tsn, page_id1));
3749
3750 let page_id2 = writer.alloc_page_id();
3752 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3753 inserted.push((tsn, page_id2));
3754
3755 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3757 match &root_page.node {
3758 Node::FreeListInternal(root_node) => {
3759 if !root_node.child_ids.is_empty() {
3761 let child_id = root_node.child_ids[0];
3762 if let Some(child_page) = writer.dirty.get(&child_id) {
3763 match &child_page.node {
3764 Node::FreeListInternal(_) => {
3765 has_split_internal = true;
3766 }
3767 _ => {}
3768 }
3769 }
3770 }
3771 }
3772 _ => {}
3773 }
3774 }
3775
3776 previous_writer_tsn = writer.tsn;
3778
3779 db.commit(&mut writer).unwrap();
3781 }
3782
3783 {
3785 let header_page = db.get_latest_header_page().unwrap();
3787 let header_node = header_page.as_header_node().unwrap();
3788 let header_page_id = header_page.page_id;
3789
3790 let mut writer = Writer::new(
3792 header_page_id,
3793 Tsn(header_node.tsn.0 + 1),
3794 header_node.next_page_id,
3795 header_node.free_lists_tree_root_id,
3796 header_node.events_tree_root_id,
3797 header_node.tags_tree_root_id,
3798 header_node.tracking_root_page_id,
3799 header_node.next_position,
3800 VERBOSE,
3801 );
3802
3803 let old_root_id = writer.free_lists_tree_root_id;
3805
3806 for (tsn, page_id) in inserted.iter() {
3808 writer.remove_free_page_id(&db, *tsn, *page_id).unwrap();
3809 }
3810
3811 assert_ne!(old_root_id, writer.free_lists_tree_root_id);
3813 assert_eq!(1, writer.dirty.len());
3814 assert!(writer.dirty.contains_key(&writer.free_lists_tree_root_id));
3815
3816 let new_root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3817 assert_eq!(writer.free_lists_tree_root_id, new_root_page.page_id);
3818
3819 let freed_page_ids: Vec<PageID> = writer.freed_page_ids.iter().cloned().collect();
3821 assert!(freed_page_ids.contains(&old_root_id));
3822
3823 assert_eq!(13, writer.freed_page_ids.len());
3826
3827 match &new_root_page.node {
3829 Node::FreeListLeaf(node) => {
3830 let expected_keys = vec![previous_writer_tsn];
3831 assert_eq!(expected_keys, node.keys);
3832
3833 let expected_values = vec![FreeListLeafValue {
3834 page_ids: vec![previous_root_id],
3835 root_id: PageID(0),
3836 }];
3837 assert_eq!(expected_values, node.values);
3838 }
3839 _ => panic!("Expected FreeListLeaf node"),
3840 }
3841
3842 let active_page_ids = vec![
3844 HEADER_PAGE_ID_0,
3845 HEADER_PAGE_ID_1,
3846 writer.free_lists_tree_root_id,
3847 writer.events_tree_root_id,
3848 writer.tags_tree_root_id,
3849 ];
3850
3851 let mut all_freed_page_ids = freed_page_ids.clone();
3853
3854 match &new_root_page.node {
3856 Node::FreeListLeaf(node) => {
3857 for (_, value) in node.keys.iter().zip(node.values.iter()) {
3858 all_freed_page_ids.extend(value.page_ids.clone());
3859 }
3860 }
3861 _ => panic!("Expected FreeListLeaf node"),
3862 }
3863
3864 let inserted_page_ids: Vec<PageID> = inserted.iter().map(|(_, id)| *id).collect();
3866
3867 let mut all_page_ids = active_page_ids.clone();
3869 all_page_ids.extend(all_freed_page_ids.clone());
3870 all_page_ids.extend(inserted_page_ids.clone());
3871 all_page_ids.sort();
3872 all_page_ids.dedup();
3873
3874 let expected_page_ids: Vec<PageID> =
3876 (0..writer.next_page_id.0).map(PageID).collect();
3877 assert_eq!(expected_page_ids, all_page_ids);
3878 }
3879 }
3880
3881 #[test]
3882 #[serial]
3883 fn test_remove_freed_page_ids_until_replace_old_id_with_orphaned_child_id() {
3884 let (_temp_dir, mut db) = construct_mvcc(128);
3895
3896 let mut inserted: Vec<(Tsn, PageID)> = Vec::new();
3898
3899 {
3900 let mut writer = db.writer().unwrap();
3902
3903 let mut has_split_internal = false;
3905 let mut tsn = writer.tsn;
3906
3907 while !has_split_internal {
3908 tsn = Tsn(tsn.0 + 1);
3910 writer.tsn = tsn;
3911
3912 let page_id1 = writer.alloc_page_id();
3914 writer.insert_freed_page_id(&mut db, tsn, page_id1).unwrap();
3915 inserted.push((tsn, page_id1));
3916
3917 let page_id2 = writer.alloc_page_id();
3919 writer.insert_freed_page_id(&mut db, tsn, page_id2).unwrap();
3920 inserted.push((tsn, page_id2));
3921
3922 let root_page = writer.dirty.get(&writer.free_lists_tree_root_id).unwrap();
3924 match &root_page.node {
3925 Node::FreeListInternal(root_node) => {
3926 if !root_node.child_ids.is_empty() {
3928 let child_id = root_node.child_ids[0];
3929 if let Some(child_page) = writer.dirty.get(&child_id) {
3930 match &child_page.node {
3931 Node::FreeListInternal(_) => {
3932 has_split_internal = true;
3933 }
3934 _ => {}
3935 }
3936 }
3937 }
3938 }
3939 _ => {}
3940 }
3941 }
3942
3943 db.commit(&mut writer).unwrap();
3945 }
3946
3947 db.reader_tsns.remove(&0);
3949 let writer = db.writer().unwrap();
3950 let reusable_page_ids = writer.reusable_page_ids.clone();
3951
3952 db.reader_tsns.insert(0, Tsn(0));
3954
3955 for (page_id, tsn) in reusable_page_ids {
3957 let mut writer = db.writer().unwrap();
3958 writer.remove_free_page_id(&db, tsn, page_id).unwrap();
3959 db.commit(&mut writer).unwrap();
3960 }
3961 }
3962
3963 #[test]
3964 #[serial]
3965 fn test_insert_freed_page_ids_overflow_single_key_moves_to_tsn_subtree() {
3966 let page_size = 64;
3968 let (_temp_dir, mut mvcc) = construct_mvcc(page_size);
3969
3970 let mut writer = mvcc.writer().unwrap();
3972 let tsn = writer.tsn;
3973
3974 let mut inserted_count: usize = 0;
3977 loop {
3978 let pid = writer.alloc_page_id();
3979 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
3980 inserted_count += 1;
3981 let dirty_page_id = {
3982 let mut keys = writer.dirty.keys();
3983 assert_eq!(keys.len(), 1);
3984 *keys.next().unwrap()
3985 };
3986 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
3987 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
3988 if !leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
3989 break;
3990 }
3991 } else {
3992 panic!("Expected leaf node")
3993 }
3994 }
3995
3996 let pid5 = writer.alloc_page_id();
3998 writer.insert_freed_page_id(&mut mvcc, tsn, pid5).unwrap();
3999 inserted_count += 1;
4000
4001 let pid6 = writer.alloc_page_id();
4003 writer.insert_freed_page_id(&mut mvcc, tsn, pid6).unwrap();
4004 inserted_count += 1;
4005
4006 let pid7 = writer.alloc_page_id();
4007 writer.insert_freed_page_id(&mut mvcc, tsn, pid7).unwrap();
4008 inserted_count += 1;
4009
4010 let dirty_ids: Vec<PageID> = { writer.dirty.keys().cloned().collect() };
4012 let mut tsn_root_id = PageID(0);
4013 for dirty_page_id in dirty_ids {
4014 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
4015 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
4016 assert_eq!(1, leaf_node.keys.len());
4017 assert_eq!(tsn, leaf_node.keys[0]);
4018 let val = &leaf_node.values[0];
4019 assert_eq!(0, val.page_ids.len());
4020 assert_ne!(PageID(0), val.root_id);
4021 tsn_root_id = val.root_id;
4022 break;
4023 }
4024 }
4025 assert_ne!(PageID(0), tsn_root_id);
4026 let tsn_root_page = writer.get_page_ref(&mvcc, tsn_root_id).unwrap();
4029 match &tsn_root_page.node {
4030 Node::FreeListTsnInternal(internal) => {
4031 assert_eq!(2, internal.child_ids.len());
4032 assert_eq!(1, internal.keys.len());
4033 }
4034 other => panic!(
4035 "Expected TSN-subtree internal node, got {:?}",
4036 other.type_name()
4037 ),
4038 }
4039
4040 let mut extra_inserts = 0usize;
4042 let mut guard = 0usize;
4043 loop {
4044 guard += 1;
4045 assert!(
4046 guard < 200,
4047 "guard hit while waiting for TSN-subtree internal split"
4048 );
4049 let pid = writer.alloc_page_id();
4050 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
4051 extra_inserts += 1;
4052
4053 let dirty_ids: Vec<PageID> = writer.dirty.keys().cloned().collect();
4055 tsn_root_id = PageID(0);
4056 for dirty_page_id in dirty_ids {
4057 let dirty_page = writer.get_mut_dirty(dirty_page_id).unwrap();
4058 if let Node::FreeListLeaf(leaf_node) = &dirty_page.node {
4059 assert_eq!(1, leaf_node.keys.len());
4060 assert_eq!(tsn, leaf_node.keys[0]);
4061 let val = &leaf_node.values[0];
4062 assert_eq!(0, val.page_ids.len());
4063 assert_ne!(PageID(0), val.root_id);
4064 tsn_root_id = val.root_id;
4065 break;
4066 }
4067 }
4068 assert_ne!(PageID(0), tsn_root_id);
4069
4070 let root_node_owned = {
4071 writer
4072 .get_page_ref(&mvcc, tsn_root_id)
4073 .unwrap()
4074 .node
4075 .clone()
4076 };
4077 match root_node_owned {
4078 Node::FreeListTsnInternal(internal_root) => {
4079 let first_child_id = internal_root.child_ids[0];
4081 let first_child_node = {
4082 writer
4083 .get_page_ref(&mvcc, first_child_id)
4084 .unwrap()
4085 .node
4086 .clone()
4087 };
4088 if matches!(first_child_node, Node::FreeListTsnInternal(_)) {
4089 assert_eq!(1, internal_root.keys.len());
4092 assert_eq!(2, internal_root.child_ids.len());
4093 break;
4094 }
4095 }
4096 other => panic!(
4097 "Expected TSN-subtree internal node, got {:?}",
4098 other.type_name()
4099 ),
4100 }
4101 }
4102
4103 let pid = writer.alloc_page_id();
4105 writer.insert_freed_page_id(&mut mvcc, tsn, pid).unwrap();
4106 extra_inserts += 1;
4107
4108 writer.find_reusable_page_ids(&mvcc).unwrap();
4110 assert_eq!(
4111 inserted_count + extra_inserts,
4112 writer.reusable_page_ids.len()
4113 );
4114 for &(_pid, _tsn) in writer.reusable_page_ids.iter() {
4115 assert_eq!(tsn, _tsn);
4116 }
4117 }
4118
4119 #[test]
4120 #[serial]
4121 fn test_remove_freed_page_id_from_tsn_subtree_leaf_pid1() {
4122 let (_temp_dir, db) = construct_mvcc(128);
4123 let mut writer = db.writer().unwrap();
4124
4125 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4126
4127 writer.find_reusable_page_ids(&db).unwrap();
4128 assert_eq!(2, writer.reusable_page_ids.len());
4129 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
4130 assert_eq!((pid2, tsn1), writer.reusable_page_ids[1]);
4131
4132 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4133 writer.find_reusable_page_ids(&db).unwrap();
4134 assert_eq!(1, writer.reusable_page_ids.len());
4135 assert_eq!((pid2, tsn1), writer.reusable_page_ids[0]);
4136 }
4137
4138 #[test]
4139 #[serial]
4140 fn test_remove_freed_page_id_from_tsn_subtree_leaf_pid2() {
4141 let (_temp_dir, db) = construct_mvcc(128);
4142 let mut writer = db.writer().unwrap();
4143
4144 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4145
4146 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4147 writer.find_reusable_page_ids(&db).unwrap();
4148 assert_eq!(1, writer.reusable_page_ids.len());
4149 assert_eq!((pid1, tsn1), writer.reusable_page_ids[0]);
4150 }
4151
4152 #[test]
4153 #[serial]
4154 fn test_tsn_subtree_random_order_insert_and_ordering() {
4155 let page_size = 256;
4157 let (_temp_dir, mut db) = construct_mvcc(page_size);
4158 let mut writer = db.writer().unwrap();
4159 let tsn = writer.tsn;
4160
4161 let n = 100usize;
4163 let mut pids: Vec<PageID> = Vec::with_capacity(n);
4164 for _ in 0..n {
4165 pids.push(writer.alloc_page_id());
4166 }
4167 for i in 0..pids.len() {
4169 let j = (i * 37 + 13) % pids.len();
4170 pids.swap(i, j);
4171 }
4172 for pid in &pids {
4174 writer.insert_freed_page_id(&mut db, tsn, *pid).unwrap();
4175 }
4176 let dup = pids[0];
4178 writer.insert_freed_page_id(&mut db, tsn, dup).unwrap();
4179
4180 writer.find_reusable_page_ids(&db).unwrap();
4182 assert_eq!(n, writer.reusable_page_ids.len());
4183 for &(_pid, _tsn) in writer.reusable_page_ids.iter() {
4184 assert_eq!(tsn, _tsn);
4185 }
4186
4187 let mut tsn_root_id = PageID(0);
4190 for page_id in writer.dirty.keys().cloned().collect::<Vec<_>>() {
4191 let page = writer.get_mut_dirty(page_id).unwrap();
4192 if let Node::FreeListLeaf(leaf_node) = &page.node {
4193 if !leaf_node.keys.is_empty() && leaf_node.keys[0] == tsn {
4194 tsn_root_id = leaf_node.values[0].root_id;
4195 break;
4196 }
4197 }
4198 }
4199 assert_ne!(PageID(0), tsn_root_id);
4200 let root_node_owned = { writer.get_page_ref(&db, tsn_root_id).unwrap().node.clone() };
4201 match root_node_owned {
4202 Node::FreeListTsnInternal(internal_root) => {
4203 let first_child_id = internal_root.child_ids[0];
4205 let first_child_node = {
4206 writer
4207 .get_page_ref(&db, first_child_id)
4208 .unwrap()
4209 .node
4210 .clone()
4211 };
4212 if let Node::FreeListTsnInternal(_in2) = first_child_node {
4213 }
4215 }
4216 Node::FreeListTsnLeaf(_) => {
4217 }
4219 other => panic!("Unexpected node type: {}", other.type_name()),
4220 }
4221 }
4222
4223 #[test]
4224 #[serial]
4225 fn test_upgrade_inline_to_tsn_subtree_with_random_inserts_and_duplicates() {
4226 let page_size = 96;
4228 let (_temp_dir, mut mvcc) = construct_mvcc(page_size);
4229 let mut writer = mvcc.writer().unwrap();
4230 let tsn = writer.tsn;
4231
4232 let mut inline_ids = Vec::new();
4234 loop {
4235 let pid = writer.alloc_page_id();
4236 let res = writer.insert_freed_page_id(&mut mvcc, tsn, pid);
4237 if res.is_err() {
4238 panic!("unexpected error inserting into inline");
4239 }
4240 inline_ids.push(pid);
4241 let dirty_id = writer.dirty.keys().cloned().next().unwrap();
4243 if let Node::FreeListLeaf(leaf_node) = &writer.get_mut_dirty(dirty_id).unwrap().node
4244 {
4245 if !leaf_node.would_fit_new_page_id(mvcc.max_node_size) {
4246 break;
4247 }
4248 }
4249 }
4250
4251 let mut extra_ids = Vec::new();
4253 for _ in 0..30 {
4254 extra_ids.push(writer.alloc_page_id());
4255 }
4256 for i in 0..extra_ids.len() {
4258 let j = (i * 29 + 7) % extra_ids.len();
4259 extra_ids.swap(i, j);
4260 }
4261 if !inline_ids.is_empty() {
4263 extra_ids.push(inline_ids[0]);
4264 }
4265 if inline_ids.len() > 1 {
4266 extra_ids.push(inline_ids[1]);
4267 }
4268
4269 for pid in &extra_ids {
4270 writer.insert_freed_page_id(&mut mvcc, tsn, *pid).unwrap();
4271 }
4272
4273 writer.find_reusable_page_ids(&mvcc).unwrap();
4275 let mut expected: Vec<PageID> = inline_ids.clone();
4276 for p in extra_ids {
4277 if !expected.contains(&p) {
4278 expected.push(p);
4279 }
4280 }
4281 expected.sort_by_key(|p| p.0);
4282 expected.dedup();
4283 let mut actual: Vec<PageID> =
4284 writer.reusable_page_ids.iter().map(|(p, _)| *p).collect();
4285 actual.sort_by_key(|p| p.0);
4286 assert_eq!(expected, actual);
4287 }
4288
4289 #[test]
4290 #[serial]
4291 fn test_remove_freed_page_id_from_tsn_subtree_leaf_all() {
4292 let (_temp_dir, db) = construct_mvcc(128);
4293 let mut writer = db.writer().unwrap();
4294
4295 let (pid1, pid2, tsn1) = build_free_list_tree_leaf_tsn_subtree_leaf(&mut writer);
4296
4297 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4298 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4299 writer.find_reusable_page_ids(&db).unwrap();
4300 assert_eq!(0, writer.reusable_page_ids.len());
4301
4302 assert_eq!(2, writer.freed_page_ids.len());
4303 }
4304
4305 #[test]
4306 #[serial]
4307 fn test_remove_freed_page_id_from_tsn_subtree_internal_leaf_pid1() {
4308 let (_temp_dir, db) = construct_mvcc(128);
4309 let mut writer = db.writer().unwrap();
4310
4311 let (pid1, pid2, pid3, pid4, tsn1) =
4312 build_free_list_tree_leaf_tsn_subtree_internal_leaf(&mut writer);
4313
4314 writer.remove_free_page_id(&db, tsn1, pid1).unwrap();
4315 writer.find_reusable_page_ids(&db).unwrap();
4316 assert_eq!(3, writer.reusable_page_ids.len());
4317 assert_eq!((pid2, tsn1), writer.reusable_page_ids[0]);
4318 assert_eq!((pid3, tsn1), writer.reusable_page_ids[1]);
4319 assert_eq!((pid4, tsn1), writer.reusable_page_ids[2]);
4320
4321 assert_eq!(1, writer.freed_page_ids.len());
4322
4323 writer.remove_free_page_id(&db, tsn1, pid2).unwrap();
4324 writer.find_reusable_page_ids(&db).unwrap();
4325 assert_eq!(2, writer.reusable_page_ids.len());
4326 assert_eq!((pid3, tsn1), writer.reusable_page_ids[0]);
4327 assert_eq!((pid4, tsn1), writer.reusable_page_ids[1]);
4328
4329 assert_eq!(3, writer.freed_page_ids.len());
4330
4331 writer.remove_free_page_id(&db, tsn1, pid3).unwrap();
4332 writer.find_reusable_page_ids(&db).unwrap();
4333 assert_eq!(1, writer.reusable_page_ids.len());
4334 assert_eq!((pid4, tsn1), writer.reusable_page_ids[0]);
4335
4336 assert_eq!(3, writer.freed_page_ids.len());
4337
4338 writer.remove_free_page_id(&db, tsn1, pid4).unwrap();
4339 writer.find_reusable_page_ids(&db).unwrap();
4340 assert_eq!(0, writer.reusable_page_ids.len());
4341
4342 assert_eq!(4, writer.freed_page_ids.len());
4343 }
4344
4345 #[test]
4346 #[serial]
4347 fn test_remove_freed_page_id_from_tsn_subtree_internal_internal_leaf_pid1() {
4348 let (_temp_dir, db) = construct_mvcc(128);
4349 let mut writer = db.writer().unwrap();
4350
4351 let (pid1, pid2, pid3, pid4, pid5, pid6, pid7, pid8, tsn) =
4352 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut writer);
4353
4354 writer.remove_free_page_id(&db, tsn, pid1).unwrap();
4355 writer.find_reusable_page_ids(&db).unwrap();
4356 assert_eq!(7, writer.reusable_page_ids.len());
4357 assert_eq!((pid2, tsn), writer.reusable_page_ids[0]);
4358 assert_eq!((pid3, tsn), writer.reusable_page_ids[1]);
4359 assert_eq!((pid4, tsn), writer.reusable_page_ids[2]);
4360 assert_eq!((pid5, tsn), writer.reusable_page_ids[3]);
4361 assert_eq!((pid6, tsn), writer.reusable_page_ids[4]);
4362 assert_eq!((pid7, tsn), writer.reusable_page_ids[5]);
4363 assert_eq!((pid8, tsn), writer.reusable_page_ids[6]);
4364
4365 assert_eq!(1, writer.freed_page_ids.len());
4366 }
4367
4368 #[test]
4369 #[serial]
4370 fn test_remove_freed_page_id_cow_does_not_leak_ids_in_tsn_subtree() {
4371 let (_temp_dir, db) = construct_mvcc(128);
4374
4375 let w1_tsn = {
4377 let mut w1 = db.writer().unwrap();
4378 let (_pid1, _pid2, _pid3, _pid4, _pid5, _pid6, _pid7, _pid8, tsn) =
4379 build_free_list_tree_leaf_tsn_subtree_internal_internal_leaf(&mut w1);
4380 db.commit(&mut w1).unwrap();
4382 tsn
4384 };
4385
4386 let mut w2 = db.writer().unwrap();
4388 w2.find_reusable_page_ids(&db).unwrap();
4390 assert!(w2.reusable_page_ids.len() >= 1);
4391 let (remove_pid, remove_tsn) = w2.reusable_page_ids[0];
4392 assert_eq!(remove_tsn, w1_tsn); w2.remove_free_page_id(&db, remove_tsn, remove_pid).unwrap();
4394
4395 let mut active: Vec<PageID> = Vec::new();
4398 let mut freed_tree: Vec<PageID> = Vec::new();
4399 active.push(HEADER_PAGE_ID_0);
4401 active.push(HEADER_PAGE_ID_1);
4402 active.push(w2.free_lists_tree_root_id);
4403 active.push(w2.events_tree_root_id);
4404 active.push(w2.tags_tree_root_id);
4405
4406 let mut stack: Vec<PageID> = vec![w2.free_lists_tree_root_id];
4408 while let Some(pid) = stack.pop() {
4409 if active.contains(&pid) == false {
4410 active.push(pid);
4411 }
4412 let node_owned = { w2.get_page_ref(&db, pid).unwrap().node.clone() };
4413 match node_owned {
4414 Node::FreeListInternal(internal) => {
4415 for child in internal.child_ids {
4416 stack.push(child);
4417 }
4418 }
4419 Node::FreeListLeaf(leaf) => {
4420 for val in &leaf.values {
4422 for &p in &val.page_ids {
4423 freed_tree.push(p);
4424 }
4425 }
4426 for val in leaf.values {
4428 if val.root_id != PageID(0) {
4429 let mut tsn_stack: Vec<PageID> = vec![val.root_id];
4430 while let Some(tid) = tsn_stack.pop() {
4431 if active.contains(&tid) == false {
4432 active.push(tid);
4433 }
4434 let tnode = { w2.get_page_ref(&db, tid).unwrap().node.clone() };
4435 match tnode {
4436 Node::FreeListTsnInternal(tint) => {
4437 for c in tint.child_ids {
4438 tsn_stack.push(c);
4439 }
4440 }
4441 Node::FreeListTsnLeaf(tleaf) => {
4442 for &p in &tleaf.page_ids {
4443 freed_tree.push(p);
4444 }
4445 }
4446 _ => panic!("Unexpected node type in TSN-subtree"),
4447 }
4448 }
4449 }
4450 }
4451 }
4452 _ => panic!("Unexpected node type in free list tree"),
4453 }
4454 }
4455
4456 for (&pid, _page) in w2.dirty.iter() {
4458 if !active.contains(&pid) {
4459 active.push(pid);
4460 }
4461 }
4462 for (&pid, _page) in w2.deserialized.iter() {
4464 if !active.contains(&pid) {
4465 active.push(pid);
4466 }
4467 }
4468
4469 let mut freed: Vec<PageID> = w2.freed_page_ids.iter().cloned().collect();
4471 freed.extend(freed_tree);
4473 for (pid, _tsn) in w2.reusable_page_ids.iter() {
4475 freed.push(*pid);
4476 }
4477
4478 active.sort_by_key(|p| p.0);
4480 active.dedup();
4481 freed.sort_by_key(|p| p.0);
4482 freed.dedup();
4483 let mut union = active.clone();
4484 for id in &freed {
4485 if !union.contains(id) {
4486 union.push(*id);
4487 }
4488 }
4489 union.sort_by_key(|p| p.0);
4490
4491 let expected: Vec<PageID> = (0..w2.next_page_id.0).map(PageID).collect();
4492 if expected != union {
4493 let mut missing: Vec<PageID> = expected
4494 .iter()
4495 .cloned()
4496 .filter(|p| !union.contains(p))
4497 .collect();
4498 let mut unexpected: Vec<PageID> = union
4499 .iter()
4500 .cloned()
4501 .filter(|p| !expected.contains(p))
4502 .collect();
4503 missing.sort_by_key(|p| p.0);
4504 unexpected.sort_by_key(|p| p.0);
4505 panic!(
4506 "Missing: {:?} Unexpected: {:?}\nactive: {:?}\nfreed: {:?}",
4507 missing, unexpected, active, freed
4508 );
4509 }
4510 assert_eq!(
4511 expected, union,
4512 "All page IDs must be accounted for (active or freed)"
4513 );
4514 }
4515 }
4516
4517 #[cfg(test)]
4518 mod schema_version_tests {
4519 use super::*;
4520 use serial_test::serial;
4521 use tempfile::tempdir;
4522
4523 #[test]
4524 #[serial]
4525 fn opening_db_with_newer_schema_version_errors() {
4526 let temp_dir = tempdir().unwrap();
4528 let db_path = temp_dir.path().join("schema-guard.db");
4529 let page_size = 64usize; let verbose = false;
4531
4532 let mvcc = Mvcc::new(
4533 verbose,
4534 StorageOptions::default()
4535 .db_path(db_path.clone())
4536 .page_size(page_size),
4537 )
4538 .expect("must create new db");
4539
4540 let h0 = mvcc.read_page(HEADER_PAGE_ID_0).expect("read header 0");
4542 let mut h0 = match &h0.node {
4543 Node::Header(node) => node.clone(),
4544 _ => panic!("Page 0 is not a header"),
4545 };
4546 h0.schema_version = DB_SCHEMA_VERSION + 1;
4547
4548 let page = Page::new(HEADER_PAGE_ID_0, Node::Header(h0));
4550 {
4551 let mut buf = mvcc.page_buf.lock().unwrap();
4552 serialize_page_into(&mut buf, &page.node, mvcc.zero_fill_pages)
4553 .expect("serialize header page");
4554 mvcc.pager
4555 .write_page(page.page_id, &buf)
4556 .expect("write modified header 0");
4557 }
4558
4559 mvcc.fsync().ok();
4561
4562 drop(mvcc);
4564
4565 match Mvcc::new(
4567 verbose,
4568 StorageOptions::default()
4569 .db_path(db_path)
4570 .page_size(page_size),
4571 ) {
4572 Ok(_) => panic!("opening should have failed due to newer on-disk schema"),
4573 Err(DcbError::InternalError(msg)) => {
4574 assert!(
4575 msg.contains("Software version is too old"),
4576 "unexpected error message: {msg}"
4577 );
4578 }
4579 Err(other) => panic!("unexpected error type: {other:?}"),
4580 }
4581 }
4582 }
4583
4584 #[cfg(test)]
4585 mod page_cache_tests {
4586 use super::*;
4587 use serial_test::serial;
4588 use tempfile::tempdir;
4589
4590 #[test]
4591 #[serial]
4592 fn test_mvcc_page_cache_max_pages() {
4593 let temp_dir = tempdir().unwrap();
4594 let db_path = temp_dir.path().join("cache-pages.db");
4595 let options = StorageOptions::default()
4596 .db_path(&db_path)
4597 .page_cache_max_pages(10);
4598
4599 let mvcc = Mvcc::new(false, options).expect("mvcc new");
4600 assert!(mvcc.page_cache.is_some());
4601
4602 let mut writer = mvcc.writer().expect("writer");
4604 let p1 = writer.alloc_page_id();
4605 let page = Page::new(
4606 p1,
4607 Node::FreeListLeaf(crate::free_lists_tree_nodes::FreeListLeafNode::default()),
4608 );
4609 writer.insert_dirty(page).expect("insert dirty");
4610 mvcc.commit(&mut writer).expect("commit");
4611
4612 {
4614 let cache = mvcc.page_cache.as_ref().unwrap();
4615 assert!(cache.get(&p1).is_some());
4616 assert!(
4617 cache.get(&HEADER_PAGE_ID_0).is_some()
4618 || cache.get(&HEADER_PAGE_ID_1).is_some()
4619 );
4620 }
4621
4622 let _read_page = mvcc.read_page(p1).expect("read page");
4624 }
4625
4626 #[test]
4627 #[serial]
4628 fn test_mvcc_page_cache_max_mb() {
4629 let temp_dir = tempdir().unwrap();
4630 let db_path = temp_dir.path().join("cache-mb.db");
4631 let options = StorageOptions::default()
4632 .db_path(&db_path)
4633 .page_cache_max_mb(1); let mvcc = Mvcc::new(false, options).expect("mvcc new");
4636 assert!(mvcc.page_cache.is_some());
4637
4638 let mut writer = mvcc.writer().expect("writer");
4640 let p1 = writer.alloc_page_id();
4641 let page = Page::new(
4642 p1,
4643 Node::FreeListLeaf(crate::free_lists_tree_nodes::FreeListLeafNode::default()),
4644 );
4645 writer.insert_dirty(page).expect("insert dirty");
4646 mvcc.commit(&mut writer).expect("commit");
4647
4648 {
4650 let cache = mvcc.page_cache.as_ref().unwrap();
4651 assert!(cache.get(&p1).is_some());
4652 }
4653
4654 let _read_page = mvcc.read_page(p1).expect("read page");
4656 }
4657
4658 #[test]
4659 #[serial]
4660 fn test_mvcc_page_cache_eviction() {
4661 let temp_dir = tempdir().unwrap();
4662 let db_path = temp_dir.path().join("cache-eviction.db");
4663 let options = StorageOptions::default()
4664 .db_path(&db_path)
4665 .page_cache_max_pages(2);
4666
4667 let mvcc = Mvcc::new(false, options).expect("mvcc new");
4668
4669 let mut writer = mvcc.writer().expect("writer");
4672 let p1 = writer.alloc_page_id();
4673 let p2 = writer.alloc_page_id();
4674 let p3 = writer.alloc_page_id();
4675
4676 writer
4677 .insert_dirty(Page::new(p1, Node::FreeListLeaf(Default::default())))
4678 .unwrap();
4679 writer
4680 .insert_dirty(Page::new(p2, Node::FreeListLeaf(Default::default())))
4681 .unwrap();
4682 writer
4683 .insert_dirty(Page::new(p3, Node::FreeListLeaf(Default::default())))
4684 .unwrap();
4685
4686 mvcc.commit(&mut writer).expect("commit");
4687
4688 let cache = mvcc.page_cache.as_ref().unwrap();
4691 cache.run_pending_tasks();
4694 assert!(cache.entry_count() <= 2);
4695 }
4696 }
4697}