fedimint_rocksdb/
db_locked.rs
1use std::path::Path;
2
3use anyhow::Context;
4use fedimint_core::db::IRawDatabase;
5use fedimint_core::{apply, async_trait_maybe_send};
6use fedimint_logging::LOG_DB;
7use tracing::{debug, info};
8
9#[derive(Debug)]
16pub struct Locked<DB> {
17 inner: DB,
18 #[allow(dead_code)] lock: fs_lock::FileLock,
20}
21
22pub struct LockedBuilder {
24 lock: fs_lock::FileLock,
25}
26
27impl LockedBuilder {
28 pub fn new(db_path: &Path) -> anyhow::Result<LockedBuilder> {
30 let lock_path = db_path.with_extension("db.lock");
31 let file = std::fs::OpenOptions::new()
32 .write(true)
33 .create(true)
34 .truncate(true)
35 .open(&lock_path)
36 .with_context(|| format!("Failed to open {}", lock_path.display()))?;
37
38 debug!(target: LOG_DB, lock=%lock_path.display(), "Acquiring database lock");
39
40 let lock = match fs_lock::FileLock::new_try_exclusive(file) {
41 Ok(lock) => lock,
42 Err((file, _)) => {
43 info!(target: LOG_DB, lock=%lock_path.display(), "Waiting for the database lock");
44
45 fs_lock::FileLock::new_exclusive(file).context("Failed to acquire a lock file")?
46 }
47 };
48 debug!(target: LOG_DB, lock=%lock_path.display(), "Acquired database lock");
49
50 Ok(LockedBuilder { lock })
51 }
52
53 pub fn with_db<DB>(self, db: DB) -> Locked<DB> {
55 Locked {
56 inner: db,
57 lock: self.lock,
58 }
59 }
60}
61
62#[apply(async_trait_maybe_send!)]
63impl<DB> IRawDatabase for Locked<DB>
64where
65 DB: IRawDatabase,
66{
67 type Transaction<'a> = DB::Transaction<'a>;
68
69 async fn begin_transaction<'a>(
70 &'a self,
71 ) -> <Locked<DB> as fedimint_core::db::IRawDatabase>::Transaction<'_> {
72 self.inner.begin_transaction().await
73 }
74
75 fn checkpoint(&self, backup_path: &Path) -> anyhow::Result<()> {
76 self.inner.checkpoint(backup_path)
77 }
78}