//! RFC 010 — a seed whose address answers as a *different* name //! (`DialError::PeerNameMismatch`) is dialed once and then parked: the //! connector must not redial it on backoff forever. //! //! Observed from the misdialed peer: each such dial establishes at the //! responder (it registers, `node_up`), then the dialer closes on the name //! check (`node_down`) — one membership blip per attempt. Cross-process: a //! *server* named `server` subscribes and reports; a *client* on fast //! timing (50–500ms backoff) seeds `("wrongname", server_addr)`. After the //! first blip the server counts further `NodeUp`s across 2s — several //! backoff periods. Parked ⇒ zero. Negative-control-verified: with the park //! stubbed out the count is ≥ 1 in the same window. #![cfg(feature = "cluster")] mod common; use common::{maybe_child, spawn_node}; use smarm::cluster::envelope::NodeMeta; use smarm::cluster::membership::{subscribe, NodeEvent}; use smarm::cluster::{start, Config, StaticSeeds, Timing}; use std::time::{Duration, Instant}; const ROLES: &[(&str, fn())] = &[("server", role_server), ("client", role_client)]; fn meta() -> NodeMeta { NodeMeta { role: "mismatch".into(), region: "local".into(), } } fn timing() -> Timing { Timing { initial_backoff: Duration::from_millis(50), max_backoff: Duration::from_millis(500), ..Timing::default() } } fn role_server() { smarm::run(|| { let cluster = start(Config { node_name: "server".into(), meta: meta(), listen_addr: std::env::var("SMARM_LISTEN_ADDR") .unwrap_or_else(|_| "127.0.0.1:0".into()), strategy: Box::new(StaticSeeds::new(Vec::<(String, String)>::new())), timing: timing(), }) .expect("binds"); let ev = subscribe().unwrap(); println!("LISTENING {}", cluster.local_addr()); // First blip: the misdialed client establishes, then closes on us. loop { match ev.rx.recv() { Ok(NodeEvent::NodeDown(i)) if i.name == "client" => break, Ok(_) => continue, Err(_) => panic!("manager gone"), } } println!("BLIP"); // Now count further NodeUps across several backoff periods. let mut more = 0usize; let t0 = Instant::now(); while t0.elapsed() < Duration::from_millis(2000) { match ev.rx.try_recv() { Ok(Some(NodeEvent::NodeUp(i))) if i.name == "client" => more += 1, Ok(_) => {} Err(_) => panic!("manager gone"), } smarm::sleep(Duration::from_millis(50)); } println!("MORE {more}"); loop { smarm::sleep(Duration::from_secs(3600)); } }); } fn role_client() { let server_addr = std::env::var("SMARM_SERVER_ADDR").expect("SMARM_SERVER_ADDR"); smarm::run(move || { let _cluster = start(Config { node_name: "client".into(), meta: meta(), listen_addr: "127.0.0.1:0".into(), strategy: Box::new(StaticSeeds::new(vec![( "wrongname".to_string(), server_addr, )])), timing: timing(), }) .expect("binds"); println!("CLIENT UP"); loop { smarm::sleep(Duration::from_secs(3600)); } }); } #[test] fn mismatched_seed_is_dialed_once_then_parked() { maybe_child(ROLES); let mut server = spawn_node("server", &[]); let saddr = server.wait_listening(); let mut client = spawn_node("client", &[("SMARM_SERVER_ADDR", &saddr)]); client.wait_line("CLIENT UP", |l| l == "CLIENT UP"); server.wait_line("BLIP", |l| l == "BLIP"); let line = server.wait_line("MORE", |l| l.starts_with("MORE ")); let more: usize = line.split_whitespace().nth(1).unwrap().parse().unwrap(); assert_eq!( more, 0, "mismatched seed was redialed {more}× after being parked" ); }