Skip to main content

moonpool_sim/network/
config.rs

1//! # Network Chaos Configuration
2//!
3//! This module provides configuration for network chaos testing, following
4//! FoundationDB's battle-tested simulation approach and TigerBeetle's deterministic
5//! testing patterns.
6//!
7//! ## Connection Failure Modes
8//!
9//! | Failure | Config Field | Default | Real-World Scenario |
10//! |---------|--------------|---------|---------------------|
11//! | Random close | `random_close_probability` | 0.001% | Reconnection logic, message redelivery, connection pooling |
12//! | Asymmetric close | `random_close_explicit_ratio` | 30% explicit | Half-closed sockets, FIN vs RST handling |
13//! | Connect failure | `connect_failure_mode` | Probabilistic | Connection establishment retries, timeout handling |
14//! | Connection cut | Manual via `cut_connection()` | N/A | Temporary network outages, transient failures |
15//!
16//! ## Network Latency & Congestion
17//!
18//! | Delay Type | Config Field | Default | Real-World Scenario |
19//! |------------|--------------|---------|---------------------|
20//! | Operation latency | `bind/accept/connect/read/write_latency` | Various ranges | Timeout settings, async operation ordering |
21//! | Latency distribution | `latency_distribution` | Uniform | Tail latency testing, P99 behavior |
22//! | Slow latency | `slow_latency_probability` | 0.1% | 99.9th percentile testing |
23//! | Write clogging | `clog_probability` + `clog_duration` | 0%, 100-300ms | Backpressure handling, flow control |
24//! | Read clogging | Same as write | Same | Symmetric flow control |
25//! | Clock drift | `clock_drift_enabled` + `clock_drift_max` | true, 100ms | Lease expiration, distributed consensus, TTL handling |
26//! | Buggified delay | `buggified_delay_enabled` + `buggified_delay_max` | true, 100ms | Race conditions, timing-dependent bugs |
27//! | Handshake delay | `handshake_delay_enabled` + `handshake_delay_max` | true, 10ms | TLS negotiation, connection startup overhead |
28//!
29//! ## Network Partitions
30//!
31//! | Partition Type | Config/Method | Default | Real-World Scenario |
32//! |----------------|---------------|---------|---------------------|
33//! | Random partition | `partition_probability` + `partition_duration` | 0%, 200ms-2s | Split-brain, quorum loss, leader election |
34//! | Bi-directional | `partition_pair()` | Manual | Complete isolation between nodes |
35//! | Send-only block | `partition_send_from()` | Manual | Asymmetric network failures |
36//! | Recv-only block | `partition_recv_to()` | Manual | Asymmetric network failures |
37//! | Partition strategy | `partition_strategy` | Random | Different failure patterns (uniform, isolate) |
38//!
39//! ## Data Integrity Faults
40//!
41//! | Fault | Config Field | Default | Real-World Scenario |
42//! |-------|--------------|---------|---------------------|
43//! | Bit flip | `bit_flip_probability` + `bit_flip_min/max_bits` | 0.01%, 1-32 bits | CRC/checksum validation, data corruption detection |
44//!
45//! ## Half-Open Connection Simulation
46//!
47//! | State | Method | Real-World Scenario |
48//! |-------|--------|---------------------|
49//! | Peer crash | `simulate_peer_crash()` | TCP keepalive, heartbeat detection, silent failures |
50//! | Half-open detection | `should_half_open_error()` | Timeout-based failure detection |
51//!
52//! ## Partial Write Simulation
53//!
54//! | Feature | Config Field | Default | Real-World Scenario |
55//! |---------|--------------|---------|---------------------|
56//! | Short writes | `partial_write_max_bytes` | 1000 bytes | TCP fragmentation handling, message framing |
57//!
58//! ## Stable Connections
59//!
60//! | Feature | Method | Real-World Scenario |
61//! |---------|--------|---------------------|
62//! | Mark stable | `mark_connection_stable()` | Exempt supervision from chaos, parent-child connections |
63//!
64//! ## Configuration Examples
65//!
66//! ### Fast Local Testing (No Chaos)
67//! ```rust
68//! use moonpool_sim::network::{NetworkConfiguration, ChaosConfiguration};
69//!
70//! let config = NetworkConfiguration::fast_local();
71//! // All chaos disabled, minimal latencies
72//! ```
73//!
74//! ### Full Chaos Testing
75//! ```rust
76//! use moonpool_sim::network::{NetworkConfiguration, ChaosConfiguration};
77//!
78//! let config = NetworkConfiguration::random_for_seed();
79//! // Randomized chaos parameters for comprehensive testing
80//! ```
81//!
82//! ### Custom Configuration
83//! ```rust
84//! use moonpool_sim::network::{NetworkConfiguration, ChaosConfiguration, LatencyDistribution, PartitionStrategy};
85//! use std::time::Duration;
86//!
87//! let mut config = NetworkConfiguration::default();
88//! config.chaos.latency_distribution = LatencyDistribution::Bimodal;
89//! config.chaos.partition_strategy = PartitionStrategy::IsolateSingle;
90//! config.chaos.partition_probability = 0.05; // 5%
91//! ```
92//!
93//! ## FDB/TigerBeetle References
94//!
95//! - Random close: FDB sim2.actor.cpp:580-605
96//! - Latency distribution: FDB sim2.actor.cpp:317-329 (`halfLatency()`)
97//! - Partitions: FDB SimClogging, TigerBeetle partition modes
98//! - Bit flips: FDB FlowTransport.actor.cpp:1297
99//! - Clock drift: FDB sim2.actor.cpp:1058-1064
100//! - Connect failures: FDB sim2.actor.cpp:1243-1250
101
102use crate::sim::rng::{sim_random_range, sim_random_range_or_default};
103use std::ops::Range;
104use std::time::Duration;
105
106/// Latency distribution mode for network operations.
107///
108/// Controls how latencies are sampled for network operations.
109/// FDB ref: sim2.actor.cpp:317-329 (`halfLatency()`)
110///
111/// # Real-World Scenario
112///
113/// Real networks exhibit bimodal latency patterns where most operations are fast,
114/// but a small percentage experience significantly higher latency (tail latency).
115/// This is crucial for testing:
116/// - P99/P99.9 latency handling
117/// - Timeout tuning
118/// - Retry logic under tail latency conditions
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
120pub enum LatencyDistribution {
121    /// Uniform distribution within the configured range.
122    /// All latencies equally likely within [min, max].
123    #[default]
124    Uniform,
125
126    /// Bimodal distribution matching FDB's halfLatency() pattern.
127    /// - 99.9% of operations: fast latency (within configured range)
128    /// - 0.1% of operations: slow latency (multiplied by `slow_latency_multiplier`)
129    ///
130    /// FDB ref: sim2.actor.cpp:317-329
131    Bimodal,
132}
133
134/// Network partition strategy for chaos testing.
135///
136/// Controls how nodes are selected for partitioning during chaos testing.
137/// TigerBeetle ref: packet_simulator.zig:12-488
138///
139/// # Real-World Scenario
140///
141/// Different partition strategies test different failure modes:
142/// - Random: General chaos, unpredictable failures
143/// - UniformSize: Tests various quorum sizes and split scenarios
144/// - IsolateSingle: Tests single-node isolation (common in production)
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
146pub enum PartitionStrategy {
147    /// Random IP pairs selected for partitioning.
148    /// Current behavior - randomly selects which connections to partition.
149    #[default]
150    Random,
151
152    /// Uniform size partitions - randomly choose partition size from 1 to n-1 nodes.
153    /// TigerBeetle pattern: creates partitions of varying sizes to test different
154    /// quorum scenarios.
155    UniformSize,
156
157    /// Isolate single node - always partition exactly one node from the rest.
158    /// Tests the common production scenario where a single node becomes unreachable.
159    IsolateSingle,
160}
161
162/// Connection establishment failure mode for fault injection.
163///
164/// Controls how connection attempts fail during chaos testing.
165/// FDB ref: sim2.actor.cpp:1243-1250 (SIM_CONNECT_ERROR_MODE)
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
167pub enum ConnectFailureMode {
168    /// Disabled - no connection failures injected
169    #[default]
170    Disabled,
171    /// Always fail with `ConnectionRefused` when buggified
172    AlwaysFail,
173    /// Probabilistic: 50% fail with `ConnectionRefused`, 50% hang forever
174    Probabilistic,
175}
176
177impl ConnectFailureMode {
178    /// Create a random failure mode for chaos testing
179    pub fn random_for_seed() -> Self {
180        match sim_random_range(0..3) {
181            0 => Self::Disabled,
182            1 => Self::AlwaysFail,
183            _ => Self::Probabilistic,
184        }
185    }
186}
187
188/// Configuration for chaos injection in simulations.
189///
190/// This struct contains all settings related to fault injection and chaos testing,
191/// following FoundationDB's BUGGIFY patterns for deterministic testing.
192#[derive(Debug, Clone)]
193pub struct ChaosConfiguration {
194    /// Clogging probability for individual writes (0.0 - 1.0)
195    pub clog_probability: f64,
196    /// Duration range for clog delays
197    pub clog_duration: Range<Duration>,
198
199    /// Network partition probability (0.0 - 1.0)
200    pub partition_probability: f64,
201    /// Duration range for network partitions
202    pub partition_duration: Range<Duration>,
203
204    /// Bit flip probability for packet corruption (0.0 - 1.0)
205    pub bit_flip_probability: f64,
206    /// Minimum number of bits to flip (power-law distribution lower bound)
207    pub bit_flip_min_bits: u32,
208    /// Maximum number of bits to flip (power-law distribution upper bound)
209    pub bit_flip_max_bits: u32,
210    /// Cooldown duration after bit flip to prevent excessive corruption
211    pub bit_flip_cooldown: Duration,
212
213    /// Maximum bytes for partial write simulation (BUGGIFY truncates writes to 0-max_bytes)
214    /// Following FDB's approach of truncating writes to test TCP backpressure handling
215    pub partial_write_max_bytes: usize,
216
217    /// Random connection close probability per I/O operation (0.0 - 1.0)
218    /// FDB default: 0.00001 (0.001%) - see sim2.actor.cpp:584
219    pub random_close_probability: f64,
220
221    /// Cooldown duration after a random close event (prevents cascading failures)
222    /// FDB uses connectionFailuresDisableDuration - see sim2.actor.cpp:583
223    pub random_close_cooldown: Duration,
224
225    /// Ratio of explicit exceptions vs silent failures (0.0 - 1.0)
226    /// FDB default: 0.3 (30% explicit) - see sim2.actor.cpp:602
227    pub random_close_explicit_ratio: f64,
228
229    /// Enable clock drift simulation
230    /// When enabled, timer() can return a time up to clock_drift_max ahead of now()
231    /// FDB ref: sim2.actor.cpp:1058-1064
232    pub clock_drift_enabled: bool,
233
234    /// Maximum clock drift (default 100ms per FDB)
235    /// timer() can be up to this much ahead of now()
236    pub clock_drift_max: Duration,
237
238    /// Enable buggified delays on sleep/timer operations
239    /// When enabled, 25% of sleep operations get extra delay
240    /// FDB ref: sim2.actor.cpp:1100-1105
241    pub buggified_delay_enabled: bool,
242
243    /// Maximum additional delay for buggified sleep (default 100ms)
244    /// Uses power-law distribution: max_delay * pow(random01(), 1000.0)
245    /// FDB ref: sim2.actor.cpp:1104
246    pub buggified_delay_max: Duration,
247
248    /// Probability of adding buggified delay (default 25% per FDB)
249    pub buggified_delay_probability: f64,
250
251    /// Connection establishment failure mode (per FDB)
252    /// FDB ref: sim2.actor.cpp:1243-1250 (SIM_CONNECT_ERROR_MODE)
253    pub connect_failure_mode: ConnectFailureMode,
254
255    /// Probability of connect failure when Probabilistic mode is enabled (default 50%)
256    pub connect_failure_probability: f64,
257
258    /// Latency distribution mode for network operations.
259    ///
260    /// Controls whether latencies follow a uniform or bimodal distribution.
261    /// FDB ref: sim2.actor.cpp:317-329 (`halfLatency()`)
262    ///
263    /// # Real-World Scenario
264    /// Bimodal latency tests tail latency handling (P99/P99.9).
265    pub latency_distribution: LatencyDistribution,
266
267    /// Probability of a slow latency sample when using bimodal distribution (0.0 - 1.0).
268    /// FDB default: 0.001 (0.1%) - see sim2.actor.cpp:319
269    ///
270    /// # Real-World Scenario
271    /// Tests handling of 99.9th percentile latencies.
272    pub slow_latency_probability: f64,
273
274    /// Multiplier for slow latencies in bimodal distribution.
275    /// FDB: slow latency is up to 10x normal latency
276    ///
277    /// # Real-World Scenario
278    /// Simulates network congestion, GC pauses, or cross-datacenter hops.
279    pub slow_latency_multiplier: f64,
280
281    /// Enable handshake delay simulation on new connections.
282    /// FDB ref: connectHandshake():389 adds `delay(0.01 * random01())`
283    ///
284    /// # Real-World Scenario
285    /// Simulates TLS negotiation, connection establishment overhead.
286    pub handshake_delay_enabled: bool,
287
288    /// Maximum handshake delay (default 10ms per FDB).
289    /// Applied once when a connection is established.
290    ///
291    /// # Real-World Scenario
292    /// Tests connection pool warm-up, startup latency.
293    pub handshake_delay_max: Duration,
294
295    /// Network partition strategy.
296    /// Controls how nodes are selected for partitioning.
297    /// TigerBeetle ref: packet_simulator.zig partition modes
298    ///
299    /// # Real-World Scenario
300    /// Different strategies test different failure scenarios:
301    /// - Random: unpredictable chaos
302    /// - UniformSize: various quorum sizes
303    /// - IsolateSingle: single node isolation (common in production)
304    pub partition_strategy: PartitionStrategy,
305}
306
307impl Default for ChaosConfiguration {
308    fn default() -> Self {
309        Self {
310            clog_probability: 0.0,
311            clog_duration: Duration::from_millis(100)..Duration::from_millis(300),
312            partition_probability: 0.0,
313            partition_duration: Duration::from_millis(200)..Duration::from_secs(2),
314            bit_flip_probability: 0.0001, // 0.01% - matches FDB's BUGGIFY_WITH_PROB(0.0001)
315            bit_flip_min_bits: 1,
316            bit_flip_max_bits: 32,
317            bit_flip_cooldown: Duration::ZERO, // No cooldown by default for maximum chaos
318            partial_write_max_bytes: 1000,     // Matches FDB's randomInt(0, 1000)
319            random_close_probability: 0.00001, // 0.001% - matches FDB's sim2.actor.cpp:584
320            random_close_cooldown: Duration::from_secs(5), // Reasonable default
321            random_close_explicit_ratio: 0.3,  // 30% explicit - matches FDB's sim2.actor.cpp:602
322            clock_drift_enabled: true,         // Enable by default for chaos testing
323            clock_drift_max: Duration::from_millis(100), // FDB default: 0.1 seconds
324            buggified_delay_enabled: true,     // Enable by default for chaos testing
325            buggified_delay_max: Duration::from_millis(100), // FDB: MAX_BUGGIFIED_DELAY
326            buggified_delay_probability: 0.25, // FDB: random01() < 0.25
327            connect_failure_mode: ConnectFailureMode::Probabilistic, // FDB: SIM_CONNECT_ERROR_MODE = 2
328            connect_failure_probability: 0.5,                        // FDB: random01() > 0.5
329            latency_distribution: LatencyDistribution::default(),
330            slow_latency_probability: 0.001, // 0.1% per FDB halfLatency()
331            slow_latency_multiplier: 10.0,   // 10x normal latency
332            handshake_delay_enabled: true,
333            handshake_delay_max: Duration::from_millis(10), // FDB: 0.01 * random01()
334            partition_strategy: PartitionStrategy::default(),
335        }
336    }
337}
338
339impl ChaosConfiguration {
340    /// Create a configuration with all chaos disabled (for fast local testing)
341    pub fn disabled() -> Self {
342        Self {
343            clog_probability: 0.0,
344            clog_duration: Duration::ZERO..Duration::ZERO,
345            partition_probability: 0.0,
346            partition_duration: Duration::ZERO..Duration::ZERO,
347            bit_flip_probability: 0.0,
348            bit_flip_min_bits: 1,
349            bit_flip_max_bits: 32,
350            bit_flip_cooldown: Duration::ZERO,
351            partial_write_max_bytes: 1000,
352            random_close_probability: 0.0,
353            random_close_cooldown: Duration::ZERO,
354            random_close_explicit_ratio: 0.3,
355            clock_drift_enabled: false,
356            clock_drift_max: Duration::from_millis(100),
357            buggified_delay_enabled: false,
358            buggified_delay_max: Duration::from_millis(100),
359            buggified_delay_probability: 0.25,
360            connect_failure_mode: ConnectFailureMode::Disabled,
361            connect_failure_probability: 0.5,
362            latency_distribution: LatencyDistribution::Uniform, // No bimodal for fast testing
363            slow_latency_probability: 0.0,                      // No slow latencies
364            slow_latency_multiplier: 1.0,                       // No multiplier
365            handshake_delay_enabled: false,                     // No handshake delays
366            handshake_delay_max: Duration::ZERO,
367            partition_strategy: PartitionStrategy::Random, // Default strategy
368        }
369    }
370
371    /// Create a randomized chaos configuration for seed-based testing
372    pub fn random_for_seed() -> Self {
373        Self {
374            clog_probability: sim_random_range(0..20) as f64 / 100.0, // 0-20% for clogging
375            clog_duration: Duration::from_micros(sim_random_range(50000..300000))
376                ..Duration::from_micros(sim_random_range(100000..500000)),
377            partition_probability: sim_random_range(0..15) as f64 / 100.0, // 0-15% (lower than faults)
378            partition_duration: Duration::from_millis(sim_random_range(100..1000))
379                ..Duration::from_millis(sim_random_range(500..3000)),
380            // Bit flip probability range: 0.001% to 0.02% (very low, like FDB)
381            bit_flip_probability: sim_random_range(1..20) as f64 / 100000.0,
382            bit_flip_min_bits: 1,
383            bit_flip_max_bits: 32,
384            bit_flip_cooldown: Duration::from_millis(sim_random_range(0..100)),
385            partial_write_max_bytes: sim_random_range(100..2000), // Vary max bytes for different scenarios
386            // Random close probability: 0.0001% to 0.01% (very low, like FDB)
387            random_close_probability: sim_random_range(1..100) as f64 / 1000000.0,
388            random_close_cooldown: Duration::from_millis(sim_random_range(1000..10000)),
389            random_close_explicit_ratio: sim_random_range(20..40) as f64 / 100.0, // 20-40%
390            clock_drift_enabled: true,
391            clock_drift_max: Duration::from_millis(sim_random_range(50..150)), // 50-150ms
392            buggified_delay_enabled: true,
393            buggified_delay_max: Duration::from_millis(sim_random_range(50..150)), // 50-150ms
394            buggified_delay_probability: sim_random_range(20..30) as f64 / 100.0,  // 20-30%
395            connect_failure_mode: ConnectFailureMode::random_for_seed(),
396            connect_failure_probability: sim_random_range(40..60) as f64 / 100.0, // 40-60%
397            // Randomly choose latency distribution (50% uniform, 50% bimodal)
398            latency_distribution: if sim_random_range(0..2) == 0 {
399                LatencyDistribution::Uniform
400            } else {
401                LatencyDistribution::Bimodal
402            },
403            slow_latency_probability: sim_random_range(1..5) as f64 / 1000.0, // 0.1% to 0.5%
404            slow_latency_multiplier: sim_random_range(5..20) as f64,          // 5x to 20x
405            handshake_delay_enabled: true,
406            handshake_delay_max: Duration::from_millis(sim_random_range(5..20)), // 5-20ms
407            // Randomly choose partition strategy
408            partition_strategy: match sim_random_range(0..3) {
409                0 => PartitionStrategy::Random,
410                1 => PartitionStrategy::UniformSize,
411                _ => PartitionStrategy::IsolateSingle,
412            },
413        }
414    }
415}
416
417/// Configuration for network simulation parameters
418#[derive(Debug, Clone)]
419pub struct NetworkConfiguration {
420    /// Latency range for bind operations
421    pub bind_latency: Range<Duration>,
422    /// Latency range for accept operations
423    pub accept_latency: Range<Duration>,
424    /// Latency range for connect operations
425    pub connect_latency: Range<Duration>,
426    /// Latency range for read operations
427    pub read_latency: Range<Duration>,
428    /// Latency range for write operations
429    pub write_latency: Range<Duration>,
430
431    /// Chaos injection configuration
432    pub chaos: ChaosConfiguration,
433}
434
435impl Default for NetworkConfiguration {
436    fn default() -> Self {
437        Self {
438            bind_latency: Duration::from_micros(50)..Duration::from_micros(150),
439            accept_latency: Duration::from_millis(1)..Duration::from_millis(6),
440            connect_latency: Duration::from_millis(1)..Duration::from_millis(11),
441            read_latency: Duration::from_micros(10)..Duration::from_micros(60),
442            write_latency: Duration::from_micros(100)..Duration::from_micros(600),
443            chaos: ChaosConfiguration::default(),
444        }
445    }
446}
447
448/// Sample a random duration from a range
449pub fn sample_duration(range: &Range<Duration>) -> Duration {
450    let start_nanos = range.start.as_nanos() as u64;
451    let end_nanos = range.end.as_nanos() as u64;
452    let random_nanos = sim_random_range_or_default(start_nanos..end_nanos);
453    Duration::from_nanos(random_nanos)
454}
455
456/// Sample a random duration with bimodal distribution support.
457///
458/// FDB ref: sim2.actor.cpp:317-329 (`halfLatency()`)
459///
460/// When `distribution` is `Bimodal`:
461/// - 99.9% of samples use normal latency (within `range`)
462/// - 0.1% of samples use slow latency (multiplied by `slow_multiplier`)
463///
464/// # Parameters
465/// - `range`: The base latency range
466/// - `chaos`: Chaos configuration with distribution settings
467///
468/// # Real-World Scenario
469/// Bimodal latency tests tail latency handling:
470/// - Most requests complete quickly
471/// - Rare requests experience significant delays (P99.9)
472pub fn sample_duration_bimodal(range: &Range<Duration>, chaos: &ChaosConfiguration) -> Duration {
473    use crate::sim::rng::sim_random;
474
475    let base_duration = sample_duration(range);
476
477    match chaos.latency_distribution {
478        LatencyDistribution::Uniform => base_duration,
479        LatencyDistribution::Bimodal => {
480            // FDB pattern: 99.9% fast, 0.1% slow
481            if sim_random::<f64>() < chaos.slow_latency_probability {
482                // Slow path: multiply by slow_latency_multiplier
483                let slow_nanos =
484                    (base_duration.as_nanos() as f64 * chaos.slow_latency_multiplier) as u64;
485                tracing::trace!(
486                    "Bimodal slow latency: {:?} -> {:?}",
487                    base_duration,
488                    Duration::from_nanos(slow_nanos)
489                );
490                Duration::from_nanos(slow_nanos)
491            } else {
492                base_duration
493            }
494        }
495    }
496}
497
498/// Sample a handshake delay based on chaos configuration.
499///
500/// FDB ref: connectHandshake():389 adds `delay(0.01 * random01())`
501///
502/// Returns `Duration::ZERO` if handshake delays are disabled.
503pub fn sample_handshake_delay(chaos: &ChaosConfiguration) -> Duration {
504    use crate::sim::rng::sim_random;
505
506    if !chaos.handshake_delay_enabled || chaos.handshake_delay_max == Duration::ZERO {
507        return Duration::ZERO;
508    }
509
510    // FDB pattern: 0.01 * random01() = up to 10ms
511    let random_factor = sim_random::<f64>();
512    Duration::from_nanos((chaos.handshake_delay_max.as_nanos() as f64 * random_factor) as u64)
513}
514
515impl NetworkConfiguration {
516    /// Create a new network configuration with default settings
517    pub fn new() -> Self {
518        Self::default()
519    }
520
521    /// Create a randomized network configuration for chaos testing
522    pub fn random_for_seed() -> Self {
523        Self {
524            bind_latency: Duration::from_micros(sim_random_range(10..200))
525                ..Duration::from_micros(sim_random_range(50..300)),
526            accept_latency: Duration::from_micros(sim_random_range(1000..10000))
527                ..Duration::from_micros(sim_random_range(5000..15000)),
528            connect_latency: Duration::from_micros(sim_random_range(1000..50000))
529                ..Duration::from_micros(sim_random_range(10000..100000)),
530            read_latency: Duration::from_micros(sim_random_range(5..100))
531                ..Duration::from_micros(sim_random_range(50..200)),
532            write_latency: Duration::from_micros(sim_random_range(50..1000))
533                ..Duration::from_micros(sim_random_range(200..2000)),
534            chaos: ChaosConfiguration::random_for_seed(),
535        }
536    }
537
538    /// Create a configuration optimized for fast local testing
539    pub fn fast_local() -> Self {
540        let one_us = Duration::from_micros(1);
541        let ten_us = Duration::from_micros(10);
542        Self {
543            bind_latency: one_us..one_us,
544            accept_latency: ten_us..ten_us,
545            connect_latency: ten_us..ten_us,
546            read_latency: one_us..one_us,
547            write_latency: one_us..one_us,
548            chaos: ChaosConfiguration::disabled(),
549        }
550    }
551}