use std::{collections::BTreeMap, path::Path};
use derive_more::Debug;
use fjall::{
DatabaseBuilder, KeyspaceCreateOptions, OptimisticTxDatabase, OptimisticTxKeyspace, PersistMode,
};
use super::write_tx::WriteTx;
use crate::{
DbError, DbResult, migration,
snapshot::Snapshot,
utils::{DbInfo, Persist},
};
#[derive(Debug, Clone)]
pub struct TxDb {
#[debug(skip)]
pub db: OptimisticTxDatabase,
pub db_info: Persist<DbInfo>,
pub path: String,
#[debug(skip)]
pub seq_tree: OptimisticTxKeyspace,
}
impl TxDb {
pub fn new<P>(path: P) -> Self
where
P: AsRef<Path>,
{
let builder = Self::builder(path.as_ref());
Self::with_builder(path, builder)
}
pub fn builder<P: AsRef<Path>>(path: P) -> DatabaseBuilder<OptimisticTxDatabase> {
let db_path = path.as_ref().join("db");
OptimisticTxDatabase::builder(db_path)
}
pub fn with_builder<P: AsRef<Path>>(
path: P,
config: DatabaseBuilder<OptimisticTxDatabase>,
) -> Self {
let db = config.open().expect("Failed to open keyspace");
let seq_tree = db
.keyspace("__sequences", KeyspaceCreateOptions::default)
.expect("Failed to open sequence tree");
let meta_path = path.as_ref().join("db.info");
let db_info = Persist::<DbInfo>::open(meta_path);
Self {
db,
db_info,
path: path.as_ref().to_string_lossy().into_owned(),
seq_tree,
}
}
pub fn open_tree(
&self,
name: &str,
options: Option<KeyspaceCreateOptions>,
) -> fjall::OptimisticTxKeyspace {
self.db
.keyspace(name, || options.unwrap_or_default())
.expect("Failed to open partition")
}
pub fn get_keyspace(&self, name: &str) -> Option<OptimisticTxKeyspace> {
if !self.db.keyspace_exists(name) {
return None;
}
self.db
.keyspace(name, KeyspaceCreateOptions::default)
.inspect_err(|err| {
error!("{err}");
})
.ok()
}
pub fn get_all_keyspaces(&self) -> BTreeMap<String, OptimisticTxKeyspace> {
self.db_info
.cloned()
.collections
.into_iter()
.filter_map(|(name, info)| {
let versioned = migration::collection_name(&name, info.version);
self.get_keyspace(&versioned).map(|ks| (name, ks))
})
.collect()
}
pub fn snapshot(&self) -> Snapshot {
let inner = self.db.read_tx();
Snapshot { inner }
}
pub fn write_tx(&self) -> DbResult<WriteTx> {
let inner = self.db.write_tx()?;
Ok(WriteTx { inner })
}
pub fn persist(&self, mode: PersistMode) -> DbResult<()> {
self.db.persist(mode).map_err(DbError::from)
}
#[instrument(skip(self), name = "Database::close", fields(path = self.path))]
pub fn close(&self) {
self.db_info.save();
self.db
.persist(PersistMode::SyncAll)
.expect("Failed to persist");
}
}