From 07ebf79a176a38b684220f4f46b8a07d9a133643 Mon Sep 17 00:00:00 2001 From: Michael Constant Date: Thu, 31 Oct 2024 19:09:04 -0700 Subject: [PATCH] Panic isn't a clean shutdown When shutting down due to a panic, don't mark the database as clean; just treat it like a crash and let the recovery code run next time. This avoids page leaks and possibly other database corruption, depending on where the panic happens. --- src/tree_store/page_store/page_manager.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/tree_store/page_store/page_manager.rs b/src/tree_store/page_store/page_manager.rs index 81ed3d83..efacc619 100644 --- a/src/tree_store/page_store/page_manager.rs +++ b/src/tree_store/page_store/page_manager.rs @@ -21,6 +21,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(debug_assertions)] use std::sync::Arc; use std::sync::Mutex; +use std::thread; // Regions have a maximum size of 4GiB. A `4GiB - overhead` value is the largest that can be represented, // because the leaf node format uses 32bit offsets @@ -991,6 +992,10 @@ impl TransactionalMemory { impl Drop for TransactionalMemory { fn drop(&mut self) { + if thread::panicking() { + return; + } + // Commit any non-durable transactions that are outstanding if self.read_from_secondary.load(Ordering::Acquire) && !self.needs_recovery.load(Ordering::Acquire) @@ -1097,4 +1102,22 @@ mod test { .unwrap(); assert!(db.check_integrity().unwrap()); } + + // Make sure the database remains consistent after a panic + #[test] + #[cfg(panic = "unwind")] + fn panic() { + let tmpfile = crate::create_tempfile(); + let table_definition: TableDefinition = TableDefinition::new("x"); + + let _ = std::panic::catch_unwind(|| { + let db = Database::create(&tmpfile).unwrap(); + let txn = db.begin_write().unwrap(); + txn.open_table(table_definition).unwrap(); + panic!(); + }); + + let mut db = Database::open(tmpfile).unwrap(); + assert!(db.check_integrity().unwrap()); + } }