net-pool 0.1.3

A set of types for network connection pool.
Documentation
use crate::backend::{Address, BackendState};
use crate::utils::bytes_to_hash_code;
use std::collections::{BTreeMap, HashSet};
use std::sync::RwLock;

/// 一致性hash负载策略

pub struct CHStrategy {
    /// usize:虚拟的后端个数
    /// BTreeMap<u64, BackendState>: 排序后用来表示环形
    /// HashSet<BackendState>: 真实的节点
    inner: RwLock<(usize, BTreeMap<u64, BackendState>, HashSet<BackendState>)>,
}

impl CHStrategy {
    pub fn new(virtual_backend_count: usize) -> Self {
        if virtual_backend_count == 0 {
            return CHStrategy::default();
        }
        CHStrategy {
            inner: RwLock::new((virtual_backend_count, BTreeMap::new(), HashSet::new())),
        }
    }

    fn sub_address(id: usize, addr: &Address) -> String {
        let mut flag = match addr {
            Address::Ori(ori) => ori.clone(),
            Address::Addr(addr) => crate::utils::socketaddr_to_ip_string(addr),
        };

        flag.push('#');
        flag.push_str(&id.to_string());
        flag
    }
}

impl Default for CHStrategy {
    fn default() -> Self {
        CHStrategy {
            inner: RwLock::new((64, BTreeMap::new(), HashSet::new())),
        }
    }
}

impl super::LbStrategy for CHStrategy {
    fn strategy(&self) -> super::Strategy {
        super::Strategy::ConsistentHash
    }

    fn contain(&self, hash_code: u64) -> bool {
        let g = self.inner.read().unwrap();
        g.2.iter()
            .find(|inner| inner.hash_code() == hash_code)
            .is_some()
    }

    fn add_backend(&self, addr: Address) {
        let mut g = self.inner.write().unwrap();
        let bs = BackendState::new(addr.clone());

        for id in 0..g.0 {
            let address = Self::sub_address(id, &addr);
            let code = bytes_to_hash_code(address.as_bytes());
            g.1.insert(code, bs.clone());
        }

        g.2.insert(bs);
    }

    fn remove_backend(&self, addr: &Address) -> bool {
        let mut g = self.inner.write().unwrap();

        for id in 0..g.0 {
            let address = Self::sub_address(id, addr);
            let code = bytes_to_hash_code(address.as_bytes());
            if let None = g.1.remove(&code) {
                return false;
            }
        }

        g.2.remove(&BackendState::new(addr.clone()))
    }

    fn get_backend(&self, key: &str) -> Option<BackendState> {
        let g = self.inner.read().unwrap();
        if g.1.is_empty() {
            return None;
        }

        let code = bytes_to_hash_code(key.as_bytes());

        let o =
            g.1.iter()
                .find(|(key, _)| if code <= **key { true } else { false });

        if o.is_none() {
            g.1.iter().min().map(|(_, bs)| bs.clone())
        } else {
            o.map(|(_, bs)| bs.clone())
        }
    }

    fn get_backends(&self) -> Vec<BackendState> {
        let g = self.inner.read().unwrap();
        let bss: Vec<BackendState> = g.1.iter().map(|kv| kv.1.clone()).collect();
        bss
    }

    fn get_origin_backends(&self) -> Vec<BackendState> {
        let g = self.inner.read().unwrap();
        g.2.iter().map(|bs| bs.clone()).collect()
    }
}