armdb 0.12.0

sharded bitcask key-value storage optimized for NVMe
Documentation
use crate::codec::Codec;
use crate::error::DbResult;
use crate::hook::TypedWriteHook;
use crate::lsm::LsmTree;
use crate::{CollectionMeta, Key};

/// Mutation view handed to an `LsmTree::atomic` closure. Every call runs under
/// the already-held stripe lock, so writes use the tree's lock-free helpers.
///
/// **Concurrency**: all methods on `LsmShard` run while the stripe lock for the
/// `atomic` shard key is held. Operations on *other* stripes are not serialized
/// against concurrent writers on those stripes — see [`LsmTree::atomic`] for the
/// full shard-scoped concurrency contract.
pub struct LsmShard<'tree, K, T, C, H>
where
    K: Key + Ord,
    T: CollectionMeta<SelfId = K> + Send + Sync,
    C: Codec<T> + Clone,
    H: TypedWriteHook<K, T>,
{
    pub(crate) tree: &'tree LsmTree<K, T, C, H>,
    pub(crate) stripe: usize,
}

impl<K, T, C, H> LsmShard<'_, K, T, C, H>
where
    K: Key + Ord,
    T: CollectionMeta<SelfId = K> + Send + Sync,
    C: Codec<T> + Clone,
    H: TypedWriteHook<K, T>,
{
    fn check_stripe(&self, key: &K) -> DbResult<()> {
        if self.tree.shard_for(key) != self.stripe {
            return Err(crate::error::DbError::ShardMismatch);
        }
        Ok(())
    }

    /// Read `key` under the held stripe lock. Returns `None` if missing or on decode error.
    ///
    /// An I/O error also reads as `None` — see [`try_get`](Self::try_get).
    pub fn get(&self, key: &K) -> Option<T> {
        self.tree.get(key)
    }

    /// Strict read under the held stripe lock: an I/O or decode error
    /// propagates instead of collapsing to `None`.
    pub fn try_get(&self, key: &K) -> DbResult<Option<T>> {
        self.tree.try_get(key)
    }

    /// Read `key` under the held stripe lock. Returns `DbError::KeyNotFound` if absent.
    pub fn get_or_err(&self, key: &K) -> DbResult<T> {
        self.tree.get_or_err(key)
    }

    /// Return `true` if `key` exists, under the held stripe lock.
    ///
    /// An I/O error reads as `false` — see [`try_contains`](Self::try_contains).
    pub fn contains(&self, key: &K) -> bool {
        self.tree.contains(key)
    }

    /// Strict existence check under the held stripe lock: an I/O error
    /// propagates instead of reading as "absent".
    pub fn try_contains(&self, key: &K) -> DbResult<bool> {
        self.tree.try_contains(key)
    }

    /// Insert or overwrite `key` with `value` under the held stripe lock.
    pub fn put(&mut self, key: &K, value: &T) -> DbResult<()> {
        self.check_stripe(key)?;
        self.tree.put_locked(key, value)
    }

    /// Insert `key` only if absent under the held stripe lock. Returns `DbError::KeyExists` otherwise.
    pub fn insert(&mut self, key: &K, value: &T) -> DbResult<()> {
        self.check_stripe(key)?;
        self.tree.insert_locked(key, value)
    }

    /// Delete `key` under the held stripe lock. Returns `true` if the key existed.
    pub fn delete(&mut self, key: &K) -> DbResult<bool> {
        self.check_stripe(key)?;
        self.tree.delete_locked(key)
    }

    /// Decide-and-write within the held stripe. The closure must be short — stripe lock is held.
    pub fn upsert<U: crate::IntoUpsert<T>>(
        &mut self,
        key: &K,
        f: impl FnOnce(Option<&T>) -> U,
    ) -> crate::DbResult<U::Out> {
        self.check_stripe(key)?;
        crate::upsert::upsert_via_held(self, key, f)
    }
}