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>(
55 self,
56 db_fn: impl FnOnce() -> anyhow::Result<DB>,
57 ) -> anyhow::Result<Locked<DB>> {
58 Ok(Locked {
59 inner: db_fn()?,
60 lock: self.lock,
61 })
62 }
63}
64
65#[apply(async_trait_maybe_send!)]
66impl<DB> IRawDatabase for Locked<DB>
67where
68 DB: IRawDatabase,
69{
70 type Transaction<'a> = DB::Transaction<'a>;
71
72 async fn begin_transaction<'a>(
73 &'a self,
74 ) -> <Locked<DB> as fedimint_core::db::IRawDatabase>::Transaction<'_> {
75 self.inner.begin_transaction().await
76 }
77
78 fn checkpoint(&self, backup_path: &Path) -> anyhow::Result<()> {
79 self.inner.checkpoint(backup_path)
80 }
81}