diff --git a/CHANGELOG.md b/CHANGELOG.md index 3462f03..4e0fa6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ release. The format is loosely based on ## irtt-proto +### 0.5.2 + +#### Added + +- Exported `VERSION`, the compiled crate's Cargo package version, for applications reporting their component versions. Wire behavior is unchanged. + ### 0.5.1 #### Added @@ -34,6 +40,25 @@ release. The format is loosely based on ## irtt-client +### 0.7.0 + +#### Added + +- Opt-in automatic session reconnection in the Tokio managed driver and `BlockingManagedClient`. Continuous measurement runs can retry initial connection failures, failed or peer-closed sessions, and server-limited session completion. Silence detection reconnects a session that stops replying while probes are being sent. +- Each desired target retries independently, leaving healthy targets running. Exponential backoff doubles from `reconnect_initial_delay` (default 250 ms) to `reconnect_max_delay` (default 10 s); a valid reply restores the initial delay. Reopening resolves the original endpoint again. Target removal and explicit shutdown cancel recovery. +- Exported `VERSION` for compiled component-version reporting. + +#### Fixed + +- Stopping or replacing a target during session opening cancels the pending open immediately, instead of waiting for its retry schedule to finish. + +#### Compatibility + +- `ManagedClientConfig` gains `reconnect_timeout`, `reconnect_initial_delay`, and `reconnect_max_delay`; update complete struct literals or use `..Default::default()`. Reconnection is disabled by default (`reconnect_timeout: None`) and is valid only for continuous normal measurement mode, with a positive schedulable silence timeout. Retry delays must be positive and schedulable, and the maximum must be at least twice the initial delay. +- `ManagedConfigError` gains `InvalidReconnectPolicy` and `InvalidReconnectDelays`; downstream exhaustive matches must handle them. Delay validation applies even when recovery is disabled. +- A reconnecting target returns to pending/opening status rather than producing a terminal outcome for each retry. Consumers must handle repeated session-start events for the same target instance and reset session-bound statistics and sequence tracking on each new session. +- Use `irtt-stats` 0.6 with this client version when exchanging `ClientEvent` values. Tokio remains optional; the default low-level client remains runtime-free. No wire-protocol migration is required. + ### 0.6.0 #### Added @@ -105,6 +130,12 @@ release. The format is loosely based on ## irtt-server +### 0.5.4 + +#### Added + +- Exported `VERSION`, the compiled crate's Cargo package version, for applications reporting their component versions. Server policy and wire behavior are unchanged. + ### 0.5.3 #### Added @@ -150,6 +181,23 @@ release. The format is loosely based on ## irtt-stats +### 0.6.0 + +#### Changed + +- Updated the public `irtt-client` dependency to 0.7. Applications passing `ClientEvent` values into this crate must upgrade both dependencies together; client 0.6 and 0.7 event types come from different crate versions and cannot be mixed. +- Added signed nanosecond changes to `IpdvPairUpdate`: `rtt_ipdv_ns`, `send_ipdv_ns`, and `receive_ipdv_ns`. Existing `Duration` fields and aggregate IPDV statistics remain absolute magnitudes. Update complete struct literals to include the new fields. +- Exported `VERSION` for compiled component-version reporting. + +#### Fixed + +- Directional IPDV is available with midpoint timestamps, using the midpoint for both server endpoints. +- Per-packet IPDV preserves the sign of the delay change instead of reporting only its magnitude. + +#### Compatibility + +- Collectors remain scoped to one target session. Reset or replace a collector when the managed client reconnects; this release does not aggregate across session sequence resets. + ### 0.5.2 #### Changed @@ -192,6 +240,30 @@ release. The format is loosely based on ## irtt-rs +### 0.10.0 + +#### Added + +- Continuous CLI recovery with `--continuous --reconnect` (or `--duration 0 --reconnect`). Failed initial opens, failed or closed sessions, server-limited completion, and reply silence trigger independent per-target retries. Healthy targets keep running, including in multi-target and `--targets-stdin` runs; removing a desired target cancels its retries. +- `--reconnect-timeout` configures reply-silence detection while sending (default 10 s). `--reconnect-initial-delay` and `--reconnect-max-delay` configure per-target exponential backoff (defaults 250 ms and 10 s); the maximum must be at least twice the initial delay. Valid replies reset the backoff. Explicit stops do not retry. +- `--no-stats` disables client statistics collection, retention warnings, and final summaries while retaining measurement events and structured output columns. Statistics-dependent fields have absent values. +- Optional `stats` Cargo feature, enabled by default and required by `tui`. Build a lightweight client without statistics using `cargo build --release -p irtt-rs --bin irtt-client --no-default-features --features client`; use `--features client,stats` for a client-only build with statistics. +- `--continuous` as an explicit alias for zero duration, `-d` for duration, `-i` for interval, `-l` for packet length, client `-f` for output format, and `-v` for verbose output. Client/TUI durations accept fractional and composite values such as `2.5s` and `1m30s`. +- Client/TUI `--timeouts` configures the session-opening attempt schedule independently of session reconnection. +- Client `-n` / `--no-test` negotiates parameters and exits without measurement probes, using the selected output format. +- `--version` on the dispatcher and applets; applets also report their compiled library versions and whether client statistics are disabled. + +#### Fixed + +- Signed per-packet IPDV is preserved in output; directional IPDV is available with midpoint timestamps. + +#### Compatibility + +- Reconnection is opt-in for the CLI and requires continuous measurement mode; it cannot be combined with `--no-test`. Statistics reset on every reopened session, so summaries describe only the latest session per target, not the whole continuous run. A warning explains this when recovery and statistics are both enabled. Continuous TUI recovery remains enabled as before. +- Statistics are no longer implied by the `client` feature. Existing custom builds using `--no-default-features --features client` must add `stats` to retain collection and summaries. `--no-stats` is accepted in builds without statistics; output schemas are preserved, with absent IPDV values. +- Nonzero duration values require units; zero (including `0s`) still selects continuous mode. `--continuous` and an explicit `--duration` are mutually exclusive. +- Application-library users must account for the added client argument/configuration fields and optional statistics feature. Dependencies are now client 0.7 and stats 0.6, with proto >=0.5.2 and server >=0.5.4 for component-version reporting. MSRV remains Rust 1.88. + ### 0.9.0 #### Added @@ -330,10 +402,15 @@ Starting with this release, crates are versioned and tagged independently rather than in lockstep. Each crate's release is tagged as `/vX.Y.Z` (e.g. `irtt-proto/v0.5.0`), pointing at the commit its version was released from. `irtt-proto`, `irtt-client`, `irtt-server`, and `irtt-stats` are -libraries published to [crates.io](https://crates.io); their tags exist to +libraries published to [crates.io](https://crates.io); publication is a separate +manual Cargo step. Their tags exist to identify the exact source for a given crates.io release and do not produce a GitHub Release. The application package (`irtt-cli` through 0.5.0, `irtt-rs` from 0.6.0 onward) is the only package with prebuilt binary artifacts: pushing an `irtt-rs/vX.Y.Z` tag triggers [cargo-dist](https://opensource.axo.dev/cargo-dist/) -to build and publish a GitHub Release with platform binaries. The historical +to build and publish a GitHub Release with platform binaries. On Gitea, the +same application tag triggers the server Docker image workflow, publishing +`X.Y.Z` and `latest` to Gitea and GHCR. Library tags +trigger the Gitea Semver workflow, but neither binary nor image publication. +The application tag also triggers that Semver check. The historical `irtt-cli/v0.5.0` tag remains as released; it is not rewritten or replaced. diff --git a/Cargo.lock b/Cargo.lock index 14be465..d02e759 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -857,7 +857,7 @@ dependencies = [ [[package]] name = "irtt-client" -version = "0.6.0" +version = "0.7.0" dependencies = [ "irtt-proto", "irtt-server", @@ -879,7 +879,7 @@ dependencies = [ [[package]] name = "irtt-proto" -version = "0.5.1" +version = "0.5.2" dependencies = [ "hmac", "md-5", @@ -890,7 +890,7 @@ dependencies = [ [[package]] name = "irtt-rs" -version = "0.9.0" +version = "0.10.0" dependencies = [ "chrono", "clap", @@ -911,7 +911,7 @@ dependencies = [ [[package]] name = "irtt-server" -version = "0.5.3" +version = "0.5.4" dependencies = [ "getrandom 0.4.3", "irtt-proto", @@ -923,7 +923,7 @@ dependencies = [ [[package]] name = "irtt-stats" -version = "0.5.2" +version = "0.6.0" dependencies = [ "irtt-client", "measurement-stats", diff --git a/Cargo.toml b/Cargo.toml index d66bc81..e466658 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,10 @@ rust-version = "1.88" repository = "https://github.com/Lochnair/irtt-rs" [workspace.dependencies] -irtt-proto = { version = "0.5.0", path = "crates/irtt-proto" } -irtt-client = { version = "0.6.0", path = "crates/irtt-client" } -irtt-server = { version = "0.5.2", path = "crates/irtt-server" } -irtt-stats = { version = "0.5.2", path = "crates/irtt-stats" } +irtt-proto = { version = "0.5.2", path = "crates/irtt-proto" } +irtt-client = { version = "0.7.0", path = "crates/irtt-client" } +irtt-server = { version = "0.5.4", path = "crates/irtt-server" } +irtt-stats = { version = "0.6.0", path = "crates/irtt-stats" } [profile.release] opt-level = "z" diff --git a/README.md b/README.md index 85a631b..d14d3f8 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,8 @@ this project's interop harness. | --- | --- | --- | | Wire protocol | Protocol version 1, verified against upstream v0.9.1 | Protocol version 1 (unchanged in v0.9.2) | | Finite probing | Supported, default 10s | Supported, default 1m | -| Continuous/indefinite operation | Supported (`--duration 0`); still computes running statistics (see below) | Supported via streaming mode (`-s`, added in v0.9.2); computes no statistics at all in that mode | -| Statistics in continuous mode | Running count/min/max/mean/variance per target; exact medians unavailable; bounded IPDV tracking | None in streaming mode — raw/event output only, by design, to avoid unbounded memory | +| Continuous/indefinite operation | Supported (`--continuous` or `--duration 0`), with optional per-target reconnection | Supported via streaming mode (`-s`, added in v0.9.2); computes no statistics at all in that mode | +| Statistics in continuous mode | Optional running count/min/max/mean/variance per session; no exact medians; bounded IPDV tracking. Use `--no-stats` or omit the `stats` feature to disable collection | None in streaming mode — raw/event output only, by design, to avoid unbounded memory | | Multi-target probing (one invocation) | Supported, with labels and staggered/burst pacing | Not supported — one remote address per invocation | | Terminal UI | `irtt-tui`, a live graph/dashboard applet | Not present | | Output formats | table, csv, tsv, jsonl, with selectable columns | Text summary, JSON (optionally gzip to file), and a fixed-field raw mode; no CSV/TSV and no column selection | @@ -115,9 +115,16 @@ irtt-client --no-test netperf-eu.bufferbloat.net:2112 Probe continuously until interrupted (`Ctrl-C`): ```sh -irtt-client netperf-eu.bufferbloat.net:2112 --duration 0 +irtt-client netperf-eu.bufferbloat.net:2112 --continuous + +# Recover failed, closed, or silent sessions independently per target +irtt-client netperf-eu.bufferbloat.net:2112 --continuous --reconnect \ + --reconnect-timeout 10s --reconnect-initial-delay 250ms --reconnect-max-delay 10s ``` +Reconnection resets statistics for that target; summaries cover its latest +session. Add `--no-stats` for measurement events without statistics collection. + Watch the same session live in the terminal UI: ```sh diff --git a/crates/irtt-app/Cargo.toml b/crates/irtt-app/Cargo.toml index b13459b..4199a20 100644 --- a/crates/irtt-app/Cargo.toml +++ b/crates/irtt-app/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "irtt-rs" -version = "0.9.0" +version = "0.10.0" description = "IRTT-compatible client, TUI, and server applications" edition.workspace = true license.workspace = true diff --git a/crates/irtt-client/Cargo.toml b/crates/irtt-client/Cargo.toml index eb66fb6..c7d22d1 100644 --- a/crates/irtt-client/Cargo.toml +++ b/crates/irtt-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "irtt-client" -version = "0.6.0" +version = "0.7.0" description = "IRTT-compatible Rust client library" edition.workspace = true license.workspace = true diff --git a/crates/irtt-client/README.md b/crates/irtt-client/README.md index 9c59ef4..b9b053f 100644 --- a/crates/irtt-client/README.md +++ b/crates/irtt-client/README.md @@ -35,6 +35,21 @@ Use `ClientEvent::from(receipt)` for an `EchoSent` event with `scheduled_at` and `timer_error` absent; the managed driver supplies those schedule measurements and publishes committed sends even when post-send processing fails. +Managed continuous measurement runs can opt into automatic recovery with +`ManagedClientConfig.reconnect_timeout: Some(timeout)`. Initial connection +failures, closed/failed sessions, server-limited completion, and reply silence +while sending trigger independent per-target retries. `reconnect_initial_delay` +(default 250 ms) doubles to `reconnect_max_delay` (default 10 s); a valid reply +resets the delay. Both delays must be positive and schedulable, with the maximum +at least twice the initial delay. Explicit stop and target removal cancel retries. +Recovery is disabled by default and is invalid for finite or negotiation-only runs. + +In 0.7, complete `ManagedClientConfig` literals must include the three recovery +fields (or use `..Default::default()`), and exhaustive `ManagedConfigError` +matches must handle the new recovery-policy variants. A target can emit repeated +session-start events within one instance; reset session-bound state, including +statistics, for each reopened session. Use `irtt-stats` 0.6 with client 0.7 events. + Tokio stays optional: the default build has no runtime and no Tokio dependency at all. diff --git a/crates/irtt-proto/Cargo.toml b/crates/irtt-proto/Cargo.toml index 7189157..4aed92c 100644 --- a/crates/irtt-proto/Cargo.toml +++ b/crates/irtt-proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "irtt-proto" -version = "0.5.1" +version = "0.5.2" description = "IRTT protocol encoding and decoding primitives" edition.workspace = true license.workspace = true diff --git a/crates/irtt-server/Cargo.toml b/crates/irtt-server/Cargo.toml index bcb8f7b..20ba773 100644 --- a/crates/irtt-server/Cargo.toml +++ b/crates/irtt-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "irtt-server" -version = "0.5.3" +version = "0.5.4" description = "IRTT-compatible Rust server library" edition.workspace = true license.workspace = true diff --git a/crates/irtt-stats/Cargo.toml b/crates/irtt-stats/Cargo.toml index 8be4096..b6388d3 100644 --- a/crates/irtt-stats/Cargo.toml +++ b/crates/irtt-stats/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "irtt-stats" -version = "0.5.2" +version = "0.6.0" description = "Statistics aggregation for irtt-client events" edition.workspace = true license.workspace = true diff --git a/crates/irtt-stats/README.md b/crates/irtt-stats/README.md index b6e3709..71896f6 100644 --- a/crates/irtt-stats/README.md +++ b/crates/irtt-stats/README.md @@ -16,10 +16,16 @@ re-export. Event interpretation, IPDV pairing, loss, and snapshot reconstruction remain here. A `StatsCollector` has one sequence/IPDV namespace and one packet count, both -scoped to a single target. A managed session covering multiple targets needs -one collector per target — as the `irtt-rs` CLI does — since a target's +scoped to a single target session. A managed session covering multiple targets +needs one collector per target — as the `irtt-rs` CLI does — since a target's sequence numbers start over at zero and feeding two targets into one -collector would pair up unrelated probes. +collector would pair up unrelated probes. Reset or replace the collector when a +target reconnects, since the new session restarts its sequence numbers. + +Version 0.6 consumes `irtt-client` 0.7 events. Applications using both crates +must upgrade them together to avoid incompatible `ClientEvent` types. +`IpdvPairUpdate` exposes signed nanosecond changes alongside the existing +absolute `Duration` values; aggregate IPDV statistics remain absolute magnitudes. ## Retention modes diff --git a/docs/irtt-client.md b/docs/irtt-client.md index 5f483c9..d5719c0 100644 --- a/docs/irtt-client.md +++ b/docs/irtt-client.md @@ -115,7 +115,8 @@ A fixed local port may cause binding conflicts when multiple target sockets are ## DYNAMIC TARGET SETS FROM STANDARD INPUT -`--targets-stdin` is available only with continuous mode (`--duration 0`). +`--targets-stdin` is available only with continuous mode (`--continuous` or +`--duration 0`). Positional targets remain an initial desired set, and may be omitted to start empty: @@ -208,12 +209,14 @@ Omitting the option preserves the library default schedule. `--duration` controls the run length: - Any nonzero duration (default `10s`) is a **finite** run: probing stops - after that long, and a final summary is always printed for table output - (see [OUTPUT](#output) below — CSV, TSV, and JSON Lines never print one). -- `--duration 0` is **continuous**: the client runs until interrupted - (`Ctrl-C`). For table output, a final summary is printed only if the run - was interrupted. Stdin-controlled mode is the exception: EOF also requests - a graceful stop and prints the final retained summary. An uninterrupted + after that long, and a final summary is printed for table output when + statistics are enabled (see [OUTPUT](#output) below — CSV, TSV, and + JSON Lines never print one). +- `--continuous` (or `--duration 0`) is **continuous**: the client runs until + interrupted (`Ctrl-C`). With statistics enabled and table output, a final + summary is printed only if the run was interrupted. Stdin-controlled mode + is the exception: EOF also requests a graceful stop and prints the final + retained summary. An uninterrupted continuous run that exits for another reason does not get one. See [MEASUREMENTS AND MEMORY](#measurements-and-memory) below for why this @@ -249,6 +252,8 @@ Example for continuous, multi-target CSV output without statistics: ```sh irtt-client --continuous --reconnect --no-stats \ + --reconnect-timeout 10s \ + --reconnect-initial-delay 250ms --reconnect-max-delay 10s \ --format csv --header never \ --columns client_receive_wall_ns,target,seq,rd_us,sd_us \ reflector1 reflector2 @@ -344,6 +349,10 @@ processing time is derived from the same server receive endpoint. ## MEASUREMENTS AND MEMORY +The statistics retention below applies only when the `stats` feature is compiled +in and `--no-stats` is absent. Disabling collection keeps the client +classification bounds but allocates no statistics collectors. + Finite and continuous modes make a different tradeoff between exact statistics and bounded memory. @@ -394,9 +403,10 @@ the client exits nonzero — so a process supervisor can restart it; an interrupted run (`Ctrl-C`) never reports peer closure as an error, whichever mode it was in. -With `--targets-stdin`, peer closure is target-local: other desired targets -and the stdin controller continue running. Including that target in a later -complete set starts a fresh generation. +With `--reconnect`, a desired target reopens automatically after peer closure. +Without recovery, `--targets-stdin` makes peer closure target-local: other +desired targets and the stdin controller continue running. Including that +target in a later complete set starts a fresh generation. ## EXIT STATUS diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 3346de1..5dc15e6 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "irtt-proto" -version = "0.5.1" +version = "0.5.2" dependencies = [ "hmac", "md-5", @@ -152,7 +152,7 @@ dependencies = [ [[package]] name = "irtt-server" -version = "0.5.3" +version = "0.5.4" dependencies = [ "getrandom", "irtt-proto",