#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("Failed to start a read transaction in the cache db: {0}")]
DbRead(Box<redb::TransactionError>),
#[error("Failed to start a write transaction in the cache db: {0}")]
DbWrite(Box<redb::TransactionError>),
#[error("Failed to open a table in the cache db: {0}")]
DbOpenTable(redb::TableError),
#[error("Failed trying to get a value from the cache db: {0}")]
DbGet(redb::StorageError),
#[error("Failed trying to insert a value into the cache db: {0}")]
DbInsert(redb::StorageError),
#[error("Failed trying to remove a stale key value from the cache db: {0}")]
DbRemove(redb::StorageError),
#[error("Failed to commit the cache database write transaction: {0}")]
DbWriteCommit(redb::CommitError),
}
#[macro_export]
macro_rules! impl_cache {
(@mk_table $table_name:literal) => {
const TABLE: redb::TableDefinition<'static, &[u8], &[u8]> =
redb::TableDefinition::new($table_name);
};
(@lookup $db:expr, $key:expr) => {{
use $crate::replica::cache::Error;
let read_txn = $db
.begin_read()
.map_err(|err| Error::DbRead(Box::new(err)))?;
let table = match read_txn.open_table(TABLE) {
Ok(val) => Some(val),
Err(err) => match &err {
redb::TableError::TableDoesNotExist(_) => None,
_ => Err(Error::DbOpenTable(err))?,
},
};
if let Some(table) = table {
if let Some(value) = table.get($key).map_err(Error::DbGet)? {
let maybe_me = postcard::from_bytes(value.value());
match maybe_me {
Ok(me) => return Ok(me),
Err(err) => {
log::error!("Failed to decode a previously cached key: {err}");
let write_txn = $db
.begin_write()
.map_err(|err| Error::DbWrite(Box::new(err)))?;
{
let mut table =
write_txn.open_table(TABLE).map_err(Error::DbOpenTable)?;
if table.remove($key).map_err(Error::DbRemove)?.is_none() {
log::warn!(
"Detected racing deletes into the cache db. (Stale key was \
already deleted)"
);
}
}
write_txn.commit().map_err(Error::DbWriteCommit)?;
}
}
}
}
}};
(@populate $db:expr, $key:expr, $value:expr) => {{
use $crate::replica::cache::Error;
let write_txn = $db
.begin_write()
.map_err(|err| Error::DbWrite(Box::new(err)))?;
{
let mut table = write_txn.open_table(TABLE).map_err(Error::DbOpenTable)?;
let me_data = postcard::to_allocvec($value).expect("Encoding should always work");
if table
.insert($key, me_data.as_slice())
.map_err(Error::DbInsert)?
.is_some()
{
log::warn!(
"Detected racing writes into the cache db. (Key insertion attempted, although \
key is already present in cache db)"
);
}
}
write_txn.commit().map_err(Error::DbWriteCommit)?;
}};
}
pub(crate) use impl_cache;