use std::collections::HashMap;
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_app::Application;
use melin_app::amortized_timer::AmortizedTimer;
use melin_transport_core::DurableWireSeqCursor;
use melin_transport_core::pipeline::{OutputPayload, OutputSlot, StageUtilization};
use melin_wire_protocol::control::TransportResponse;
use melin_wire_protocol::control_codec;
#[cfg(feature = "latency-trace")]
use melin_transport_core::trace;
const MAX_BATCH: usize = 1024;
const MAX_RESPONSE_BUF: usize = 512;
const MAX_TX_FRAME: usize = 4 + MAX_RESPONSE_BUF;
#[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,
durability_mode: 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>,
busy_spin: bool,
encoder: crate::response::ResponseEncoderArc<A>,
) {
use crate::durability_policy::DurabilityMode;
let mut active_mode =
DurabilityMode::from_u8(durability_mode.load(Ordering::Relaxed)).unwrap_or_else(|| {
tracing::error!(
"durability_mode atomic held a corrupted byte at startup; defaulting to hybrid (DPDK)"
);
DurabilityMode::Hybrid
});
let mut policy = active_mode.to_policy();
let mut connections: HashMap<u64, ConnectionHeartbeat> = HashMap::with_capacity(256);
let mut batch = [OutputSlot::<A::Report, A::QueryResponse>::default(); MAX_BATCH];
let mut encode_buf = [0u8; MAX_RESPONSE_BUF];
let mut cached_durable_pos: u64;
let startup_now = Instant::now();
let mut last_policy_check = startup_now;
const DEGRADED_LOG_INTERVAL: Duration = Duration::from_secs(5);
const POLICY_CHECK_INTERVAL: Duration = Duration::from_secs(1);
const GATE_ACCRUAL_INTERVAL: Duration = Duration::from_millis(10);
let mut degraded_logger;
{
let journal_pos = journal_persisted_wire_seq.load();
let metrics_ref = replication_metrics.as_deref();
let active_ref = replica_active.as_ref();
let status =
crate::response::evaluate_durability(&policy, journal_pos, metrics_ref, active_ref);
cached_durable_pos = status.durable_pos;
utilization
.policy_degraded
.store(status.degraded, Ordering::Relaxed);
degraded_logger = if status.degraded {
crate::response::DegradationLogger::new_starting_degraded(startup_now, &policy)
} else {
crate::response::DegradationLogger::new(startup_now)
};
}
let mut heartbeat_frame = [0u8; 8];
let heartbeat_len = control_codec::encode_transport_response(
&TransportResponse::Heartbeat,
&mut heartbeat_frame,
)
.expect("heartbeat encodes");
let mut last_heartbeat_scan = Instant::now();
let mut heartbeat_timer = AmortizedTimer::new();
let mut idle_spins: u32 = 0;
let mut busy_count: u64 = 0;
let mut idle_count: u64 = 0;
let mut gate_accrual_timer = AmortizedTimer::new();
#[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)");
loop {
let observed_byte = durability_mode.load(Ordering::Relaxed);
if observed_byte != active_mode.as_u8() {
match DurabilityMode::from_u8(observed_byte) {
Some(next) => {
tracing::info!(
prev = active_mode.as_str(),
next = next.as_str(),
"durability mode swapped at runtime (DPDK)"
);
active_mode = next;
policy = active_mode.to_policy();
cached_durable_pos = 0;
degraded_logger.reseed(&utilization, Instant::now());
}
None => {
tracing::error!(
byte = observed_byte,
"durability_mode atomic held a corrupted byte; retaining prior mode (DPDK)"
);
}
}
}
if shutdown.load(Ordering::Relaxed) {
utilization.busy.store(busy_count, Ordering::Relaxed);
utilization.idle.store(idle_count, Ordering::Relaxed);
return;
}
process_control_events(
&control_rx,
&mut connections,
&active_connections,
last_heartbeat_scan,
);
let count = consumer.consume_batch(&mut batch, MAX_BATCH);
if count == 0 {
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), busy_spin || idle_spins < 1000)
.is_some()
{
let now = Instant::now();
last_heartbeat_scan = 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();
if now_ts.duration_since(last_policy_check) >= POLICY_CHECK_INTERVAL {
last_policy_check = now_ts;
let journal_pos = journal_persisted_wire_seq.load();
let metrics_ref = replication_metrics.as_deref();
let active_ref = replica_active.as_ref();
let status = crate::response::evaluate_durability(
&policy,
journal_pos,
metrics_ref,
active_ref,
);
degraded_logger.tick(
&policy,
&utilization,
status.degraded,
now_ts,
DEGRADED_LOG_INTERVAL,
);
cached_durable_pos = status.durable_pos;
}
}
if busy_spin || idle_spins < 1000 {
idle_spins = idle_spins.wrapping_add(1);
std::hint::spin_loop();
} else {
std::thread::yield_now();
}
continue;
}
idle_spins = 0;
busy_count += 1;
#[cfg(feature = "latency-trace")]
let consume_ts = trace::mono_trace_ns();
#[cfg(feature = "tick-to-trade")]
let mut gate_tracker;
{
let needed = batch[..count]
.iter()
.map(|s| s.wire_seq)
.max()
.expect("non-empty batch");
#[cfg(feature = "tick-to-trade")]
{
gate_tracker = crate::response::GateCrossTracker::new(needed);
}
if cached_durable_pos < needed {
loop {
let observed_byte = durability_mode.load(Ordering::Relaxed);
if observed_byte != active_mode.as_u8()
&& let Some(next) = DurabilityMode::from_u8(observed_byte)
{
tracing::info!(
prev = active_mode.as_str(),
next = next.as_str(),
"durability mode swapped during gate wait (DPDK)"
);
active_mode = next;
policy = active_mode.to_policy();
degraded_logger.reseed(&utilization, Instant::now());
}
let journal_pos = journal_persisted_wire_seq.load();
let metrics_ref = replication_metrics.as_deref();
let active_ref = replica_active.as_ref();
let repl_min =
crate::response::connected_persisted_min(metrics_ref, active_ref);
#[cfg(feature = "tick-to-trade")]
gate_tracker.observe(journal_pos.get(), repl_min, trace::mono_trace_ns());
let status = crate::response::evaluate_durability(
&policy,
journal_pos,
metrics_ref,
active_ref,
);
cached_durable_pos = status.durable_pos;
utilization
.policy_degraded
.store(status.degraded, Ordering::Relaxed);
if gate_accrual_timer
.tick(GATE_ACCRUAL_INTERVAL, true)
.is_some()
{
degraded_logger.tick(
&policy,
&utilization,
status.degraded,
Instant::now(),
DEGRADED_LOG_INTERVAL,
);
}
if cached_durable_pos >= needed {
if journal_pos.get() <= repl_min {
utilization.gate_journal.fetch_add(1, Ordering::Relaxed);
} else {
utilization.gate_replication.fetch_add(1, Ordering::Relaxed);
}
break;
}
std::hint::spin_loop();
}
}
}
let batch_now = Instant::now();
let degraded_now = utilization.policy_degraded.load(Ordering::Relaxed);
degraded_logger.tick(
&policy,
&utilization,
degraded_now,
batch_now,
DEGRADED_LOG_INTERVAL,
);
last_policy_check = batch_now;
for slot in &batch[..count] {
#[cfg(feature = "latency-trace")]
spsc_rec.record_elapsed(slot.match_complete_ts, consume_ts);
#[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);
}
if !connections.contains_key(&slot.connection_id) {
continue;
}
let tid = (slot.connection_id >> 56) as usize % tx_producers.len();
let conn_id = slot.connection_id;
let payload_result: Option<Result<usize, &'static str>> = match slot.payload {
OutputPayload::Report(ref report) => {
Some(encoder.encode_report(report, &mut encode_buf))
}
OutputPayload::QueryResponse(ref q) => {
Some(encoder.encode_query(q, &mut encode_buf))
}
OutputPayload::EngineError => Some(
control_codec::encode_transport_response(
&TransportResponse::EngineError,
&mut encode_buf,
)
.map_err(|_| "encode error"),
),
OutputPayload::BatchEnd => None,
};
if let Some(result) = payload_result {
#[cfg(feature = "tick-to-trade")]
let encode_start = trace::mono_trace_ns();
push_frame(result, conn_id, &mut tx_producers[tid], &encode_buf);
#[cfg(feature = "tick-to-trade")]
encode_rec.record_elapsed(encode_start, trace::mono_trace_ns());
}
if slot.is_last_in_request {
let result = control_codec::encode_transport_response(
&TransportResponse::BatchEnd,
&mut encode_buf,
)
.map_err(|_| "encode error");
push_frame(result, conn_id, &mut tx_producers[tid], &encode_buf);
}
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());
}
if let Some(state) = connections.get_mut(&slot.connection_id) {
state.last_send = batch_now;
}
}
#[cfg(feature = "latency-trace")]
dispatch_rec.record_elapsed(consume_ts, trace::mono_trace_ns());
}
}
#[inline]
fn push_frame(
result: Result<usize, &'static str>,
conn_id: u64,
tx: &mut spsc::Producer<TxFrame>,
encode_buf: &[u8],
) {
let written = match result {
Ok(n) => n,
Err(reason) => {
tracing::error!(connection_id = conn_id, reason, "encode error");
return;
}
};
let len = written as u16;
tx.push_with(|frame| {
frame.connection_id = conn_id;
frame.len = len;
frame.data[..written].copy_from_slice(&encode_buf[..written]);
});
}
struct ConnectionHeartbeat {
last_send: Instant,
}
fn process_control_events(
control_rx: &mpsc::Receiver<ControlEvent>,
connections: &mut HashMap<u64, ConnectionHeartbeat>,
active_connections: &AtomicU64,
now: Instant,
) {
while let Ok(event) = control_rx.try_recv() {
match event {
ControlEvent::Connected { connection_id } => {
connections.insert(connection_id, ConnectionHeartbeat { last_send: now });
}
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 = HashMap::new();
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 = HashMap::new();
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 = HashMap::new();
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 = HashMap::new();
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);
}
}