#![cfg(feature = "failpoints")]
use seerdb::{DBOptions, SyncPolicy};
use std::mem::forget;
use tempfile::TempDir;
#[test]
fn test_failpoint_flush_after_sstable_write() {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().to_path_buf();
{
let db = DBOptions::default()
.sync_policy(SyncPolicy::SyncAll)
.background_flush(false)
.background_compaction(false)
.open(&db_path)
.unwrap();
for i in 0..100 {
db.put(format!("key_{:03}", i).as_bytes(), b"value")
.unwrap();
}
fail::cfg("flush::after_sstable_write", "panic").unwrap();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
db.flush().unwrap();
}));
assert!(result.is_err(), "Flush should have panicked at failpoint");
fail::remove("flush::after_sstable_write");
forget(db);
}
{
let db = DBOptions::default()
.background_flush(false)
.background_compaction(false)
.open(&db_path)
.unwrap();
for i in 0..100 {
let value = db.get(format!("key_{:03}", i).as_bytes()).unwrap();
assert!(
value.is_some(),
"Key {} should be present after recovery",
i
);
}
}
}
#[test]
fn test_failpoint_flush_before_wal_clear() {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().to_path_buf();
{
let db = DBOptions::default()
.sync_policy(SyncPolicy::SyncAll)
.background_flush(false)
.background_compaction(false)
.open(&db_path)
.unwrap();
for i in 0..50 {
db.put(format!("key_{:03}", i).as_bytes(), b"value")
.unwrap();
}
fail::cfg("flush::before_wal_clear", "panic").unwrap();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
db.flush().unwrap();
}));
assert!(result.is_err(), "Flush should have panicked at failpoint");
fail::remove("flush::before_wal_clear");
forget(db);
}
{
let db = DBOptions::default()
.background_flush(false)
.background_compaction(false)
.open(&db_path)
.unwrap();
for i in 0..50 {
let value = db.get(format!("key_{:03}", i).as_bytes()).unwrap();
assert!(value.is_some(), "Key {} should be present", i);
assert_eq!(value.unwrap().as_ref(), b"value");
}
}
}
#[test]
fn test_failpoint_compaction_after_output_write() {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().to_path_buf();
{
let db = DBOptions::default()
.sync_policy(SyncPolicy::SyncAll)
.memtable_capacity(4096) .background_flush(false)
.background_compaction(false)
.open(&db_path)
.unwrap();
for batch in 0..5 {
for i in 0..20 {
db.put(
format!("key_{:02}_{:03}", batch, i).as_bytes(),
format!("value_{}", batch).as_bytes(),
)
.unwrap();
}
db.flush().unwrap();
}
fail::cfg("compaction::after_output_write", "panic").unwrap();
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
db.flush().unwrap(); }));
fail::remove("compaction::after_output_write");
forget(db);
}
{
let db = DBOptions::default()
.background_flush(false)
.background_compaction(false)
.open(&db_path)
.unwrap();
for batch in 0..5 {
for i in 0..20 {
let key = format!("key_{:02}_{:03}", batch, i);
let value = db.get(key.as_bytes()).unwrap();
assert!(value.is_some(), "Key {} should be present", key);
}
}
}
}
#[test]
fn test_failpoint_wal_after_sync() {
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().to_path_buf();
{
let db = DBOptions::default()
.sync_policy(SyncPolicy::SyncAll)
.background_flush(false)
.open(&db_path)
.unwrap();
db.put(b"key1", b"value1").unwrap();
fail::cfg("wal::after_sync", "panic").unwrap();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
db.put(b"key2", b"value2").unwrap();
}));
assert!(result.is_err(), "Put should have panicked at failpoint");
fail::remove("wal::after_sync");
forget(db);
}
{
let db = DBOptions::default()
.background_flush(false)
.open(&db_path)
.unwrap();
assert!(db.get(b"key1").unwrap().is_some(), "key1 should exist");
}
}