feat(cluster): RFC 010 c10–c16, follow-ups and Phase 6 (squash of 16ef583..d9c62a8)
Tree snapshot of d9c62a8 (2026-08-18). The 20 source commits between
16ef583 (c9) and d9c62a8 were never pushed and the clone that held them
was lost; this commit carries their combined tree verbatim so the build
history stays auditable from the c1–c9 commits below it. Original
hashes as recorded in the session handoff:
c10 f03e94d pid targeting + auto-serialization (RemotePid, D14 name
on the wire); Phase 3 gate
c11 7ef4bad DownReason::Disconnected, wire tag 5
c12 d124162 remote monitors (Monitor/Demonitor/Down frames)
c13 9de967b connection-loss synthesis (A+B: Monitors::teardown +
unread-command Disconnected); Phase 4 gate
c14 7e822b7 eager pg eviction (reaper actor, ReaperInboxes)
dbe1a22 InboundVerdict::label(), trace::Event::ClusterInbound
31a9877 tests/channel.rs monitor-churn target gated on `go`
653559e Discovery::Withdrawn{name, addr}
c15 b41d76e distributed pg: Sync on NodeUp, Join/Leave broadcast,
NodeDown sweep, members_all; PgMsg wire type
c16 fafa881 pick_any / dispatch_any; Phase 5 complete
Phase 6 Tier A:
195c73e p4 NodeEvent::NodeDown(NodeInfo)
48fd766 p1 connector Candidate{name, addr, state}
ce8cf99 p2+p7 conn.rs select arms as Vec<Arm>; Outbound::Drained
bf24988 p6 RemotePid::from_local -> Option
9ae0380 p3 PeerStanding{Free, Claimed, Dialing}
c7d62a1 p11 cluster::Timing knobs, threaded by value
46f171d p11 cluster_disconnect un-ignored on SMARM_FAST_TIMING
Phase 6 Tier B:
391a9ae p5 cluster::RemoteDownReason{Local, Disconnected};
DownReason::Disconnected removed from core
7ddd908 p9 pg ctl channel unconditional, one cfg seam at spawn
d9c62a8 PeerNameMismatch parks the candidate; ClusterDial trace
Verified at d9c62a8: default 361/0, cluster 448/0, clippy --lib on
default / cluster / cluster+smarm-trace, fmt, 10x flake on
cluster_dial_mismatch, 5x on cluster_pg.
This commit is contained in:
+49
-11
@@ -81,10 +81,45 @@ pub enum Frame {
|
||||
},
|
||||
Down {
|
||||
monitor_id: u64,
|
||||
reason: DownReason,
|
||||
reason: RemoteDownReason,
|
||||
},
|
||||
}
|
||||
|
||||
/// Why a remotely-monitored actor is reported down: either the target's own
|
||||
/// terminal [`DownReason`] as its node recorded it, or the *link* to that
|
||||
/// node was lost (or absent) — which says nothing about the actor itself.
|
||||
///
|
||||
/// This is the cluster-side widening of `DownReason` (p5): `Disconnected`
|
||||
/// is a fact about a connection, never about a local actor, so it lives
|
||||
/// here rather than in the core enum — a local `Down` can never carry it,
|
||||
/// and matches on `DownReason` stay exhaustive over actor outcomes only.
|
||||
/// On the wire `Local(r)` uses `r`'s tag and `Disconnected` is tag 5,
|
||||
/// bound since c11; no peer emits it today (a lost link is synthesized
|
||||
/// locally), but the codec honours it both ways.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RemoteDownReason {
|
||||
/// The target itself terminated; the peer reported this reason.
|
||||
Local(DownReason),
|
||||
/// The link to the target's node was lost or was never up.
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
impl RemoteDownReason {
|
||||
/// The actor's own reason, if this was not a link loss.
|
||||
pub fn local(self) -> Option<DownReason> {
|
||||
match self {
|
||||
RemoteDownReason::Local(r) => Some(r),
|
||||
RemoteDownReason::Disconnected => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DownReason> for RemoteDownReason {
|
||||
fn from(r: DownReason) -> Self {
|
||||
RemoteDownReason::Local(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Frame tags. 0 is deliberately unassigned so an all-zero buffer never parses.
|
||||
const TAG_HELLO: u8 = 1;
|
||||
const TAG_HELLO_ACK: u8 = 2;
|
||||
@@ -101,11 +136,12 @@ const REJ_HASH_MISMATCH: u8 = 1;
|
||||
const REJ_NAME_TAKEN: u8 = 2;
|
||||
const REJ_PROTO_VERSION: u8 = 3;
|
||||
|
||||
// DownReason tags. c11 adds `Disconnected = 5`; do not reuse tags.
|
||||
// DownReason tags. Do not reuse tags.
|
||||
const DR_EXIT: u8 = 1;
|
||||
const DR_PANIC: u8 = 2;
|
||||
const DR_STOPPED: u8 = 3;
|
||||
const DR_NOPROC: u8 = 4;
|
||||
const DR_DISCONNECTED: u8 = 5;
|
||||
|
||||
/// Frame could not be encoded. The output buffer is left exactly as it was.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -272,10 +308,11 @@ impl Frame {
|
||||
out.push(TAG_DOWN);
|
||||
put_u64(out, *monitor_id);
|
||||
out.push(match reason {
|
||||
DownReason::Exit => DR_EXIT,
|
||||
DownReason::Panic => DR_PANIC,
|
||||
DownReason::Stopped => DR_STOPPED,
|
||||
DownReason::NoProc => DR_NOPROC,
|
||||
RemoteDownReason::Local(DownReason::Exit) => DR_EXIT,
|
||||
RemoteDownReason::Local(DownReason::Panic) => DR_PANIC,
|
||||
RemoteDownReason::Local(DownReason::Stopped) => DR_STOPPED,
|
||||
RemoteDownReason::Local(DownReason::NoProc) => DR_NOPROC,
|
||||
RemoteDownReason::Disconnected => DR_DISCONNECTED,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -364,13 +401,14 @@ impl Frame {
|
||||
TAG_DOWN => Frame::Down {
|
||||
monitor_id: r.u64()?,
|
||||
reason: match r.u8()? {
|
||||
DR_EXIT => DownReason::Exit,
|
||||
DR_PANIC => DownReason::Panic,
|
||||
DR_STOPPED => DownReason::Stopped,
|
||||
DR_NOPROC => DownReason::NoProc,
|
||||
DR_EXIT => RemoteDownReason::Local(DownReason::Exit),
|
||||
DR_PANIC => RemoteDownReason::Local(DownReason::Panic),
|
||||
DR_STOPPED => RemoteDownReason::Local(DownReason::Stopped),
|
||||
DR_NOPROC => RemoteDownReason::Local(DownReason::NoProc),
|
||||
DR_DISCONNECTED => RemoteDownReason::Disconnected,
|
||||
t => {
|
||||
return Err(DecodeError::UnknownEnumTag {
|
||||
what: "DownReason",
|
||||
what: "RemoteDownReason",
|
||||
tag: t,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user