mod kv;
#[cfg(test)]
mod tests;
pub(super) mod to_db_key;
pub(super) mod used_space;
pub(crate) use kv::{Key, Value};
use super::{
deserialise, Subdir,
{encoding::serialise, Error, Result},
};
use serde::de::DeserializeOwned;
use sled::Db;
use std::{marker::PhantomData, path::Path};
use to_db_key::ToDbKey;
use tracing::info;
use used_space::UsedSpace;
const DB_DIR: &str = "db";
#[derive(Clone, Debug)]
pub(crate) struct KvStore<V> {
used_space: UsedSpace,
db: Db,
_v: PhantomData<V>,
}
impl<V: Value> KvStore<V>
where
Self: Subdir,
{
pub(crate) fn new<P: AsRef<Path>>(root: P, used_space: UsedSpace) -> Result<Self> {
let dir = root.as_ref().join(DB_DIR).join(Self::subdir());
used_space.add_dir(&dir);
let db = sled::Config::default()
.path(&dir)
.flush_every_ms(Some(10000))
.open()
.map_err(Error::from)?;
Ok(KvStore {
used_space,
db,
_v: PhantomData,
})
}
}
impl<V: Value + Send + Sync> KvStore<V> {
pub(crate) async fn total_used_space(&self) -> u64 {
self.used_space.total().await
}
pub(crate) fn has(&self, key: &V::Key) -> Result<bool> {
let key = key.to_db_key()?;
self.db.contains_key(key).map_err(Error::from)
}
pub(crate) fn delete(&self, key: &V::Key) -> Result<()> {
let key = key.to_db_key()?;
self.db.remove(key).map_err(Error::from).map(|_| ())
}
pub(crate) async fn store(&self, value: &V) -> Result<()> {
debug!("Writing value to KV store");
let key = value.key().to_db_key()?;
let serialised_value = serialise(value)?.to_vec();
let exists = self.db.contains_key(key.clone()).map_err(Error::from)?;
if !exists
&& !self
.used_space
.can_consume(serialised_value.len() as u64)
.await
{
return Err(Error::NotEnoughSpace);
}
match self
.db
.compare_and_swap::<_, &[u8], _>(key, None, Some(serialised_value))?
{
Ok(()) => debug!("Successfully wrote new value"),
Err(sled::CompareAndSwapError { .. }) => {
debug!("Value already existed, so we didn't have to write")
}
}
Ok(())
}
pub(crate) fn get(&self, key: &V::Key) -> Result<V> {
let db_key = key.to_db_key()?;
let res = self
.db
.get(db_key.clone())
.map_err(|_| Error::KeyNotFound(db_key.clone()))?;
if let Some(data) = res {
let value: V = deserialise(&data)?;
if value.key() == key {
return Ok(value);
}
}
Err(Error::KeyNotFound(db_key))
}
pub(crate) async fn used_space_ratio(&self) -> f64 {
let used = self.total_used_space().await;
let max_capacity = self.used_space.max_capacity();
let used_space_ratio = used as f64 / max_capacity as f64;
info!("Used space: {:?}", used);
info!("Max capacity: {:?}", max_capacity);
info!("Used space ratio: {:?}", used_space_ratio);
used_space_ratio
}
pub(crate) fn keys(&self) -> Result<Vec<V::Key>> {
let keys = self
.db
.iter()
.flatten()
.map(|(key, _)| key)
.map(convert)
.flatten()
.collect();
Ok(keys)
}
#[cfg(test)]
pub(crate) async fn flush(&self) -> Result<()> {
let _bytes_flushed = self.db.flush_async().await?;
Ok(())
}
}
pub(crate) fn convert<T: DeserializeOwned>(key: sled::IVec) -> Result<T> {
let db_key = &String::from_utf8(key.to_vec()).map_err(|_| Error::CouldNotConvertDbKey)?;
to_db_key::from_db_key(db_key)
}