Add URL-level concurrency (-c flag, Step 7)

Probe multiple URLs in parallel with a bounded worker pool; the N samples
of each URL remain sequential to preserve accurate min/avg/max statistics.
Default auto-concurrency is min(numURLs, 8); -c 1 restores serial mode.
Output is always buffered and printed in original input order. Verified
clean with go test -race.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 01:33:03 +02:00
parent 45583ac2be
commit 9a69ba946f
9 changed files with 394 additions and 22 deletions

View File

@@ -7,7 +7,11 @@ make build
# binary is now at go/latprobe
```
Estimated runtimes assume a typical home broadband connection.
**Concurrency note:** Since Step 7, `latprobe` probes URLs in parallel by default
(`min(numURLs, 8)` workers). Estimated runtimes below reflect this — they are
roughly _slowest-single-URL × samples_ rather than the sum across all URLs.
Use `-c 1` to restore serial execution for the most accurate per-phase numbers.
The timeout flag is shortened to `--timeout 3s` wherever non-routable IPs are
used, so each timed-out sample fails in 3 s instead of the default 10 s.
@@ -32,7 +36,7 @@ used, so each timed-out sample fails in 3 s instead of the default 10 s.
https://www.timeslive.co.za
```
*Estimated runtime: ~30 s — shows per-phase breakdown once per site.*
*Estimated runtime: ~58 s — all 10 sites measured in parallel; shows per-phase breakdown once per site.*
---
@@ -55,7 +59,7 @@ Same 10 sites, 10 requests each — reveals real latency distribution (min/avg/m
https://www.timeslive.co.za
```
*Estimated runtime: ~35 min — best for spotting jitter and TTFB variance.*
*Estimated runtime: ~2030 s — 8 workers run in parallel; wall time ≈ slowest single URL × 10 samples.*
---
@@ -86,7 +90,7 @@ Same as Example 2, machine-readable. Pipe into `jq` to extract specific phases.
| jq '.[] | {url, avg_total_ms: .phases.total.avg_ms}'
```
*Estimated runtime: ~35 min.*
*Estimated runtime: ~2030 s.*
---
@@ -110,7 +114,7 @@ The `.invalid` TLD is guaranteed NXDOMAIN by RFC 6761.
https://nonexistent-host-two.invalid
```
*Estimated runtime: ~23 min — DNS failures resolve near-instantly.*
*Estimated runtime: ~2030 s — DNS failures resolve near-instantly; 8 workers run in parallel.*
---
@@ -133,7 +137,7 @@ The `.invalid` TLD is guaranteed NXDOMAIN by RFC 6761.
http://127.0.0.1:19999
```
*Estimated runtime: ~23 min — refused connections fail immediately.*
*Estimated runtime: ~2030 s — refused connections fail immediately; 8 workers run in parallel.*
---
@@ -158,7 +162,7 @@ sample to 3 s instead of the default 10 s.
https://203.0.113.1
```
*Estimated runtime: ~34 min — timeout IPs add 3 s × 10 samples = 30 s each.*
*Estimated runtime: ~3540 s — all 10 URLs measured in parallel; the 2 timeout IPs each add 3 s × 10 samples = 30 s and are the bottleneck.*
---
@@ -182,7 +186,7 @@ would exit 0; with it, exit code becomes 6.
https://httpbin.org/status/503
```
*Estimated runtime: ~23 min — full timing captured even for error responses.*
*Estimated runtime: ~2030 s — full timing captured even for error responses; all 10 URLs measured in parallel.*
---
@@ -206,7 +210,7 @@ would exit 0; with it, exit code becomes 6.
https://expired.badssl.com
```
*Estimated runtime: ~23 min — TLS failures surface DNS + connect timing.*
*Estimated runtime: ~2030 s — TLS failures surface DNS + connect timing; all 10 URLs measured in parallel.*
---
@@ -237,7 +241,7 @@ HTTP error with `--fail` (exit 6). Highest code wins → **exit 5** (TLS is
https://self-signed.badssl.com
```
*Estimated runtime: ~57 min — the comprehensive stress test.*
*Estimated runtime: ~3545 s — 15 URLs measured with 8 workers; the 2 timeout IPs (30 s × each) are the bottleneck.*
---
@@ -276,7 +280,7 @@ JSON output lets you pipe results to `jq` for filtering.
| jq '.[] | select(.failed > 0 or .status >= 400)'
```
*Estimated runtime: ~3060 s — fastest end-to-end check of all error paths.*
*Estimated runtime: ~46 s — 15 URLs probed in parallel with `-n 1`; only the 2 s timeout IPs add meaningful delay.*
---