dbeel 0.1.0

dbeel is a fast and minimal distributed thread-per-core document database.
Documentation
use std::{rc::Rc, time::Duration};

use glommio::{
    executor, spawn_local_into, timer::sleep, Latency, Shares, Task,
};
use log::{error, info};
use rand::{seq::IteratorRandom, thread_rng};

use crate::{
    error::Result,
    gossip::GossipEvent,
    messages::{ShardEvent, ShardMessage},
    remote_shard_connection::RemoteShardConnection,
    shards::MyShard,
};

async fn run_failure_detector(my_shard: Rc<MyShard>) -> Result<()> {
    let interval =
        Duration::from_millis(my_shard.args.failure_detection_interval);
    let mut should_sleep = true;

    loop {
        if should_sleep {
            sleep(interval).await;
        }
        should_sleep = true;

        let mut rng = thread_rng();
        let node = if let Some(node) = my_shard
            .nodes
            .borrow()
            .iter()
            .map(|(_, node)| node)
            .filter(|node| !node.ids.is_empty())
            .choose(&mut rng)
        {
            node.clone()
        } else {
            continue;
        };

        sleep(interval).await;
        should_sleep = false;

        let connection = RemoteShardConnection::from_args(
            format!(
                "{}:{}",
                node.ip,
                node.ids
                    .iter()
                    .map(|id| node.remote_shard_base_port + id)
                    .choose(&mut rng)
                    .unwrap()
            ),
            &my_shard.args,
        );

        if let Err(e) = connection.ping().await {
            my_shard.clone().handle_dead_node(&node.name).await;

            info!(
                "Notifying cluster that we failed to ping '{}': {}",
                connection.address, e
            );

            let gossip_event = GossipEvent::Dead(node.name);

            if let Err(e) = my_shard
                .clone()
                .broadcast_message_to_local_shards(&ShardMessage::Event(
                    ShardEvent::Gossip(gossip_event.clone()),
                ))
                .await
            {
                error!(
                    "Failed to broadcast to local shards, node death event: {}",
                    e
                );
            }

            if let Err(e) = my_shard.gossip(gossip_event).await {
                error!("Failed to gossip node death event: {}", e);
            }
        }
    }
}

pub fn spawn_failure_detector_task(my_shard: Rc<MyShard>) -> Task<Result<()>> {
    let shares = my_shard.args.background_tasks_shares.into();
    spawn_local_into(
        async move {
            let result = run_failure_detector(my_shard).await;
            if let Err(e) = &result {
                error!("Error starting failure detector: {}", e);
            }
            result
        },
        executor().create_task_queue(
            Shares::Static(shares),
            Latency::NotImportant,
            "failure-detector",
        ),
    )
    .unwrap()
}