use crate::{StorageError, StorageResult};
use firecloud_core::{Chunk, ChunkHash, ChunkMetadata};
use sled::Db;
use std::path::Path;
use tracing::{debug, info};
const TREE_CHUNKS: &str = "chunks";
const TREE_METADATA: &str = "metadata";
pub struct ChunkStore {
db: Db,
}
impl ChunkStore {
pub fn open<P: AsRef<Path>>(path: P) -> StorageResult<Self> {
let db = sled::open(path)
.map_err(|e| StorageError::Database(e.to_string()))?;
info!("Opened chunk store with Sled");
Ok(Self { db })
}
pub fn put(&self, chunk: &Chunk) -> StorageResult<()> {
let chunks_tree = self.db.open_tree(TREE_CHUNKS)
.map_err(|e| StorageError::Database(e.to_string()))?;
let metadata_tree = self.db.open_tree(TREE_METADATA)
.map_err(|e| StorageError::Database(e.to_string()))?;
let key = chunk.metadata.hash.0;
chunks_tree.insert(key, chunk.data.as_ref())
.map_err(|e| StorageError::Database(e.to_string()))?;
let metadata_bytes = bincode::serialize(&chunk.metadata)
.map_err(|e| StorageError::Serialization(e.to_string()))?;
metadata_tree.insert(key, metadata_bytes)
.map_err(|e| StorageError::Database(e.to_string()))?;
debug!("Stored chunk: {}", chunk.metadata.hash);
Ok(())
}
pub fn get(&self, hash: &ChunkHash) -> StorageResult<Option<Chunk>> {
let chunks_tree = self.db.open_tree(TREE_CHUNKS)
.map_err(|e| StorageError::Database(e.to_string()))?;
let metadata_tree = self.db.open_tree(TREE_METADATA)
.map_err(|e| StorageError::Database(e.to_string()))?;
let key = hash.0;
let data = match chunks_tree.get(key)
.map_err(|e| StorageError::Database(e.to_string()))? {
Some(d) => d.to_vec(),
None => return Ok(None),
};
let metadata_bytes = metadata_tree.get(key)
.map_err(|e| StorageError::Database(e.to_string()))?
.ok_or_else(|| StorageError::ChunkNotFound(hash.to_hex()))?;
let metadata: ChunkMetadata = bincode::deserialize(&metadata_bytes)
.map_err(|e| StorageError::Serialization(e.to_string()))?;
Ok(Some(Chunk { data: data.into(), metadata }))
}
pub fn contains(&self, hash: &ChunkHash) -> StorageResult<bool> {
let chunks_tree = self.db.open_tree(TREE_CHUNKS)
.map_err(|e| StorageError::Database(e.to_string()))?;
Ok(chunks_tree.contains_key(hash.0)
.map_err(|e| StorageError::Database(e.to_string()))?)
}
pub fn delete(&self, hash: &ChunkHash) -> StorageResult<bool> {
let chunks_tree = self.db.open_tree(TREE_CHUNKS)
.map_err(|e| StorageError::Database(e.to_string()))?;
let metadata_tree = self.db.open_tree(TREE_METADATA)
.map_err(|e| StorageError::Database(e.to_string()))?;
let key = hash.0;
let existed = chunks_tree.remove(key)
.map_err(|e| StorageError::Database(e.to_string()))?
.is_some();
metadata_tree.remove(key)
.map_err(|e| StorageError::Database(e.to_string()))?;
if existed { debug!("Deleted chunk: {}", hash); }
Ok(existed)
}
pub fn count(&self) -> StorageResult<usize> {
let chunks_tree = self.db.open_tree(TREE_CHUNKS)
.map_err(|e| StorageError::Database(e.to_string()))?;
Ok(chunks_tree.len())
}
pub fn total_size(&self) -> StorageResult<u64> {
let chunks_tree = self.db.open_tree(TREE_CHUNKS)
.map_err(|e| StorageError::Database(e.to_string()))?;
let mut total = 0u64;
for result in chunks_tree.iter() {
let (_, value) = result.map_err(|e| StorageError::Database(e.to_string()))?;
total += value.len() as u64;
}
Ok(total)
}
pub fn flush(&self) -> StorageResult<()> {
self.db.flush().map_err(|e| StorageError::Database(e.to_string()))?;
Ok(())
}
}