mod block_size;
mod compression;
mod filter;
mod hash_ratio;
mod pinning;
mod restart_interval;
pub use block_size::BlockSizePolicy;
pub use compression::CompressionPolicy;
pub use filter::{BloomConstructionPolicy, FilterPolicy, FilterPolicyEntry};
pub use hash_ratio::HashRatioPolicy;
pub use pinning::PinningPolicy;
pub use restart_interval::RestartIntervalPolicy;
pub type PartitioningPolicy = PinningPolicy;
use crate::{
compaction::filter::Factory, path::absolute_path, version::DEFAULT_LEVEL_COUNT, AnyTree,
BlobTree, Cache, CompressionType, DescriptorTable, SequenceNumberCounter, Tree,
};
use std::{
path::{Path, PathBuf},
sync::Arc,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TreeType {
Standard,
Blob,
}
impl From<TreeType> for u8 {
fn from(val: TreeType) -> Self {
match val {
TreeType::Standard => 0,
TreeType::Blob => 1,
}
}
}
impl TryFrom<u8> for TreeType {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Standard),
1 => Ok(Self::Blob),
_ => Err(()),
}
}
}
const DEFAULT_FILE_FOLDER: &str = ".lsm.data";
#[derive(Clone, Debug, PartialEq)]
pub struct KvSeparationOptions {
#[doc(hidden)]
pub compression: CompressionType,
#[doc(hidden)]
pub file_target_size: u64,
#[doc(hidden)]
pub separation_threshold: u32,
#[doc(hidden)]
pub staleness_threshold: f32,
#[doc(hidden)]
pub age_cutoff: f32,
}
impl Default for KvSeparationOptions {
fn default() -> Self {
Self {
#[cfg(feature="lz4")]
compression: CompressionType::Lz4,
#[cfg(not(feature="lz4"))]
compression: CompressionType::None,
file_target_size: 64 * 1_024 * 1_024,
separation_threshold: 1_024,
staleness_threshold: 0.25,
age_cutoff: 0.25,
}
}
}
impl KvSeparationOptions {
#[must_use]
pub fn compression(mut self, compression: CompressionType) -> Self {
self.compression = compression;
self
}
#[must_use]
pub fn file_target_size(mut self, bytes: u64) -> Self {
self.file_target_size = bytes;
self
}
#[must_use]
pub fn separation_threshold(mut self, bytes: u32) -> Self {
self.separation_threshold = bytes;
self
}
#[must_use]
pub fn staleness_threshold(mut self, ratio: f32) -> Self {
self.staleness_threshold = ratio;
self
}
#[must_use]
pub fn age_cutoff(mut self, ratio: f32) -> Self {
self.age_cutoff = ratio;
self
}
}
pub struct Config {
#[doc(hidden)]
pub path: PathBuf,
#[doc(hidden)]
pub cache: Arc<Cache>,
#[doc(hidden)]
pub descriptor_table: Option<Arc<DescriptorTable>>,
pub level_count: u8,
pub data_block_compression_policy: CompressionPolicy,
pub index_block_compression_policy: CompressionPolicy,
pub data_block_restart_interval_policy: RestartIntervalPolicy,
pub index_block_restart_interval_policy: RestartIntervalPolicy,
pub data_block_size_policy: BlockSizePolicy,
pub index_block_pinning_policy: PinningPolicy,
pub filter_block_pinning_policy: PinningPolicy,
pub top_level_index_block_pinning_policy: PinningPolicy,
pub top_level_filter_block_pinning_policy: PinningPolicy,
pub data_block_hash_ratio_policy: HashRatioPolicy,
pub index_block_partitioning_policy: PartitioningPolicy,
pub filter_block_partitioning_policy: PartitioningPolicy,
pub index_block_partition_size_policy: BlockSizePolicy,
pub filter_block_partition_size_policy: BlockSizePolicy,
pub(crate) expect_point_read_hits: bool,
pub filter_policy: FilterPolicy,
pub compaction_filter_factory: Option<Arc<dyn Factory>>,
#[doc(hidden)]
pub kv_separation_opts: Option<KvSeparationOptions>,
pub(crate) seqno: SequenceNumberCounter,
pub(crate) visible_seqno: SequenceNumberCounter,
}
impl Default for Config {
fn default() -> Self {
Self {
path: absolute_path(Path::new(DEFAULT_FILE_FOLDER)),
descriptor_table: Some(Arc::new(DescriptorTable::new(256))),
seqno: SequenceNumberCounter::default(),
visible_seqno: SequenceNumberCounter::default(),
cache: Arc::new(Cache::with_capacity_bytes(
16 * 1_024 * 1_024,
)),
data_block_restart_interval_policy: RestartIntervalPolicy::all(16),
index_block_restart_interval_policy: RestartIntervalPolicy::all(1),
level_count: DEFAULT_LEVEL_COUNT,
data_block_size_policy: BlockSizePolicy::all(4_096),
index_block_pinning_policy: PinningPolicy::new([true, true, false]),
filter_block_pinning_policy: PinningPolicy::new([true, false]),
top_level_index_block_pinning_policy: PinningPolicy::all(true), top_level_filter_block_pinning_policy: PinningPolicy::all(true),
index_block_partitioning_policy: PinningPolicy::new([false, false, false, true]),
filter_block_partitioning_policy: PinningPolicy::new([false, false, false, true]),
index_block_partition_size_policy: BlockSizePolicy::all(4_096), filter_block_partition_size_policy: BlockSizePolicy::all(4_096),
data_block_compression_policy: ({
#[cfg(feature = "lz4")]
let c = CompressionPolicy::new([CompressionType::None, CompressionType::Lz4]);
#[cfg(not(feature = "lz4"))]
let c = CompressionPolicy::new([CompressionType::None]);
c
}),
index_block_compression_policy: CompressionPolicy::all(CompressionType::None),
data_block_hash_ratio_policy: HashRatioPolicy::all(0.0),
filter_policy: FilterPolicy::all(FilterPolicyEntry::Bloom(
BloomConstructionPolicy::BitsPerKey(10.0),
)),
compaction_filter_factory: None,
expect_point_read_hits: false,
kv_separation_opts: None,
}
}
}
impl Config {
pub fn new<P: AsRef<Path>>(
path: P,
seqno: SequenceNumberCounter,
visible_seqno: SequenceNumberCounter,
) -> Self {
Self {
path: absolute_path(path.as_ref()),
seqno,
visible_seqno,
..Default::default()
}
}
#[must_use]
pub fn use_cache(mut self, cache: Arc<Cache>) -> Self {
self.cache = cache;
self
}
#[must_use]
pub fn use_descriptor_table(mut self, descriptor_table: Option<Arc<DescriptorTable>>) -> Self {
self.descriptor_table = descriptor_table;
self
}
#[must_use]
pub fn expect_point_read_hits(mut self, b: bool) -> Self {
self.expect_point_read_hits = b;
self
}
#[must_use]
pub fn filter_block_partitioning_policy(mut self, policy: PinningPolicy) -> Self {
self.filter_block_partitioning_policy = policy;
self
}
#[must_use]
pub fn index_block_partitioning_policy(mut self, policy: PinningPolicy) -> Self {
self.index_block_partitioning_policy = policy;
self
}
#[must_use]
pub fn filter_block_pinning_policy(mut self, policy: PinningPolicy) -> Self {
self.filter_block_pinning_policy = policy;
self
}
#[must_use]
pub fn index_block_pinning_policy(mut self, policy: PinningPolicy) -> Self {
self.index_block_pinning_policy = policy;
self
}
#[must_use]
pub fn data_block_restart_interval_policy(mut self, policy: RestartIntervalPolicy) -> Self {
self.data_block_restart_interval_policy = policy;
self
}
#[must_use]
pub fn filter_policy(mut self, policy: FilterPolicy) -> Self {
self.filter_policy = policy;
self
}
#[must_use]
pub fn data_block_compression_policy(mut self, policy: CompressionPolicy) -> Self {
self.data_block_compression_policy = policy;
self
}
#[must_use]
pub fn index_block_compression_policy(mut self, policy: CompressionPolicy) -> Self {
self.index_block_compression_policy = policy;
self
}
#[must_use]
pub fn data_block_size_policy(mut self, policy: BlockSizePolicy) -> Self {
self.data_block_size_policy = policy;
self
}
#[must_use]
pub fn data_block_hash_ratio_policy(mut self, policy: HashRatioPolicy) -> Self {
self.data_block_hash_ratio_policy = policy;
self
}
#[must_use]
pub fn with_kv_separation(mut self, opts: Option<KvSeparationOptions>) -> Self {
self.kv_separation_opts = opts;
self
}
#[must_use]
pub fn with_compaction_filter_factory(mut self, factory: Option<Arc<dyn Factory>>) -> Self {
self.compaction_filter_factory = factory;
self
}
pub fn open(self) -> crate::Result<AnyTree> {
Ok(if self.kv_separation_opts.is_some() {
AnyTree::Blob(BlobTree::open(self)?)
} else {
AnyTree::Standard(Tree::open(self)?)
})
}
}