#![cfg(feature = "tracking")]
use super::BlockStore;
use cid::{Cid, Code};
use db::{Error, Store};
use std::cell::RefCell;
use std::error::Error as StdError;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct BSStats {
pub r: usize,
pub w: usize,
pub br: usize,
pub bw: usize,
}
#[derive(Debug)]
pub struct TrackingBlockStore<'bs, BS> {
base: &'bs BS,
pub stats: RefCell<BSStats>,
}
impl<'bs, BS> TrackingBlockStore<'bs, BS>
where
BS: BlockStore,
{
pub fn new(base: &'bs BS) -> Self {
Self {
base,
stats: Default::default(),
}
}
}
impl<BS> BlockStore for TrackingBlockStore<'_, BS>
where
BS: BlockStore,
{
fn get_bytes(&self, cid: &Cid) -> Result<Option<Vec<u8>>, Box<dyn StdError>> {
self.stats.borrow_mut().r += 1;
let bytes = self.base.get_bytes(cid)?;
if let Some(bytes) = &bytes {
self.stats.borrow_mut().br += bytes.len();
}
Ok(bytes)
}
fn put_raw(&self, bytes: Vec<u8>, code: Code) -> Result<Cid, Box<dyn StdError>> {
self.stats.borrow_mut().w += 1;
self.stats.borrow_mut().bw += bytes.len();
let cid = cid::new_from_cbor(&bytes, code);
self.write(cid.to_bytes(), bytes)?;
Ok(cid)
}
}
impl<BS> Store for TrackingBlockStore<'_, BS>
where
BS: Store,
{
fn read<K>(&self, key: K) -> Result<Option<Vec<u8>>, Error>
where
K: AsRef<[u8]>,
{
self.base.read(key)
}
fn write<K, V>(&self, key: K, value: V) -> Result<(), Error>
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
self.base.write(key, value)
}
fn delete<K>(&self, key: K) -> Result<(), Error>
where
K: AsRef<[u8]>,
{
self.base.delete(key)
}
fn exists<K>(&self, key: K) -> Result<bool, Error>
where
K: AsRef<[u8]>,
{
self.base.exists(key)
}
fn bulk_read<K>(&self, keys: &[K]) -> Result<Vec<Option<Vec<u8>>>, Error>
where
K: AsRef<[u8]>,
{
self.base.bulk_read(keys)
}
fn bulk_write<K, V>(&self, values: &[(K, V)]) -> Result<(), Error>
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
self.base.bulk_write(values)
}
fn bulk_delete<K>(&self, keys: &[K]) -> Result<(), Error>
where
K: AsRef<[u8]>,
{
self.base.bulk_delete(keys)
}
}
#[cfg(test)]
mod tests {
use super::*;
use cid::Code::Blake2b256;
#[test]
fn basic_tracking_store() {
let mem = db::MemoryDB::default();
let tr_store = TrackingBlockStore::new(&mem);
assert_eq!(*tr_store.stats.borrow(), BSStats::default());
type TestType = (u8, String);
let object: TestType = (8, "test".to_string());
let obj_bytes_len = encoding::to_vec(&object).unwrap().len();
tr_store
.get::<u8>(&cid::new_from_cbor(&[0], Blake2b256))
.unwrap();
assert_eq!(
*tr_store.stats.borrow(),
BSStats {
r: 1,
..Default::default()
}
);
let put_cid = tr_store.put(&object, Blake2b256).unwrap();
assert_eq!(tr_store.get::<TestType>(&put_cid).unwrap(), Some(object));
assert_eq!(
*tr_store.stats.borrow(),
BSStats {
r: 2,
br: obj_bytes_len,
w: 1,
bw: obj_bytes_len,
}
);
}
}