use crate::storage::buffer_pool::BufferPool;
use crate::storage::disk_manager::DiskManager;
use crate::storage::page::Page;
use crate::wal::log_manager::LogManager;
use crate::wal::log_record::LogRecord;
use crate::wal::recovery::RecoveryManager;
use std::sync::Arc;
use tempfile::tempdir;
#[test]
fn recovery_redo_and_undo_behaviour() {
let dir = tempdir().expect("tempdir");
let db_path = dir.path().join("ent.db");
let wal_path = dir.path().join("ent.wal");
let dm = Arc::new(DiskManager::new(&db_path).expect("disk manager"));
let bp = Arc::new(BufferPool::new(8, Arc::clone(&dm)));
let log = Arc::new(LogManager::new(&wal_path, 4096).expect("log manager"));
let page_id = {
let mut p = bp.new_page().expect("new page");
p.body_mut()[0] = 0;
p.body_mut()[1] = 0;
p.page_id()
};
bp.flush_page(page_id).expect("flush base page");
bp.flush_all().expect("flush all");
log.append(LogRecord::Begin { txn_id: 1 })
.expect("txn1 begin");
log.append(LogRecord::Update {
txn_id: 1,
page_id,
offset: 0,
old_data: vec![0],
new_data: vec![7],
})
.expect("txn1 update");
log.append(LogRecord::Commit { txn_id: 1 })
.expect("txn1 commit");
log.append(LogRecord::Begin { txn_id: 2 })
.expect("txn2 begin");
log.append(LogRecord::Update {
txn_id: 2,
page_id,
offset: 1,
old_data: vec![0],
new_data: vec![9],
})
.expect("txn2 update");
log.flush().expect("flush wal");
drop(bp);
drop(dm);
drop(log);
let dm2 = Arc::new(DiskManager::new(&db_path).expect("reopen disk manager"));
let bp2 = Arc::new(BufferPool::new(8, Arc::clone(&dm2)));
let log2 = Arc::new(LogManager::new(&wal_path, 4096).expect("reopen log manager"));
let recovery = RecoveryManager::new(Arc::clone(&log2), Arc::clone(&bp2));
recovery.recover().expect("recover");
let mut page = Page::default();
dm2.read_page(page_id, &mut page)
.expect("read recovered page");
assert_eq!(page.body()[0], 7, "committed update must be redone");
assert_eq!(page.body()[1], 0, "uncommitted update must be undone");
}
#[test]
fn recovery_is_idempotent_on_repeated_runs() {
let dir = tempdir().expect("tempdir");
let db_path = dir.path().join("ent_idempotent.db");
let wal_path = dir.path().join("ent_idempotent.wal");
let dm = Arc::new(DiskManager::new(&db_path).expect("disk manager"));
let bp = Arc::new(BufferPool::new(4, Arc::clone(&dm)));
let log = Arc::new(LogManager::new(&wal_path, 4096).expect("log manager"));
let page_id = {
let p = bp.new_page().expect("new page");
p.page_id()
};
log.append(LogRecord::Begin { txn_id: 1 }).expect("begin");
log.append(LogRecord::Update {
txn_id: 1,
page_id,
offset: 0,
old_data: vec![0],
new_data: vec![33],
})
.expect("update");
log.append(LogRecord::Commit { txn_id: 1 }).expect("commit");
log.flush().expect("flush wal");
let recovery = RecoveryManager::new(Arc::clone(&log), Arc::clone(&bp));
recovery.recover().expect("recover #1");
recovery.recover().expect("recover #2");
let mut page = Page::default();
dm.read_page(page_id, &mut page).expect("read page");
assert_eq!(page.body()[0], 33);
}