chiralmap 0.1.2

Left-Right map using IndexMap
Documentation
//! Implementing left-right for indexmap: SingleMap

#[cfg(feature = "metrics")]
use metrics::{Counter, Gauge, counter, gauge};
#[cfg(feature = "metrics")]
use std::sync::Arc;
#[cfg(feature = "metrics")]
use std::sync::atomic::{AtomicU64, Ordering};

use indexmap::IndexMap;
use left_right::Absorb;
use std::hash::Hash;

use crate::{ChiralMapConfig, chiral_map::Operation};

/// A hash map that supports the left-right algorythm for fast reads.
/// This will duplicate the map and its data so it will take double memory.
/// The advantage is that reads are non blocking while writes need to decide
/// when to flush the data.
/// This also makes this map eventually consistent.
pub(crate) struct SingleMap<K, V> {
    map: IndexMap<K, V>,

    #[cfg(feature = "metrics")]
    id: String,
    #[cfg(feature = "metrics")]
    instances: Arc<AtomicU64>,
    #[cfg(feature = "metrics")]
    met_inserts: Arc<Counter>,
    #[cfg(feature = "metrics")]
    met_deletions: Arc<Counter>,
    #[cfg(feature = "metrics")]
    met_size: Arc<Gauge>,
    #[cfg(feature = "metrics")]
    met_capacity: Arc<Gauge>,
}

impl<K, V> Absorb<Operation<K, V>> for SingleMap<K, V>
where
    K: Hash + Eq + Clone,
    V: Clone,
{
    fn absorb_first(&mut self, operation: &mut Operation<K, V>, _other: &Self) {
        self.step(operation.clone());
    }

    fn absorb_second(&mut self, operation: Operation<K, V>, _other: &Self) {
        self.step(operation);
    }

    fn sync_with(&mut self, first: &Self) {
        first.map.iter().for_each(|(k, v)| {
            self.map.insert(k.clone(), v.clone());
        });
    }
}

impl<K, V> SingleMap<K, V>
where
    K: Hash + Eq + Clone,
    V: Clone,
{
    pub(crate) fn step(&mut self, operation: Operation<K, V>) {
        match operation {
            Operation::Insert(k, v) => {
                self.map.insert(k, v);

                #[cfg(feature = "metrics")]
                self.met_inserts.increment(1);
                #[cfg(feature = "metrics")]
                self.met_size.set(self.map.len() as f64);
                #[cfg(feature = "metrics")]
                self.met_capacity.set(self.map.capacity() as f64);
            }
            Operation::Delete(k) => {
                self.map.swap_remove(&k);

                #[cfg(feature = "metrics")]
                self.met_deletions.increment(1);
                #[cfg(feature = "metrics")]
                self.met_size.set(self.map.len() as f64);
                #[cfg(feature = "metrics")]
                self.met_capacity.set(self.map.capacity() as f64);
            }
        };
    }
}

impl<K, V> SingleMap<K, V>
where
    K: Hash + Eq + Clone,
    V: Clone,
{
    pub(crate) fn new(config: &ChiralMapConfig) -> Self {
        let map = IndexMap::<K, V>::with_capacity(config.initial_capacity);

        #[cfg(feature = "metrics")]
        let instances = Arc::new(AtomicU64::from(1));
        #[cfg(feature = "metrics")]
        let instance = 1;
        #[cfg(feature = "metrics")]
        let met_inserts = counter!("chiral_map_inserts", "id" => config.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        let met_deletions = counter!("chiral_map_deletions", "id" => config.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        let met_size = gauge!("chiral_map_size", "id" => config.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        let met_capacity = gauge!("chiral_map_capacity", "id" => config.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        met_capacity.set(map.capacity() as f64);
        Self {
            map,
            #[cfg(feature = "metrics")]
            id: config.id.clone(),
            #[cfg(feature = "metrics")]
            instances,
            #[cfg(feature = "metrics")]
            met_inserts: met_inserts.into(),
            #[cfg(feature = "metrics")]
            met_deletions: met_deletions.into(),
            #[cfg(feature = "metrics")]
            met_size: met_size.into(),
            #[cfg(feature = "metrics")]
            met_capacity: met_capacity.into(),
        }
    }

    pub(crate) fn get(&self, k: &K) -> Option<&V> {
        self.map.get(k)
    }
}

impl<K, V> Clone for SingleMap<K, V>
where
    K: Hash + Eq + Clone,
    V: Clone,
{
    fn clone(&self) -> Self {
        #[cfg(feature = "metrics")]
        let instance = self.instances.fetch_add(1, Ordering::Relaxed) + 1;
        #[cfg(feature = "metrics")]
        let met_inserts = counter!("chiral_map_inserts", "id" => self.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        let met_deletions = counter!("chiral_map_deletions", "id" => self.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        let met_size =
            gauge!("chiral_map_size", "id" => self.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        let met_capacity = gauge!("chiral_map_capacity", "id" => self.id.clone(), "instance" => instance.to_string());
        #[cfg(feature = "metrics")]
        met_capacity.set(self.map.capacity() as f64);
        Self {
            map: self.map.clone(),
            #[cfg(feature = "metrics")]
            id: self.id.clone(),
            #[cfg(feature = "metrics")]
            instances: self.instances.clone(),
            #[cfg(feature = "metrics")]
            met_inserts: met_inserts.into(),
            #[cfg(feature = "metrics")]
            met_deletions: met_deletions.into(),
            #[cfg(feature = "metrics")]
            met_size: met_size.into(),
            #[cfg(feature = "metrics")]
            met_capacity: met_capacity.into(),
        }
    }
}