Stale queue#23
Merged
Merged
Conversation
FIFO (completion-order) consumption has a difficulty bias: long generations span several weight versions, so their min_version is old on arrival, and queueing behind fresher short samples pushes them past max_staleness — the buffer preferentially expires exactly the long/hard prompts. Measured under rollout over-provisioning (6 inference GPUs vs 2 trainer GPUs, Qwen3-4B math RL): dropped samples are 1.3-1.7x longer than consumed ones and 17% of production expires. Replace the fresh-queue deque with a priority heap ordered by dataflow.buffer.queue_order: - "edf" (new default): ascending min_version — consume the sample closest to its staleness deadline first. The per-token invariant is unchanged (current_version - min_version <= max_staleness for everything trained on); ties break by arrival, so rollout groups stay contiguous. - "fifo": arrival order, bit-for-bit the historical behavior, kept for comparison runs. Plumb the option through AgentConfig -> AstraFlow -> data serving -> RolloutBuffer (per-model override supported), and surface the bias in metrics: buffer/consumed_len_avg, buffer/skipped_stale_len_avg, plus the previously computed-but-unreported eviction count. state_dict stays backward compatible with deque-era checkpoints (priorities are recomputed from metadata on load, so checkpoints also survive switching queue_order). A/B on 8xH100 (only queue_order differs): edf halves the drop rate, drops length-fairly (1.1x vs 1.7x), trains on ~25% longer samples, and reaches 57.6 overall avg@k at step 100 vs fifo's 52.5 (+5.1, reproduced across two independent edf runs; largest gain on the hardest set, AIME25 +9.4). Both arms converge by step 350 — edf is a sample-efficiency win, reaching fifo's plateau ~150 steps earlier. Covered by 14 new unit tests including a deterministic mixed-length load simulation asserting the bias exists under fifo and disappears under edf with no staleness violations in either mode.
Two Qwen3-4B math-RL recipes identical except dataflow.buffer.queue_order (and trial_name), for reproducing the rollout-buffer consumption-order comparison. The 6-RaaS/2-trainer GPU split deliberately over-provisions rollout so the fresh buffer builds real staleness pressure; ctx 16k / max_new_tokens 14000 makes generations span multiple weight versions. READMEs document the setup and the measured results (edf: +5.1 overall avg@k at step 100, +9.4 on AIME25, ~150 steps faster to the eval plateau; converged by step 350).
Following the conda install steps verbatim leaves flash-attn 2.8.3 silently uninstalled: pyproject's [tool.uv] exclude-dependencies=["flash-attn"] (needed so project installs don't try to resolve/build it) also applies to the explicit `uv pip install flash-attn` in Step 4 when run from the repo directory — the command exits 0 without installing anything. The failure is then masked because flash-attn-4 (sglang's attention backend) leaves a namespace stub at site-packages/flash_attn/, so `import flash_attn` appears to work and Step 6's `flash_attn.__version__` check fails with a confusing AttributeError instead of a clear signal. Step 4 now cds to /tmp before installing (mirroring the Dockerfile fix from d192bbe) and explains why; Step 6 verifies by importing flash_attn_func and reading the wheel metadata version, with the failure signature and remedy spelled out. Both envs built from these docs on this cluster had hit the trap unnoticed.
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.
Title:
feat(dataflow): earliest-deadline-first (EDF) rollout buffer consumption
Body:
Problem: FIFO consumption creates a difficulty bias in staleness drops
Long generations span several weight updates while they are being produced, so
their
min_versionis already old the moment they land in the rollout buffer.Consuming in completion order (FIFO) then parks them behind fresher short
samples until they exceed
max_stalenessand get dropped.The two effects compound into a systematic bias: the long/hard samples a
model most needs to learn are preferentially discarded. In our measurements,
FIFO's staleness-dropped samples are 1.3–1.7× longer than the samples it
trains on.
Change: consume by staleness deadline instead of arrival order
The rollout buffer is now a priority queue consumed in ascending
min_versionorder (earliest deadline first) — the samples closest to expiring are
trained on first, before they cross the staleness bound.
dataflow.buffer.queue_order:edf(default) orfifo(the historical behavior, kept for comparison).
buffered samples are consumed first; nothing older than
max_stalenessisever trained on, and the replay path is untouched.
min_versionbreak by arrival order, so rollout groups staycontiguous; unversioned samples are consumed eagerly.
buffer/consumed_len_avg,buffer/skipped_stale_len_avg, plus buffer-eviction accounting.state_dictround-trips acrossqueue_ordersettings (priorities are recomputed from metadata on load), and legacy
deque-format checkpoints still load.
mixed-length load simulation showing the bias and its fix, and
state-dict round-trips (
astraflow/dataflow/tests/test_rollout_buffer_queue_order.py).A/B results (Qwen3-4B, M2PO on math, 8×H100)
Controlled pair under real staleness pressure (6 GPUs RaaS vs 2 GPUs FSDP,
max_staleness: 8, ctx 16k /max_new_tokens14000). Recipes included inthis PR:
examples/math/qwen3-4b-m2po-edf/and.../qwen3-4b-m2po-fifo/.Overall avg@k across AIME24/25, AMC, Minerva, MATH500:
AMC +5.0.
(dropped/consumed length ratio 1.3–1.7×) with 8.3–17.1% drop rate; edf is
near-fair (~1.1×) at 7.4–9.6%.
steps earlier; with enough steps both arms converge.
Also in this PR
docs:installation.mdnow warns thatflash-attnmust be installed fromoutside the repo directory —
[tool.uv] exclude-dependenciesmakesuv pip install flash-attna silent no-op inside it, and a leftovernamespace stub makes
import flash_attnfalsely succeed. The verify stepnow imports
flash_attn_funcand checks the installed version.Compatibility / rollout notes
edf. Existing YAMLs withoutqueue_orderpick it upautomatically; set
dataflow.buffer.queue_order: fifoto restore the oldbehavior exactly.
(
loader → AgentConfig → AstraFlow → MultiModelDataServing → RolloutBuffer).