Skip to content

feat: erasure-aware FEC v2 - #1

Draft
tookdes wants to merge 49 commits into
masterfrom
feat/erasure-aware-fec-v2
Draft

feat: erasure-aware FEC v2#1
tookdes wants to merge 49 commits into
masterfrom
feat/erasure-aware-fec-v2

Conversation

@tookdes

@tookdes tookdes commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Draft implementation of the erasure-aware fecraw v2 data plane.

Stage 1 — validated Loss Model + adaptive RS

  • protocol-v2 outer sequence + 128-bit ACK feedback
  • directional physical-loss / RTT / delivery telemetry before FEC recovery
  • Queqiao-inspired memoryless erasure-floor and burst/congestion separation
  • residual-probability RS planner, applied only at block boundaries

Confirmed WAN result

Beijing Ali-Cloud <-> GCP US-West, ~16.6% physical loss:

  • fixed RS 20:10: 11.27 Mbps TCP receive Goodput
  • adaptive controller settled around 20:13: 15.20 Mbps
  • improvement: ~34.8%

The codec remained the original UDPspeeder RS implementation, proving the loss model / redundancy controller has value independently of a new codec.

Stage 2 — sliding-window RLNC + pacing liveness

Added GF(256) sliding-window RLNC (0x11d), deterministic RID/index coefficients, up to 256 source symbols/window, 512-symbol decoder width, large-TUN-packet fragmentation/reassembly, and --fec-codec rs|rlnc|auto.

Confirmed WAN result on be38b078c9a236b57fa9cdca33ee2085b6fbd574

Group Configuration Goodput TCP retrans Result
B RS adaptive 15.20 Mbps 192 baseline, trusted floor ~0.175, live RS ~20:13
C RLNC window=64 adaptive 15.30 Mbps, peaks >20 Mbps 427 codec works; slight independent gain
D1 RS adaptive + pacing 0.42 Mbps 4 continuous transfer; cold-start deadlock fixed
D2 RLNC adaptive + pacing 0.11 Mbps 50 continuous transfer; cold-start deadlock fixed

Stage 2 solved pacing liveness but exposed a separate bandwidth-estimation collapse.

Stage 3 — validated WindowRate + diagnostic pacing sampler

RLNC WindowRate

Stage 3 separates RS block sizing from RLNC continuous WindowRate and ports Queqiao's windowChaining=2.5 model. At the original floor=0.175, RTT~200ms, window=64 point it computes about 0.3187 repair/source instead of the Stage-2 inherited 20:13 = 0.65 repair/source.

Deterministic CI regression:
window-rate: floor=0.175 RS=20:11 RLNC=0.3187 residual=0.0000 recovered=516

Confirmed WAN result on 7c035e4fb81b0faf0f3ac7c4597189e881cd0fc2

The path had changed by Aug 25: RTT ~190–230ms and erasure floor was mostly ~0, with one short loss window. A same-time RS control was therefore added.

Run Channel Goodput TCP retrans WindowRate
E1 #1 RLNC short loss window, floor 1.0–4.5% 6.71 Mbps 538 0.0312–0.0688
E1 #2 RLNC clean, floor ~0 21.0 Mbps 380 falls to minimum
Same-time RS clean, RS 20:0 15.4 Mbps 228 n/a

Results:

  • live WindowRate scales correctly with the measured floor;
  • on the same clean path RLNC reached 21.0 vs 15.4 Mbps (+36%);
  • RLNC still had more TCP retransmissions than RS and degraded sharply during the short burst-loss interval, supporting explicit burst/tail protection rather than globally raising continuous repair.

Pacing Stage-3 WAN result

RS adaptive + pacing, no --max-bandwidth cap:

  • 1.89 Mbps / retrans 11 (15s)
  • 1.10 Mbps / retrans 14 (20s)
  • continuous transfer; no liveness stalls

This improved the old D1 0.42 Mbps but remained far below the unpaced path. The new logs exposed two control-loop problems:

  1. bandwidth samples were still dominated by the pacer's own restricted output, allowing a low self-referential estimate;
  2. Stage 3 also treated burst_factor > 1.6 as congestion, independently of the erasure floor. In PROBE_BW that could produce 0.85 * 0.50 = 0.425 of the current wire estimate, matching the observed rate/wire_bw ratio even when floor=0.

Stage 4 — RLNC protectBurst + RTT-scale pacing startup

Current head: 704541270151759baa210daf45de689db8111782.

RLNC tail / producer-drain protection

Ported the relevant Queqiao protectBurst() behavior without changing fecraw's wire format:

  • track source symbols and already-emitted repairs since the last producer drain/full RLNC window;
  • after producer idle, calculate the short-tail block requirement with the measured loss, burst factor, and the same RTT-dependent residual target;
  • emit only the missing existing RLNC repair frames over the trailing burst;
  • reset burst repair credit after sealing the tail;
  • client/server use the existing 10ms feedback timer as the drain detector; source packets remain immediate and tail repair is emitted only after an idle edge (effective threshold ~5–10ms);
  • RLNC -> other-codec transitions force-seal the outstanding tail before resetting the encoder.

The tail planner follows the block residual model used by Queqiao ShardsFor: burst correlation reduces the effective independent-trial count, so correlated tails receive more repair without increasing the continuous WindowRate.

x86_64 deterministic tests currently report:

  • tail-protect: floor=0.030 k=4 want=2; burst2 k=8 want=3
  • final source loss is recovered by the added tail equations.

Pacing startup / estimator

Stage 4 changes the controller rather than tuning a cap:

  • fail-open bootstrap lasts at least one measured RTT and four genuine delivery samples;
  • bandwidth becomes an 8-second time-based max filter (32 x 250ms buckets), so a short real high-capacity sample survives many low ACK samples;
  • STARTUP uses 2.0x pacing gain and evaluates bandwidth growth on RTT-scale rounds;
  • STARTUP exits only after repeated no-growth rounds or RTT inflation, then DRAIN -> PROBE_BW;
  • PROBE_BW gain cycle advances on RTT-scale rounds rather than every ACK callback;
  • burst_factor is removed from congestion-rate reduction; only trusted excess congestive loss above the erasure floor may lower congestion_scale;
  • stale feedback still fails open and clears the bandwidth epoch.

Diagnostic log now includes:
delivered / wire_bw / rate / state / ready / floor / recent / cong / burst / scale / rtt

x86_64 regression confirms a 20 Mbps retained wire sample produces a 40 Mbps STARTUP rate and burst correlation alone leaves scale ~= 1.0.

Current validation

Build #74 (704541270151759baa210daf45de689db8111782):

  • x86_64: PASS — unit tests, full build/link, artifact upload
  • ARM64 Docker/QEMU: currently still running make test && make; no failure reported yet

Protocol v2 remains intentionally wire-incompatible with master. PR remains draft pending Stage-4 WAN validation.

Recommended Stage-4 WAN matrix

  1. F1 — RLNC adaptive, pacing off
    --fec 20:10 --fec-codec rlnc --rlnc-window 64 --adaptive-fec
    • compare Goodput/retrans with same-time RS and Stage-3 E1
    • capture RLNC window rate and RLNC tail protect symbols=... have=... want=... added=... logs
    • tail repair may correctly be zero on a truly clean window; a lossy/bursty window is needed to validate its live benefit.
  2. F2 — RS adaptive + Stage-4 pacing
    --fec 20:10 --fec-codec rs --adaptive-fec --enable-pacing
    • leave --max-bandwidth unset/0
    • confirm bootstrap begins fail-open, then enters startup
    • during STARTUP expect rate above retained wire_bw (nominally 2x before any true congestion scale), not the old ~0.425x self-lock
    • when cong ~= 0, burst alone must not reduce scale.
  3. Only if F2 is sane, test F3 — RLNC adaptive + pacing.

Full design/status notes: docs/ERASURE-AWARE-V2.md (Stage-4 WAN results should be added after live validation).

tookdes added 30 commits August 21, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant