mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
peer: route peer-loss reports by kind
Add a LostKind discriminator to the ReportLost action so the executor can route an un-promoted handshake failure to the connected-guarded reconnect reflex (note_handshake_timeout) and an established peer's link-death to the unconditional one (note_link_dead), instead of collapsing every loss to note_link_dead. The two loss producers dispatched today, the liveness reap and the inbound-restart-then-promote arm, both keep the link-dead routing, so behavior is unchanged. The handshake-timeout and dial-failure producers are tagged accordingly but stay dormant until their events are dispatched.
This commit is contained in:
@@ -19,14 +19,14 @@
|
||||
//! realized as those planes are wired).
|
||||
//! `RegisterDecryptSession` is a deliberate no-op — see its arm for the note.
|
||||
|
||||
use crate::PeerIdentity;
|
||||
use crate::node::Node;
|
||||
use crate::node::reject::{HandshakeReject, RejectReason};
|
||||
use crate::peer::machine::{PeerAction, PeerEvent};
|
||||
use crate::peer::machine::{LostKind, PeerAction, PeerEvent};
|
||||
use crate::proto::fmp::PromotionResult;
|
||||
use crate::proto::fmp::wire::build_msg2;
|
||||
use crate::transport::{LinkId, TransportAddr, TransportId};
|
||||
use crate::utils::index::SessionIndex;
|
||||
use crate::{NodeAddr, PeerIdentity};
|
||||
use std::collections::VecDeque;
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
@@ -449,18 +449,22 @@ impl Node {
|
||||
// INERT — the legacy tick timers still run, so driving
|
||||
// these would double-schedule.
|
||||
}
|
||||
PeerAction::ReportLost { peer } => {
|
||||
// The single loss token → the reconciler reflex (`driver.rs:48`).
|
||||
self.report_peer_lost(peer, ambient.now_ms);
|
||||
PeerAction::ReportLost { peer, kind } => {
|
||||
// The single loss token, routed to the reconciler reflex the
|
||||
// `kind` names: an un-promoted handshake attempt takes the
|
||||
// connected-guarded `note_handshake_timeout` (`driver.rs:28`),
|
||||
// an established peer's link-death takes the unconditional
|
||||
// `note_link_dead` (`driver.rs:48`).
|
||||
match kind {
|
||||
LostKind::HandshakeTimeout => {
|
||||
self.note_handshake_timeout(peer, ambient.now_ms);
|
||||
}
|
||||
LostKind::LinkDead => {
|
||||
self.note_link_dead(peer, ambient.now_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `ReportLost` → `note_link_dead` (kept as a named seam so the ambient clock
|
||||
/// source is explicit and the reconciler-computed backoff can be threaded later).
|
||||
#[allow(dead_code)]
|
||||
fn report_peer_lost(&mut self, peer: NodeAddr, now_ms: u64) {
|
||||
self.note_link_dead(peer, now_ms);
|
||||
}
|
||||
}
|
||||
|
||||
+56
-9
@@ -257,6 +257,21 @@ pub(crate) enum PeerEvent {
|
||||
Tick,
|
||||
}
|
||||
|
||||
/// Why a peer was reported lost. Selects the reconciler reflex the executor
|
||||
/// routes the `ReportLost` token to: an un-promoted handshake attempt that
|
||||
/// failed (`HandshakeTimeout`, connected-guarded like the old `schedule_retry`)
|
||||
/// versus an established peer whose link died (`LinkDead`, unconditional like
|
||||
/// the old `schedule_reconnect`).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum LostKind {
|
||||
/// An outbound handshake attempt timed out or its dial failed before the
|
||||
/// peer promoted — routes to the connected-guarded reflex.
|
||||
HandshakeTimeout,
|
||||
/// An established peer's link went dead or is being replaced — routes to
|
||||
/// the unconditional reconnect reflex.
|
||||
LinkDead,
|
||||
}
|
||||
|
||||
/// An effect the driver executes on the machine's behalf. Runtime-agnostic
|
||||
/// plain data — no tokio handles, time only as `at_ms` fields.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -303,8 +318,9 @@ pub(crate) enum PeerAction {
|
||||
/// Cancel a scheduled timer.
|
||||
CancelTimer { kind: TimerKind },
|
||||
/// Report the peer lost to the reconciler (the single loss token — there is
|
||||
/// deliberately no `ScheduleRetry` machine action).
|
||||
ReportLost { peer: NodeAddr },
|
||||
/// deliberately no `ScheduleRetry` machine action). `kind` selects the
|
||||
/// reflex (handshake-timeout vs link-dead) the executor routes to.
|
||||
ReportLost { peer: NodeAddr, kind: LostKind },
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -527,7 +543,13 @@ impl PeerMachine {
|
||||
}
|
||||
let mut actions = Vec::new();
|
||||
if let Some(peer) = self.addr() {
|
||||
actions.push(PeerAction::ReportLost { peer });
|
||||
// Dial failure on an un-promoted leg routes like a handshake timeout
|
||||
// (the connected-guarded reflex). Dormant today — no `TransportFailed`
|
||||
// event is dispatched until the connection-oriented cutover (C5).
|
||||
actions.push(PeerAction::ReportLost {
|
||||
peer,
|
||||
kind: LostKind::HandshakeTimeout,
|
||||
});
|
||||
}
|
||||
self.state = PeerState::Closed {
|
||||
backoff_deadline_ms: now + CLOSED_BACKOFF_MS,
|
||||
@@ -641,7 +663,12 @@ impl PeerMachine {
|
||||
if let Some(idx) = self.our_index.take() {
|
||||
actions.push(PeerAction::UnregisterDecryptSession { index: idx });
|
||||
}
|
||||
actions.push(PeerAction::ReportLost { peer });
|
||||
// An established peer being replaced by a fresh inbound leg — the
|
||||
// unconditional reconnect reflex (a live, cut-over producer).
|
||||
actions.push(PeerAction::ReportLost {
|
||||
peer,
|
||||
kind: LostKind::LinkDead,
|
||||
});
|
||||
actions.extend(self.inbound_classify(link, &wire));
|
||||
actions
|
||||
}
|
||||
@@ -1017,7 +1044,12 @@ impl PeerMachine {
|
||||
PeerAction::TeardownConnectedUdp,
|
||||
];
|
||||
if let Some(peer) = self.addr() {
|
||||
actions.push(PeerAction::ReportLost { peer });
|
||||
// An established peer whose link died — the unconditional reconnect
|
||||
// reflex (the live liveness-reap producer).
|
||||
actions.push(PeerAction::ReportLost {
|
||||
peer,
|
||||
kind: LostKind::LinkDead,
|
||||
});
|
||||
}
|
||||
self.state = PeerState::Closed {
|
||||
backoff_deadline_ms: now + CLOSED_BACKOFF_MS,
|
||||
@@ -1088,7 +1120,13 @@ impl PeerMachine {
|
||||
for act in Fmp::new().poll_timeouts(vec![snap]) {
|
||||
match act {
|
||||
ConnAction::ScheduleRetry { peer } => {
|
||||
lost.push(PeerAction::ReportLost { peer });
|
||||
// Handshake timeout on an un-promoted leg — the connected-
|
||||
// guarded reflex. Dormant today (no `Timeout` event is
|
||||
// dispatched until the timeout fold in C5).
|
||||
lost.push(PeerAction::ReportLost {
|
||||
peer,
|
||||
kind: LostKind::HandshakeTimeout,
|
||||
});
|
||||
}
|
||||
ConnAction::Teardown { .. } => {
|
||||
if let Some(idx) = self.conn.our_index() {
|
||||
@@ -1338,7 +1376,10 @@ mod tests {
|
||||
PeerAction::CancelTimer {
|
||||
kind: TimerKind::Liveness,
|
||||
},
|
||||
PeerAction::ReportLost { peer },
|
||||
PeerAction::ReportLost {
|
||||
peer,
|
||||
kind: LostKind::LinkDead,
|
||||
},
|
||||
];
|
||||
for a in &sample {
|
||||
// Exhaustiveness guard: no `_` wildcard, so adding a variant without
|
||||
@@ -1587,7 +1628,10 @@ mod tests {
|
||||
PeerAction::UnregisterDecryptSession {
|
||||
index: SessionIndex::new(0xDEAD)
|
||||
},
|
||||
PeerAction::ReportLost { peer: peer_addr },
|
||||
PeerAction::ReportLost {
|
||||
peer: peer_addr,
|
||||
kind: LostKind::LinkDead,
|
||||
},
|
||||
]
|
||||
);
|
||||
assert!(matches!(
|
||||
@@ -1988,7 +2032,10 @@ mod tests {
|
||||
vec![
|
||||
PeerAction::InvalidateSendState,
|
||||
PeerAction::TeardownConnectedUdp,
|
||||
PeerAction::ReportLost { peer: addr },
|
||||
PeerAction::ReportLost {
|
||||
peer: addr,
|
||||
kind: LostKind::LinkDead,
|
||||
},
|
||||
]
|
||||
);
|
||||
assert!(matches!(m.state(), PeerState::Closed { .. }));
|
||||
|
||||
Reference in New Issue
Block a user