use std::sync::Arc;
use std::time::Duration;
use super::Db;
use super::flush_tests::{PATH, key, open, options, value};
use crate::log::fault::FaultStorage;
use crate::{Durability, Error, KeyspaceId, ReadOptions, WriteBatch, WriteOptions};
struct Resume(FaultStorage);
impl Drop for Resume {
fn drop(&mut self) {
self.0.resume_syncs();
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_dropped_database_holds_its_directory_until_its_background_work_ends() {
let storage = FaultStorage::default();
let db = open(&storage).await;
let buffered = WriteOptions {
durability: Durability::Buffered,
..WriteOptions::default()
};
let write = |index: u32| {
let mut batch = WriteBatch::new();
batch
.put(KeyspaceId::DEFAULT, &key(index), &value(index))
.expect("small entry");
let (db, buffered) = (&db, &buffered);
async move { db.write(&batch, buffered).await }
};
let mut written = 0;
while written < 10 {
write(written).await.expect("written");
written += 1;
}
storage.pause_syncs();
let resume = Resume(storage.clone());
while !storage.sync_waiting("") {
let landed = tokio::time::timeout(Duration::from_secs(1), write(written)).await;
if landed.is_ok() {
landed.expect("in time").expect("written");
written += 1;
}
assert!(written < 200, "no background sync started");
tokio::task::yield_now().await;
}
drop(db);
let opened = tokio::time::timeout(
Duration::from_secs(5),
Db::open_with_storage(PATH, options(), Arc::new(storage.clone())),
)
.await;
drop(resume);
assert!(
matches!(opened, Ok(Err(Error::Locked { .. }))),
"a second database got past the lock while the first one's background work was running: \
{:?}",
opened.as_ref().map(|result| result.as_ref().err())
);
let reopened = tokio::time::timeout(Duration::from_secs(10), async {
loop {
match Db::open_with_storage(PATH, options(), Arc::new(storage.clone())).await {
Ok(db) => return db,
Err(Error::Locked { .. }) => tokio::task::yield_now().await,
Err(error) => panic!("reopening failed: {error:?}"),
}
}
})
.await
.expect("the directory is released once the background work ends");
for index in 0..written {
let read = reopened
.get(KeyspaceId::DEFAULT, &key(index), &ReadOptions::default())
.await
.expect("readable");
assert_eq!(read, Some(value(index)), "key {index}");
}
reopened.close().await.expect("closed");
}