use rustc_hash::FxHashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::mpsc;
use std::time::{Duration, Instant};
use melin_pipeline::ring;
use melin_pipeline::spsc;
use melin_pipeline::wait::WaitStrategy;
use crate::durability_gate::{DurabilityGate, GateInputs, GateOutcome};
use crate::halt::RefusalQueue;
use melin_app::Application;
use melin_app::amortized_timer::AmortizedTimer;
use melin_transport_core::DurableWireSeqCursor;
use melin_transport_core::pipeline::{OutputSlot, StageUtilization};
use melin_wire_protocol::control::TransportResponse;
use melin_wire_protocol::control_codec;
use crate::response_frame::{EncodeBuf, MAX_APP_FRAME, encode_slot_payload, frame_app_response};
#[cfg(feature = "latency-trace")]
use melin_transport_core::trace;
const MAX_BATCH: usize = 1024;
const MAX_BATCH_END_FRAME: usize = 8;
const MAX_TX_FRAME: usize = MAX_APP_FRAME + MAX_BATCH_END_FRAME;
#[derive(Clone, Copy)]
pub struct TxFrame {
pub connection_id: u64,
pub len: u16,
pub data: [u8; MAX_TX_FRAME],
}
impl Default for TxFrame {
fn default() -> Self {
TxFrame {
connection_id: 0,
len: 0,
data: [0u8; MAX_TX_FRAME],
}
}
}
impl TxFrame {
pub fn as_bytes(&self) -> &[u8] {
&self.data[..self.len as usize]
}
}
pub enum ControlEvent {
Connected { connection_id: u64 },
Disconnected { connection_id: u64 },
}
#[allow(clippy::too_many_arguments)]
pub fn run<A: Application>(
mut consumer: ring::Consumer<OutputSlot<A::Report, A::QueryResponse>>,
control_rx: mpsc::Receiver<ControlEvent>,
journal_persisted_wire_seq: DurableWireSeqCursor,
ack_policy: Arc<std::sync::atomic::AtomicU8>,
replication_metrics: Option<Arc<crate::replication::ReplicationMetrics>>,
replica_active: Option<[Arc<AtomicBool>; 2]>,
shutdown: &AtomicBool,
heartbeat_interval: Option<Duration>,
active_connections: Arc<AtomicU64>,
mut tx_producers: Vec<spsc::Producer<TxFrame>>,
utilization: Arc<StageUtilization>,
wait: WaitStrategy,
encoder: crate::response::ResponseEncoderArc<A>,
mut refusals: RefusalQueue<A::Report>,
) {
let mut gate = DurabilityGate::new(
GateInputs {
journal_persisted_wire_seq,
ack_policy,
replication_metrics,
replica_active,
utilization: Arc::clone(&utilization),
wait,
},
"dpdk response",
);
let mut connections: FxHashMap<u64, ConnectionHeartbeat> =
FxHashMap::with_capacity_and_hasher(256, Default::default());
let mut encode_buf: EncodeBuf = [0u8; MAX_APP_FRAME];
let batch_end_wire_frame = {
let mut buf = [0u8; MAX_BATCH_END_FRAME];
let written =
control_codec::encode_transport_response(&TransportResponse::BatchEnd, &mut buf)
.expect("BatchEnd encodes");
buf[..written].to_vec()
};
let mut heartbeat_frame = [0u8; 8];
let heartbeat_len = control_codec::encode_transport_response(
&TransportResponse::Heartbeat,
&mut heartbeat_frame,
)
.expect("heartbeat encodes");
let mut heartbeat_timer = AmortizedTimer::new();
let mut waiter = wait.waiter();
let mut busy_count: u64 = 0;
let mut idle_count: u64 = 0;
let mut last_input_seq = u64::MAX;
#[cfg(feature = "latency-trace")]
let mut spsc_rec =
trace::register_stage("response: SPSC wakeup (matching publish → response consume)");
#[cfg(feature = "latency-trace")]
let mut dispatch_rec = trace::register_stage("response: dispatch (consume → SPSC publish)");
#[cfg(feature = "latency-trace")]
let mut server_e2e_rec =
trace::register_stage("server e2e (reader recv → response SPSC publish)");
#[cfg(feature = "tick-to-trade")]
let mut journal_wait_rec =
trace::register_stage("response: journal-wait (match_complete → journal cursor crossed)");
#[cfg(feature = "tick-to-trade")]
let mut replica_wait_rec = trace::register_stage(
"response: replica-wait (match_complete → replication cursor crossed)",
);
#[cfg(feature = "tick-to-trade")]
let mut encode_rec = trace::register_stage("response: encode (per-kind wire encoding)");
#[cfg(feature = "latency-trace")]
let mut last_stats_flush = Instant::now();
'run: loop {
gate.observe_policy_swap();
if shutdown.load(Ordering::Relaxed) {
utilization.busy.store(busy_count, Ordering::Relaxed);
utilization.idle.store(idle_count, Ordering::Relaxed);
return;
}
let idle_refusal_bound = refusals.idle_bound();
let slots = consumer.read_contiguous(MAX_BATCH);
let refusals_waiting = refusals.sync();
process_control_events(
&control_rx,
&mut connections,
&active_connections,
Instant::now,
);
if slots.is_empty() {
if let Some(bound) = idle_refusal_bound {
let now = Instant::now();
refusals.release(bound, |refusal| {
push_refusal(
refusal,
&*encoder,
&mut connections,
&mut tx_producers,
&mut encode_buf,
&batch_end_wire_frame,
now,
);
});
}
idle_count += 1;
if idle_count.is_multiple_of(1024) {
utilization.busy.store(busy_count, Ordering::Relaxed);
utilization.idle.store(idle_count, Ordering::Relaxed);
}
if let Some(interval) = heartbeat_interval
&& heartbeat_timer
.tick(Duration::from_secs(1), waiter.spinning())
.is_some()
{
let now = Instant::now();
let mut failed: Vec<u64> = Vec::new();
for (&conn_id, state) in connections.iter_mut() {
if now.duration_since(state.last_send) >= interval {
let mut frame = TxFrame {
connection_id: conn_id,
len: heartbeat_len as u16,
..Default::default()
};
frame.data[..heartbeat_len]
.copy_from_slice(&heartbeat_frame[..heartbeat_len]);
let tid = (conn_id >> 56) as usize % tx_producers.len();
if tx_producers[tid].try_publish(frame).is_err() {
failed.push(conn_id);
continue;
}
state.last_send = now;
}
}
for conn_id in failed {
connections.remove(&conn_id);
active_connections.fetch_sub(1, Ordering::Relaxed);
}
}
{
let now_ts = Instant::now();
gate.idle_recheck(now_ts);
#[cfg(feature = "latency-trace")]
if now_ts.duration_since(last_stats_flush) >= trace::IDLE_FLUSH_INTERVAL {
last_stats_flush = now_ts;
spsc_rec.flush();
dispatch_rec.flush();
server_e2e_rec.flush();
#[cfg(feature = "tick-to-trade")]
{
journal_wait_rec.flush();
replica_wait_rec.flush();
encode_rec.flush();
}
}
}
waiter.idle();
continue;
}
waiter.reset();
busy_count += 1;
#[cfg(feature = "latency-trace")]
let consume_ts = trace::mono_trace_ns();
let batch_now = Instant::now();
for slot in slots {
#[cfg(feature = "latency-trace")]
spsc_rec.record_elapsed(slot.match_complete_ts, consume_ts);
let first_of_event = slot.input_seq != last_input_seq;
last_input_seq = slot.input_seq;
if refusals_waiting && first_of_event {
refusals.release(slot.input_seq, |refusal| {
push_refusal(
refusal,
&*encoder,
&mut connections,
&mut tx_producers,
&mut encode_buf,
&batch_end_wire_frame,
batch_now,
);
});
}
#[cfg(feature = "tick-to-trade")]
let mut gate_tracker = crate::response::GateCrossTracker::new(slot.wire_seq);
if gate.needs_wait(slot)
&& let GateOutcome::Shutdown = gate.wait_durable(
slot.wire_seq,
shutdown,
#[cfg(feature = "tick-to-trade")]
&mut gate_tracker,
)
{
continue 'run;
}
#[cfg(feature = "tick-to-trade")]
if let Some(ts) = gate_tracker.journal_crossed() {
journal_wait_rec.record_elapsed(slot.match_complete_ts, ts);
}
#[cfg(feature = "tick-to-trade")]
if let Some(ts) = gate_tracker.replica_crossed() {
replica_wait_rec.record_elapsed(slot.match_complete_ts, ts);
}
let Some(conn_state) = connections.get_mut(&slot.connection_id) else {
if slot.connection_id != 0 {
tracing::debug!(
connection_id = slot.connection_id,
wire_seq = slot.wire_seq,
"reply dropped: connection not registered"
);
}
continue;
};
let tid = (slot.connection_id >> 56) as usize % tx_producers.len();
let conn_id = slot.connection_id;
let payload_result = encode_slot_payload(&slot.payload, &*encoder, &mut encode_buf);
let trailer: &[u8] = if slot.is_last_in_request {
&batch_end_wire_frame
} else {
&[]
};
#[cfg(feature = "tick-to-trade")]
let encode_start = trace::mono_trace_ns();
push_frame(
payload_result,
conn_id,
&mut tx_producers[tid],
&encode_buf,
trailer,
);
#[cfg(feature = "tick-to-trade")]
encode_rec.record_elapsed(encode_start, trace::mono_trace_ns());
tx_producers[tid].flush();
#[cfg(feature = "latency-trace")]
if slot.is_last_in_request {
server_e2e_rec.record_elapsed(slot.recv_ts, trace::mono_trace_ns());
}
conn_state.last_send = batch_now;
}
consumer.commit();
gate.after_batch();
#[cfg(feature = "latency-trace")]
dispatch_rec.record_elapsed(consume_ts, trace::mono_trace_ns());
}
}
#[inline]
fn push_frame(
payload: Option<Result<usize, &'static str>>,
conn_id: u64,
tx: &mut spsc::Producer<TxFrame>,
encode_buf: &[u8],
trailer: &[u8],
) {
let written = match payload {
Some(Ok(n)) => n,
Some(Err(reason)) => {
tracing::error!(connection_id = conn_id, reason, "encode error");
0
}
None => 0,
};
if written == 0 && trailer.is_empty() {
return;
}
let total = written + trailer.len();
debug_assert!(total <= MAX_TX_FRAME);
let len = total as u16;
tx.push_with(|frame| {
frame.connection_id = conn_id;
frame.len = len;
frame.data[..written].copy_from_slice(&encode_buf[..written]);
frame.data[written..total].copy_from_slice(trailer);
});
}
fn push_refusal<R: Copy, Q: Copy>(
refusal: crate::halt::Refusal<R>,
encoder: &dyn melin_app::encoder::ResponseEncoder<Report = R, Query = Q>,
connections: &mut FxHashMap<u64, ConnectionHeartbeat>,
tx_producers: &mut [spsc::Producer<TxFrame>],
encode_buf: &mut EncodeBuf,
batch_end_wire_frame: &[u8],
now: Instant,
) {
let conn_id = refusal.connection_id;
let Some(conn_state) = connections.get_mut(&conn_id) else {
tracing::debug!(
connection_id = conn_id,
"refusal dropped: connection not registered"
);
return;
};
let tid = (conn_id >> 56) as usize % tx_producers.len();
let payload = frame_app_response(encode_buf, |body| {
encoder.encode_report(&refusal.report, body)
});
push_frame(
Some(payload),
conn_id,
&mut tx_producers[tid],
encode_buf,
batch_end_wire_frame,
);
tx_producers[tid].flush();
conn_state.last_send = now;
}
struct ConnectionHeartbeat {
last_send: Instant,
}
fn process_control_events(
control_rx: &mpsc::Receiver<ControlEvent>,
connections: &mut FxHashMap<u64, ConnectionHeartbeat>,
active_connections: &AtomicU64,
clock: impl Fn() -> Instant,
) {
let mut now = None;
while let Ok(event) = control_rx.try_recv() {
match event {
ControlEvent::Connected { connection_id } => {
let last_send = *now.get_or_insert_with(&clock);
connections.insert(connection_id, ConnectionHeartbeat { last_send });
}
ControlEvent::Disconnected { connection_id } => {
if connections.remove(&connection_id).is_some() {
active_connections.fetch_sub(1, Ordering::Relaxed);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::AtomicU64;
use std::sync::mpsc;
use std::time::Instant;
#[test]
fn active_connections_single_lifecycle() {
let counter = AtomicU64::new(0);
let (tx, rx) = mpsc::channel();
let mut connections = FxHashMap::default();
let now = Instant::now();
counter.fetch_add(1, Ordering::Relaxed);
tx.send(ControlEvent::Connected { connection_id: 1 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, || now);
assert_eq!(counter.load(Ordering::Relaxed), 1);
assert_eq!(connections.len(), 1);
tx.send(ControlEvent::Disconnected { connection_id: 1 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, || now);
assert_eq!(counter.load(Ordering::Relaxed), 0);
assert_eq!(connections.len(), 0);
}
#[test]
fn disconnect_unknown_connection_no_decrement() {
let counter = AtomicU64::new(0);
let (tx, rx) = mpsc::channel();
let mut connections = FxHashMap::default();
let now = Instant::now();
tx.send(ControlEvent::Disconnected { connection_id: 999 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, || now);
assert_eq!(counter.load(Ordering::Relaxed), 0);
}
#[test]
fn active_connections_multiple_lifecycle() {
let counter = AtomicU64::new(0);
let (tx, rx) = mpsc::channel();
let mut connections = FxHashMap::default();
let now = Instant::now();
for id in 1..=3 {
counter.fetch_add(1, Ordering::Relaxed);
tx.send(ControlEvent::Connected { connection_id: id })
.unwrap();
}
process_control_events(&rx, &mut connections, &counter, || now);
assert_eq!(counter.load(Ordering::Relaxed), 3);
assert_eq!(connections.len(), 3);
tx.send(ControlEvent::Disconnected { connection_id: 2 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, || now);
assert_eq!(counter.load(Ordering::Relaxed), 2);
assert_eq!(connections.len(), 2);
tx.send(ControlEvent::Disconnected { connection_id: 1 })
.unwrap();
tx.send(ControlEvent::Disconnected { connection_id: 3 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, || now);
assert_eq!(counter.load(Ordering::Relaxed), 0);
assert_eq!(connections.len(), 0);
}
#[test]
fn duplicate_disconnect_single_decrement() {
let counter = AtomicU64::new(0);
let (tx, rx) = mpsc::channel();
let mut connections = FxHashMap::default();
let now = Instant::now();
counter.fetch_add(1, Ordering::Relaxed);
tx.send(ControlEvent::Connected { connection_id: 1 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, || now);
tx.send(ControlEvent::Disconnected { connection_id: 1 })
.unwrap();
tx.send(ControlEvent::Disconnected { connection_id: 1 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, || now);
assert_eq!(counter.load(Ordering::Relaxed), 0);
}
#[test]
fn connected_stamps_the_clock_read_lazily_at_drain() {
let counter = AtomicU64::new(0);
let (tx, rx) = mpsc::channel();
let mut connections = FxHashMap::default();
let reads = std::cell::Cell::new(0u32);
let at_drain = Instant::now() + std::time::Duration::from_secs(60);
let clock = || {
reads.set(reads.get() + 1);
at_drain
};
process_control_events(&rx, &mut connections, &counter, clock);
tx.send(ControlEvent::Disconnected { connection_id: 9 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, clock);
assert_eq!(reads.get(), 0);
tx.send(ControlEvent::Connected { connection_id: 1 })
.unwrap();
tx.send(ControlEvent::Connected { connection_id: 2 })
.unwrap();
process_control_events(&rx, &mut connections, &counter, clock);
assert_eq!(reads.get(), 1);
assert_eq!(connections[&1].last_send, at_drain);
assert_eq!(connections[&2].last_send, at_drain);
}
mod push_frame {
use super::*;
const TRAILER: &[u8] = b"END";
fn pushed(
payload: Option<Result<usize, &'static str>>,
trailer: &[u8],
) -> Vec<(u64, Vec<u8>)> {
let (mut tx, mut rx) = spsc::channel::<TxFrame>(8, WaitStrategy::SpinThenYield);
let encode_buf = b"response-body";
super::super::push_frame(payload, 42, &mut tx, encode_buf, trailer);
tx.flush();
let mut out = Vec::new();
while let Some((_, frame)) = rx.try_consume() {
out.push((frame.connection_id, frame.as_bytes().to_vec()));
}
out
}
#[test]
fn payload_and_trailer_ship_as_one_frame() {
let frames = pushed(Some(Ok(8)), TRAILER);
assert_eq!(frames.len(), 1, "two SPSC slots where one suffices");
assert_eq!(frames[0].0, 42);
assert_eq!(frames[0].1, b"responseEND".to_vec());
}
#[test]
fn payload_without_trailer_ships_alone() {
let frames = pushed(Some(Ok(8)), &[]);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].1, b"response".to_vec());
}
#[test]
fn trailer_without_payload_ships_alone() {
let frames = pushed(None, TRAILER);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].1, TRAILER.to_vec());
}
#[test]
fn nothing_to_send_pushes_no_frame() {
assert!(pushed(None, &[]).is_empty());
}
#[test]
fn encode_failure_still_terminates_the_request() {
let frames = pushed(Some(Err("encode error")), TRAILER);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].1, TRAILER.to_vec());
}
#[test]
fn encode_failure_without_trailer_pushes_no_frame() {
assert!(pushed(Some(Err("encode error")), &[]).is_empty());
}
#[test]
fn the_encoded_terminator_fits_its_reserved_room() {
let batch_end = {
let mut buf = [0u8; MAX_BATCH_END_FRAME];
let written = control_codec::encode_transport_response(
&TransportResponse::BatchEnd,
&mut buf,
)
.expect("BatchEnd encodes");
buf[..written].to_vec()
};
assert!(batch_end.len() <= MAX_BATCH_END_FRAME);
}
}
}