|
@@ -0,0 +1,203 @@
|
|
|
|
|
+# FLARE `transport is poisoned`: an Engram disk stall, not a RoCE fault (2026-11-03)
|
|
|
|
|
+
|
|
|
|
|
+The prod FLARE lane on dgx1–4 (`dsv41-flash-1017-4x-flare-gb10-prod4`) died
|
|
|
|
|
+repeatedly with two messages that look like fabric failures:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+tensor-parallel peer failed: RoCE collective on rank 2 timed out waiting for
|
|
|
|
|
+rank 0, HCA 0, at sequence 7122648; the transport is poisoned
|
|
|
|
|
+
|
|
|
|
|
+RDMA write to rank 3 failed: transport retry counter exceeded
|
|
|
|
|
+(vendor_err 0x81, seq 250342)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+They are **not** fabric faults on this stand. The first is the collective
|
|
|
|
|
+spin-wait expiring because a peer rank is starved on **disk-backed Engram
|
|
|
|
|
+reads**; the second belongs to a different, rarer class (real QP retry
|
|
|
|
|
+exhaustion) and did not reproduce in any 2026-11-03 run. Everything below is
|
|
|
|
|
+measured, on the production package and flags.
|
|
|
|
|
+
|
|
|
|
|
+## Verdict
|
|
|
|
|
+
|
|
|
|
|
+- `RoCE collective … timed out … transport is poisoned` — the b12x/RoCEnante
|
|
|
|
|
+ GPU kernel polls a peer's stripe flag for `DEFAULT_SPIN_LIMIT = 20_000_000`
|
|
|
|
|
+ polls (≈20 s on GB10; compiled **into the prepared package**, not a
|
|
|
|
|
+ serve-time flag) and then poisons the transport. FLARE is fail-stop: no
|
|
|
|
|
+ retry, no reroute, restart every rank.
|
|
|
|
|
+- The stall is `flare_engram_disk_wait_nanoseconds_total` on the starved rank:
|
|
|
|
|
+ prefill advances in bursts, GPU idle 10–20 s at a time, and the slowest
|
|
|
|
|
+ reader still gets its data — the *peer's* spin limit expires first.
|
|
|
|
|
+- Root cause on this stand: **dgx4's SSD**. All four prod nodes run the
|
|
|
|
|
+ generic `ESL01TBTLCZ-27J2-TYN` 1 TB drive except dgx4, which carries a
|
|
|
|
|
+ **KingSpec XG7000-2TB 2242** (fw `GT0d9d63`, vid `0x1e4b`) — a budget
|
|
|
|
|
+ consumer drive, no first-party firmware tool, our firmware not even listed
|
|
|
|
|
+ in the public database (`SN13683` is what smarthdd knows for this model).
|
|
|
|
|
+ Sustained random-read ceiling measured on the wedge node: **≈29 K IOPS**
|
|
|
|
|
+ (4 threads, O_DIRECT 4 K, 10 s) vs ≈33 K on a healthy dgx5 — while a
|
|
|
|
|
+ first-touch prefill demanded **≈24 K reads/s**, i.e. 80–90 % of the drive.
|
|
|
|
|
+- **Mitigation that works today**: the unmerged upstream io_uring reader
|
|
|
|
|
+ (PR #184) on top of current `main`. Same hardware, same package, same
|
|
|
|
|
+ prompt: 3/3 wedges before, clean run after (numbers below).
|
|
|
|
|
+
|
|
|
|
|
+## Evidence
|
|
|
|
|
+
|
|
|
|
|
+### Run ledger (same package, same flags, 157 K-token first-touch prompt)
|
|
|
|
|
+
|
|
|
|
|
+| # | arrangement | runtime | result |
|
|
|
|
|
+|---|---|---|---|
|
|
|
|
|
+| 1 | orbit `prod4`, rank 0 = dgx4 | packaged `flare` | **poison** @ seq 7122648 (93 K tokens prefilled) |
|
|
|
|
|
+| 2 | orbit `prod4`, rank 0 = dgx4 | packaged `flare` | **poison** @ seq 2109 (frozen ~93 K) |
|
|
|
|
|
+| 3 | orbit `prod4`, rank 0 = dgx4 | packaged `flare` | **poison** @ seq 1284 (frozen ~61 K) |
|
|
|
|
|
+| 4 | orbit `prod4b`, rank 0 = **dgx1**, dgx4 = rank 3 | packaged `flare` | **poison** @ seq 1557 (`rank 1 waiting for rank 3`) |
|
|
|
|
|
+| 5 | orbit `prod5` = dgx1+dgx2+dgx3+**dgx8**, no dgx4 | packaged `flare` | **clean**, 0→157,086 in ~61 s |
|
|
|
|
|
+| 6 | orbit `prod4b`, dgx4 = rank 3 | **main + PR #184** | **clean**, 0→157,086 in ~60 s (~2,600 t/s) |
|
|
|
|
|
+| 7 | orbit `sparks` = dgx5–8 (2 runs, incl. after 45 min idle) | packaged `flare` | clean |
|
|
|
|
|
+
|
|
|
|
|
+Runs 4 and 6 matter most: with dgx4 demoted from rank 0 the failure
|
|
|
|
|
+**followed the node, not the rank**. Run 5 isolates it further: remove dgx4
|
|
|
|
|
+entirely and the packaged binary is clean. The wedging pair is always
|
|
|
|
|
+dgx2-or-dgx1 waiting on **dgx4's** data.
|
|
|
|
|
+
|
|
|
|
|
+### Rank 0 metrics through one wedge (1 Hz, `prod4`, layout `ESP`)
|
|
|
|
|
+
|
|
|
|
|
+| t | prefill | engram disk reads | disk wait (cum) |
|
|
|
|
|
+|---|---|---|---|
|
|
|
|
|
+| 20:52:55 | 52,993 | 2.32 M | 17.01 s |
|
|
|
|
|
+| 20:52:56–59 | **frozen** | +108 K reads | 17.01 s (frozen) |
|
|
|
|
|
+| 20:53:00–01 | **frozen** | slow | **27.17 s (+10.2 s in 2 s)** |
|
|
|
|
|
+| 20:53:02 | burst → 61,185 | +2.6 K | 27.17 s |
|
|
|
|
|
+| 20:53:03–08 | **frozen** | frozen | 27.17 s |
|
|
|
|
|
+| 20:53:09 | server dead — `poisoned`, seq 1284 | | |
|
|
|
|
|
+
|
|
|
|
|
+The disk queue backs up while the GPU starves; `engram_row_cache_misses`
|
|
|
|
|
+keeps climbing (69 % miss with the 1 GiB row cache the package recommends for
|
|
|
|
|
+unified-memory GB10).
|
|
|
|
|
+
|
|
|
|
|
+### Wire counters (all four nodes, sampled every 10 s across the wedges)
|
|
|
|
|
+
|
|
|
|
|
+`local_ack_timeout_err`, `req_transport_retries_exceeded`, `req_cqe_error`:
|
|
|
|
|
+**0 everywhere, all day**. `out_of_sequence` / `packet_seq_err` /
|
|
|
|
|
+`roce_adp_retrans` are non-zero but flat-rate and retried successfully
|
|
|
|
|
+(98.7 M RDMA write requests on dgx4). `rx_write_requests` freezes on all four
|
|
|
|
|
+ranks simultaneously at the wedge — writes stop because the *producer* stopped,
|
|
|
|
|
+not because the fabric dropped them. No mlx5/Xid events in the journal.
|
|
|
|
|
+
|
|
|
|
|
+### What did **not** help
|
|
|
|
|
+
|
|
|
|
|
+- `--embedding-table-cache-size 4GiB` (from 1 GiB): wedged **faster**
|
|
|
|
|
+ (froze ~35 K). More cached rows did not change the read burst pattern.
|
|
|
|
|
+- `--embedding-table-workers 4`: rejected —
|
|
|
|
|
+ `parallel embedding disk workers require Windows`. The Linux multi-lane
|
|
|
|
|
+ reader exists only as unmerged PR #184.
|
|
|
|
|
+- MTU / GID / link checks: MTU 9000 on both CX7 functions, RoCEv2 IPv4 GID at
|
|
|
|
|
+ index 3, static addresses (one /24 per host pair), `ping -M do -s 8972`
|
|
|
|
|
+ clean, SMART clean (`media_errors 0`, `num_err_log_entries 0`, 42 °C).
|
|
|
|
|
+- Rank-0 role / controller co-residency: disproved by runs 4 and 5.
|
|
|
|
|
+
|
|
|
|
|
+### The mitigation (our build)
|
|
|
|
|
+
|
|
|
|
|
+`pr184-on-main` = `NVIDIA/spark-local-inference` `main` (`44ec239`) +
|
|
|
|
|
+cherry-pick of PR #184 `4ed10e1b` ("Run parallel Engram disk workers on Linux
|
|
|
|
|
+with io_uring lanes"). Cherry-pick merges cleanly; 5 files, +508/−309, all in
|
|
|
|
|
+the runtime (`helios/src/engram/reader*`, `runtime_package/selection.rs`,
|
|
|
|
|
+`docs/cli-design.md`) — no package ABI change, the same prepared packages
|
|
|
|
|
+serve unchanged. Release build aarch64 on a Spark: ~50 s.
|
|
|
|
|
+
|
|
|
|
|
+Deployed with `orbit run --executable /home/kilork/flare-patched/flare`
|
|
|
|
|
+(launcher: `executable` in `[flare.clusters.<cluster>]`), packaged agent
|
|
|
|
|
+install untouched.
|
|
|
|
|
+
|
|
|
|
|
+Result (run 6): prefill 0 → 157,086 tokens in ~60 s, no burst gaps, total
|
|
|
|
|
+`engram_disk_wait` **48.7 s** — statistically identical to the healthy dgx5
|
|
|
|
|
+lane (48.4 s). dgx4's KingSpec drive is still the slowest disk in the fleet;
|
|
|
|
|
+the pipelined reader simply keeps it under the collective's patience.
|
|
|
|
|
+
|
|
|
|
|
+## Upstream material
|
|
|
|
|
+
|
|
|
|
|
+### Status in `NVIDIA/spark-local-inference` (was `flare-local-inference`)
|
|
|
|
|
+
|
|
|
|
|
+- Poison = fail-stop is **documented behaviour** (`docs/multi-host.md`,
|
|
|
|
|
+ `docs/roce-ring.md`): "A collective failure invalidates the serving run …
|
|
|
|
|
+ stop and restart all ranks together".
|
|
|
|
|
+- Open issue **#151** carries the same message text, but deterministic at
|
|
|
|
|
+ `sequence 1` with two rails on the `f1` functions; a commenter serves fine
|
|
|
|
|
+ on `f0`. Not this failure (ours is mid-run and needs no second rail).
|
|
|
|
|
+- Open issue **#78** documents disk-Engram first-touch prefill cost (28.6 s
|
|
|
|
|
+ @250 K, 61.3 s @500 K on a 4× RTX PRO box); cache budget 8→32 GiB does not
|
|
|
|
|
+ fix first touch.
|
|
|
|
|
+- PR **#184** (parallel Windows-style lanes via io_uring on Linux) was closed
|
|
|
|
|
+ unmerged on 2026-11-03 with "the filesystem was the real bottleneck" —
|
|
|
|
|
+ measured on Btrfs. Our counterexample is ext4 on a GB10 Spark where the
|
|
|
|
|
+ single-lane reader is the difference between a dead lane and a clean one.
|
|
|
|
|
+- The transport code (`helios/native/roce_proxy.c`) has not changed since
|
|
|
|
|
+ `e350914a` (2026-10-21) apart from chain support and licensing.
|
|
|
|
|
+- **Nothing upstream tracks a stalled rank turning into a poisoned
|
|
|
|
|
+ transport**, and only rank 0 exposes `/metrics` — the ranks that wait are
|
|
|
|
|
+ blind.
|
|
|
|
|
+
|
|
|
|
|
+### Issue draft (ready to paste)
|
|
|
|
|
+
|
|
|
|
|
+> **Title:** Disk-Engram read stall on one rank poisons the TP transport
|
|
|
|
|
+> (`RoCE collective … timed out … transport is poisoned`) — reproduced 3/3,
|
|
|
|
|
+> fixed by PR #184's io_uring reader
|
|
|
|
|
+>
|
|
|
|
|
+> **Summary.** On a 4× DGX Spark (GB10) TP4 lane serving
|
|
|
|
|
+> `local-inference-lab/DeepSeek-V4.1-Flash-1017-FLARE` (`gb10`,
|
|
|
|
|
+> `--embedding-table-storage disk --embedding-table-cache-size 1GiB
|
|
|
|
|
+> --embedding-table-resident-scales`, `--max-concurrency 32
|
|
|
|
|
+> --mtp-draft-tokens 5 --mtp-adaptive-verification`), a 157 K-token
|
|
|
|
|
+> first-touch prompt kills the run on the same hardware it serves well
|
|
|
|
|
+> otherwise:
|
|
|
|
|
+>
|
|
|
|
|
+> ```
|
|
|
|
|
+> RoCE collective on rank 2 timed out waiting for rank 0, HCA 0, at
|
|
|
|
|
+> sequence 7122648; the transport is poisoned
|
|
|
|
|
+> ```
|
|
|
|
|
+>
|
|
|
|
|
+> **Diagnosis.** Rank 0's `/metrics` shows stop-and-go prefill:
|
|
|
|
|
+> `flare_prefill_tokens_total` frozen for 3–6 s at a time while
|
|
|
|
|
+> `flare_engram_disk_wait_nanoseconds_total` jumps (+10.2 s within one 2 s
|
|
|
|
|
+> window) and `flare_engram_row_cache_misses_total` climbs at ≈24 K
|
|
|
|
|
+> random-reads/s — 80–90 % of that node's measured 4 K O_DIRECT ceiling
|
|
|
|
|
+> (≈29 K IOPS). The waiting rank hits the 20 M-poll kernel spin limit
|
|
|
|
|
+> (`DEFAULT_SPIN_LIMIT`, compiled into the package) and poisons the
|
|
|
|
|
+> transport. RDMA counters are clean throughout: `local_ack_timeout_err`,
|
|
|
|
|
+> `req_transport_retries_exceeded`, `req_cqe_error` all 0;
|
|
|
|
|
+> `rx_write_requests` freezes on every rank because the producer stopped.
|
|
|
|
|
+>
|
|
|
|
|
+> **Rank-0 role is not the trigger.** Reordering the orbit so another node
|
|
|
|
|
+> is rank 0 still fails — the same node's data is what the peer waits for.
|
|
|
|
|
+> Removing that node from the cluster (dgx1+dgx2+dgx3+dgx8) serves the same
|
|
|
|
|
+> prompt cleanly with the packaged binary.
|
|
|
|
|
+>
|
|
|
|
|
+> **Fix.** Current `main` + PR #184 cherry-picked (`pr184-on-main`) serves
|
|
|
|
|
+> the identical package and prompt: prefill 0→157 K in ~60 s, total Engram
|
|
|
|
|
+> disk wait 48.7 s (healthy-lane parity), no poison — 1/1 so far after 3/3
|
|
|
|
|
+> failures without it.
|
|
|
|
|
+>
|
|
|
|
|
+> **Requests.**
|
|
|
|
|
+> 1. Reconsider #184: its close reasoning ("the filesystem was the
|
|
|
|
|
+> bottleneck") holds for Btrfs, but on ext4/GB10 the single-lane reader
|
|
|
|
|
+> is what makes the lane survive a disk that is merely slow.
|
|
|
|
|
+> 2. Make the collective spin limit configurable at serve time (or raise it
|
|
|
|
|
+> when disk-Engram delivery is in play) — it is currently baked into the
|
|
|
|
|
+> prepared package, so operators cannot trade latency for stability.
|
|
|
|
|
+> 3. Expose `/metrics` on **every** rank (or at least the readiness/disk
|
|
|
|
|
+> counters): the rank that waits is invisible today, which turns a disk
|
|
|
|
|
+> stall into an unattributable fabric error.
|
|
|
|
|
+> 4. Consider a preflight/warning when a host's Engram disk falls below the
|
|
|
|
|
+> package's implied read rate; a 6 W consumer 2242 at 29 K IOPS is not
|
|
|
|
|
+> the same class as the reference platforms.
|
|
|
|
|
+
|
|
|
|
|
+## Operational state after this investigation
|
|
|
|
|
+
|
|
|
|
|
+- Production lane: orbit **`prod4b`** (`dgx1 → dgx2 → dgx3 → dgx4`,
|
|
|
|
|
+ rank 0 = dgx1) + `executable = /home/kilork/flare-patched/flare`
|
|
|
|
|
+ (staged on all four prod nodes; only the controller's copy is uploaded).
|
|
|
|
|
+- `prod4` (rank 0 = dgx4) is kept in `orbit.toml` for rollback; `prod5`
|
|
|
|
|
+ (dgx1+dgx2+dgx3+dgx8) is the no-dgx4 control.
|
|
|
|
|
+- Hardware follow-up still open: replace dgx4's KingSpec
|
|
|
|
|
+ `XG7000-2TB 2242` with an enterprise-class 2242 (Samsung PM9A3/PM9E1 M.2
|
|
|
|
|
+ 2242, or Corsair MP700 Micro) — the software patch buys stability, not a
|
|
|
|
|
+ fast disk. Back up `~/.config/flare/orbit/controller.key` first: restoring
|
|
|
|
|
+ it after a DGX OS reinstall avoids re-pairing all eight agents.
|