use crate::{
cache::{BlockInfo, CacheTracker, WriteInfo},
cidbytes::CidBytes,
db::*,
Block, BlockStore, Result, StoreStats, TempPin,
};
use fnv::FnvHashSet;
use libipld::{cid, codec::References, store::StoreParams, Cid, Ipld};
use parking_lot::Mutex;
use std::{
convert::TryFrom,
iter::FromIterator,
marker::PhantomData,
mem,
sync::{
atomic::{AtomicI64, Ordering},
Arc,
},
time::Duration,
};
pub struct Transaction<'a, S> {
inner: rusqlite::Transaction<'a>,
info: Mutex<TransactionInfo>,
expired_temp_pins: Arc<Mutex<Vec<i64>>>,
_s: PhantomData<S>,
}
struct TransactionInfo {
written: Vec<WriteInfo>,
accessed: Vec<BlockInfo>,
committed: bool,
tracker: Arc<dyn CacheTracker>,
}
impl Drop for TransactionInfo {
fn drop(&mut self) {
if !self.accessed.is_empty() {
let blocks = mem::replace(&mut self.accessed, Vec::new());
self.tracker.blocks_accessed(blocks);
}
if self.committed && !self.written.is_empty() {
let blocks = mem::replace(&mut self.written, Vec::new());
self.tracker.blocks_written(blocks);
}
}
}
impl<'a, S> Transaction<'a, S>
where
S: StoreParams,
Ipld: References<S::Codecs>,
{
pub(crate) fn new(owner: &'a mut BlockStore<S>) -> Result<Self> {
Ok(Self {
inner: owner.conn.transaction()?,
info: Mutex::new(TransactionInfo {
written: Vec::new(),
accessed: Vec::new(),
committed: false,
tracker: owner.config.cache_tracker.clone(),
}),
expired_temp_pins: owner.expired_temp_pins.clone(),
_s: PhantomData,
})
}
fn txn(&self) -> &rusqlite::Transaction<'a> {
&self.inner
}
pub fn alias(&self, name: impl AsRef<[u8]>, link: Option<&Cid>) -> Result<()> {
let link: Option<CidBytes> = link.map(CidBytes::try_from).transpose()?;
alias(self.txn(), name.as_ref(), link.as_ref())?;
Ok(())
}
pub fn reverse_alias(&self, cid: &Cid) -> Result<Option<Vec<Vec<u8>>>> {
let cid = CidBytes::try_from(cid)?;
reverse_alias(self.txn(), cid.as_ref())
}
pub fn resolve(&self, name: impl AsRef<[u8]>) -> Result<Option<Cid>> {
Ok(resolve::<CidBytes>(self.txn(), name.as_ref())?
.map(|c| Cid::try_from(&c))
.transpose()?)
}
pub fn temp_pin(&self) -> TempPin {
TempPin {
id: AtomicI64::new(0),
expired_temp_pins: self.expired_temp_pins.clone(),
}
}
pub fn extend_temp_pin(&self, pin: &TempPin, link: &Cid) -> Result<()> {
let link = CidBytes::try_from(link)?;
let pin0 = pin.id.load(Ordering::SeqCst);
extend_temp_pin(self.txn(), pin0, vec![link])?;
pin.id.store(pin0, Ordering::SeqCst);
Ok(())
}
pub fn has_cid(&self, cid: &Cid) -> Result<bool> {
let cid = CidBytes::try_from(cid)?;
has_cid(self.txn(), cid)
}
pub fn has_block(&self, cid: &Cid) -> Result<bool> {
let cid = CidBytes::try_from(cid)?;
has_block(self.txn(), cid)
}
pub fn get_known_cids<C: FromIterator<Cid>>(&self) -> Result<C> {
let res = get_known_cids::<CidBytes>(self.txn())?;
let res = res.iter().map(Cid::try_from).collect::<cid::Result<C>>()?;
Ok(res)
}
pub fn get_block_cids<C: FromIterator<Cid>>(&self) -> Result<C> {
let res = get_block_cids::<CidBytes>(self.txn())?;
let res = res.iter().map(Cid::try_from).collect::<cid::Result<C>>()?;
Ok(res)
}
pub fn get_descendants<C: FromIterator<Cid>>(&self, cid: &Cid) -> Result<C> {
let cid = CidBytes::try_from(cid)?;
let res = get_descendants(self.txn(), cid)?;
let res = res.iter().map(Cid::try_from).collect::<cid::Result<C>>()?;
Ok(res)
}
pub fn get_missing_blocks<C: FromIterator<Cid>>(&self, cid: &Cid) -> Result<C> {
let cid = CidBytes::try_from(cid)?;
let result = log_execution_time("get_missing_blocks", Duration::from_millis(10), || {
get_missing_blocks(self.txn(), cid)
})?;
let res = result
.iter()
.map(Cid::try_from)
.collect::<cid::Result<C>>()?;
Ok(res)
}
pub fn aliases<C: FromIterator<(Vec<u8>, Cid)>>(&self) -> Result<C> {
let result: Vec<(Vec<u8>, CidBytes)> = aliases(self.txn())?;
let res = result
.into_iter()
.map(|(alias, cid)| {
let cid = Cid::try_from(&cid)?;
Ok((alias, cid))
})
.collect::<cid::Result<C>>()?;
Ok(res)
}
pub fn put_block(&self, block: &Block<S>, pin: Option<&TempPin>) -> Result<()> {
let mut pin0 = pin.map(|pin| pin.id.load(Ordering::SeqCst));
let cid_bytes = CidBytes::try_from(block.cid())?;
let mut links = Vec::new();
block.references(&mut links)?;
let links = links
.iter()
.map(CidBytes::try_from)
.collect::<std::result::Result<FnvHashSet<_>, cid::Error>>()?;
let res = put_block(self.txn(), &cid_bytes, &block.data(), links, &mut pin0)?;
let write_info = WriteInfo::new(
BlockInfo::new(res.id, block.cid(), block.data().len()),
res.block_exists,
);
if let (Some(pin), Some(p)) = (pin, pin0) {
pin.id.store(p, Ordering::SeqCst);
}
self.info.lock().written.push(write_info);
Ok(())
}
pub fn get_block(&self, cid: &Cid) -> Result<Option<Vec<u8>>> {
let response = get_block(self.txn(), &CidBytes::try_from(cid)?)?;
if let Some(info) = response
.as_ref()
.map(|(id, data)| BlockInfo::new(*id, cid, data.len()))
{
self.info.lock().accessed.push(info);
}
Ok(response.map(|(_id, data)| data))
}
pub fn get_store_stats(&self) -> Result<StoreStats> {
get_store_stats(self.txn())
}
pub fn commit(self) -> Result<()> {
self.inner.commit()?;
self.info.lock().committed = true;
Ok(())
}
}