use super::QueuedLookup;
use std::time::Duration;
use tokio::sync::{mpsc, watch};
pub struct LookupQueue {
lookup_rx: mpsc::Receiver<QueuedLookup>,
re_enqueue_rx: mpsc::UnboundedReceiver<QueuedLookup>,
max_batch_size: usize,
batch_timeout: Duration,
cluster_rx: watch::Receiver<u64>,
}
impl LookupQueue {
pub fn new(
queue_size: usize,
max_batch_size: usize,
batch_timeout_ms: u64,
cluster_rx: watch::Receiver<u64>,
) -> (
Self,
mpsc::Sender<QueuedLookup>,
mpsc::UnboundedSender<QueuedLookup>,
) {
let (lookup_tx, lookup_rx) = mpsc::channel(queue_size);
let (re_enqueue_tx, re_enqueue_rx) = mpsc::unbounded_channel();
let queue = Self {
lookup_rx,
re_enqueue_rx,
max_batch_size,
batch_timeout: Duration::from_millis(batch_timeout_ms),
cluster_rx,
};
(queue, lookup_tx, re_enqueue_tx)
}
pub async fn drain(&mut self) -> Vec<QueuedLookup> {
let mut lookups = Vec::with_capacity(self.max_batch_size);
let deadline = tokio::time::Instant::now() + self.batch_timeout;
loop {
let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
if remaining.is_zero() {
break;
}
while lookups.len() < self.max_batch_size {
match self.re_enqueue_rx.try_recv() {
Ok(lookup) => lookups.push(lookup),
Err(_) => break,
}
}
if lookups.len() >= self.max_batch_size {
break;
}
let sleep = tokio::time::sleep(remaining);
tokio::select! {
biased;
maybe = self.lookup_rx.recv() => {
match maybe {
Some(lookup) => {
lookups.push(lookup);
while lookups.len() < self.max_batch_size {
match self.lookup_rx.try_recv() {
Ok(lookup) => lookups.push(lookup),
Err(_) => break,
}
}
}
None => break,
}
}
_ = self.cluster_rx.changed() => {
if !lookups.is_empty() {
break;
}
}
_ = sleep => break,
}
if lookups.len() >= self.max_batch_size {
break;
}
}
lookups
}
pub fn drain_all(&mut self) -> Vec<QueuedLookup> {
let mut lookups = Vec::new();
while let Ok(lookup) = self.re_enqueue_rx.try_recv() {
lookups.push(lookup);
}
while let Ok(lookup) = self.lookup_rx.try_recv() {
lookups.push(lookup);
}
lookups
}
pub fn has_undrained(&self) -> bool {
!self.lookup_rx.is_empty() || !self.re_enqueue_rx.is_empty()
}
}