use {
crate::{
ContentHash, Ident,
database::{GenerationEpoch, MergeResult},
hash::ContentHasher,
record::LaburnumRecordRef,
},
serde::Serialize,
std::{fmt::Debug, hash::Hash, sync::Arc},
};
pub trait Partitions: Send + Sync + 'static {
type Stores: Send + Sync + crate::database::partitions::RefcountOps + crate::database::partitions::CollectCascadingRefs<Self>;
type RecordRef<'a>: LaburnumRecordRef
where
Self: 'a;
fn new_stores() -> Self::Stores;
fn get_any(
stores: &Self::Stores,
partition: Ident,
hash: ContentHash,
) -> Option<Self::RecordRef<'_>>;
fn partition_keys() -> &'static [Ident];
fn merge_stores(
global_stores: &Self::Stores,
source: &Self::Stores,
epoch: GenerationEpoch,
) -> MergeResult;
fn collect_index_hashes(stores: &Self::Stores) -> Vec<crate::database::ContentHashRef>;
fn index_remove_prefix(
stores: &Self::Stores,
partition_key: Ident,
prefix: &str,
);
fn index_insert(
stores: &Self::Stores,
partition_key: Ident,
sort_key: String,
content_hash: ContentHash,
);
fn index_range(
stores: &Self::Stores,
partition_key: Ident,
prefix: &str,
) -> Vec<(Ident, String, ContentHash)>;
fn index_get(
stores: &Self::Stores,
partition_key: Ident,
sort_key: &str,
) -> Option<ContentHash>;
fn index_less_than(
stores: &Self::Stores,
partition_key: Ident,
value: &str,
inclusive: bool,
) -> Vec<(Ident, String, ContentHash)>;
fn index_greater_than(
stores: &Self::Stores,
partition_key: Ident,
value: &str,
inclusive: bool,
) -> Vec<(Ident, String, ContentHash)>;
fn index_between(
stores: &Self::Stores,
partition_key: Ident,
from: &str,
to: &str,
) -> Vec<(Ident, String, ContentHash)>;
fn span_index_replace(
stores: &Self::Stores,
partition_key: Ident,
uri: crate::Uri,
index: crate::database::partitions::span_index::SpanIndex,
);
fn span_index_remove(
stores: &Self::Stores,
partition_key: Ident,
uri: &crate::Uri,
);
fn span_index_query(
stores: &Self::Stores,
partition_key: Ident,
uri: &crate::Uri,
byte_offset: u64,
) -> Option<ContentHash>;
fn gc_mark_tick(
gc: &crate::database::gc::GarbageCollector,
stores: &Self::Stores,
budget: usize,
) -> bool;
fn gc_sweep(
gc: &crate::database::gc::GarbageCollector,
stores: &Self::Stores,
);
}
pub struct ContentAddressedStorage<P: Partitions> {
stores: Arc<P::Stores>,
}
impl<P: Partitions> ContentAddressedStorage<P> {
pub fn new() -> Self {
Self {
stores: Arc::new(P::new_stores()),
}
}
pub(crate) fn stores_arc(&self) -> Arc<P::Stores> {
Arc::clone(&self.stores)
}
pub(crate) fn get<Part>(
&self,
hash: ContentHash,
) -> Option<crate::database::RecordRef<'_, Part>>
where
Part: crate::database::partitions::Partition + 'static,
P::Stores: crate::database::partitions::HasPartition<Part>,
{
use crate::database::partitions::HasPartition;
<P::Stores as HasPartition<Part>>::store(&self.stores).get(&hash)
}
pub(crate) fn get_any(
&self,
partition: Ident,
hash: ContentHash,
) -> Option<P::RecordRef<'_>> {
P::get_any(&self.stores, partition, hash)
}
pub(crate) fn stores(&self) -> &P::Stores {
&self.stores
}
pub(crate) fn collect_index_hashes(&self) -> Vec<crate::database::ContentHashRef> {
P::collect_index_hashes(&self.stores)
}
pub(in crate::database) fn index_remove_prefix(&self, partition_key: Ident, prefix: &str) {
P::index_remove_prefix(&self.stores, partition_key, prefix);
}
pub(crate) fn index_range(
&self,
partition_key: Ident,
prefix: &str,
) -> Vec<(Ident, String, ContentHash)> {
P::index_range(&self.stores, partition_key, prefix)
}
pub(crate) fn index_get(
&self,
partition_key: Ident,
sort_key: &str,
) -> Option<ContentHash> {
P::index_get(&self.stores, partition_key, sort_key)
}
pub(crate) fn index_less_than(
&self,
partition_key: Ident,
value: &str,
inclusive: bool,
) -> Vec<(Ident, String, ContentHash)> {
P::index_less_than(&self.stores, partition_key, value, inclusive)
}
pub(crate) fn index_greater_than(
&self,
partition_key: Ident,
value: &str,
inclusive: bool,
) -> Vec<(Ident, String, ContentHash)> {
P::index_greater_than(&self.stores, partition_key, value, inclusive)
}
pub(crate) fn index_between(
&self,
partition_key: Ident,
from: &str,
to: &str,
) -> Vec<(Ident, String, ContentHash)> {
P::index_between(&self.stores, partition_key, from, to)
}
pub(in crate::database) fn span_index_replace(
&self,
partition_key: Ident,
uri: crate::Uri,
index: crate::database::partitions::span_index::SpanIndex,
) {
P::span_index_replace(&self.stores, partition_key, uri, index);
}
pub(crate) fn span_index_query(
&self,
partition_key: Ident,
uri: &crate::Uri,
byte_offset: u64,
) -> Option<crate::ContentHash> {
P::span_index_query(&self.stores, partition_key, uri, byte_offset)
}
pub(in crate::database) fn increment_refcount(&self, partition_key: Ident, hash: ContentHash)
where
P::Stores: crate::database::partitions::RefcountOps,
{
use crate::database::partitions::RefcountOps;
self.stores.increment_refcount(partition_key, hash);
}
#[cfg_attr(not(test), allow(dead_code))]
pub(in crate::database) fn decrement_refcount(&self, partition_key: Ident, hash: ContentHash) -> usize
where
P::Stores: crate::database::partitions::RefcountOps,
{
use crate::database::partitions::RefcountOps;
self.stores.decrement_refcount(partition_key, hash)
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn get_refcount(&self, partition_key: Ident, hash: ContentHash) -> usize
where
P::Stores: crate::database::partitions::RefcountOps,
{
use crate::database::partitions::RefcountOps;
self.stores.get_refcount(partition_key, hash)
}
}
impl<P: Partitions> Default for ContentAddressedStorage<P> {
fn default() -> Self {
Self::new()
}
}
impl<P: Partitions> std::fmt::Debug for ContentAddressedStorage<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ContentAddressedStorage")
.finish_non_exhaustive()
}
}
pub trait RecordStorage: Debug + Send + Sync {
type Index: Serialize + Clone + Hash + Eq + Debug + Send + Sync;
type RecordRef<'a>: LaburnumRecordRef
where
Self: 'a;
type Builder: PartitionsBuilder<Storage = Self>;
fn get(&self, idx: &Self::Index) -> Option<Self::RecordRef<'_>>;
fn hash_contents(&self, hasher: &mut ContentHasher);
}
pub trait PartitionsBuilder: Default + Send {
type Storage: RecordStorage;
type Record;
fn push(
&mut self,
record: Self::Record,
) -> <Self::Storage as RecordStorage>::Index;
fn build(self) -> Self::Storage;
}