fedimint_cli/
db_locked.rs1use std::path::Path;
2
3use anyhow::Context;
4use fedimint_core::db::IRawDatabase;
5use fedimint_core::task::block_in_place;
6use fedimint_core::{apply, async_trait_maybe_send};
7use fedimint_logging::LOG_CLIENT;
8use tracing::{debug, info};
9
10#[derive(Debug)]
17pub struct Locked<DB> {
18 inner: DB,
19 #[allow(dead_code)] lock: fs_lock::FileLock,
21}
22
23pub struct LockedBuilder {
25 lock: fs_lock::FileLock,
26}
27
28impl LockedBuilder {
29 pub fn new(lock_path: &Path) -> anyhow::Result<LockedBuilder> {
31 block_in_place(|| {
32 let file = std::fs::OpenOptions::new()
33 .write(true)
34 .create(true)
35 .truncate(true)
36 .open(lock_path)
37 .with_context(|| format!("Failed to open {}", lock_path.display()))?;
38
39 debug!(target: LOG_CLIENT, "Acquiring database lock");
40
41 let lock = match fs_lock::FileLock::new_try_exclusive(file) {
42 Ok(lock) => lock,
43 Err((file, _)) => {
44 info!(target: LOG_CLIENT, "Waiting for the database lock");
45
46 fs_lock::FileLock::new_exclusive(file)
47 .context("Failed to acquire a lock file")?
48 }
49 };
50 debug!(target: LOG_CLIENT, "Acquired database lock");
51
52 Ok(LockedBuilder { lock })
53 })
54 }
55
56 pub fn with_db<DB>(self, db: DB) -> Locked<DB> {
58 Locked {
59 inner: db,
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}