use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use freenet_stdlib::prelude::{ContractKey, WrappedState};
use crate::node::OpManager;
use crate::ring::PeerKeyLocation;
use crate::transport::BroadcastDeliveryOutcome;
use super::broadcast_payload_mix::PayloadArm;
use super::p2p_protoc::P2pBridge;
const STREAM_COMPLETION_TIMEOUT: Duration = Duration::from_secs(120);
const BROADCAST_QUEUE_PAYLOAD_SIZE_THRESHOLD: usize = 64 * 1024;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "simulation_tests", allow(dead_code))]
pub(super) enum QueuedPayloadClass {
Small,
Large,
}
#[cfg_attr(feature = "simulation_tests", allow(dead_code))]
pub(super) struct SendLanePermit {
permit: Option<tokio::sync::OwnedSemaphorePermit>,
lane: QueuedPayloadClass,
large_pool: Arc<tokio::sync::Semaphore>,
upgrade_slots: Arc<tokio::sync::Semaphore>,
}
const UPGRADE_SLOT_HOLD_WINDOW: Duration = Duration::from_millis(250);
#[cfg_attr(feature = "simulation_tests", allow(dead_code))]
const MAX_PARKED_LANE_UPGRADES: usize = 12;
#[cfg_attr(feature = "simulation_tests", allow(dead_code))]
impl SendLanePermit {
fn new(
lane: QueuedPayloadClass,
permit: tokio::sync::OwnedSemaphorePermit,
large_pool: Arc<tokio::sync::Semaphore>,
upgrade_slots: Arc<tokio::sync::Semaphore>,
) -> Self {
Self {
permit: Some(permit),
lane,
large_pool,
upgrade_slots,
}
}
async fn ensure_capacity_for(&mut self, payload_size: usize) {
if payload_size < BROADCAST_QUEUE_PAYLOAD_SIZE_THRESHOLD
|| matches!(self.lane, QueuedPayloadClass::Large)
{
return;
}
let acquire = self.large_pool.clone().acquire_owned();
match tokio::time::timeout(UPGRADE_SLOT_HOLD_WINDOW, acquire).await {
Ok(Ok(large)) => return self.take_large(large),
Ok(Err(_)) => return self.large_pool_closed(payload_size),
Err(_elapsed) => {}
}
let _parked = match self.upgrade_slots.clone().acquire_owned().await {
Ok(slot) => {
self.permit = None;
Some(slot)
}
Err(_) => None,
};
match self.large_pool.clone().acquire_owned().await {
Ok(large) => self.take_large(large),
Err(_) => self.large_pool_closed(payload_size),
}
}
fn take_large(&mut self, large: tokio::sync::OwnedSemaphorePermit) {
self.permit = Some(large);
self.lane = QueuedPayloadClass::Large;
}
fn large_pool_closed(&self, payload_size: usize) {
tracing::debug!(
payload_size,
"Large broadcast pool closed; sending mispredicted payload without \
large-lane capacity"
);
}
}
#[cfg_attr(feature = "simulation_tests", allow(dead_code))]
pub(super) struct QueueScheduling {
queued_class: QueuedPayloadClass,
permit: SendLanePermit,
}
pub(crate) static BROADCAST_STREAM_METRICS: BroadcastStreamMetrics = BroadcastStreamMetrics::new();
pub(crate) struct BroadcastStreamMetrics {
streaming_attempts_total: AtomicU64,
streaming_failures_total: AtomicU64,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct BroadcastStreamMetricsSnapshot {
pub streaming_attempts_total: u64,
pub streaming_failures_total: u64,
}
impl BroadcastStreamMetrics {
const fn new() -> Self {
Self {
streaming_attempts_total: AtomicU64::new(0),
streaming_failures_total: AtomicU64::new(0),
}
}
fn record_attempt(&self, delivered: bool) {
self.streaming_attempts_total
.fetch_add(1, Ordering::Relaxed);
if !delivered {
self.streaming_failures_total
.fetch_add(1, Ordering::Relaxed);
}
}
pub(crate) fn snapshot(&self) -> BroadcastStreamMetricsSnapshot {
BroadcastStreamMetricsSnapshot {
streaming_attempts_total: self.streaming_attempts_total.load(Ordering::Relaxed),
streaming_failures_total: self.streaming_failures_total.load(Ordering::Relaxed),
}
}
}
pub(super) fn should_broadcast_contract(op_manager: &Arc<OpManager>, key: &ContractKey) -> bool {
op_manager.ring.should_summarize_or_broadcast(key)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum FanoutSendPlan {
Skip,
Send,
Probe,
}
#[derive(Clone, Copy)]
pub(super) struct SummaryPair<'a> {
pub ours: &'a freenet_stdlib::prelude::StateSummary<'static>,
pub theirs: &'a freenet_stdlib::prelude::StateSummary<'static>,
}
pub(super) fn plan_fanout_send<T: crate::util::time_source::TimeSource + Sync>(
interest_manager: &crate::ring::interest::InterestManager<T>,
key: &ContractKey,
summaries: SummaryPair<'_>,
probes_used: usize,
) -> FanoutSendPlan {
use crate::node::{StalenessProbeAction, plan_staleness_probe};
let SummaryPair { ours, theirs } = summaries;
if ours.as_ref() == theirs.as_ref() {
return FanoutSendPlan::Skip;
}
let cached = interest_manager.cached_staleness_verdict(key, theirs.as_ref(), ours.as_ref());
match plan_staleness_probe(cached, probes_used) {
StalenessProbeAction::UseCached(true) => FanoutSendPlan::Send,
StalenessProbeAction::UseCached(false) => FanoutSendPlan::Skip,
StalenessProbeAction::RunProbe => FanoutSendPlan::Probe,
StalenessProbeAction::BudgetExhaustedFallBack => FanoutSendPlan::Send,
}
}
pub(super) async fn fanout_send_needed(
op_manager: &OpManager,
key: &ContractKey,
summaries: SummaryPair<'_>,
probes_used: &mut usize,
) -> bool {
match plan_fanout_send(&op_manager.interest_manager, key, summaries, *probes_used) {
FanoutSendPlan::Send => true,
FanoutSendPlan::Skip => false,
FanoutSendPlan::Probe => {
*probes_used += 1;
let SummaryPair { ours, theirs } = summaries;
let verdict = op_manager
.interest_manager
.peer_summary_has_pending_state(op_manager, key, theirs, ours)
.await;
crate::ring::interest::summary_indicates_stale_peer(ours, theirs, verdict)
}
}
}
#[cfg(not(feature = "simulation_tests"))]
mod queue {
use std::collections::{BTreeMap, HashMap};
use std::future::Future;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Instant;
use freenet_stdlib::prelude::{ContractKey, WrappedState};
use tokio::sync::{Mutex, Notify, Semaphore};
use crate::node::OpManager;
use crate::ring::{PeerKey, PeerKeyLocation};
use super::super::p2p_protoc::P2pBridge;
use super::{QueueScheduling, QueuedPayloadClass, SendLanePermit, broadcast_to_single_peer};
const DEFAULT_SMALL_PAYLOAD_CONCURRENCY: usize = 12;
const DEFAULT_LARGE_PAYLOAD_CONCURRENCY: usize = 2;
const DEFAULT_MAX_QUEUE_DEPTH: usize = 256;
type DedupeKey = (ContractKey, PeerKeyLocation);
pub(super) fn classify_payload_lane(
state_size: usize,
delta_expected: bool,
) -> QueuedPayloadClass {
if state_size < super::BROADCAST_QUEUE_PAYLOAD_SIZE_THRESHOLD || delta_expected {
QueuedPayloadClass::Small
} else {
QueuedPayloadClass::Large
}
}
fn delta_send_expected(
interest_manager: &crate::ring::interest::InterestManager<
crate::util::time_source::DynTimeSource,
>,
delta_incompat: &crate::ring::delta_incompat::DeltaIncompat,
key: &ContractKey,
target: &PeerKeyLocation,
) -> bool {
let peer_key = PeerKey::from(target.pub_key().clone());
interest_manager.has_peer_summary(key, &peer_key)
&& !delta_incompat.deltas_suppressed_peek(key.id())
}
fn lane_for(
interest_manager: &crate::ring::interest::InterestManager<
crate::util::time_source::DynTimeSource,
>,
delta_incompat: &crate::ring::delta_incompat::DeltaIncompat,
key: &ContractKey,
target: &PeerKeyLocation,
state_size: usize,
) -> QueuedPayloadClass {
classify_payload_lane(
state_size,
delta_send_expected(interest_manager, delta_incompat, key, target),
)
}
struct BroadcastEntry {
key: ContractKey,
target: PeerKeyLocation,
new_state: WrappedState,
state_size: usize,
lane: QueuedPayloadClass,
seq: u64,
}
struct QueueState {
small_order: BTreeMap<u64, DedupeKey>,
large_order: BTreeMap<u64, DedupeKey>,
next_seq: u64,
entries: HashMap<DedupeKey, BroadcastEntry>,
active: HashMap<DedupeKey, u64>,
small_queued: usize,
hol_block: Option<HolBlockHandle>,
}
#[derive(Clone)]
struct HolBlockHandle(Arc<HolBlockState>);
struct HolBlockState {
finished: AtomicBool,
observation: std::sync::Mutex<HolBlockObservation>,
}
struct HolBlockObservation {
started: Instant,
last_updated: Instant,
small_queued: usize,
small_entry_millis: u128,
observed_small: bool,
}
impl HolBlockHandle {
fn is_finished(&self) -> bool {
self.0.finished.load(Ordering::Acquire)
}
fn set_small_queued(&self, next: usize, now: Instant) {
if self.is_finished() {
return;
}
let mut block = self.0.observation.lock().unwrap();
if self.is_finished() {
return;
}
block.advance(now);
block.small_queued = next;
block.observed_small |= next > 0;
}
fn finish_now(&self) -> Option<(u64, u64)> {
let mut block = self.0.observation.lock().unwrap();
if self.is_finished() {
return None;
}
let now = Instant::now();
self.finish_locked(&mut block, now)
}
#[cfg(test)]
fn finish_at(&self, now: Instant) -> Option<(u64, u64)> {
let mut block = self.0.observation.lock().unwrap();
if self.is_finished() {
return None;
}
self.finish_locked(&mut block, now)
}
fn finish_locked(
&self,
block: &mut HolBlockObservation,
now: Instant,
) -> Option<(u64, u64)> {
block.advance(now);
let result = block.observed_small.then(|| {
let blocked_millis = now
.saturating_duration_since(block.started)
.as_millis()
.min(u128::from(u64::MAX)) as u64;
let small_entry_millis = block.small_entry_millis.min(u128::from(u64::MAX)) as u64;
(blocked_millis, small_entry_millis)
});
self.0.finished.store(true, Ordering::Release);
result
}
}
impl HolBlockObservation {
fn advance(&mut self, now: Instant) {
if now <= self.last_updated {
return;
}
let millis = now.saturating_duration_since(self.last_updated).as_millis();
self.small_entry_millis = self
.small_entry_millis
.saturating_add(millis.saturating_mul(self.small_queued as u128));
self.last_updated = now;
}
}
impl QueueState {
fn new() -> Self {
Self {
small_order: BTreeMap::new(),
large_order: BTreeMap::new(),
next_seq: 0,
entries: HashMap::new(),
active: HashMap::new(),
small_queued: 0,
hol_block: None,
}
}
fn len(&self) -> usize {
self.entries.len()
}
fn order_mut(&mut self, lane: QueuedPayloadClass) -> &mut BTreeMap<u64, DedupeKey> {
match lane {
QueuedPayloadClass::Small => &mut self.small_order,
QueuedPayloadClass::Large => &mut self.large_order,
}
}
fn pop_lane(&mut self, lane: QueuedPayloadClass, now: Instant) -> Option<BroadcastEntry> {
let (_, key) = self.order_mut(lane).pop_first()?;
let entry = self
.entries
.remove(&key)
.expect("order maps and entries are mutated together");
debug_assert_eq!(entry.lane, lane, "entry popped from the wrong lane");
if matches!(lane, QueuedPayloadClass::Small) {
self.set_small_queued(self.small_queued.saturating_sub(1), now);
}
Some(entry)
}
fn evict_oldest(&mut self, now: Instant) -> Option<BroadcastEntry> {
let oldest_small = self.small_order.keys().next().copied();
let oldest_large = self.large_order.keys().next().copied();
let lane = match (oldest_small, oldest_large) {
(Some(small), Some(large)) if large < small => QueuedPayloadClass::Large,
(Some(_), _) => QueuedPayloadClass::Small,
(None, Some(_)) => QueuedPayloadClass::Large,
(None, None) => return None,
};
self.pop_lane(lane, now)
}
fn push_new(&mut self, key: DedupeKey, entry: BroadcastEntry) {
self.order_mut(entry.lane).insert(entry.seq, key.clone());
self.entries.insert(key, entry);
}
fn relane(&mut self, key: &DedupeKey, from: QueuedPayloadClass, to: QueuedPayloadClass) {
let Some(seq) = self.entries.get(key).map(|entry| entry.seq) else {
return;
};
self.order_mut(from).remove(&seq);
self.order_mut(to).insert(seq, key.clone());
}
fn set_small_queued(&mut self, next: usize, now: Instant) {
self.small_queued = next;
if self
.hol_block
.as_ref()
.is_some_and(HolBlockHandle::is_finished)
{
self.hol_block = None;
}
if let Some(block) = &self.hol_block {
block.set_small_queued(next, now);
}
}
fn start_hol(&mut self, now: Instant) -> HolBlockHandle {
let block = HolBlockHandle(Arc::new(HolBlockState {
finished: AtomicBool::new(false),
observation: std::sync::Mutex::new(HolBlockObservation {
started: now,
last_updated: now,
small_queued: self.small_queued,
small_entry_millis: 0,
observed_small: self.small_queued > 0,
}),
}));
self.hol_block = Some(block.clone());
block
}
fn track_active(&mut self, key: DedupeKey) -> bool {
if let Some(count) = self.active.get_mut(&key) {
*count = count.saturating_add(1);
return true;
}
if self.active.len() >= DEFAULT_MAX_QUEUE_DEPTH {
return false;
}
self.active.insert(key, 1);
true
}
fn untrack_active(&mut self, key: &DedupeKey) {
if let Some(count) = self.active.get_mut(key) {
if *count > 1 {
*count -= 1;
} else {
self.active.remove(key);
}
}
}
}
struct ActiveSendGuard {
queue: Arc<Mutex<QueueState>>,
key: DedupeKey,
tracked: bool,
}
impl ActiveSendGuard {
async fn finish(mut self) {
if self.tracked {
self.queue.lock().await.untrack_active(&self.key);
self.tracked = false;
}
}
}
impl Drop for ActiveSendGuard {
fn drop(&mut self) {
if !self.tracked {
return;
}
if let Ok(mut queue) = self.queue.try_lock() {
queue.untrack_active(&self.key);
return;
}
let queue = self.queue.clone();
let key = self.key.clone();
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
runtime.spawn(async move {
queue.lock().await.untrack_active(&key);
});
}
}
}
#[derive(Clone)]
pub(crate) struct BroadcastQueue {
queue: Arc<Mutex<QueueState>>,
small_notify: Arc<Notify>,
large_notify: Arc<Notify>,
small_payload_concurrency: usize,
large_payload_concurrency: usize,
max_queue_depth: usize,
}
impl BroadcastQueue {
pub(crate) fn new() -> Self {
Self {
queue: Arc::new(Mutex::new(QueueState::new())),
small_notify: Arc::new(Notify::new()),
large_notify: Arc::new(Notify::new()),
small_payload_concurrency: DEFAULT_SMALL_PAYLOAD_CONCURRENCY,
large_payload_concurrency: DEFAULT_LARGE_PAYLOAD_CONCURRENCY,
max_queue_depth: DEFAULT_MAX_QUEUE_DEPTH,
}
}
fn notify_for(&self, lane: QueuedPayloadClass) -> &Arc<Notify> {
match lane {
QueuedPayloadClass::Small => &self.small_notify,
QueuedPayloadClass::Large => &self.large_notify,
}
}
pub(crate) async fn enqueue(
&self,
op_manager: &Arc<OpManager>,
key: ContractKey,
target: PeerKeyLocation,
new_state: WrappedState,
) {
let lane = lane_for(
&op_manager.interest_manager,
&op_manager.ring.delta_incompat,
&key,
&target,
new_state.size(),
);
self.enqueue_in_lane(lane, key, target, new_state).await;
}
async fn enqueue_in_lane(
&self,
lane: QueuedPayloadClass,
key: ContractKey,
target: PeerKeyLocation,
new_state: WrappedState,
) {
let dedup_key = (key, target.clone());
let state_size = new_state.size();
let mut queue = self.queue.lock().await;
if queue.active.contains_key(&dedup_key) {
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS.record_enqueue_while_active();
}
let previous_lane = queue.entries.get(&dedup_key).map(|existing| existing.lane);
if let Some(previous_lane) = previous_lane {
if let Some(existing) = queue.entries.get_mut(&dedup_key) {
existing.new_state = new_state;
existing.state_size = state_size;
existing.lane = lane;
}
if previous_lane != lane {
let small_queued = match lane {
QueuedPayloadClass::Small => queue.small_queued.saturating_add(1),
QueuedPayloadClass::Large => queue.small_queued.saturating_sub(1),
};
queue.set_small_queued(small_queued, Instant::now());
queue.relane(&dedup_key, previous_lane, lane);
}
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS.record_dedup_replacement();
tracing::trace!(
contract = %dedup_key.0,
peer = ?target.socket_addr(),
"Broadcast queue: replaced stale entry with newer state"
);
} else {
while queue.len() >= self.max_queue_depth {
if let Some(entry) = queue.evict_oldest(Instant::now()) {
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS.record_capacity_eviction();
tracing::warn!(
contract = %entry.key,
peer = ?entry.target.socket_addr(),
queue_depth = self.max_queue_depth,
"Broadcast queue full, evicted oldest entry"
);
} else {
break;
}
}
if matches!(lane, QueuedPayloadClass::Small) {
let next = queue.small_queued.saturating_add(1);
queue.set_small_queued(next, Instant::now());
}
let seq = queue.next_seq;
queue.next_seq += 1;
queue.push_new(
dedup_key.clone(),
BroadcastEntry {
key,
target,
new_state,
state_size,
lane,
seq,
},
);
}
crate::transport::shadow_demand::record_broadcast_queue_depth(queue.len());
drop(queue);
self.notify_for(lane).notify_one();
}
pub(crate) fn start_worker(
&self,
bridge: P2pBridge,
op_manager: Arc<OpManager>,
) -> [tokio::task::JoinHandle<()>; 2] {
let small_semaphore = Arc::new(Semaphore::new(self.small_payload_concurrency));
let large_semaphore = Arc::new(Semaphore::new(self.large_payload_concurrency));
let upgrade_slots = Arc::new(Semaphore::new(super::MAX_PARKED_LANE_UPGRADES));
let group = LaneGroup {
small_pool: small_semaphore.clone(),
large_pool: large_semaphore.clone(),
upgrade_slots,
small_notify: self.small_notify.clone(),
large_notify: self.large_notify.clone(),
};
let spawn_lane = |lane| {
let queue = self.queue.clone();
let group = group.clone();
let bridge = bridge.clone();
let op_manager = op_manager.clone();
tokio::spawn(drain_lane(
lane,
queue,
group,
move |entry: BroadcastEntry, scheduling: QueueScheduling| {
let bridge = bridge.clone();
let op_manager = op_manager.clone();
async move {
broadcast_to_single_peer(
&bridge,
&op_manager,
entry.key,
entry.new_state,
entry.target,
Some(scheduling),
)
.await;
}
},
))
};
[
spawn_lane(QueuedPayloadClass::Small),
spawn_lane(QueuedPayloadClass::Large),
]
}
}
#[derive(Clone)]
struct LaneGroup {
small_pool: Arc<Semaphore>,
large_pool: Arc<Semaphore>,
upgrade_slots: Arc<Semaphore>,
small_notify: Arc<Notify>,
large_notify: Arc<Notify>,
}
impl LaneGroup {
fn pool(&self, lane: QueuedPayloadClass) -> &Arc<Semaphore> {
match lane {
QueuedPayloadClass::Small => &self.small_pool,
QueuedPayloadClass::Large => &self.large_pool,
}
}
fn notify(&self, lane: QueuedPayloadClass) -> &Arc<Notify> {
match lane {
QueuedPayloadClass::Small => &self.small_notify,
QueuedPayloadClass::Large => &self.large_notify,
}
}
fn shutdown(&self) {
self.small_pool.close();
self.large_pool.close();
self.upgrade_slots.close();
self.small_notify.notify_one();
self.large_notify.notify_one();
}
}
struct LaneDrainGuard(LaneGroup);
impl Drop for LaneDrainGuard {
fn drop(&mut self) {
self.0.shutdown();
}
}
async fn drain_lane<D, F>(
lane: QueuedPayloadClass,
queue: Arc<Mutex<QueueState>>,
group: LaneGroup,
dispatch: D,
) where
D: Fn(BroadcastEntry, QueueScheduling) -> F,
F: Future<Output = ()> + Send + 'static,
{
let _stop_sibling_on_exit = LaneDrainGuard(group.clone());
let notify = group.notify(lane).clone();
let semaphore = group.pool(lane).clone();
let large_pool = group.large_pool.clone();
let upgrade_slots = group.upgrade_slots.clone();
let lane_is_large = matches!(lane, QueuedPayloadClass::Large);
loop {
let notified = notify.notified();
let mut drained_any = false;
loop {
let (entry, active_tracked) = {
let mut q = queue.lock().await;
let entry = q.pop_lane(lane, Instant::now());
let active_tracked = entry.as_ref().is_none_or(|entry| {
let tracked = q.track_active((entry.key, entry.target.clone()));
if !tracked {
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.record_active_tracking_overflow();
}
tracked
});
crate::transport::shadow_demand::record_broadcast_queue_depth(q.len());
(entry, active_tracked)
};
let Some(entry) = entry else {
break; };
drained_any = true;
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.record_scheduled(lane_is_large, entry.state_size);
let blocked = lane_is_large && semaphore.available_permits() == 0;
let hol_block = if blocked {
Some(queue.lock().await.start_hol(Instant::now()))
} else {
None
};
let active_guard = ActiveSendGuard {
queue: queue.clone(),
key: (entry.key, entry.target.clone()),
tracked: active_tracked,
};
let permit = semaphore.clone().acquire_owned().await;
let hol_metrics = hol_block.as_ref().and_then(HolBlockHandle::finish_now);
let Ok(permit) = permit else {
if let Some((blocked_millis, small_entry_millis)) = hol_metrics {
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.record_large_head_block(blocked_millis, small_entry_millis);
}
active_guard.finish().await;
tracing::error!(
?lane,
"Broadcast queue semaphore closed unexpectedly; stopping BOTH \
lane drains so this cannot degrade into half the broadcast \
traffic silently disappearing"
);
group.shutdown();
return;
};
let scheduling = QueueScheduling {
queued_class: lane,
permit: SendLanePermit::new(
lane,
permit,
large_pool.clone(),
upgrade_slots.clone(),
),
};
let send = dispatch(entry, scheduling);
tokio::spawn(async move {
send.await;
active_guard.finish().await;
});
if let Some((blocked_millis, small_entry_millis)) = hol_metrics {
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.record_large_head_block(blocked_millis, small_entry_millis);
}
}
if !drained_any {
notified.await;
if semaphore.is_closed() {
tracing::error!(
?lane,
"Broadcast lane drain stopping: pool closed; stopping BOTH lanes"
);
group.shutdown();
return;
}
}
}
}
#[cfg(test)]
mod observation_tests {
use super::*;
use std::time::Duration;
#[test]
fn hol_integral_includes_small_entries_arriving_during_wait() {
let start = Instant::now();
let mut queue = QueueState::new();
let hol = queue.start_hol(start);
queue.set_small_queued(1, start + Duration::from_millis(10));
queue.set_small_queued(2, start + Duration::from_millis(20));
assert_eq!(
hol.finish_at(start + Duration::from_millis(30)),
Some((30, 30)),
"integral is 0×10ms + 1×10ms + 2×10ms"
);
queue.set_small_queued(9, start + Duration::from_millis(40));
assert!(hol.is_finished());
assert!(
queue.hol_block.is_none(),
"first later mutation clears the handle"
);
assert_eq!(hol.finish_at(start + Duration::from_millis(40)), None);
}
#[test]
fn active_refcount_survives_one_of_two_overlapping_completions() {
let mut queue = QueueState::new();
let code = freenet_stdlib::prelude::ContractCode::from(vec![7]);
let params = freenet_stdlib::prelude::Parameters::from(vec![9]);
let key = (
ContractKey::from_params_and_code(¶ms, &code),
PeerKeyLocation::random(),
);
assert!(queue.track_active(key.clone()));
assert!(queue.track_active(key.clone()));
queue.untrack_active(&key);
assert_eq!(queue.active.get(&key), Some(&1));
queue.untrack_active(&key);
assert!(!queue.active.contains_key(&key));
}
#[test]
fn worker_wires_hol_boundaries_and_direct_normal_cleanup() {
let src = include_str!("broadcast_queue.rs");
let start = src.find(" async fn drain_lane<D, F>(").unwrap();
let end = src[start..].find("mod observation_tests").unwrap() + start;
let worker = &src[start..end];
let hol_start = worker.find("start_hol(Instant::now())").unwrap();
const ACQUIRE: &str = "semaphore.clone().acquire_owned().await";
let acquire = worker.find(ACQUIRE).unwrap();
assert!(
hol_start < acquire,
"HOL observation must start before waiting"
);
let freeze = worker.find("and_then(HolBlockHandle::finish_now)").unwrap();
let permit_match = worker.find("let Ok(permit) = permit").unwrap();
assert!(
acquire < freeze && freeze < permit_match,
"linearize the integral immediately after permit acquisition"
);
assert!(
!worker[acquire + ACQUIRE.len()..freeze].contains(".await"),
"no further await may separate permit acquisition from HOL linearization"
);
let success = &worker[permit_match..];
let spawn = success.find("tokio::spawn(async move").unwrap();
let task = &success[spawn..];
assert!(
task.find("send.await;").unwrap()
< task.find("active_guard.finish().await").unwrap(),
"normal tracking cleanup must run directly after releasing capacity"
);
assert!(
worker.contains("upgrade_slots.clone(),"),
"each dispatched send must get a clone of the SHARED parking area"
);
assert!(
!worker.contains("Semaphore::new("),
"the drain loop must not construct a pool of its own"
);
}
#[test]
fn start_worker_spawns_one_drain_per_lane() {
let src = include_str!("broadcast_queue.rs");
let start = src.find("pub(crate) fn start_worker(").unwrap();
let end = src[start..]
.find(" /// The pools and wakeups both lane drains share.")
.unwrap()
+ start;
let body = &src[start..end];
assert_eq!(
body.matches("spawn_lane(QueuedPayloadClass::").count(),
2,
"start_worker must spawn exactly one drain per lane"
);
for lane in ["Small", "Large"] {
assert!(
body.contains(&format!("spawn_lane(QueuedPayloadClass::{lane})")),
"start_worker must spawn a drain for the {lane} lane"
);
}
for wiring in [
"small_pool: small_semaphore",
"large_pool: large_semaphore",
"small_notify: self.small_notify",
"large_notify: self.large_notify",
] {
assert!(
body.contains(wiring),
"start_worker must wire the shared LaneGroup as `{wiring}`"
);
}
assert_eq!(
body.matches("Arc::new(Semaphore::new(super::MAX_PARKED_LANE_UPGRADES))")
.count(),
1,
"the parking area must be constructed exactly once in start_worker"
);
let created = body
.find("let group = LaneGroup {")
.expect("the shared pools must be bundled into one LaneGroup here");
let per_lane = body
.find("let group = group.clone();")
.expect("each drain must CLONE the shared LaneGroup, not build its own");
assert!(
created < per_lane,
"the shared LaneGroup must be created before the per-lane clone"
);
let enq = src
.find(" pub(crate) async fn enqueue(")
.expect("enqueue renamed or removed");
let enq_end = src[enq..]
.find("\n /// The lane-agnostic half")
.expect("end of enqueue not found")
+ enq;
let enq_body = &src[enq..enq_end];
assert!(
enq_body.contains("let lane = lane_for("),
"enqueue must derive its lane from lane_for — not merely mention it"
);
assert!(
enq_body.contains("self.enqueue_in_lane(lane,"),
"...and must enqueue with THAT lane; deriving it and then passing a \
different one would pass the check above"
);
}
const BIG: usize = super::super::BROADCAST_QUEUE_PAYLOAD_SIZE_THRESHOLD + 1;
fn contract(seed: u8) -> ContractKey {
let code = freenet_stdlib::prelude::ContractCode::from(vec![seed]);
let params = freenet_stdlib::prelude::Parameters::from(vec![seed]);
ContractKey::from_params_and_code(¶ms, &code)
}
fn lane_of(
dispatched: Option<(QueuedPayloadClass, ContractKey)>,
) -> Option<QueuedPayloadClass> {
dispatched.map(|(lane, _)| lane)
}
fn state(size: usize) -> WrappedState {
WrappedState::new(vec![0u8; size])
}
struct Lanes {
small_permits: Arc<Semaphore>,
large_permits: Arc<Semaphore>,
dispatched: tokio::sync::mpsc::UnboundedReceiver<(QueuedPayloadClass, ContractKey)>,
workers: Vec<tokio::task::JoinHandle<()>>,
}
impl Lanes {
async fn next_dispatch(&mut self) -> Option<(QueuedPayloadClass, ContractKey)> {
tokio::time::timeout(Duration::from_secs(5), self.dispatched.recv())
.await
.ok()
.flatten()
}
}
impl Drop for Lanes {
fn drop(&mut self) {
for worker in &self.workers {
worker.abort();
}
}
}
fn spawn_lanes(queue: &BroadcastQueue, small: usize, large: usize) -> Lanes {
let small_permits = Arc::new(Semaphore::new(small));
let large_permits = Arc::new(Semaphore::new(large));
let upgrade_slots = Arc::new(Semaphore::new(super::super::MAX_PARKED_LANE_UPGRADES));
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let group = LaneGroup {
small_pool: small_permits.clone(),
large_pool: large_permits.clone(),
upgrade_slots,
small_notify: queue.small_notify.clone(),
large_notify: queue.large_notify.clone(),
};
let workers = [QueuedPayloadClass::Small, QueuedPayloadClass::Large]
.into_iter()
.map(|lane| {
let tx = tx.clone();
tokio::spawn(drain_lane(
lane,
queue.queue.clone(),
group.clone(),
move |entry: BroadcastEntry, scheduling: QueueScheduling| {
let tx = tx.clone();
async move {
let _scheduling = scheduling;
let _ = tx.send((lane, entry.key));
std::future::pending::<()>().await;
}
},
))
})
.collect();
Lanes {
small_permits,
large_permits,
dispatched: rx,
workers,
}
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn one_lane_drain_exiting_stops_the_other() {
for exit_while_idle in [true, false] {
let queue = BroadcastQueue::new();
let mut lanes = spawn_lanes(&queue, 12, 1);
assert_eq!(lanes.next_dispatch().await, None, "both drains are idle");
if !exit_while_idle {
for seed in 30..32u8 {
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
contract(seed),
PeerKeyLocation::random(),
state(BIG),
)
.await;
}
assert_eq!(
lanes.next_dispatch().await,
Some((QueuedPayloadClass::Large, contract(30))),
"the first large send takes the only permit"
);
assert_eq!(
lanes.large_permits.available_permits(),
0,
"so the second is blocked in acquire_owned"
);
}
lanes.large_permits.close();
if exit_while_idle {
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
contract(21),
PeerKeyLocation::random(),
state(BIG),
)
.await;
}
let mut workers = std::mem::take(&mut lanes.workers);
let large_worker = workers.pop().expect("large drain");
tokio::time::timeout(Duration::from_secs(5), large_worker)
.await
.unwrap_or_else(|_| {
panic!("[idle={exit_while_idle}] the drain whose pool closed must exit")
})
.expect("drain must return rather than panic");
let small_worker = workers.pop().expect("small drain");
tokio::time::timeout(Duration::from_secs(5), small_worker)
.await
.unwrap_or_else(|_| {
panic!("[idle={exit_while_idle}] it must take the OTHER drain down with it")
})
.expect("drain must return rather than panic");
assert!(
lanes.small_permits.is_closed(),
"[idle={exit_while_idle}] the surviving lane's pool must be closed \
too, so it cannot keep draining as if healthy"
);
}
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn a_drain_that_stops_without_shutting_down_still_stops_the_other() {
let queue = BroadcastQueue::new();
let small_permits = Arc::new(Semaphore::new(12));
let large_permits = Arc::new(Semaphore::new(2));
let group = LaneGroup {
small_pool: small_permits.clone(),
large_pool: large_permits.clone(),
upgrade_slots: Arc::new(Semaphore::new(2)),
small_notify: queue.small_notify.clone(),
large_notify: queue.large_notify.clone(),
};
let (tx, mut ran) = tokio::sync::mpsc::unbounded_channel();
let workers: Vec<_> = [QueuedPayloadClass::Small, QueuedPayloadClass::Large]
.into_iter()
.map(|lane| {
let tx = tx.clone();
tokio::spawn(drain_lane(
lane,
queue.queue.clone(),
group.clone(),
move |entry: BroadcastEntry, _s: QueueScheduling| {
let tx = tx.clone();
async move {
let _ = tx.send((lane, entry.key));
}
},
))
})
.collect();
let [small, large]: [_; 2] = workers.try_into().expect("two drains");
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
contract(40),
PeerKeyLocation::random(),
state(BIG),
)
.await;
assert_eq!(
lane_of(
tokio::time::timeout(Duration::from_secs(5), ran.recv())
.await
.expect("the large drain must run before we stop it")
),
Some(QueuedPayloadClass::Large),
);
large.abort();
let _ = large.await;
assert!(
small_permits.is_closed(),
"the surviving drain's pool must be closed by the guard, so it \
cannot keep carrying half the traffic"
);
tokio::time::timeout(Duration::from_secs(5), small)
.await
.expect("the sibling drain must stop too")
.expect("it must return rather than panic");
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn mispredicting_burst_parks_boundedly_without_starving_the_large_lane() {
let queue = BroadcastQueue {
max_queue_depth: 256,
..BroadcastQueue::new()
};
let small_permits = Arc::new(Semaphore::new(12));
let large_permits = Arc::new(Semaphore::new(2));
let upgrade_slots = Arc::new(Semaphore::new(2));
let (tx, mut dispatched) = tokio::sync::mpsc::unbounded_channel();
let evictions_before = crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.snapshot()
.capacity_evictions;
let group = LaneGroup {
small_pool: small_permits.clone(),
large_pool: large_permits.clone(),
upgrade_slots: upgrade_slots.clone(),
small_notify: queue.small_notify.clone(),
large_notify: queue.large_notify.clone(),
};
let release = Arc::new(Semaphore::new(0));
let workers: Vec<_> = [QueuedPayloadClass::Small, QueuedPayloadClass::Large]
.into_iter()
.map(|lane| {
let tx = tx.clone();
let release = release.clone();
tokio::spawn(drain_lane(
lane,
queue.queue.clone(),
group.clone(),
move |entry: BroadcastEntry, mut scheduling: QueueScheduling| {
let tx = tx.clone();
let release = release.clone();
async move {
let _ = tx.send((lane, entry.key));
if matches!(lane, QueuedPayloadClass::Large) {
let _ = release.acquire_owned().await;
} else {
scheduling.permit.ensure_capacity_for(BIG).await;
}
}
},
))
})
.collect();
for seed in 0..2u8 {
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
contract(seed),
PeerKeyLocation::random(),
state(BIG),
)
.await;
}
for _ in 0..2 {
assert_eq!(
lane_of(
tokio::time::timeout(Duration::from_secs(5), dispatched.recv())
.await
.expect("both large permits must be taken and held")
),
Some(QueuedPayloadClass::Large),
"both large permits must be taken and held"
);
}
assert_eq!(large_permits.available_permits(), 0);
for seed in 10..26u8 {
queue
.enqueue_in_lane(
QueuedPayloadClass::Small,
contract(seed),
PeerKeyLocation::random(),
state(BIG),
)
.await;
}
for _ in 0..12 {
assert_eq!(
lane_of(
tokio::time::timeout(Duration::from_secs(5), dispatched.recv())
.await
.expect("the small lane dispatches up to its pool width")
),
Some(QueuedPayloadClass::Small),
"the small lane dispatches up to its pool width"
);
}
tokio::time::sleep(super::super::UPGRADE_SLOT_HOLD_WINDOW * 2).await;
assert_eq!(
upgrade_slots.available_permits(),
0,
"the burst must actually REACH the parking area — this is the \
assertion the first version of this test could not make"
);
let mut dispatched_small = 12usize;
while let Ok(Some((lane, _))) =
tokio::time::timeout(Duration::from_millis(1), dispatched.recv()).await
{
if matches!(lane, QueuedPayloadClass::Small) {
dispatched_small += 1;
}
}
assert_eq!(
dispatched_small, 14,
"dispatch must stop at 12 small permits + 2 parking slots; \
unbounded parking would have put all 16 in flight"
);
release.add_permits(1);
let key = contract(2);
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
key,
PeerKeyLocation::random(),
state(BIG),
)
.await;
let mut saw_large = false;
for _ in 0..4 {
match tokio::time::timeout(Duration::from_secs(5), dispatched.recv()).await {
Ok(Some((QueuedPayloadClass::Large, k))) if k == key => {
saw_large = true;
break;
}
Ok(Some(_)) => continue,
_ => break,
}
}
assert!(
saw_large,
"the large lane must keep draining while upgraders contend for \
its pool — starving it is what backs `large_order` up into \
evict_oldest and drops whole fan-outs (#5118)"
);
assert_eq!(
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.snapshot()
.capacity_evictions,
evictions_before,
"and nothing was evicted — dropping queued entries under a \
misprediction burst is the user-visible half of #5118, since \
eviction walks seq order and a fan-out's per-peer entries form \
a temporal cluster"
);
release.add_permits(64);
for worker in workers {
worker.abort();
}
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn small_entry_is_scheduled_while_the_large_lane_is_saturated() {
let queue = BroadcastQueue::new();
let mut lanes = spawn_lanes(&queue, 12, 2);
for seed in 0..2u8 {
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
contract(seed),
PeerKeyLocation::random(),
state(BIG),
)
.await;
}
for _ in 0..2 {
let (lane, _) = lanes
.next_dispatch()
.await
.expect("both large sends must start");
assert_eq!(lane, QueuedPayloadClass::Large);
}
assert_eq!(
lanes.large_permits.available_permits(),
0,
"precondition: the large lane is saturated"
);
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
contract(2),
PeerKeyLocation::random(),
state(BIG),
)
.await;
assert_eq!(lanes.small_permits.available_permits(), 12);
let small = contract(3);
queue
.enqueue_in_lane(
QueuedPayloadClass::Small,
small,
PeerKeyLocation::random(),
state(16),
)
.await;
assert_eq!(
lanes.next_dispatch().await,
Some((QueuedPayloadClass::Small, small)),
"a small entry must not wait on large-lane capacity it does not need"
);
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn large_state_with_an_expected_delta_takes_the_small_lane() {
assert_eq!(
classify_payload_lane(BIG, true),
QueuedPayloadClass::Small,
"a delta-sized payload belongs in the small lane however big the state is"
);
assert_eq!(
classify_payload_lane(BIG, false),
QueuedPayloadClass::Large,
"without a cached summary the whole state goes on the wire"
);
assert_eq!(
classify_payload_lane(BIG - 1, false),
QueuedPayloadClass::Large,
"the threshold itself is large (the comparison is `< threshold`)"
);
assert_eq!(
classify_payload_lane(BIG - 2, false),
QueuedPayloadClass::Small,
"a state below the threshold is small whatever the payload shape"
);
let queue = BroadcastQueue::new();
let mut lanes = spawn_lanes(&queue, 12, 2);
let blocker = lanes
.large_permits
.clone()
.acquire_many_owned(2)
.await
.expect("large pool open");
let key = contract(9);
queue
.enqueue_in_lane(
classify_payload_lane(BIG, true),
key,
PeerKeyLocation::random(),
state(BIG),
)
.await;
assert_eq!(
lanes.next_dispatch().await,
Some((QueuedPayloadClass::Small, key)),
"a delta send on a large-state contract must not queue behind full-state capacity"
);
assert_eq!(
lanes.small_permits.available_permits(),
11,
"and it must have taken a small-lane permit, not a large one"
);
drop(blocker);
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn dedup_replacement_relanes_and_refreshes_state_size() {
let queue = BroadcastQueue::new();
let key = contract(5);
let target = PeerKeyLocation::random();
queue
.enqueue_in_lane(QueuedPayloadClass::Small, key, target.clone(), state(16))
.await;
queue
.enqueue_in_lane(QueuedPayloadClass::Large, key, target.clone(), state(BIG))
.await;
{
let q = queue.queue.lock().await;
assert_eq!(q.entries.len(), 1, "dedup replaces in place");
let entry = q.entries.values().next().expect("the replaced entry");
assert_eq!(
entry.lane,
QueuedPayloadClass::Large,
"the replacement state re-lanes the entry"
);
assert_eq!(
entry.state_size, BIG,
"state_size must follow the replacement, not the superseded state"
);
assert!(q.small_order.is_empty(), "the small lane must let it go");
assert_eq!(q.large_order.len(), 1, "and the large lane must own it");
assert_eq!(
q.small_queued, 0,
"small-entry accounting follows the re-lane"
);
}
queue
.enqueue_in_lane(QueuedPayloadClass::Small, key, target, state(16))
.await;
let q = queue.queue.lock().await;
let entry = q.entries.values().next().expect("the replaced entry");
assert_eq!(entry.lane, QueuedPayloadClass::Small);
assert_eq!(entry.state_size, 16);
assert!(q.large_order.is_empty());
assert_eq!(q.small_order.len(), 1);
assert_eq!(q.small_queued, 1);
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn capacity_eviction_drops_the_globally_oldest_across_lanes() {
let queue = BroadcastQueue {
max_queue_depth: 2,
..BroadcastQueue::new()
};
let oldest = contract(1);
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
oldest,
PeerKeyLocation::random(),
state(BIG),
)
.await;
for seed in 2..4u8 {
queue
.enqueue_in_lane(
QueuedPayloadClass::Small,
contract(seed),
PeerKeyLocation::random(),
state(16),
)
.await;
}
let q = queue.queue.lock().await;
assert_eq!(q.entries.len(), 2, "the depth cap still holds");
assert!(
!q.entries.keys().any(|(key, _)| *key == oldest),
"the evicted entry must be the globally oldest, even though it sat in the other lane"
);
assert!(q.large_order.is_empty());
assert_eq!(q.small_order.len(), 2);
assert_eq!(q.small_queued, 2);
drop(q);
let queue = BroadcastQueue {
max_queue_depth: 2,
..BroadcastQueue::new()
};
let oldest = contract(5);
queue
.enqueue_in_lane(
QueuedPayloadClass::Small,
oldest,
PeerKeyLocation::random(),
state(16),
)
.await;
for seed in 6..8u8 {
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
contract(seed),
PeerKeyLocation::random(),
state(BIG),
)
.await;
}
let q = queue.queue.lock().await;
assert_eq!(q.entries.len(), 2);
assert!(
!q.entries.keys().any(|(key, _)| *key == oldest),
"the small entry was the globally oldest, so it is the one evicted"
);
assert!(q.small_order.is_empty());
assert_eq!(q.large_order.len(), 2);
assert_eq!(q.small_queued, 0, "and the small-lane count follows it");
}
#[tokio::test(start_paused = true)]
#[serial_test::serial(broadcast_queue_depth_gauge)]
async fn each_lane_is_woken_by_its_own_enqueue() {
let queue = BroadcastQueue::new();
let mut lanes = spawn_lanes(&queue, 12, 2);
assert_eq!(lanes.next_dispatch().await, None, "nothing queued yet");
let key = contract(11);
queue
.enqueue_in_lane(
QueuedPayloadClass::Large,
key,
PeerKeyLocation::random(),
state(BIG),
)
.await;
assert_eq!(
lanes.next_dispatch().await,
Some((QueuedPayloadClass::Large, key)),
"a large enqueue must wake the LARGE worker"
);
}
fn small_lane_permit(small: &Arc<Semaphore>, large: &Arc<Semaphore>) -> SendLanePermit {
parked_small_lane_permit(
small,
large,
&Arc::new(Semaphore::new(super::super::MAX_PARKED_LANE_UPGRADES)),
)
}
fn parked_small_lane_permit(
small: &Arc<Semaphore>,
large: &Arc<Semaphore>,
parking: &Arc<Semaphore>,
) -> SendLanePermit {
SendLanePermit::new(
QueuedPayloadClass::Small,
small
.clone()
.try_acquire_owned()
.expect("small pool has capacity"),
large.clone(),
parking.clone(),
)
}
#[tokio::test(start_paused = true)]
async fn mispredicted_large_payload_waits_for_large_lane_capacity() {
let small = Arc::new(Semaphore::new(12));
let large = Arc::new(Semaphore::new(1));
let mut permit = small_lane_permit(&small, &large);
permit.ensure_capacity_for(1_000).await;
permit.ensure_capacity_for(BIG - 2).await;
assert_eq!(
large.available_permits(),
1,
"no upgrade for a payload below the threshold"
);
assert_eq!(small.available_permits(), 11);
let occupied = large
.clone()
.acquire_owned()
.await
.expect("large pool open");
let mut upgrading = tokio::spawn(async move {
permit.ensure_capacity_for(BIG - 1).await;
permit
});
assert!(
tokio::time::timeout(super::super::UPGRADE_SLOT_HOLD_WINDOW / 2, &mut upgrading)
.await
.is_err(),
"a payload at the threshold must wait for large-lane capacity"
);
assert_eq!(
small.available_permits(),
11,
"within the hold window the small permit is kept, so the drain \
cannot dispatch more concurrent sends than the pools are sized for"
);
assert!(
tokio::time::timeout(super::super::UPGRADE_SLOT_HOLD_WINDOW * 2, &mut upgrading)
.await
.is_err(),
"and it still must not send without large-lane capacity"
);
assert_eq!(
small.available_permits(),
12,
"past the hold window the small-lane slot is released"
);
drop(occupied);
let permit = tokio::time::timeout(Duration::from_secs(5), upgrading)
.await
.expect("the upgrade completes once capacity frees up")
.expect("upgrade task");
assert_eq!(
large.available_permits(),
0,
"the send now holds the large-lane permit"
);
assert_eq!(small.available_permits(), 12);
drop(permit);
assert_eq!(large.available_permits(), 1, "and releases it when done");
}
#[test]
fn parking_matches_one_small_pool_turnover() {
assert_eq!(
super::super::MAX_PARKED_LANE_UPGRADES,
DEFAULT_SMALL_PAYLOAD_CONCURRENCY,
"at most one full turnover of the small pool may be parked at once"
);
}
#[tokio::test(start_paused = true)]
async fn large_lane_send_does_not_upgrade_against_itself() {
let large = Arc::new(Semaphore::new(1));
let mut permit = SendLanePermit::new(
QueuedPayloadClass::Large,
large
.clone()
.try_acquire_owned()
.expect("large pool has capacity"),
large.clone(),
Arc::new(Semaphore::new(super::super::MAX_PARKED_LANE_UPGRADES)),
);
assert_eq!(large.available_permits(), 0, "the pool is now empty");
let started = tokio::time::Instant::now();
permit.ensure_capacity_for(BIG).await;
assert_eq!(
tokio::time::Instant::now(),
started,
"a large-lane send must not wait on its own pool"
);
assert_eq!(
large.available_permits(),
0,
"and must still hold the permit it started with"
);
drop(permit);
assert_eq!(large.available_permits(), 1);
}
#[tokio::test(start_paused = true)]
async fn parking_area_is_bounded_and_returns_its_slots() {
let small = Arc::new(Semaphore::new(12));
let large = Arc::new(Semaphore::new(1));
let parking = Arc::new(Semaphore::new(1));
let occupied = large
.clone()
.acquire_owned()
.await
.expect("large pool open");
let mut first = parked_small_lane_permit(&small, &large, &parking);
let mut second = parked_small_lane_permit(&small, &large, &parking);
assert_eq!(small.available_permits(), 10, "both hold a small slot");
let mut parked = tokio::spawn(async move {
first.ensure_capacity_for(BIG).await;
first
});
assert!(
tokio::time::timeout(super::super::UPGRADE_SLOT_HOLD_WINDOW * 2, &mut parked)
.await
.is_err()
);
assert_eq!(
parking.available_permits(),
0,
"the parked send is holding the only parking slot"
);
assert_eq!(
small.available_permits(),
11,
"and gave its small-lane slot back"
);
let mut waiting = tokio::spawn(async move {
second.ensure_capacity_for(BIG).await;
second
});
assert!(
tokio::time::timeout(super::super::UPGRADE_SLOT_HOLD_WINDOW * 4, &mut waiting)
.await
.is_err(),
"no large-lane capacity yet, so it is still waiting"
);
assert_eq!(
small.available_permits(),
11,
"with parking full the second send keeps its small-lane slot — \
backpressure, not an unbounded parked set"
);
drop(occupied);
let first = tokio::time::timeout(Duration::from_secs(5), parked)
.await
.expect("first send completes")
.expect("task");
assert_eq!(
parking.available_permits(),
0,
"the released slot goes straight to the send waiting for one"
);
assert_eq!(
small.available_permits(),
12,
"so the second send parks too, and the small lane is free again"
);
drop(first);
let second = tokio::time::timeout(Duration::from_secs(5), waiting)
.await
.expect("second send completes once capacity frees up")
.expect("task");
assert_eq!(small.available_permits(), 12);
assert_eq!(large.available_permits(), 0);
assert_eq!(
parking.available_permits(),
1,
"and the parking area is empty once both sends have left it"
);
drop(second);
assert_eq!(large.available_permits(), 1);
}
#[derive(Debug, Clone, Copy)]
enum Phase {
HoldingSlot,
Parked,
QueuedForParking,
}
#[tokio::test(start_paused = true)]
async fn cancelling_a_waiting_send_releases_its_permit_in_either_phase() {
for phase in [Phase::HoldingSlot, Phase::Parked, Phase::QueuedForParking] {
let park_first = !matches!(phase, Phase::HoldingSlot);
let small = Arc::new(Semaphore::new(12));
let large = Arc::new(Semaphore::new(2));
let _occupied = large
.clone()
.acquire_many_owned(2)
.await
.expect("large pool open");
let parking = Arc::new(Semaphore::new(1));
let _squatter = matches!(phase, Phase::QueuedForParking)
.then(|| parking.clone().try_acquire_owned().expect("parking free"));
let mut permit = parked_small_lane_permit(&small, &large, &parking);
let mut waiting = tokio::spawn(async move {
permit.ensure_capacity_for(BIG).await;
permit
});
let elapse = if park_first {
super::super::UPGRADE_SLOT_HOLD_WINDOW * 2
} else {
super::super::UPGRADE_SLOT_HOLD_WINDOW / 2
};
assert!(
tokio::time::timeout(elapse, &mut waiting).await.is_err(),
"[park_first={park_first}] precondition: still waiting"
);
let parked_now = matches!(phase, Phase::Parked);
assert_eq!(
parking.available_permits(),
usize::from(matches!(phase, Phase::HoldingSlot)),
"[{phase:?}] precondition: holds a parking slot iff parked"
);
assert_eq!(
small.available_permits(),
if parked_now { 12 } else { 11 },
"[{phase:?}] precondition: still holds its small-lane slot unless parked"
);
waiting.abort();
let cancelled = match waiting.await {
Err(err) => err.is_cancelled(),
Ok(_) => false,
};
assert!(
cancelled,
"[{phase:?}] the send must have been cancelled mid-wait"
);
assert_eq!(
small.available_permits(),
12,
"[{phase:?}] the small-lane slot must come back"
);
drop(_squatter);
assert_eq!(
parking.available_permits(),
1,
"[{phase:?}] and so must the parking slot"
);
}
}
#[tokio::test(start_paused = true)]
async fn closed_large_pool_does_not_strand_the_send() {
let small = Arc::new(Semaphore::new(12));
let large = Arc::new(Semaphore::new(1));
let mut permit = small_lane_permit(&small, &large);
large.close();
tokio::time::timeout(Duration::from_secs(5), permit.ensure_capacity_for(BIG))
.await
.expect("a closed pool must not block the send forever");
drop(permit);
assert_eq!(
small.available_permits(),
12,
"and the small-lane permit is still released exactly once"
);
}
#[tokio::test(start_paused = true)]
async fn large_pool_closing_mid_park_does_not_strand_the_send() {
let small = Arc::new(Semaphore::new(12));
let large = Arc::new(Semaphore::new(1));
let parking = Arc::new(Semaphore::new(1));
let _occupied = large
.clone()
.acquire_owned()
.await
.expect("large pool open");
let mut permit = parked_small_lane_permit(&small, &large, &parking);
let mut waiting = tokio::spawn(async move {
permit.ensure_capacity_for(BIG).await;
permit
});
assert!(
tokio::time::timeout(super::super::UPGRADE_SLOT_HOLD_WINDOW * 2, &mut waiting)
.await
.is_err(),
"precondition: parked, waiting on a pool that will never free"
);
assert_eq!(small.available_permits(), 12, "precondition: parked");
large.close();
let permit = tokio::time::timeout(Duration::from_secs(5), waiting)
.await
.expect("closing the pool must release the parked send")
.expect("task");
drop(permit);
assert_eq!(
parking.available_permits(),
1,
"and the parking slot goes back"
);
}
#[test]
fn delta_send_expected_requires_a_cached_summary_and_an_unarmed_memo() {
use crate::ring::delta_incompat::{DeltaIncompat, INCOMPAT_TRIP_THRESHOLD};
use crate::ring::interest::InterestManager;
use crate::util::time_source::{DynTimeSource, SharedMockTimeSource};
use freenet_stdlib::prelude::StateSummary;
let clock = SharedMockTimeSource::new();
let time: DynTimeSource = Arc::new(clock);
let interest = InterestManager::new(time.clone());
let memo = DeltaIncompat::new(time);
let key = contract(3);
let target = PeerKeyLocation::random();
let peer = PeerKey::from(target.pub_key().clone());
assert!(
!delta_send_expected(&interest, &memo, &key, &target),
"no interest entry at all: the first send must be full state"
);
interest.register_peer_interest(&key, peer.clone(), None, false);
assert!(
!delta_send_expected(&interest, &memo, &key, &target),
"interested but summary-less: nothing to compute a delta against"
);
interest.update_peer_summary(&key, &peer, StateSummary::from(vec![1, 2, 3]));
assert!(
delta_send_expected(&interest, &memo, &key, &target),
"a cached summary is what makes the send a delta"
);
let addr = |port: u16| -> std::net::SocketAddr {
format!("127.0.0.1:{port}").parse().unwrap()
};
for i in 0..INCOMPAT_TRIP_THRESHOLD {
memo.record_delta_sent(*key.id(), addr(6000 + i as u16));
memo.note_resync_request(*key.id(), addr(6000 + i as u16));
}
assert!(
memo.deltas_suppressed_peek(key.id()),
"precondition: the memo is armed"
);
assert!(
!delta_send_expected(&interest, &memo, &key, &target),
"an armed memo means full state, cached summary or not"
);
}
#[test]
fn lane_for_routes_a_large_state_delta_send_to_the_small_lane() {
use crate::ring::delta_incompat::{DeltaIncompat, INCOMPAT_TRIP_THRESHOLD};
use crate::ring::interest::InterestManager;
use crate::util::time_source::{DynTimeSource, SharedMockTimeSource};
use freenet_stdlib::prelude::StateSummary;
let time: DynTimeSource = Arc::new(SharedMockTimeSource::new());
let interest = InterestManager::new(time.clone());
let memo = DeltaIncompat::new(time);
let key = contract(4);
let target = PeerKeyLocation::random();
let peer = PeerKey::from(target.pub_key().clone());
let lane = |state_size| lane_for(&interest, &memo, &key, &target, state_size);
assert_eq!(
lane(BIG),
QueuedPayloadClass::Large,
"no cached summary: the whole state goes on the wire"
);
assert_eq!(
lane(16),
QueuedPayloadClass::Small,
"a small state is small however the payload is shaped"
);
interest.register_peer_interest(&key, peer.clone(), None, false);
interest.update_peer_summary(&key, &peer, StateSummary::from(vec![1, 2, 3]));
assert_eq!(
lane(BIG),
QueuedPayloadClass::Small,
"THE #4961 CASE: a >64 KiB contract sending a delta to a peer whose \
summary we hold must not spend one of the 2 large-lane permits"
);
let addr = |port: u16| -> std::net::SocketAddr {
format!("127.0.0.1:{port}").parse().unwrap()
};
for i in 0..INCOMPAT_TRIP_THRESHOLD {
memo.record_delta_sent(*key.id(), addr(7000 + i as u16));
memo.note_resync_request(*key.id(), addr(7000 + i as u16));
}
assert_eq!(
lane(BIG),
QueuedPayloadClass::Large,
"...but once the contract is known to reject deltas, it is a \
full-state send again"
);
}
}
}
#[cfg(not(feature = "simulation_tests"))]
pub(crate) use queue::BroadcastQueue;
fn streaming_completion_delivered(completion: StreamCompletionResult) -> bool {
matches!(completion, Ok(Ok(BroadcastDeliveryOutcome::Delivered)))
}
type StreamCompletionResult = Result<
Result<BroadcastDeliveryOutcome, tokio::sync::oneshot::error::RecvError>,
tokio::time::error::Elapsed,
>;
#[allow(clippy::too_many_arguments)]
fn record_streaming_delivery<T: crate::util::time_source::TimeSource + Sync>(
interest_manager: &crate::ring::interest::InterestManager<T>,
completion: StreamCompletionResult,
sent_delta: bool,
key: &ContractKey,
peer_key: &crate::ring::PeerKey,
our_summary: Option<&freenet_stdlib::prelude::StateSummary<'static>>,
state_size: usize,
payload_size: usize,
) -> bool {
let delivered = streaming_completion_delivered(completion);
if delivered {
record_delivery_to_interest(
interest_manager,
sent_delta,
key,
peer_key,
our_summary,
state_size,
payload_size,
);
}
delivered
}
fn record_delivery_to_interest<T: crate::util::time_source::TimeSource + Sync>(
interest_manager: &crate::ring::interest::InterestManager<T>,
sent_delta: bool,
key: &ContractKey,
peer_key: &crate::ring::PeerKey,
our_summary: Option<&freenet_stdlib::prelude::StateSummary<'static>>,
state_size: usize,
payload_size: usize,
) {
if sent_delta {
interest_manager.record_delta_send(state_size, payload_size);
crate::config::GlobalTestMetrics::record_delta_send();
} else {
interest_manager.record_full_state_send();
crate::config::GlobalTestMetrics::record_full_state_send();
}
interest_manager.refresh_peer_interest(key, peer_key);
if let Some(summary) = our_summary {
interest_manager.upsert_peer_summary_from(
key,
peer_key,
summary.clone(),
crate::ring::interest::SummaryPopulationSource::Delivery,
);
}
}
pub(super) async fn broadcast_to_single_peer(
bridge: &P2pBridge,
op_manager: &Arc<OpManager>,
key: ContractKey,
new_state: WrappedState,
target: PeerKeyLocation,
scheduling: Option<QueueScheduling>,
) {
use crate::message::{DeltaOrFullState, NetMessage};
use crate::node::network_bridge::NetworkBridge;
use crate::operations::update::{BroadcastStreamingPayload, UpdateMsg};
use crate::ring::PeerKey;
use crate::transport::peer_connection::StreamId;
let Some(peer_addr) = target.socket_addr() else {
return;
};
let (queued_class, mut lane_permit) = match scheduling {
Some(QueueScheduling {
queued_class,
permit,
}) => (Some(queued_class), Some(permit)),
None => (None, None),
};
if !should_broadcast_contract(op_manager, &key) {
tracing::trace!(
contract = %key,
peer = %peer_addr,
"Skipping broadcast - contract not hosted or in use"
);
return;
}
let peer_key = PeerKey::from(target.pub_key().clone());
let cost_clock = op_manager.ring.time_source.clone();
let send_wasm_started = cost_clock.now();
let report_send_cpu = |op_manager: &Arc<OpManager>| {
use crate::topology::meter::ResourceType;
let elapsed_us = cost_clock
.now()
.saturating_duration_since(send_wasm_started)
.as_micros() as f64;
op_manager.ring.report_contract_resource_usage(
*key.id(),
ResourceType::ExecCpuMicros,
elapsed_us,
);
};
let our_summary = op_manager
.interest_manager
.get_contract_summary(op_manager, &key)
.await;
let (their_summary, tracked_missing_reason, missing_attempt) = if our_summary.is_some() {
match op_manager
.interest_manager
.begin_peer_summary_broadcast(&key, &peer_key)
{
crate::ring::interest::PeerSummaryForBroadcast::Known(summary) => {
(Some(summary), None, None)
}
crate::ring::interest::PeerSummaryForBroadcast::Missing { reason, attempt } => {
(None, reason, attempt)
}
}
} else {
(None, None, None)
};
let mut missing_attempt_guard = missing_attempt.map(|attempt| {
op_manager
.interest_manager
.missing_summary_attempt_guard(attempt)
});
if let (Some(ours), Some(theirs)) = (&our_summary, &their_summary) {
let mut staleness_probes_used = 0usize;
if !fanout_send_needed(
op_manager,
&key,
SummaryPair { ours, theirs },
&mut staleness_probes_used,
)
.await
{
tracing::trace!(
contract = %key,
peer = %peer_addr,
"Skipping broadcast - peer already has our state (byte-equal \
or logically converged summaries)"
);
op_manager
.interest_manager
.refresh_peer_interest(&key, &peer_key);
report_send_cpu(op_manager);
return;
}
}
let deltas_suppressed = op_manager.ring.delta_incompat.suppress_deltas(key.id());
if deltas_suppressed {
tracing::debug!(
contract = %key,
peer = %peer_addr,
event = "delta_suppressed_incompat",
"Contract is in delta-incompat backoff — sending full state instead of a delta"
);
}
let mut not_efficient_gate_inputs: Option<(usize, usize)> = None;
let (payload, sent_delta, payload_arm) = match (&our_summary, &their_summary) {
(Some(_), Some(_)) if deltas_suppressed => (
DeltaOrFullState::FullState(new_state.as_ref().to_vec()),
false,
PayloadArm::FullDeltaSuppressed,
),
(Some(ours), Some(theirs)) => {
match op_manager
.interest_manager
.compute_delta(op_manager, &key, theirs, ours, new_state.size())
.await
{
Ok(Some(delta)) => (
DeltaOrFullState::Delta(delta.as_ref().to_vec()),
true,
PayloadArm::Delta,
),
Ok(None) => {
tracing::trace!(
contract = %key,
peer = %peer_addr,
"Skipping broadcast - contract reported empty delta \
(peer converged)"
);
op_manager
.interest_manager
.refresh_peer_interest(&key, &peer_key);
report_send_cpu(op_manager);
return;
}
Err(err) => {
tracing::debug!(
contract = %key,
error = %err,
"Delta computation failed, falling back to full state"
);
let arm = match err {
crate::ring::interest::DeltaUnavailable::NotEfficient {
summary_size,
state_size,
} => {
not_efficient_gate_inputs = Some((summary_size, state_size));
PayloadArm::FullNotEfficient
}
crate::ring::interest::DeltaUnavailable::ComputeFailed(_) => {
PayloadArm::FullComputeFailed
}
};
(
DeltaOrFullState::FullState(new_state.as_ref().to_vec()),
false,
arm,
)
}
}
}
(None, _) => (
DeltaOrFullState::FullState(new_state.as_ref().to_vec()),
false,
PayloadArm::FullNoOurSummary,
),
(Some(_), None) => {
let arm = if tracked_missing_reason.is_some() {
PayloadArm::FullNoTheirSummaryTracked
} else {
PayloadArm::FullNoTheirSummaryUntracked
};
(
DeltaOrFullState::FullState(new_state.as_ref().to_vec()),
false,
arm,
)
}
};
let payload_size = payload.size();
match (
queued_class,
payload_size < BROADCAST_QUEUE_PAYLOAD_SIZE_THRESHOLD,
) {
(Some(QueuedPayloadClass::Large), true) => {
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.record_queued_large_actual_small(payload_size);
}
(Some(QueuedPayloadClass::Small), false) => {
crate::node::BROADCAST_QUEUE_EFFICIENCY_METRICS
.record_queued_small_actual_large(payload_size);
}
_ => {}
}
report_send_cpu(op_manager);
if let Some(permit) = lane_permit.as_mut() {
permit.ensure_capacity_for(payload_size).await;
}
let update_tx = crate::message::Transaction::new::<crate::operations::update::UpdateMsg>();
let use_streaming = matches!(&payload, DeltaOrFullState::FullState(_))
&& crate::operations::should_use_streaming(op_manager.streaming_threshold, payload_size);
let send_result = if use_streaming {
let sender_summary_bytes = our_summary
.as_ref()
.map(|s| s.as_ref().to_vec())
.unwrap_or_default();
let state_bytes = match payload {
DeltaOrFullState::FullState(data) => data,
_ => unreachable!("checked above"),
};
let streaming_payload = BroadcastStreamingPayload {
state_bytes,
sender_summary_bytes,
};
let payload_bytes = match bincode::serialize(&streaming_payload) {
Ok(b) => b,
Err(e) => {
tracing::warn!(
tx = %update_tx,
error = %e,
"Failed to serialize BroadcastStreamingPayload, skipping"
);
return;
}
};
let sid = StreamId::next_operations();
tracing::debug!(
tx = %update_tx,
contract = %key,
peer = %peer_addr,
stream_id = %sid,
payload_size,
"Using streaming for BroadcastTo (via queue)"
);
let msg = UpdateMsg::BroadcastToStreaming {
id: update_tx,
stream_id: sid,
key,
total_size: payload_bytes.len() as u64,
};
let net_msg: NetMessage = msg.into();
let metadata = match bincode::serialize(&net_msg) {
Ok(bytes) => Some(bytes::Bytes::from(bytes)),
Err(e) => {
tracing::warn!(
?peer_addr,
error = %e,
"Failed to serialize BroadcastTo metadata for embedding"
);
None
}
};
let send_res = bridge.send(peer_addr, net_msg).await;
if send_res.is_err() {
BROADCAST_STREAM_METRICS.record_attempt(false);
} else {
let (completion_tx, completion_rx) = tokio::sync::oneshot::channel();
if let Err(err) = bridge
.send_stream_with_completion(
peer_addr,
sid,
bytes::Bytes::from(payload_bytes),
metadata,
Some(completion_tx),
None,
)
.await
{
BROADCAST_STREAM_METRICS.record_attempt(false);
tracing::warn!(
tx = %update_tx,
peer = %peer_addr,
error = %err,
"Failed to send broadcast stream data"
);
} else {
let completion =
tokio::time::timeout(STREAM_COMPLETION_TIMEOUT, completion_rx).await;
let delivered = record_streaming_delivery(
&op_manager.interest_manager,
completion,
sent_delta,
&key,
&peer_key,
our_summary.as_ref(),
new_state.size(),
payload_size,
);
BROADCAST_STREAM_METRICS.record_attempt(delivered);
if delivered {
if let Some(guard) = missing_attempt_guard.as_mut() {
guard.mark_delivered(payload_size);
}
op_manager.ring.report_contract_resource_usage(
*key.id(),
crate::topology::meter::ResourceType::BroadcastFanoutCost,
payload_size as f64,
);
op_manager.payload_mix.record_delivered(
payload_arm,
key.id(),
payload_size,
not_efficient_gate_inputs,
tracked_missing_reason,
);
tracing::debug!(
tx = %update_tx,
peer = %peer_addr,
"Broadcast stream completed successfully"
);
} else {
tracing::debug!(
tx = %update_tx,
peer = %peer_addr,
timeout_secs = STREAM_COMPLETION_TIMEOUT.as_secs(),
"Broadcast stream dropped or timed out before delivery \
(permit released, interest NOT refreshed)"
);
}
}
}
send_res
} else {
let msg = UpdateMsg::BroadcastTo {
id: update_tx,
key,
payload,
sender_summary_bytes: our_summary
.as_ref()
.map(|s| s.as_ref().to_vec())
.unwrap_or_default(),
};
let res = bridge.send(peer_addr, msg.into()).await;
if res.is_ok() {
if let Some(guard) = missing_attempt_guard.as_mut() {
guard.mark_delivered(payload_size);
}
op_manager.ring.report_contract_resource_usage(
*key.id(),
crate::topology::meter::ResourceType::BroadcastFanoutCost,
payload_size as f64,
);
op_manager.payload_mix.record_delivered(
payload_arm,
key.id(),
payload_size,
not_efficient_gate_inputs,
tracked_missing_reason,
);
if sent_delta {
op_manager
.ring
.delta_incompat
.record_delta_sent(*key.id(), peer_addr);
}
record_delivery_to_interest(
&op_manager.interest_manager,
sent_delta,
&key,
&peer_key,
our_summary.as_ref(),
new_state.size(),
payload_size,
);
}
res
};
if let Err(err) = &send_result {
tracing::warn!(
tx = %update_tx,
peer = %peer_addr,
error = %err,
"Failed to send state change broadcast (queued)"
);
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use freenet_stdlib::prelude::{
CodeHash, ContractInstanceId, ContractKey, StateDelta, StateSummary,
};
use crate::ring::PeerKey;
use crate::ring::interest::InterestManager;
use crate::transport::{BroadcastDeliveryOutcome, TransportKeypair};
use crate::util::time_source::SharedMockTimeSource;
use super::{
BroadcastStreamMetrics, FanoutSendPlan, SummaryPair, plan_fanout_send,
record_streaming_delivery, streaming_completion_delivered,
};
#[test]
fn broadcast_stream_metrics_counts_attempts_and_failures() {
let m = BroadcastStreamMetrics::new();
let s = m.snapshot();
assert_eq!(s.streaming_attempts_total, 0, "starts at zero");
assert_eq!(s.streaming_failures_total, 0, "starts at zero");
m.record_attempt(true);
let s = m.snapshot();
assert_eq!(s.streaming_attempts_total, 1);
assert_eq!(s.streaming_failures_total, 0, "delivered is not a failure");
m.record_attempt(false);
let s = m.snapshot();
assert_eq!(s.streaming_attempts_total, 2, "every attempt counts");
assert_eq!(s.streaming_failures_total, 1, "the drop is counted");
m.record_attempt(false);
m.record_attempt(true);
let s = m.snapshot();
assert_eq!(s.streaming_attempts_total, 4);
assert_eq!(s.streaming_failures_total, 2);
}
fn make_contract_key(seed: u8) -> ContractKey {
ContractKey::from_id_and_code(
ContractInstanceId::new([seed; 32]),
CodeHash::new([seed.wrapping_add(1); 32]),
)
}
fn make_peer_key() -> PeerKey {
PeerKey(TransportKeypair::new().public().clone())
}
async fn dropped_oneshot()
-> Result<BroadcastDeliveryOutcome, tokio::sync::oneshot::error::RecvError> {
let (tx, rx) = tokio::sync::oneshot::channel::<BroadcastDeliveryOutcome>();
drop(tx);
rx.await.map(|_| unreachable!("sender was dropped"))
}
async fn elapsed_timeout() -> tokio::time::error::Elapsed {
let (tx, rx) = tokio::sync::oneshot::channel::<BroadcastDeliveryOutcome>();
let res = tokio::time::timeout(Duration::from_millis(1), rx).await;
drop(tx);
res.expect_err("never-resolving recv must time out")
}
#[tokio::test]
async fn streaming_completion_delivered_only_on_explicit_delivery() {
assert!(
streaming_completion_delivered(Ok(Ok(BroadcastDeliveryOutcome::Delivered))),
"an explicit Delivered outcome must be treated as a delivery"
);
assert!(
!streaming_completion_delivered(Ok(Ok(BroadcastDeliveryOutcome::Dropped))),
"an explicit Dropped outcome must NOT be treated as a delivery (#4235)"
);
assert!(
!streaming_completion_delivered(Ok(dropped_oneshot().await)),
"a dropped completion oneshot must NOT be treated as a delivery (#4235)"
);
assert!(
!streaming_completion_delivered(Err(elapsed_timeout().await)),
"a completion-wait timeout must NOT be treated as a delivery (#4235)"
);
}
#[tokio::test]
async fn drop_outcome_does_not_refresh_interest_or_cache_summary() {
let our_summary = StateSummary::from(vec![9, 9, 9, 9]);
let dropped = dropped_oneshot().await;
let timed_out = elapsed_timeout().await;
let cases: Vec<(&str, super::StreamCompletionResult, bool)> = vec![
(
"delivered",
Ok(Ok(BroadcastDeliveryOutcome::Delivered)),
true,
),
(
"explicit-drop",
Ok(Ok(BroadcastDeliveryOutcome::Dropped)),
false,
),
("dropped-oneshot", Ok(dropped), false),
("timeout", Err(timed_out), false),
];
for (name, completion, expect_delivered) in cases {
let time_source = SharedMockTimeSource::new();
let manager = InterestManager::new(time_source.clone());
let contract = make_contract_key(7);
let peer = make_peer_key();
manager.register_peer_interest(&contract, peer.clone(), None, false);
let baseline = manager
.get_peer_interest(&contract, &peer)
.expect("peer interest registered")
.last_refreshed;
time_source.advance_time(Duration::from_secs(5));
let delivered = record_streaming_delivery(
&manager,
completion,
true,
&contract,
&peer,
Some(&our_summary),
1024,
64,
);
assert_eq!(
delivered, expect_delivered,
"[{name}] classification mismatch"
);
let interest = manager
.get_peer_interest(&contract, &peer)
.expect("peer interest still registered");
if expect_delivered {
assert!(
interest.last_refreshed > baseline,
"[{name}] a real delivery MUST refresh the peer interest TTL"
);
assert_eq!(
manager.get_peer_summary(&contract, &peer),
Some(our_summary.clone()),
"[{name}] a real delivery MUST cache the peer summary"
);
} else {
assert_eq!(
interest.last_refreshed, baseline,
"[{name}] a dropped/timed-out broadcast MUST NOT refresh the \
peer interest TTL (#4235)"
);
assert_eq!(
manager.get_peer_summary(&contract, &peer),
None,
"[{name}] a dropped/timed-out broadcast MUST NOT cache the peer \
summary, or the next summary-mismatch resend is suppressed (#4235)"
);
}
}
}
#[tokio::test]
async fn full_state_delivery_caches_summary_so_next_broadcast_is_delta() {
let our_summary = StateSummary::from(vec![1, 2, 3, 4]);
let time_source = SharedMockTimeSource::new();
let manager = InterestManager::new(time_source.clone());
let contract = make_contract_key(42);
let peer = make_peer_key();
manager.register_peer_interest(&contract, peer.clone(), None, false);
assert_eq!(
manager.get_peer_summary(&contract, &peer),
None,
"precondition: a brand-new subscriber has no cached summary, so the \
first broadcast must be full state"
);
let delivered = record_streaming_delivery(
&manager,
Ok(Ok(BroadcastDeliveryOutcome::Delivered)),
false,
&contract,
&peer,
Some(&our_summary),
4096,
4096,
);
assert!(delivered, "a Delivered outcome must classify as delivered");
assert_eq!(
manager.get_peer_summary(&contract, &peer),
Some(our_summary.clone()),
"#4145: a delivered FULL-STATE broadcast must cache the peer summary, \
so the next broadcast can be a delta — otherwise the peer is trapped \
sending full state forever (the #4233 storm)"
);
let their_summary = manager.get_peer_summary(&contract, &peer);
assert!(
their_summary.is_some(),
"#4145: with a cached peer summary the next broadcast takes the delta \
path (compute_delta), not another full state"
);
}
#[tokio::test]
async fn untracked_peer_delivery_seeds_interest_and_summary() {
let our_summary = StateSummary::from(vec![5, 6, 7, 8]);
let time_source = SharedMockTimeSource::new();
let manager = InterestManager::new(time_source.clone());
let contract = make_contract_key(11);
let peer = make_peer_key();
assert!(
manager.get_peer_interest(&contract, &peer).is_none(),
"precondition: the peer must be untracked"
);
let delivered = record_streaming_delivery(
&manager,
Ok(Ok(BroadcastDeliveryOutcome::Delivered)),
false,
&contract,
&peer,
Some(&our_summary),
4096,
4096,
);
assert!(delivered);
assert_eq!(
manager.get_peer_summary(&contract, &peer),
Some(our_summary),
"#4952: a delivered full-state broadcast to an UNTRACKED peer must \
seed the interest entry + summary, so the next broadcast is a \
delta — otherwise the pair is a full-state fixed point"
);
}
#[tokio::test]
async fn untracked_peer_drop_outcome_does_not_fabricate_interest() {
let our_summary = StateSummary::from(vec![3, 3, 3]);
let dropped = dropped_oneshot().await;
let timed_out = elapsed_timeout().await;
let cases: Vec<(&str, super::StreamCompletionResult)> = vec![
("explicit-drop", Ok(Ok(BroadcastDeliveryOutcome::Dropped))),
("dropped-oneshot", Ok(dropped)),
("timeout", Err(timed_out)),
];
for (name, completion) in cases {
let time_source = SharedMockTimeSource::new();
let manager = InterestManager::new(time_source.clone());
let contract = make_contract_key(12);
let peer = make_peer_key();
let delivered = record_streaming_delivery(
&manager,
completion,
false,
&contract,
&peer,
Some(&our_summary),
2048,
2048,
);
assert!(!delivered, "[{name}] must not classify as delivered");
assert!(
manager.get_peer_interest(&contract, &peer).is_none(),
"[{name}] a non-delivered send must NOT fabricate an interest \
entry for an untracked peer — a summary the peer never \
received would suppress the mismatch resend (#4235)"
);
}
}
#[tokio::test]
async fn dropped_full_state_stream_does_not_cache_summary() {
let our_summary = StateSummary::from(vec![5, 6, 7, 8]);
let dropped = dropped_oneshot().await;
let timed_out = elapsed_timeout().await;
let cases: Vec<(&str, super::StreamCompletionResult)> = vec![
("explicit-drop", Ok(Ok(BroadcastDeliveryOutcome::Dropped))),
("dropped-oneshot", Ok(dropped)),
("timeout", Err(timed_out)),
];
for (name, completion) in cases {
let time_source = SharedMockTimeSource::new();
let manager = InterestManager::new(time_source.clone());
let contract = make_contract_key(43);
let peer = make_peer_key();
manager.register_peer_interest(&contract, peer.clone(), None, false);
let delivered = record_streaming_delivery(
&manager,
completion,
false,
&contract,
&peer,
Some(&our_summary),
4096,
4096,
);
assert!(
!delivered,
"[{name}] a dropped/timed-out full-state stream must NOT classify \
as delivered"
);
assert_eq!(
manager.get_peer_summary(&contract, &peer),
None,
"[{name}] #4145 must not weaken the #2763/#4235 guard: a DROPPED \
full-state stream must NOT cache the summary (the peer never got \
the state), or the next summary-mismatch resend is suppressed"
);
}
}
#[test]
fn broadcast_single_peer_gates_summarize_on_hosted_or_in_use_pin() {
let src = include_str!("broadcast_queue.rs");
let helper_start = src
.find("pub(super) fn should_broadcast_contract(")
.expect("should_broadcast_contract helper not found");
let helper_end = helper_start
+ src[helper_start..]
.find("\n}\n")
.expect("should_broadcast_contract body end not found");
let helper_src = &src[helper_start..helper_end];
assert!(
helper_src.contains("should_summarize_or_broadcast"),
"should_broadcast_contract must delegate to the composed \
should_summarize_or_broadcast predicate (single source of truth, \
#4610), not re-inline a partial (is_hosting || in_use) gate that \
would re-admit phantom stateless contracts"
);
let fn_start = src
.find("pub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer not found");
let fn_src = &src[fn_start..];
let gate_off = fn_src.find("should_broadcast_contract(op_manager").expect(
"broadcast_to_single_peer must call should_broadcast_contract — a bare \
get_contract_summary here reintroduces the #4473 storm",
);
let summarize_off = fn_src
.find("get_contract_summary(")
.expect("broadcast_to_single_peer get_contract_summary call not found");
assert!(
gate_off < summarize_off,
"broadcast_to_single_peer must gate on should_broadcast_contract BEFORE \
calling get_contract_summary (#4473) — otherwise the summarize storm \
fires for every phantom contract before the gate can skip it"
);
}
#[test]
fn record_delivery_routes_summary_cache_through_upsert() {
let src = include_str!("broadcast_queue.rs");
let fn_start = src
.find("fn record_delivery_to_interest<")
.expect("record_delivery_to_interest not found");
let after = &src[fn_start..];
let fn_end = after
.find("\npub(super) async fn broadcast_to_single_peer(")
.expect("end of record_delivery_to_interest not found");
let body: String = after[..fn_end].split_whitespace().collect();
assert!(
body.contains(
"interest_manager.upsert_peer_summary_from(key,peer_key,summary.clone(),"
),
"post-delivery cache must upsert (create-if-absent) the peer summary"
);
assert!(
!body.contains("interest_manager.update_peer_summary("),
"update_peer_summary silently no-ops for untracked peers — the \
#4952 fixed point. Use upsert_peer_summary here."
);
}
#[test]
fn broadcast_to_single_peer_gates_deltas_on_incompat_memo() {
let src = include_str!("broadcast_queue.rs");
let fn_start = src
.find("pub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer not found");
let after = &src[fn_start..];
let fn_end = after
.find("\nmod tests {")
.or_else(|| after.find("\n#[cfg(test)]"))
.expect("end of broadcast_to_single_peer not found");
let body = &after[..fn_end];
let gate_pos = body
.find(".suppress_deltas(")
.expect("broadcast_to_single_peer must consult the delta-incompat memo");
let delta_pos = body
.find(".compute_delta(")
.expect("compute_delta call not found");
assert!(
gate_pos < delta_pos,
"the delta-incompat gate must be consulted BEFORE compute_delta \
(gate {gate_pos} < compute_delta {delta_pos}) — otherwise the \
doomed delta is still computed and sent"
);
assert!(
body.contains("if deltas_suppressed => ("),
"suppression must short-circuit the payload match to FullState"
);
assert!(
body.contains("(Some(_), Some(_)) if deltas_suppressed => ("),
"the suppression guard must be scoped to the both-summaries-present \
case — a wildcard guard mis-attributes missing-summary full states \
to FullDeltaSuppressed (#3335 payload-mix accuracy)"
);
let guard_arm = body
.find("if deltas_suppressed => (")
.expect("guard arm not found");
let compute_arm = body
.find("(Some(ours), Some(theirs)) => {")
.expect("compute_delta arm `(Some(ours), Some(theirs))` not found");
assert!(
guard_arm < compute_arm,
"the `_ if deltas_suppressed` guard arm must come BEFORE the \
`(Some(ours), Some(theirs))` compute_delta arm (guard {guard_arm} \
< compute {compute_arm}) — a suppressed delta-incapable contract \
must never reach compute_delta"
);
let record_pos = body
.find(".record_delta_sent(")
.expect("broadcast_to_single_peer must record delivered delta sends");
let sent_delta_gate = body
.find("if sent_delta {")
.expect("record_delta_sent must be gated on sent_delta");
assert!(
sent_delta_gate < record_pos,
"record_delta_sent must sit inside the `if sent_delta` gate \
(gate {sent_delta_gate} < record {record_pos})"
);
}
#[test]
fn broadcast_to_single_peer_records_attempt_on_every_streaming_exit_pin() {
let src = include_str!("broadcast_queue.rs");
let fn_start = src
.find("pub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer not found");
let after = &src[fn_start..];
let fn_end = after
.find("\nmod tests {")
.or_else(|| after.find("\n#[cfg(test)]"))
.expect("end of broadcast_to_single_peer (start of tests module) not found");
let body = &after[..fn_end];
let record_calls = body.matches(".record_attempt(").count();
assert_eq!(
record_calls, 3,
"broadcast_to_single_peer's streaming branch must call record_attempt \
on all three exits (initial-send Err, dispatch Err, post-dispatch \
outcome) — got {record_calls}. A dropped early-exit record silently \
biases the v0.2.73 incident gauge LOW under congestion."
);
let failure_calls = body.matches(".record_attempt(false)").count();
assert_eq!(
failure_calls, 2,
"exactly the two early-failure exits must record record_attempt(false) \
(got {failure_calls}); the third exit records record_attempt(delivered)"
);
}
fn make_manager() -> InterestManager<SharedMockTimeSource> {
InterestManager::new(SharedMockTimeSource::new())
}
#[test]
fn nondeterministic_converged_summaries_skip_fanout_resend() {
let manager = make_manager();
let contract = make_contract_key(50);
let ours = StateSummary::from(vec![1u8, 2, 3]);
let theirs = StateSummary::from(vec![3u8, 2, 1]);
assert_ne!(
ours.as_ref(),
theirs.as_ref(),
"precondition: summaries differ byte-wise (the pre-fix byte-compare \
would NOT skip, and the delta path fell back to full state)"
);
manager.cache_delta(
&contract,
theirs.as_ref(),
ours.as_ref(),
StateDelta::from(Vec::<u8>::new()),
);
assert_eq!(
plan_fanout_send(
&manager,
&contract,
SummaryPair {
ours: &ours,
theirs: &theirs
},
0
),
FanoutSendPlan::Skip,
"a converged-but-byte-differing pair must be skipped by the fan-out \
(pre-fix: full state was re-sent on every fan-out — the heal storm)"
);
}
#[test]
fn genuinely_diverged_summaries_still_send() {
let manager = make_manager();
let contract = make_contract_key(51);
let ours = StateSummary::from(vec![9u8, 9, 9]);
let theirs = StateSummary::from(vec![1u8]);
manager.cache_delta(
&contract,
theirs.as_ref(),
ours.as_ref(),
StateDelta::from(vec![42u8]),
);
assert_eq!(
plan_fanout_send(
&manager,
&contract,
SummaryPair {
ours: &ours,
theirs: &theirs
},
0
),
FanoutSendPlan::Send,
"a genuine divergence (non-empty delta) must still be sent"
);
}
#[test]
fn byte_equal_summaries_skip_before_cache_lookup() {
let manager = make_manager();
let contract = make_contract_key(52);
let ours = StateSummary::from(vec![7u8, 7, 7]);
let theirs = StateSummary::from(vec![7u8, 7, 7]);
manager.cache_delta(
&contract,
theirs.as_ref(),
ours.as_ref(),
StateDelta::from(vec![1u8]),
);
assert_eq!(
plan_fanout_send(
&manager,
&contract,
SummaryPair {
ours: &ours,
theirs: &theirs
},
0
),
FanoutSendPlan::Skip,
"byte-identical summaries are trivially converged; the byte-equal \
short-circuit must precede any delta-cache verdict"
);
}
#[test]
fn probe_budget_gates_wasm_probe_and_falls_back_to_send() {
use crate::node::MAX_STALENESS_PROBES_PER_SUMMARIES;
let manager = make_manager();
let contract = make_contract_key(53);
let ours = StateSummary::from(vec![1u8, 2, 3]);
let theirs = StateSummary::from(vec![3u8, 2, 1]);
assert_eq!(
plan_fanout_send(
&manager,
&contract,
SummaryPair {
ours: &ours,
theirs: &theirs
},
0
),
FanoutSendPlan::Probe,
"a cache miss within budget must run the bounded WASM probe"
);
assert_eq!(
plan_fanout_send(
&manager,
&contract,
SummaryPair {
ours: &ours,
theirs: &theirs
},
MAX_STALENESS_PROBES_PER_SUMMARIES - 1
),
FanoutSendPlan::Probe,
"the last budget slot is still spendable"
);
assert_eq!(
plan_fanout_send(
&manager,
&contract,
SummaryPair {
ours: &ours,
theirs: &theirs
},
MAX_STALENESS_PROBES_PER_SUMMARIES
),
FanoutSendPlan::Send,
"an exhausted probe budget must fall back to the conservative \
byte-differ ⇒ send behavior, never a silent skip"
);
manager.cache_delta(
&contract,
theirs.as_ref(),
ours.as_ref(),
StateDelta::from(Vec::<u8>::new()),
);
assert_eq!(
plan_fanout_send(
&manager,
&contract,
SummaryPair {
ours: &ours,
theirs: &theirs
},
MAX_STALENESS_PROBES_PER_SUMMARIES * 10
),
FanoutSendPlan::Skip,
"cache hits never consume budget and still answer (converged ⇒ skip)"
);
}
#[test]
fn fanout_path_uses_semantic_delta_skip_pin() {
let src = include_str!("broadcast_queue.rs");
let fn_start = src
.find("pub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer not found");
let after = &src[fn_start..];
let fn_end = after
.find("\nmod tests {")
.or_else(|| after.find("\n#[cfg(test)]"))
.expect("end of broadcast_to_single_peer (start of tests module) not found");
let body = &after[..fn_end];
assert!(
body.contains("fanout_send_needed("),
"broadcast_to_single_peer must route the per-peer skip decision \
through fanout_send_needed — a bare summary byte comparison \
re-opens the nondeterministic-summary heal storm"
);
let ok_none_off = body
.find("Ok(None) =>")
.expect("compute_delta Ok(None) arm not found in broadcast_to_single_peer");
let err_off = body[ok_none_off..]
.find("Err(err) =>")
.expect("compute_delta Err arm not found after Ok(None) arm");
let ok_none_arm = &body[ok_none_off..ok_none_off + err_off];
assert!(
!ok_none_arm.contains("FullState"),
"the Ok(None) (empty delta = converged) arm must NOT fall back to \
sending full state — that re-flood on every fan-out IS the heal \
storm. Arm body:\n{ok_none_arm}"
);
assert!(
ok_none_arm.contains("return;"),
"the Ok(None) (empty delta = converged) arm must skip the send \
entirely (return). Arm body:\n{ok_none_arm}"
);
let helpers_start = src
.find("pub(super) fn plan_fanout_send")
.expect("plan_fanout_send not found");
let helpers_end = src
.find("// The `BroadcastQueue` struct (constants, types, impl)")
.expect("queue module comment anchor not found");
assert!(
helpers_start < helpers_end,
"plan_fanout_send / fanout_send_needed must be defined before the \
queue module"
);
let helpers = &src[helpers_start..helpers_end];
assert!(
helpers.contains("plan_staleness_probe"),
"plan_fanout_send must ration WASM probes through \
plan_staleness_probe (the MAX_STALENESS_PROBES_PER_SUMMARIES cap)"
);
assert!(
helpers.contains("cached_staleness_verdict"),
"plan_fanout_send must consult the shared delta cache \
(cached_staleness_verdict) before trusting summary bytes"
);
assert!(
helpers.contains("peer_summary_has_pending_state"),
"fanout_send_needed must resolve cache misses via the bounded \
contract delta probe (peer_summary_has_pending_state)"
);
assert!(
helpers.contains("summary_indicates_stale_peer"),
"fanout_send_needed must decide from the probe verdict via \
summary_indicates_stale_peer (semantic policy), not inline byte \
inequality"
);
}
#[test]
fn broadcast_to_single_peer_reports_send_cost_pin() {
let src = include_str!("broadcast_queue.rs");
let fn_start = src
.find("pub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer not found");
let after = &src[fn_start..];
let fn_end = after
.find("\nmod tests {")
.or_else(|| after.find("\n#[cfg(test)]"))
.expect("end of broadcast_to_single_peer (start of tests module) not found");
let body = &after[..fn_end];
let report_invocations = body.matches("report_send_cpu(op_manager)").count();
assert_eq!(
report_invocations, 3,
"broadcast_to_single_peer must invoke report_send_cpu at all three \
exits (summaries-equal skip + empty-delta converged skip + send \
attempt) — got {report_invocations}. A dropped report blinds \
cost-pressure eviction (#4861) to per-send CPU."
);
let cpu_needle = concat!("ResourceType::", "Exec", "CpuMicros");
let bytes_needle = concat!("ResourceType::", "Broadcast", "FanoutCost");
assert!(
body.contains(cpu_needle),
"report_send_cpu must attribute on the ExecCpuMicros axis"
);
assert_eq!(
body.matches(bytes_needle).count(),
2,
"fan-out bytes must be charged on the BroadcastFanoutCost axis at \
exactly the two real-delivery sites (streaming Delivered + inline \
send Ok) — never up-front (review round-3 Fix 4)"
);
assert_eq!(
body.matches("payload_size as f64").count(),
2,
"each delivery-gated bytes report must charge the selected \
payload_size (delta or full state), not the pre-delta full-state size"
);
}
#[test]
fn broadcast_to_single_peer_gates_wire_payload_on_lane_capacity_pin() {
let src = include_str!("broadcast_queue.rs");
let start = src
.find("\npub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer renamed or removed");
let end = src[start..]
.find("\n#[cfg(test)]")
.expect("end of broadcast_to_single_peer not found")
+ start;
let body = &src[start..end];
let upgrade = body
.find("ensure_capacity_for(payload_size)")
.expect("the actual wire payload must be gated on lane capacity");
for send in ["bridge.send(peer_addr", "send_stream_with_completion("] {
let at = body
.find(send)
.unwrap_or_else(|| panic!("send site `{send}` not found"));
assert!(
upgrade < at,
"the lane correction (offset {upgrade}) must run BEFORE `{send}` \
(offset {at}) — bytes must never reach the wire under a permit \
that does not cover them"
);
}
let cpu = body
.rfind("report_send_cpu(op_manager);")
.expect("send-CPU report not found");
assert!(
cpu < upgrade,
"report_send_cpu (offset {cpu}) must run BEFORE the lane correction \
(offset {upgrade}), or a semaphore wait is charged as contract CPU"
);
}
#[test]
fn broadcast_to_single_peer_refreshes_interest_on_every_skip_pin() {
let src = include_str!("broadcast_queue.rs");
let fn_start = src
.find("pub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer not found");
let after = &src[fn_start..];
let fn_end = after
.find("\nmod tests {")
.or_else(|| after.find("\n#[cfg(test)]"))
.expect("end of broadcast_to_single_peer (start of tests module) not found");
let body = &after[..fn_end];
let refreshes = body
.matches("refresh_peer_interest(&key,&peer_key)")
.count()
+ body
.matches("refresh_peer_interest(&key, &peer_key)")
.count();
assert_eq!(
refreshes, 2,
"broadcast_to_single_peer must refresh the interest TTL at BOTH \
converged-skip exits (summaries-equal and empty-delta) — got \
{refreshes}. Skipping the payload must not expire the interest."
);
let equal_skip = body
.find("Skipping broadcast - peer already has our state")
.expect("summaries-equal skip arm not found");
let empty_delta_skip = body
.find("Skipping broadcast - contract reported empty delta")
.expect("empty-delta skip arm not found");
assert!(
equal_skip < empty_delta_skip,
"unexpected arm order; the offsets below assume summaries-equal \
precedes empty-delta"
);
assert!(
body[equal_skip..empty_delta_skip].contains("refresh_peer_interest("),
"the summaries-equal skip must refresh the interest TTL before it \
returns"
);
assert!(
body[empty_delta_skip..].contains("refresh_peer_interest("),
"the empty-delta (converged) skip must refresh the interest TTL \
before it returns — same outcome as the skip above, same obligation"
);
assert!(
!body[equal_skip..empty_delta_skip].contains("record_delivery_to_interest("),
"the summaries-equal skip must refresh ONLY — recording a delivery \
that did not happen corrupts the delta/full-state telemetry"
);
}
#[test]
fn broadcast_to_single_peer_tags_every_payload_arm_pin() {
let src = include_str!("broadcast_queue.rs");
let fn_start = src
.find("pub(super) async fn broadcast_to_single_peer(")
.expect("broadcast_to_single_peer not found");
let after = &src[fn_start..];
let fn_end = after
.find("\nmod tests {")
.or_else(|| after.find("\n#[cfg(test)]"))
.expect("end of broadcast_to_single_peer (start of tests module) not found");
let body = &after[..fn_end];
for arm in [
"PayloadArm::Delta",
"PayloadArm::FullDeltaSuppressed",
"PayloadArm::FullNotEfficient",
"PayloadArm::FullComputeFailed",
"PayloadArm::FullNoOurSummary",
"PayloadArm::FullNoTheirSummaryUntracked",
"PayloadArm::FullNoTheirSummaryTracked",
] {
assert!(
body.contains(arm),
"broadcast_to_single_peer must tag the {arm} arm — an untagged \
fallback makes the #3335 payload-mix measurement attribute \
bytes to the wrong cause"
);
}
assert_eq!(
body.matches("begin_peer_summary_broadcast(&key, &peer_key)")
.count(),
1,
"broadcast payload selection must use the atomic missing-summary \
observation/classification operation exactly once"
);
let collapsed: String = body.chars().filter(|c| !c.is_whitespace()).collect();
let record_needle = concat!(
".record_",
"delivered(payload_arm,key.id(),payload_size,not_efficient_gate_inputs,tracked_missing_reason,)"
);
assert_eq!(
collapsed.matches(record_needle).count(),
2,
"payload mix must be recorded at exactly the two real-delivery \
sites (streaming Delivered + inline send Ok) — recording up-front \
would count dropped/failed sends as bytes on the wire"
);
assert_eq!(
collapsed
.matches(concat!("op_manager.payload_mix.record_", "delivered("))
.count(),
2,
"the payload mix must be recorded on op_manager.payload_mix (the \
per-node accumulator), not a process-global static"
);
assert!(
body.contains("DeltaUnavailable::NotEfficient")
&& body.contains("DeltaUnavailable::ComputeFailed"),
"the delta-failure arm must keep the typed NotEfficient vs \
ComputeFailed split — collapsing them re-blinds the measurement"
);
assert!(
collapsed.contains("(None,_)=>") && collapsed.contains("(Some(_),None)=>"),
"the no-summary arms must branch on WHICH side of the pair is \
missing — a catch-all `_` arm re-blinds the split"
);
assert!(
collapsed.contains(
"PeerSummaryForBroadcast::Missing{reason,attempt}=>{(None,reason,attempt)}"
),
"the atomic peer-summary result must carry the reason through so \
tracked and untracked missing-summary sends remain distinct"
);
}
}