docs: the reseq probe's readings, and the invariants resequencing changes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4549c622fc
commit
eacb3f5efc
11 files changed
+258
-23
No files matched your search
+63
-11
@@ -201,6 +201,59 @@ as distinct from the fault injection below, which exercises ingest:
|
||||
- `--quiet-at S` freezes `sine` at its held value from stream second `S` onwards, so it stops
|
||||
crossing zero: the dropout trigger's input.
|
||||
|
||||
- `UDPSCOPE_PROBE_RESEQ_MS` — one mid-run reading of the resequencing path. Four cases,
|
||||
**run each as its own process** — four back to back starve the mock past its 2 s silence
|
||||
timeout and every case then reads a reconnect instead of what it was testing. Common
|
||||
conditions for all four: the mock at `--rate 10000 --batch 100 --freq 5`, udpscope with
|
||||
no extra options, under `xvfb-run` with
|
||||
`UDPSCOPE_EXIT_AFTER_MS=8000 UDPSCOPE_ENABLE_ALL_MS=600 UDPSCOPE_EXIT_DUMP=1
|
||||
UDPSCOPE_PROBE_RESEQ_MS=5000`.
|
||||
|
||||
| case | mock extra flags | key reading |
|
||||
| --- | --- | --- |
|
||||
| **base** | *(none)* | every counter 0; proves the machinery costs nothing |
|
||||
| **reorder** | `--reorder-every 10` | `reseq=51` (mid-run) / `102` (dump); `holes=0`; resequencing inside the window leaves no reservation |
|
||||
| **late** | `--reorder-every 10 --reorder-delay-ms 50` | `holes=42 bands=42 bands_repaired=42 nan_slots=0`; every band is the faint, repaired tier |
|
||||
| **mixed** | `--drop-every 20 --reorder-every 10 --reorder-delay-ms 50` | `bands=42 bands_repaired=20 nan_slots=2300`; all three band states at once |
|
||||
|
||||
Full measured readings (probe line first, then exit-dump; `bands_repaired` was added after the
|
||||
original probe and any earlier reading without it is obsolete):
|
||||
|
||||
```
|
||||
=== base mock: none
|
||||
[reseq-probe] reseq=0 late=0 stale=0 holes=0 hole_slots=0 backfilled=0 open_holes=0 bands=0
|
||||
bands_repaired=0 nan_slots=0 scanned=44000 trigger_in_hole=0
|
||||
[udpscope] frames=1024 packets=1025 gaps=0 channels=4 span=838.861s stride=1 reseq=0 late=0
|
||||
stale=0 holes=0 hole_slots=0 backfilled=0
|
||||
[udpscope] channel 0 'sine' stored=95200 published=95000
|
||||
|
||||
=== reorder mock: --reorder-every 10
|
||||
[reseq-probe] reseq=51 late=0 stale=0 holes=0 hole_slots=0 backfilled=0 open_holes=0 bands=0
|
||||
bands_repaired=0 nan_slots=0 scanned=44000 trigger_in_hole=0
|
||||
[udpscope] frames=1028 packets=1029 gaps=0 channels=4 span=838.861s stride=1 reseq=102 late=0
|
||||
stale=0 holes=0 hole_slots=0 backfilled=0
|
||||
[udpscope] channel 0 'sine' stored=95400 published=95100
|
||||
|
||||
=== late mock: --reorder-every 10 --reorder-delay-ms 50
|
||||
[reseq-probe] reseq=142 late=48 stale=0 holes=42 hole_slots=4200 backfilled=16800 open_holes=42
|
||||
bands=42 bands_repaired=42 nan_slots=0 scanned=44000 trigger_in_hole=0
|
||||
[udpscope] frames=1026 packets=1027 gaps=100 channels=4 span=838.861s stride=1 reseq=288
|
||||
late=100 stale=0 holes=94 hole_slots=9400 backfilled=37600
|
||||
[udpscope] channel 0 'sine' stored=95700 published=95500
|
||||
|
||||
=== mixed mock: --drop-every 20 --reorder-every 10 --reorder-delay-ms 50
|
||||
[reseq-probe] reseq=163 late=24 stale=0 holes=43 hole_slots=4300 backfilled=8000 open_holes=43
|
||||
bands=42 bands_repaired=20 nan_slots=2300 scanned=44100 trigger_in_hole=0
|
||||
[udpscope] frames=974 packets=975 gaps=101 channels=4 span=838.861s stride=1 reseq=314 late=50
|
||||
stale=0 holes=94 hole_slots=9400 backfilled=18400
|
||||
[udpscope] channel 0 'sine' stored=95100 published=94900
|
||||
```
|
||||
|
||||
The `mixed` case's `open_holes=43` against `bands=42` is correct: the 43rd hole has scrolled
|
||||
out of the visible window, and `build_invalid_spans()` drops a span outside the window rather
|
||||
than pinning it to the edge. The per-field reference is in
|
||||
[probe-readings.md](probe-readings.md).
|
||||
|
||||
The mock also carries three **fault injections** that no probe schedules, because what they exercise
|
||||
is the ingest rather than the UI. The first: `--time-glitch-at <stream seconds> --time-glitch-by <seconds>` makes
|
||||
**one** packet's time signal jump ahead and then carries on from where the true timeline was, which
|
||||
@@ -229,12 +282,12 @@ session for that port; the figures above are the default 2 s window (50 % pre),
|
||||
`~/.config/udpscope/<host>_<port>.toml` aside to reproduce them.
|
||||
|
||||
The other two are **packet loss** (`--drop-every N`, `--drop-burst K`) and **wire inversion**
|
||||
(`--reorder-every N`). Both withhold a datagram the counter and the stream clock have already
|
||||
advanced over, which is what makes them loss and reordering rather than a slower producer: the
|
||||
client orders the stream by the header's counter alone (`udps_client.c:504`), counting a forward
|
||||
jump as `counter_gaps` and rejecting anything that does not advance it as `stale_packets`. There is
|
||||
**no resequencing and no interpolation** anywhere below the ring — an inverted packet's samples are
|
||||
discarded and the hole they leave is a jump in x, which is the behaviour these options are for.
|
||||
(`--reorder-every N`). Loss withholds a datagram the counter and the stream clock have already
|
||||
advanced over; the client counts the forward jump as `counter_gaps`. An inversion withholds a
|
||||
packet until the one behind it is sent, so the receiver sees a later counter first; a packet that
|
||||
arrives within the 20 ms reorder window is resequenced with no hole, and one that arrives past it
|
||||
leaves a committed hole that it then backfills (`UDPSCOPE_PROBE_RESEQ_MS` exercises both paths).
|
||||
There is **no interpolation** anywhere below the ring.
|
||||
|
||||
All of the readings below are off `UDPSCOPE_EXIT_DUMP`'s `[udpscope] frames= packets= gaps=` line,
|
||||
over an 8 s run against `--rate 10000 --batch 100 --freq 5` (100 packets a second) except where the
|
||||
@@ -247,11 +300,10 @@ including the clean one, then read a reconnect instead of what it was testing:
|
||||
- `--drop-every 10`: `frames=925 packets=926 gaps=102`. Every tenth packet never reaches the wire,
|
||||
so `packets` falls with `frames` and `gaps` is the count of them. No warning: a one-packet hole is
|
||||
a one-packet jump in the stamps, far inside the glitch guard.
|
||||
- `--reorder-every 10`: `frames=925 packets=1028 gaps=102`. This is the signature to look for —
|
||||
`packets` is the clean figure (every datagram arrived) while `frames` is the dropped one, because
|
||||
the overtaken packet is decoded far enough to be read as stale and then thrown away. One inversion
|
||||
costs one `gaps` (for the packet that overtook it) **and** one `stale_packets`, which udpscope does
|
||||
not currently display; the ~104 missing frames are the only place it shows.
|
||||
- `--reorder-every 10`: `frames=1028 packets=1029 gaps=0`. The inverted packet lands inside the
|
||||
20 ms reorder window and is resequenced: every datagram arrives and `gaps` is 0. The pre-resequencing
|
||||
signature was `frames=925 packets=1028 gaps=102`; that is now the reading for
|
||||
`--reorder-every 10 --reorder-delay-ms 50`, which forces a late arrival past the window.
|
||||
- `--drop-every 1000 --drop-burst 200` at `--rate 100000 --batch 100` (1000 packets a second):
|
||||
`frames=7000 packets=7001 gaps=1600`, eight bursts of 200, and still no warning.
|
||||
- `--drop-every 1000 --drop-burst 300`, same stream: `frames=6203 packets=6204 gaps=2400` **and the
|
||||
|
||||
Reference in new issue
Block a user