test(xmpp): maak de inbound-op-flood-test deterministisch (geen flaky linux-gate) #1456

Merged
brenno merged 1 commit from fix/xmpp-ratelimit-deterministic-clock into main 2026-08-11 09:47:29 +00:00
Owner

Probleem

De test a flood of ops from one sender is rate-limited (#1433) in test/xmpp/xmpp_transport_test.dart is flaky onder belasting en velde intermittent de linux-gate (o.a. de linux-gate-run van #1447: Expected: ≤60 / Actual: 71; ook eerder op main). Geen echte regressie — issue #1433 is gesloten (dat is de feature-issue die de rate-limiter toevoegde; deze test verifieert 'm).

Oorzaak. De test stuurt 100 ops en verwachtte dat er ~50 doorkwamen. De rate-limiter (XmppTransport._rateAllow) is een vast-venster-teller op DateTime.now(), begrensd op 50/seconde/sender. De test nam aan dat de hele burst binnen één venster van 1s landt. Op de capacity-1 serial-runner onder CPU-druk rekken de 100 seriële crypto-opens (de demux verwerkt strikt op volgorde, #1420) uit tot >1 seconde, dus opende een tweede venster en kwamen er >50 door. De marge was al opgerekt van ≤60 → ≤80 op main — een pleister die onder zwaardere last óók kan barsten.

Fix — deterministisch i.p.v. een bredere marge

XmppTransport krijgt een injecteerbare klok (DateTime Function()? now, standaard DateTime.now) — exact het patroon dat openkat_wizard_controller, rehearsal_controller en s3_service al gebruiken. De klok voedt zowel de op-rate-limiter als de resync-timers, zodat de transport één tijdbron heeft. Productiegedrag ongewijzigd (default = de echte klok).

De flood-test bevriest de klok, zodat alle 100 ops in hetzelfde venster vallen. Dan is de begrenzing exact toetsbaar:

  • received.length == 50 — precies het vensterplafond;
  • de doorgelaten ops zijn de eerste 50, op volgorde (versies 1..50) — bewijst dat de staart gedropt wordt, niet willekeurig ertussenuit.

De cap staat als één _rateLimitCap = 50 in de test, gekoppeld aan de private lib-constante met een comment; wijzigt de cap, dan faalt de test luid.

Verificatie

  • Determinisme bewezen onder last: de flood-test 10× groen met alle 18 cores vol (yes-hogs), plus 20× groen idle. De bevroren klok maakt de check wandklok-onafhankelijk — belasting kán 'm per constructie niet meer beïnvloeden.
  • Volledige test/xmpp/xmpp_transport_test.dart groen (ook de resync-tests, die nu óók via de geïnjecteerde klok lopen).
  • make checkgroen (CHECK_EXIT=0; volledige suite + dekkingsvloeren + conventies/klasseplafonds).
  • make check-secrets → geen leaks.

🤖 Generated with Claude Code

## Probleem De test `a flood of ops from one sender is rate-limited (#1433)` in `test/xmpp/xmpp_transport_test.dart` is **flaky onder belasting** en velde intermittent de linux-gate (o.a. de linux-gate-run van #1447: `Expected: ≤60` / `Actual: 71`; ook eerder op `main`). Geen echte regressie — issue #1433 is gesloten (dat is de feature-issue die de rate-limiter *toevoegde*; deze test verifieert 'm). **Oorzaak.** De test stuurt 100 ops en verwachtte dat er ~50 doorkwamen. De rate-limiter (`XmppTransport._rateAllow`) is een vast-venster-teller op `DateTime.now()`, begrensd op 50/seconde/sender. De test nam aan dat de hele burst binnen één venster van 1s landt. Op de capacity-1 serial-runner onder CPU-druk rekken de 100 *seriële* crypto-opens (de demux verwerkt strikt op volgorde, #1420) uit tot **>1 seconde**, dus opende een tweede venster en kwamen er >50 door. De marge was al opgerekt van ≤60 → ≤80 op `main` — een pleister die onder zwaardere last óók kan barsten. ## Fix — deterministisch i.p.v. een bredere marge `XmppTransport` krijgt een **injecteerbare klok** (`DateTime Function()? now`, standaard `DateTime.now`) — exact het patroon dat `openkat_wizard_controller`, `rehearsal_controller` en `s3_service` al gebruiken. De klok voedt zowel de op-rate-limiter als de resync-timers, zodat de transport één tijdbron heeft. **Productiegedrag ongewijzigd** (default = de echte klok). De flood-test **bevriest** de klok, zodat alle 100 ops in hetzelfde venster vallen. Dan is de begrenzing exact toetsbaar: - `received.length == 50` — precies het vensterplafond; - de doorgelaten ops zijn de **eerste 50, op volgorde** (versies 1..50) — bewijst dat de staart gedropt wordt, niet willekeurig ertussenuit. De cap staat als één `_rateLimitCap = 50` in de test, gekoppeld aan de private lib-constante met een comment; wijzigt de cap, dan faalt de test luid. ## Verificatie - **Determinisme bewezen onder last**: de flood-test **10× groen met alle 18 cores vol** (`yes`-hogs), plus 20× groen idle. De bevroren klok maakt de check wandklok-onafhankelijk — belasting kán 'm per constructie niet meer beïnvloeden. - Volledige `test/xmpp/xmpp_transport_test.dart` groen (ook de resync-tests, die nu óók via de geïnjecteerde klok lopen). - `make check` → **groen** (`CHECK_EXIT=0`; volledige suite + dekkingsvloeren + conventies/klasseplafonds). - `make check-secrets` → geen leaks. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
test(xmpp): maak de inbound-op-flood-test deterministisch met een injecteerbare klok
All checks were successful
scans / scans (pull_request) Successful in 1m41s
static-gate / static-gate (pull_request) Successful in 6m26s
a088e7d43f
De test 'a flood of ops from one sender is rate-limited (#1433)' flakete op de
Linux serial-runner onder belasting. Hij stuurt 100 ops en verwachtte dat er ~50
doorkwamen (marge ≤60, op main al opgerekt naar ≤80), maar de rate-limiter meet
met DateTime.now(): tijdens de 100 seriële crypto-opens liep de wandklok onder
CPU-druk over de venstergrens van 1s, opende een tweede venster en liet er >50
door (geobserveerd 71 → poort rood op PR #1447). Een bredere marge is een
pleister, geen fix.

Nu krijgt XmppTransport een injecteerbare klok ('DateTime Function()? now',
standaard DateTime.now — hetzelfde patroon als openkat_wizard_controller,
rehearsal_controller en s3_service). De test bevriest die klok, zodat alle 100
ops in één venster vallen en de begrenzing exact toetsbaar wordt: precies 50
komen door, de eerste 50 op volgorde (de demux verwerkt strikt serieel, #1420).
Deterministisch en belasting-onafhankelijk — 10× groen met alle 18 cores vol.

De klok voedt ook de resync-timers, zodat de transport één tijdbron heeft.
Productiegedrag ongewijzigd (default = de echte klok).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit ad6a4b56a2 into main 2026-08-11 09:47:29 +00:00
brenno deleted branch fix/xmpp-ratelimit-deterministic-clock 2026-08-11 09:47:29 +00:00
Sign in to join this conversation.
No description provided.