aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-05 01:48:26 +0100
committertv <tv@krebsco.de>2026-11-05 01:48:26 +0100
commitbef410759df8ebe2fd57aa4f987103e28bb4ea97 (patch)
tree7c27b78231b73914098a67165f16f748755ef9a6
parentb1cd80d1d49c63140c438a854d1b3570d3335d15 (diff)
bench tor round trips
-rw-r--r--docs/development.md2
-rw-r--r--src/simtest/bench.rs52
2 files changed, 51 insertions, 3 deletions
diff --git a/docs/development.md b/docs/development.md
index 2ed15f6..519d64c 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -22,7 +22,7 @@ messages, `document/bench.rs`), the remakes' replay (`remake/bench.rs`), and wha
passing changes on costs over the simulated network (`simtest/bench.rs`: messages and
bytes per write by kind, propagation delays, for a full mesh, a writer behind NAT, two
transports, partial meshes of 50 and 100 nodes, lossy links (with delays at random,
-and with Mycelium's round trips, each link its own), a member away for a while, and
+and with Mycelium's round trips, each link its own; and Tor's, with some slow circuits), a member away for a while, and
members joining during the writes; several minutes, or one scenario alone
with `BENCH_SCENARIO`, part of its name: `BENCH_SCENARIO=NAT`; all at once with
`BENCH_PARALLEL=1`, faster on many cores, though sharing them makes delays come out
diff --git a/src/simtest/bench.rs b/src/simtest/bench.rs
index 82c8985..1d6d411 100644
--- a/src/simtest/bench.rs
+++ b/src/simtest/bench.rs
@@ -63,6 +63,9 @@ struct Scenario {
/// registration reached them, and their trees with them. Their catching up counts
/// in the messages, but not in the delays (`swarm::stats`: not until caught up).
joining: usize,
+ /// The first wait for a sync answer, if not `SYNC_REPLY_TIMEOUT`: longer where round
+ /// trips take seconds, as Tor's transport asks for (`Transport::sync_reply_timeout`).
+ reply_timeout: Option<Duration>,
}
fn sim_only(_: usize) -> &'static [&'static str] {
@@ -127,6 +130,31 @@ fn mycelium_like(sim: &Sim, ids: &[String]) {
}
}
+/// Each link a round trip of its own, as over Tor: 250 to 400 ms, each message within a
+/// fifth of its link's; one link in ten through a slow circuit, 1 to 3 s. Nothing lost
+/// (Tor carries streams reliably). The simulated net can't make one link slow for a
+/// while only, so the circuits that are slow at times are slow throughout here. With a
+/// first wait for answers of 5 s, as Tor's transport waits longer (30 s in production).
+fn tor_like(sim: &Sim, ids: &[String]) {
+ let mut rng = crate::testrand::Rng::new(9);
+ for (i, from) in ids.iter().enumerate() {
+ for to in &ids[i + 1..] {
+ let rtt_ms = if rng.chance(0.1) {
+ 1000.0 + rng.next_f64() * 2000.0
+ } else {
+ 250.0 + rng.next_f64() * 150.0
+ };
+ let half = Duration::from_secs_f64(rtt_ms / 2.0 / 1000.0);
+ let link = LinkParams {
+ delay: (half * 4 / 5, half * 6 / 5),
+ ..LinkParams::default()
+ };
+ sim.net.set_link(from, to, link.clone());
+ sim.net.set_link(to, from, link);
+ }
+ }
+}
+
const SCENARIOS: &[Scenario] = &[
Scenario {
name: "full mesh, 13 nodes",
@@ -136,6 +164,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: None,
joining: 0,
+ reply_timeout: None,
},
Scenario {
name: "writer behind NAT (one link), 13 nodes",
@@ -145,6 +174,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: None,
joining: 0,
+ reply_timeout: None,
},
Scenario {
name: "two transports, 3 bridges, 30 nodes",
@@ -154,6 +184,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 5,
away: None,
joining: 0,
+ reply_timeout: None,
},
Scenario {
name: "partial mesh, 50 nodes",
@@ -163,6 +194,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: None,
joining: 0,
+ reply_timeout: None,
},
Scenario {
name: "partial mesh, lossy and slow links, 50 nodes",
@@ -172,6 +204,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: None,
joining: 0,
+ reply_timeout: None,
},
Scenario {
name: "partial mesh, 100 nodes",
@@ -181,6 +214,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: None,
joining: 0,
+ reply_timeout: None,
},
Scenario {
name: "partial mesh, a member away for 10 s, 50 nodes",
@@ -190,6 +224,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: Some(3),
joining: 0,
+ reply_timeout: None,
},
Scenario {
name: "partial mesh, 5 members joining during each workload, 50 nodes",
@@ -199,6 +234,7 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: None,
joining: 5,
+ reply_timeout: None,
},
// Last, so the others keep their seeds.
Scenario {
@@ -209,6 +245,17 @@ const SCENARIOS: &[Scenario] = &[
writer: 1,
away: None,
joining: 0,
+ reply_timeout: None,
+ },
+ Scenario {
+ name: "partial mesh, Tor's round trips, 50 nodes",
+ nodes: 50,
+ on: sim_only,
+ net: tor_like,
+ writer: 1,
+ away: None,
+ joining: 0,
+ reply_timeout: Some(Duration::from_secs(5)),
},
];
@@ -378,8 +425,9 @@ async fn run(seed: u64, scenario: &'static Scenario) -> String {
let sim = Sim::with_identity(seed);
let ids: Vec<String> = (0..scenario.nodes).map(|i| format!("n{i:03}")).collect();
(scenario.net)(&sim, &ids);
- let timing = |config: &mut crate::node::NodeConfig| {
- config.timing = Timing::new(KEEPALIVE, SYNC_REPLY_TIMEOUT, DELIVERY_LIMIT);
+ let reply_timeout = scenario.reply_timeout.unwrap_or(SYNC_REPLY_TIMEOUT);
+ let timing = move |config: &mut crate::node::NodeConfig| {
+ config.timing = Timing::new(KEEPALIVE, reply_timeout, DELIVERY_LIMIT);
// As in production (`--neighbours`).
config.neighbours_k = 12;
};