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