Repository navigation
feat: write router-replay ids and sampling masks by handle on multi-node runs - #3903
Closed
faresobeid wants to merge 4 commits into
Closed
faresobeid wants to merge 4 commits into
faresobeid wants to merge 4 commits into
Conversation
This was referenced Oct 6, 2026
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 6, 2026 14:53
1bcca41 to
d0aff47
Compare
faresobeid
force-pushed
the
feat/payload-by-handle
branch
from
October 6, 2026 14:53
a4a98c5 to
0d8d29a
Compare
This was referenced Oct 6, 2026
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 6, 2026 17:29
d0aff47 to
28baf5a
Compare
faresobeid
force-pushed
the
feat/payload-by-handle
branch
from
October 6, 2026 17:29
0d8d29a to
1ed3731
Compare
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 7, 2026 00:39
28baf5a to
cfc1d59
Compare
faresobeid
force-pushed
the
feat/payload-by-handle
branch
2 times, most recently
from
October 7, 2026 12:43
9b43ad3 to
8e3e321
Compare
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 7, 2026 13:59
cfc1d59 to
93ea816
Compare
faresobeid
force-pushed
the
feat/payload-by-handle
branch
from
October 7, 2026 13:59
8e3e321 to
4f6d3c1
Compare
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 8, 2026 14:39
93ea816 to
f97b76a
Compare
faresobeid
force-pushed
the
feat/payload-by-handle
branch
from
October 8, 2026 14:40
4f6d3c1 to
47d764e
Compare
faresobeid
force-pushed
the
feat/payload-by-handle
branch
from
October 8, 2026 19:16
47d764e to
968669e
Compare
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 8, 2026 19:16
f97b76a to
5ed3500
Compare
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 9, 2026 23:48
5ed3500 to
c2f9290
Compare
faresobeid
force-pushed
the
feat/payload-by-handle
branch
from
October 9, 2026 23:48
968669e to
5baf380
Compare
…ode runs A train request whose extra_args carries payload_dir (set by the orchestrator when orchestrator.payload_root is set; rl sets it to <run dir>/payloads on multi-node runs with router replay or sampling-mask capture) gets its routed_experts and sampling_mask written to one file under payload_dir/v<dispatch version> and returned as payload segments. Verifiers attributes the segments to trace nodes; the orchestrator clips and packs them. GC: the orchestrator wipes payload_root on a fresh run and deletes v<k> once no unread batch can reference it. Files that no queued sample points into (dropped groups, untrainable branches, pruned samples, traces swept as stale) are deleted as soon as the sink drops them. Ported to vLLM 0.31: the inline path keys on aux_output_config.enable_return_routed_experts. Disaggregated P/D and score centering stay inline (rejected with an explicit payload_root). Bumps verifiers and renderers to their by-handle branches.
faresobeid
force-pushed
the
feat/payload-by-handle
branch
from
October 10, 2026 04:00
5baf380 to
fcfb7f9
Compare
faresobeid
force-pushed
the
feat/payload-segments
branch
from
October 10, 2026 15:11
5a862b4 to
123233f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #3902 (which sits on #3900).
What
Turns on passing router-replay ids and sampling masks by handle on multi-node runs, and owns the payload directory's lifecycle. Inline stays everywhere else.
Switch (one config field):
orchestrator.payload_root.rlsets it to<run dir>/payloads(absolute) when router replay or sampling-mask capture is on, the deployment ismulti_node, and inference is not disaggregated. Inference needs no config: the directory travels with each request. Setting it by hand with disaggregated P/D is rejected, because the PD router does not stitch by-handle routing segments.Request: the dispatcher gives each live-sourced train rollout
payload_dir = <payload_root>/v<k>, wherekis the rollout's own dispatch policy version. That is never older than its group's start, which the staleness sweep keys on, so the cleanup bound below holds.Env._samplingputs it inextra_body.extra_args; verifiers flattensextra_bodyinto the generatesampling_paramsand renderers forwards them, and vLLM'sGenerateRequestparses it intosampling_params.extra_args["payload_dir"].Writer (
serving_tokens.py, on top of perf(inference): pack /generate per-token payloads and tune API-server GC #3900's_PackedOutputs): for a request withpayload_dir,routed_expertsand the packedsampling_maskare taken off the engine output (upstream encodes nothing inline), written into one file<payload_dir>/<uuid>.binin a thread, closed before responding (NFS close-to-open), and returned aspayloadsegments on the choice.completion_logprobsstays packed inline.compact_routed_experts, split out ofserialize_routed_experts) and anchored at the end: the segment covers[prompt_len + completion_len - 1 - rows, prompt_len + completion_len - 1), i.e. it ends at the last forwarded token. On a colocated server that equalsrouted_experts_prompt_start. Under 0.31 P/D (feat(inference): router replay on the V2 runner everywhere #3830) a decode instance emits rows only from its first forward (prompt_len - 1), so end-anchored positions stay correct there, and router#60 can clip decode rows by position.prompt_len + i: int32[tokens, widest row]padded with -1, built from perf(inference): pack /generate per-token payloads and tune API-server GC #3900's CSRPackedSamplingMask.payload_dir(eval, frozen-source rollouts, standalone clients) keep perf(inference): pack /generate per-token payloads and tune API-server GC #3900's inline path.v<k>directory when it creates it) before responding; the trainer's reader reopens the file and retries a short read 4 times with backoff (0.1 s → 0.8 s), then raises with the file, offset and byte counts.Orchestrator (
trajectories.py):trace_to_samplestakesbranch.payload(verifiers#2765), clips it to the sample and adds a 1-row segment for the final token that points at the previous row's bytes (what the inline path's placeholder copies). A branch with any other routing gap drops its routing with a warning, likeBranch.routed_expertsreturning None.Retention: delete on consume. A file belongs to one trace (verifiers attributes a request's segments to that trace only), and a trace's samples ship together in one batch, so no per-file refcount is needed.
TrainSink.process_batchputs the files of every selected trace (shipped or pruned at batch time) onTrainBatch.payload_files;finalize_train_batchrecords them under the batch's step aftersender.send(an empty or over-max_stepsbatch deletes them at once).Orchestrator.on_policy_update(step), after inference appliesv{step}, deletes the files of every recorded batch≤ step: the trainer reads batchsin full (DataLoader.get_batch) before it publishesv{s}.max_off_policy_stepsis. Before, every trained file stayed until itsv<k>aged out, i.e.max_off_policy_steps + 2steps of payloads (~100 GB at GLM-4.5-Air SWE batch 512,max_off_policy_steps = 32; ~5 TB for GLM-5-class at batch 4096 × 30k tokens,max_off_policy_steps = 64).v<k>is still removed fork < min_fresh_version(step + 1, max_off_policy_steps); batchb ≥ step + 1ships only rollouts whose group started atmin_fresh_version(b)or later, and a rollout'skis at least its group's start.on_policy_updateruns under the weight watcher'supdate_lock, and unlinking a step's files on a shared filesystem takes 10-35 s at batch 512 (below), which would otherwise delay the next weight update.rlcleans future batches on resume, so nothing there is read again; the backstop removes it).Discarded rollouts:
TrainSink.process_groupdeletes the files no queued sample points into (dropped groups, untrainable branches, pruned samples);_drop_staledeletes the files of queued traces it sweeps.Docs:
docs/inference.md§ Payloads by handle.Measured (GLM-4.5-Air SWE, batch 512 = 32 × 16, router replay on, 1 inference + 1 trainer node, CP 4,
max_off_policy_steps = 32, 8 steps)Same tree, config and nodes as the earlier run of this chain's previous head; this run adds the concurrent reads (#3902) and delete-on-consume. Payload root on the shared /home filesystem (virtiofs, the only shared filesystem on the nodes).
time/load_data, steps 3-8 (s)v<k>reaches the 32-version bound), ~90-100 GB steady statermtreeofv<k>awaited underupdate_lock(from step 34 on)Mean staleness 0.6-0.9 (max 3), as before; no read errors, no stale drops. 84.6k files (16.0 GB) were deleted on consume during the run.
Pins
Both pins combine two open companion PRs each, on pin branches:
deps/verifiers→ 5b432ddf (pin/payload-by-handle-packed-da75527): verifiers#2765's two commits on top of verifiers#2778's head (cef65c30, on verifiers main da75527).deps/renderers→ 0c60f6c (pin/payload-passthrough-packed-41f9290): renderers#171's commit on top of renderers#174's head (19c558f, on renderers main 41f9290).Re-pin to the merged commits once those PRs land.
Tests
test_payload_dir_writes_routing_and_masks_by_handledrives the real_PackedOutputsiterator: routing and mask go into one file with end-anchored routing positions, CSR masks are padded with -1, and the inline fields are cleared.test_read_file_retries_short_reads: a short read is retried after the file is reopened, then raises.pytest tests/unit/orchestrator tests/unit/test_configs.py tests/unit/inference tests/unit/train/rl -m "not gpu"on vLLM 0.31.0 with the pinned verifiers/renderers on the path,ruff check,ruff format --check: pass.