summaryrefslogtreecommitdiffstats
path: root/src/swarm/peer/lanes.rs
blob: 88639ea0ecca46039e74710c5297d9805a658390 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//! Sync lanes: inbound sync messages are applied off the consumer, so one slow apply (a
//! large delta takes seconds) doesn't hold up keepalives, HELLOs and blob chunks behind
//! it. Each sender's messages go to the same lane, in arrival order; a few lanes are
//! enough, since applying takes the document's write lock anyway.

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

use tokio::sync::mpsc::{self, error::TrySendError};
use tracing::{debug, warn};

use crate::state::AppState;
use crate::swarm::sync::handle_sync;
use crate::transport::SyncEnvelope;

const LANES: usize = 4;
/// Sync messages waiting per lane. A full lane drops what arrives: its sender gets no
/// answer and resends after its reply timeout, by which time the backlog is gone.
const QUEUED_PER_LANE: usize = 256;

pub(crate) struct SyncLanes {
    lanes: Vec<mpsc::Sender<(String, SyncEnvelope)>>,
}

impl SyncLanes {
    /// Starts a task per lane.
    pub(crate) fn start(state: &AppState) -> Self {
        let lanes = (0..LANES)
            .map(|_| {
                let (tx, mut rx) = mpsc::channel::<(String, SyncEnvelope)>(QUEUED_PER_LANE);
                let state = state.clone();
                tokio::spawn(async move {
                    while let Some((peer, envelope)) = rx.recv().await {
                        if let Err(error) = handle_sync(&state, &peer, envelope).await {
                            warn!(peer = %peer, error = %format!("{error:#}"), "Failed to process sync message");
                        }
                    }
                });
                tx
            })
            .collect();
        Self { lanes }
    }

    /// Queues `envelope` from `peer` on its lane.
    pub(crate) fn enqueue(&self, peer: &str, envelope: SyncEnvelope) {
        let mut hasher = DefaultHasher::new();
        peer.hash(&mut hasher);
        let lane = &self.lanes[hasher.finish() as usize % LANES];
        match lane.try_send((peer.to_owned(), envelope)) {
            Ok(()) => {}
            Err(TrySendError::Full(_)) => {
                debug!(peer = %peer, "Sync lane full; dropping the message (its sender resends)")
            }
            Err(TrySendError::Closed(_)) => {
                warn!(peer = %peer, "Sync lane stopped; dropping the message")
            }
        }
    }
}