Skip to main content

recon_protocols/
detector.rs

1//! The detector port: what a layer above a failure detector may depend on, and the whole of it.
2//!
3//! Two detectors exist now — [`crate::perfect_failure_detector`] and
4//! [`crate::eventually_perfect_failure_detector`] — and Ω is written against whichever it is given.
5//! That is the second consumer `CLAUDE.md` constraint 4 asks for before a port is extracted, and
6//! the reason this module did not exist while there was only one.
7//!
8//! # Why the vocabulary is not pinned to one pair of types
9//!
10//! The same argument [`crate::link`] makes. Pinning `Ind = pfd::Ind` admits the perfect detector
11//! and rejects `◇P`, whose `Ind` has a second variant — and admitting both is the entire point,
12//! because a leader detector written against one cannot compose over the other. So a detector keeps
13//! its own `Ind`, and the port supplies the one translation a layer above needs: is this a
14//! suspicion, or the withdrawal of one?
15//!
16//! # A detector that never retracts says so by never producing a withdrawal
17//!
18//! `P` classifies its `Crash` as [`DetectorInd::Suspect`] and never yields
19//! [`DetectorInd::Restore`] — not because a flag says so, but because it has no indication that
20//! could become one. `docs/scope-annotated-modules.md` forbids a module declaring what it cannot
21//! observe, and this is that rule applied to a detector: a layer above handles both arms, and over
22//! `P` the second is unreachable rather than merely unused.
23//!
24//! There is deliberately no `RetractingDetector` marker trait. `link.rs` records at length what
25//! happened to `ScopedLink` — introduced for a bound nothing could take, deleted for want of a
26//! consumer — and the same would be true here. Ω needs no such bound: it handles a withdrawal if one
27//! comes and is correct if none ever does.
28
29use recon_core::{NodeId, Protocol};
30
31/// What a layer above a failure detector may depend on.
32///
33/// Satisfying the port is a decision, not an accident of shape: a detector says so by implementing
34/// this, exactly as a link does. A protocol that has not is rejected when the project is built.
35///
36/// ```compile_fail
37/// # use recon_protocols::detector::Detector;
38/// # use recon_core::NodeId;
39/// fn needs_a_detector<D: Detector>(_: D) {}
40/// // `PerfectLink` is a protocol, and is not a detector.
41/// needs_a_detector(recon_protocols::perfect_link::PerfectLink::<u32>::new(
42///     NodeId::new(1),
43///     core::time::Duration::from_millis(1),
44/// ));
45/// ```
46pub trait Detector: Protocol<Note = crate::Note> {
47    /// Read one of this detector's indications in the port's terms.
48    ///
49    /// Total, as [`crate::link::Link::classify`] is: every indication a detector raises is either a
50    /// suspicion or the withdrawal of one, and a detector with something else to say would be
51    /// saying it to a layer that cannot hear it.
52    fn classify(ind: Self::Ind) -> DetectorInd;
53}
54
55/// A detector's indication, in the port's terms.
56///
57/// The layer above matches on this rather than on the detector's own indication type, which is how
58/// one leader detector serves both.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum DetectorInd {
61    /// `node` is suspected of having crashed. A perfect detector means it; an eventually perfect
62    /// one may be wrong and may take it back.
63    Suspect { node: NodeId },
64    /// `node` is no longer suspected. Raised only by a detector that can observe its own mistake.
65    Restore { node: NodeId },
66}
67
68/// A detector that keeps nothing durable *the layer above has to know about*.
69///
70/// The conjunction written once rather than at every composing layer, as
71/// [`crate::link::VolatileLink`] is. Every detector in this crate satisfies it.
72pub trait VolatileDetector:
73    Detector<Meta = core::convert::Infallible, Entry = core::convert::Infallible>
74{
75}
76
77impl<D> VolatileDetector for D where
78    D: Detector<Meta = core::convert::Infallible, Entry = core::convert::Infallible>
79{
80}