absurder_sql/storage/
export_import_lock.rs1use std::cell::RefCell;
7use std::collections::HashMap;
8use std::rc::Rc;
9use crate::types::DatabaseError;
10
11const LOCK_TIMEOUT_RETRIES: u32 = 3000; struct ExportImportLock {
15 locked: bool,
17}
18
19impl ExportImportLock {
20 fn new() -> Self {
21 Self {
22 locked: false,
23 }
24 }
25
26 fn try_acquire(&mut self) -> bool {
28 if !self.locked {
29 self.locked = true;
30 true
31 } else {
32 false
33 }
34 }
35
36 fn release(&mut self) {
38 self.locked = false;
39 }
40}
41
42thread_local! {
43 static EXPORT_IMPORT_LOCKS: RefCell<HashMap<String, Rc<RefCell<ExportImportLock>>>> =
45 RefCell::new(HashMap::new());
46}
47
48pub async fn acquire_export_import_lock(db_name: &str) -> Result<LockGuard, DatabaseError> {
59 let db_name_owned = db_name.to_string();
60 let mut retries = 0u32;
61
62 loop {
63 let acquired = EXPORT_IMPORT_LOCKS.with(|locks| {
65 let mut locks_map = locks.borrow_mut();
66 let lock_rc = locks_map
67 .entry(db_name_owned.clone())
68 .or_insert_with(|| Rc::new(RefCell::new(ExportImportLock::new())));
69
70 let mut lock = lock_rc.borrow_mut();
71 lock.try_acquire()
72 });
73
74 if acquired {
75 log::debug!("Acquired export/import lock for: {}", db_name);
76 return Ok(LockGuard {
77 db_name: db_name_owned,
78 });
79 }
80
81 retries += 1;
83 if retries >= LOCK_TIMEOUT_RETRIES {
84 return Err(DatabaseError::new(
85 "LOCK_TIMEOUT",
86 &format!("Failed to acquire export/import lock for {} after {} seconds",
87 db_name, LOCK_TIMEOUT_RETRIES * 10 / 1000)
88 ));
89 }
90
91 #[cfg(target_arch = "wasm32")]
93 {
94 let promise = js_sys::Promise::new(&mut |resolve, _reject| {
96 web_sys::window()
97 .unwrap()
98 .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, 10)
99 .unwrap();
100 });
101 wasm_bindgen_futures::JsFuture::from(promise).await.ok();
102 }
103
104 #[cfg(not(target_arch = "wasm32"))]
105 {
106 use std::time::Duration;
107 tokio::time::sleep(Duration::from_millis(10)).await;
108 }
109 }
110}
111
112pub struct LockGuard {
114 db_name: String,
115}
116
117impl Drop for LockGuard {
118 fn drop(&mut self) {
119 EXPORT_IMPORT_LOCKS.with(|locks| {
121 let locks_map = locks.borrow();
122 if let Some(lock_rc) = locks_map.get(&self.db_name) {
123 let mut lock = lock_rc.borrow_mut();
124 lock.release();
125 log::debug!("Released export/import lock for: {}", self.db_name);
126 }
127 });
128 }
129}
130
131