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).
|
//! realized as those planes are wired).
|
||||||
//! `RegisterDecryptSession` is a deliberate no-op — see its arm for the note.
|
//! `RegisterDecryptSession` is a deliberate no-op — see its arm for the note.
|
||||||
|
|
||||||
|
use crate::PeerIdentity;
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
use crate::node::reject::{HandshakeReject, RejectReason};
|
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::PromotionResult;
|
||||||
use crate::proto::fmp::wire::build_msg2;
|
use crate::proto::fmp::wire::build_msg2;
|
||||||
use crate::transport::{LinkId, TransportAddr, TransportId};
|
use crate::transport::{LinkId, TransportAddr, TransportId};
|
||||||
use crate::utils::index::SessionIndex;
|
use crate::utils::index::SessionIndex;
|
||||||
use crate::{NodeAddr, PeerIdentity};
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
@@ -449,18 +449,22 @@ impl Node {
|
|||||||
// INERT — the legacy tick timers still run, so driving
|
// INERT — the legacy tick timers still run, so driving
|
||||||
// these would double-schedule.
|
// these would double-schedule.
|
||||||
}
|
}
|
||||||
PeerAction::ReportLost { peer } => {
|
PeerAction::ReportLost { peer, kind } => {
|
||||||
// The single loss token → the reconciler reflex (`driver.rs:48`).
|
// The single loss token, routed to the reconciler reflex the
|
||||||
self.report_peer_lost(peer, ambient.now_ms);
|
// `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,
|
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
|
/// An effect the driver executes on the machine's behalf. Runtime-agnostic
|
||||||
/// plain data — no tokio handles, time only as `at_ms` fields.
|
/// plain data — no tokio handles, time only as `at_ms` fields.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@@ -303,8 +318,9 @@ pub(crate) enum PeerAction {
|
|||||||
/// Cancel a scheduled timer.
|
/// Cancel a scheduled timer.
|
||||||
CancelTimer { kind: TimerKind },
|
CancelTimer { kind: TimerKind },
|
||||||
/// Report the peer lost to the reconciler (the single loss token — there is
|
/// Report the peer lost to the reconciler (the single loss token — there is
|
||||||
/// deliberately no `ScheduleRetry` machine action).
|
/// deliberately no `ScheduleRetry` machine action). `kind` selects the
|
||||||
ReportLost { peer: NodeAddr },
|
/// 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();
|
let mut actions = Vec::new();
|
||||||
if let Some(peer) = self.addr() {
|
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 {
|
self.state = PeerState::Closed {
|
||||||
backoff_deadline_ms: now + CLOSED_BACKOFF_MS,
|
backoff_deadline_ms: now + CLOSED_BACKOFF_MS,
|
||||||
@@ -641,7 +663,12 @@ impl PeerMachine {
|
|||||||
if let Some(idx) = self.our_index.take() {
|
if let Some(idx) = self.our_index.take() {
|
||||||
actions.push(PeerAction::UnregisterDecryptSession { index: idx });
|
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.extend(self.inbound_classify(link, &wire));
|
||||||
actions
|
actions
|
||||||
}
|
}
|
||||||
@@ -1017,7 +1044,12 @@ impl PeerMachine {
|
|||||||
PeerAction::TeardownConnectedUdp,
|
PeerAction::TeardownConnectedUdp,
|
||||||
];
|
];
|
||||||
if let Some(peer) = self.addr() {
|
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 {
|
self.state = PeerState::Closed {
|
||||||
backoff_deadline_ms: now + CLOSED_BACKOFF_MS,
|
backoff_deadline_ms: now + CLOSED_BACKOFF_MS,
|
||||||
@@ -1088,7 +1120,13 @@ impl PeerMachine {
|
|||||||
for act in Fmp::new().poll_timeouts(vec![snap]) {
|
for act in Fmp::new().poll_timeouts(vec![snap]) {
|
||||||
match act {
|
match act {
|
||||||
ConnAction::ScheduleRetry { peer } => {
|
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 { .. } => {
|
ConnAction::Teardown { .. } => {
|
||||||
if let Some(idx) = self.conn.our_index() {
|
if let Some(idx) = self.conn.our_index() {
|
||||||
@@ -1338,7 +1376,10 @@ mod tests {
|
|||||||
PeerAction::CancelTimer {
|
PeerAction::CancelTimer {
|
||||||
kind: TimerKind::Liveness,
|
kind: TimerKind::Liveness,
|
||||||
},
|
},
|
||||||
PeerAction::ReportLost { peer },
|
PeerAction::ReportLost {
|
||||||
|
peer,
|
||||||
|
kind: LostKind::LinkDead,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
for a in &sample {
|
for a in &sample {
|
||||||
// Exhaustiveness guard: no `_` wildcard, so adding a variant without
|
// Exhaustiveness guard: no `_` wildcard, so adding a variant without
|
||||||
@@ -1587,7 +1628,10 @@ mod tests {
|
|||||||
PeerAction::UnregisterDecryptSession {
|
PeerAction::UnregisterDecryptSession {
|
||||||
index: SessionIndex::new(0xDEAD)
|
index: SessionIndex::new(0xDEAD)
|
||||||
},
|
},
|
||||||
PeerAction::ReportLost { peer: peer_addr },
|
PeerAction::ReportLost {
|
||||||
|
peer: peer_addr,
|
||||||
|
kind: LostKind::LinkDead,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@@ -1988,7 +2032,10 @@ mod tests {
|
|||||||
vec![
|
vec![
|
||||||
PeerAction::InvalidateSendState,
|
PeerAction::InvalidateSendState,
|
||||||
PeerAction::TeardownConnectedUdp,
|
PeerAction::TeardownConnectedUdp,
|
||||||
PeerAction::ReportLost { peer: addr },
|
PeerAction::ReportLost {
|
||||||
|
peer: addr,
|
||||||
|
kind: LostKind::LinkDead,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert!(matches!(m.state(), PeerState::Closed { .. }));
|
assert!(matches!(m.state(), PeerState::Closed { .. }));
|
||||||
|
|||||||
Reference in New Issue
Block a user