Merge maint into master, carrying the dedup-cache test fix and the rekey comment correction

The two rekey comments conflicted: master no longer limits the rekey
snapshot to healthy peers, so the corrected wording is kept without
that qualifier.
This commit is contained in:
Johnathan Corgan
2026-09-23 01:28:42 +00:00
3 changed files with 30 additions and 12 deletions
+8 -7
View File
@@ -126,13 +126,14 @@ impl Node {
};
// The shell snapshots each peer's rekey ages/flags (every clock
// read resolved here); the core decides cutover/drain/trigger with no
// clock, phase-grouped to preserve the pre-refactor execution order.
// The batch `poll_rekey` + snapshots STAY SHELL-SIDE and BYTE-UNCHANGED:
// the cross-peer phase-grouping (all Cutover → all Drain →
// all InitiateRekey) governs the shared `index_allocator` free-then-alloc
// SEQUENCE that appears on the wire. The machine must NOT re-poll; it
// CONSUMES each decided `ConnAction` in the same order the batch returned.
// read resolved here); the core decides cutover, drain, retirement and
// trigger with no clock and returns the actions phase-grouped: all
// Cutover, then all Drain, then all RetirePending, then all
// InitiateRekey. That grouping fixes the shared `index_allocator`
// free-then-allocate sequence that appears on the wire, so the batch
// `poll_rekey` call stays here in the shell. The machine must NOT
// re-poll; it CONSUMES each decided `ConnAction` in the order the batch
// returned.
let snapshots = self.rekey_peers();
for action in self.fmp.poll_rekey(snapshots, &cfg) {
match action {
+20 -2
View File
@@ -670,12 +670,25 @@ fn register_peers(node: &mut Node, count: usize) -> Vec<crate::NodeAddr> {
.collect()
}
/// A node whose dedup entries cannot age out while a flood test runs.
///
/// The handler stamps each entry with the wall clock and purges entries older
/// than `recent_expiry_secs` (10 s by default) on every arrival. The flood
/// tests below measure capacity, not expiry, and a 4096-request flood on a
/// loaded host can take longer than the default, so the earliest entries
/// would be purged before the test reads the cache.
fn unexpiring_node() -> Node {
let mut config = Config::new();
config.node.lookup.recent_expiry_secs = 86_400;
make_node_with(config)
}
#[tokio::test]
async fn test_a_full_dedup_cache_admits_the_new_request_by_evicting_the_oldest() {
// A full cache used to drop the arriving request, which let one peer
// spend 4096 fresh request_ids and stop the node forwarding anyone
// else's lookups until the entries aged out.
let mut node = make_node();
let mut node = unexpiring_node();
let from = make_node_addr(0xAA);
flood_requests(&mut node, &from, 1, MAX_RECENT_LOOKUP_REQUESTS as u64).await;
@@ -746,11 +759,16 @@ async fn test_a_node_whose_dedup_cache_is_flooded_still_answers_a_lookup_for_its
// The availability claim. Filling the cache used to make the node
// unresolvable, because the cache-full drop sat ahead of the check for
// whether the request names us.
let mut node = make_node();
let mut node = unexpiring_node();
let flooder = make_node_addr(0xAA);
let other = make_node_addr(0xAB);
flood_requests(&mut node, &flooder, 1, MAX_RECENT_LOOKUP_REQUESTS as u64).await;
assert_eq!(
node.lookup.recent_requests.len(),
MAX_RECENT_LOOKUP_REQUESTS,
"precondition: the cache is full, or the lookup below is not tested against a flood"
);
let my_addr = *node.node_addr();
let payload = lookup_request_payload(u64::MAX, &my_addr);
+2 -3
View File
@@ -523,8 +523,7 @@ impl Fmp {
}
/// Decide the per-tick rekey choreography for the peers the shell
/// snapshotted. Reproduces the pre-refactor priority and phase grouping
/// exactly:
/// snapshotted, in this priority:
///
/// - **Cutover** takes precedence: a peer with a pending session this node
/// initiated and no in-flight rekey cuts over and is considered for
@@ -536,7 +535,7 @@ impl Fmp {
/// trigger fires when the peer is neither mid-rekey, dampened, nor
/// holding a pending session, and its jittered time threshold or send
/// counter is reached. A draining peer can thus both drain and
/// re-trigger in the same tick, as before.
/// re-trigger in the same tick.
///
/// Actions are returned phase-grouped (all cutovers, then all drains, then
/// all retirements, then all rekey initiations) to preserve the global