feat: implement dual-transport WAL architecture with permanent background leader and lightweight stubs
This commit is contained in:
1 parent
a2febd1b00
commit
e4ff476b6d
16 files changed
+1910
-52
No files matched your search
@@ -0,0 +1,82 @@
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::RwLock;
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub fn read_json_file<T: DeserializeOwned + Default>(path: &Path) -> T {
|
||||
if let Ok(data) = fs::read(path)
|
||||
&& let Ok(parsed) = serde_json::from_slice(&data) {
|
||||
return parsed;
|
||||
}
|
||||
let bak_path = path.with_extension("json.bak");
|
||||
if let Ok(data) = fs::read(&bak_path)
|
||||
&& let Ok(parsed) = serde_json::from_slice(&data) {
|
||||
let _ = fs::write(path, data);
|
||||
return parsed;
|
||||
}
|
||||
T::default()
|
||||
}
|
||||
|
||||
pub fn write_json_atomic<T: Serialize>(path: &Path, data: &T) -> std::io::Result<()> {
|
||||
if path.exists() {
|
||||
let bak_path = path.with_extension("json.bak");
|
||||
let _ = fs::copy(path, &bak_path);
|
||||
}
|
||||
let tmp_path = path.with_extension("json.tmp");
|
||||
let json_data = serde_json::to_string_pretty(data)?;
|
||||
fs::write(&tmp_path, json_data)?;
|
||||
fs::rename(&tmp_path, path)
|
||||
}
|
||||
|
||||
pub struct Store<T> {
|
||||
pub path: PathBuf,
|
||||
pub cache: RwLock<(T, SystemTime)>,
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned + Default + Serialize + Clone> Store<T> {
|
||||
fn mtime(&self) -> SystemTime {
|
||||
fs::metadata(&self.path)
|
||||
.and_then(|m| m.modified())
|
||||
.unwrap_or(SystemTime::UNIX_EPOCH)
|
||||
}
|
||||
|
||||
pub fn new(path: PathBuf) -> Self {
|
||||
let mtime = fs::metadata(&path)
|
||||
.and_then(|m| m.modified())
|
||||
.unwrap_or(SystemTime::UNIX_EPOCH);
|
||||
let initial_data = read_json_file(&path);
|
||||
Self {
|
||||
path,
|
||||
cache: RwLock::new((initial_data, mtime)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self) -> T {
|
||||
let current_mtime = self.mtime();
|
||||
{
|
||||
let lock = self.cache.read().unwrap();
|
||||
if lock.1 == current_mtime {
|
||||
return lock.0.clone();
|
||||
}
|
||||
}
|
||||
let mut lock = self.cache.write().unwrap();
|
||||
let new_mtime = self.mtime();
|
||||
if lock.1 != new_mtime {
|
||||
lock.0 = read_json_file(&self.path);
|
||||
lock.1 = new_mtime;
|
||||
}
|
||||
lock.0.clone()
|
||||
}
|
||||
|
||||
pub fn modify<F: FnOnce(&mut T)>(&self, f: F) {
|
||||
let mut lock = self.cache.write().unwrap();
|
||||
let current_mtime = self.mtime();
|
||||
if lock.1 != current_mtime {
|
||||
lock.0 = read_json_file(&self.path);
|
||||
}
|
||||
f(&mut lock.0);
|
||||
let _ = write_json_atomic(&self.path, &lock.0);
|
||||
lock.1 = self.mtime();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user