Skip to content

[RL] Add /pull_weights: engine-side pull of published weights into a host-local checkpoint - #30367

Open
nanjiangwill wants to merge 1 commit into
sgl-project:mainfrom
nanjiangwill:pull-weights-main
Open

[RL] Add /pull_weights: engine-side pull of published weights into a host-local checkpoint#30367
nanjiangwill wants to merge 1 commit into
sgl-project:mainfrom
nanjiangwill:pull-weights-main

Conversation

@nanjiangwill

@nanjiangwill nanjiangwill commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Motivation

RL trainers with disaggregated rollout publish each weight sync as a version dir weight_v{N:06d}/ on a shared filesystem — a full HF checkpoint, or zstd-compressed per-tensor byte deltas (xor/overwrite) with per-tensor checksums, packaged as a canonical HF checkpoint dir. This adds the engine-side receiver: POST /pull_weights brings a host-local checkpoint up to a target version on every host the deployment spans, so the trainer talks to one endpoint per engine.

Trainer PR: radixark/miles#1235 · sglang-miles variant: #30366 · Refs: THUDM/slime#2181

Behavior

POST /pull_weights {local_checkpoint_dir, source_dir, target_version} fans out to every scheduler rank on every node. Each host seeds from the newest full version ≤ target (or the server's own model_path — version 0 is the engine's base), then applies the delta chain in place via mmap, parallelized across tensors. A checksum mismatch or out-of-order apply raises — never serve bad weights; a per-host flock + applied-version marker collapse co-located ranks to one pull. Success is gathered across the TP group, so the reply covers every host. The trainer then reloads via the ordinary /update_weights_from_disk — weight loading never sees the delta format.

--custom-pull-weights-pre-read-hook <import.path>: refresh hook for object-store-backed mounts without cross-host read-after-write consistency (POSIX shared FS needs none).

Notes


CI States

Latest PR Test (Base): ❌ Run #29774219418
Latest PR Test (Extra): ❌ Run #29774219280

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new /pull_weights endpoint to enable host-local pulling of published weight deltas into local checkpoints, adding dependencies on xxhash and zstandard. The review feedback highlights critical compatibility issues, including a potential crash on non-POSIX systems (macOS/Windows) due to the use of os.posix_fadvise and another crash in non-distributed environments when torch.distributed is uninitialized. Additionally, several performance improvements were suggested to eliminate redundant memory copies when handling compressed data and byte buffers.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread python/sglang/srt/weight_sync/local_checkpoint.py
Comment thread python/sglang/srt/managers/scheduler_components/weight_updater.py Outdated
Comment thread python/sglang/srt/weight_sync/local_checkpoint.py Outdated
Comment thread python/sglang/srt/weight_sync/local_checkpoint.py
Comment thread python/sglang/srt/weight_sync/local_checkpoint.py Outdated
@nanjiangwill
nanjiangwill force-pushed the pull-weights-main branch 3 times, most recently from 003bb57 to 9577941 Compare July 7, 2026 18:22
nanjiangwill added a commit to modal-projects/sglang that referenced this pull request Jul 13, 2026
…-local checkpoint

Add a disaggregated weight-sync receiver so an inference engine can bring its own
host-local checkpoint up to a published version while it keeps serving, decoupled
from the trainer.

- POST /pull_weights {local_checkpoint_dir, source_dir, target_version}: each host
  walks the published weight_v{N:06d}/ chain back to the nearest full anchor and
  replays the deltas forward (xor + zstd, xxh3-128 checksum) into a host-local
  checkpoint that /update_weights_from_disk then reloads. New
  weight_sync/local_checkpoint.py plus wiring through io_struct, scheduler,
  weight_updater, tokenizer_control_mixin, http_server, and server_args
  (custom_pull_weights_pre_read_hook lets the deployment refresh its volume view
  before a read).

- Robust to an eventually-consistent volume mount whose view can change under a
  running engine: read each delta blob whole-file into memory and size-verify it
  against its own safetensors header before the xor, so the apply is immune to the
  mount shifting mid-read; a short read (source not yet materialized) raises
  rather than applying a partial delta, and the caller reloads + retries. One
  reload per host (a per-host file lock collapses co-located ranks; only the lock
  winner reloads). Reseed from the pristine boot checkpoint captured on the first
  pull, never the mutated model_path.

Includes unit tests for torn/truncated/missing-blob sources.

Upstreaming: sgl-project#30367
nanjiangwill added a commit to modal-projects/sglang that referenced this pull request Jul 13, 2026
…-local checkpoint

Add a disaggregated weight-sync receiver so an inference engine can bring its own
host-local checkpoint up to a published version while it keeps serving, decoupled
from the trainer.

- POST /pull_weights {local_checkpoint_dir, source_dir, target_version}: each host
  walks the published weight_v{N:06d}/ chain back to the nearest full anchor and
  replays the deltas forward (xor + zstd, xxh3-128 checksum) into a host-local
  checkpoint that /update_weights_from_disk then reloads. New
  weight_sync/local_checkpoint.py plus wiring through io_struct, scheduler,
  weight_updater, tokenizer_control_mixin, http_server, and server_args
  (custom_pull_weights_pre_read_hook lets the deployment refresh its volume view
  before a read).

- Robust to an eventually-consistent volume mount whose view can change under a
  running engine: read each delta blob whole-file into memory and size-verify it
  against its own safetensors header before the xor, so the apply is immune to the
  mount shifting mid-read; a short read (source not yet materialized) raises
  rather than applying a partial delta, and the caller reloads + retries. One
  reload per host (a per-host file lock collapses co-located ranks; only the lock
  winner reloads). Reseed from the pristine boot checkpoint captured on the first
  pull, never the mutated model_path.

Includes unit tests for torn/truncated/missing-blob sources.

Upstreaming: sgl-project#30367
nanjiangwill added a commit to modal-projects/sglang that referenced this pull request Jul 20, 2026
…-local checkpoint

Add a disaggregated weight-sync receiver so an inference engine can bring its own
host-local checkpoint up to a published version while it keeps serving, decoupled
from the trainer.

- POST /pull_weights {local_checkpoint_dir, source_dir, target_version}: each host
  walks the published weight_v{N:06d}/ chain back to the nearest full anchor and
  replays the deltas forward (xor + zstd, xxh3-128 checksum) into a host-local
  checkpoint that /update_weights_from_disk then reloads. New
  weight_sync/local_checkpoint.py plus wiring through io_struct, scheduler,
  weight_updater, tokenizer_control_mixin, http_server, and server_args
  (custom_pull_weights_pre_read_hook lets the deployment refresh its volume view
  before a read).

- Robust to an eventually-consistent volume mount whose view can change under a
  running engine: read each delta blob whole-file into memory and size-verify it
  against its own safetensors header before the xor, so the apply is immune to the
  mount shifting mid-read; a short read (source not yet materialized) raises
  rather than applying a partial delta, and the caller reloads + retries. One
  reload per host (a per-host file lock collapses co-located ranks; only the lock
  winner reloads). Reseed from the pristine boot checkpoint captured on the first
  pull, never the mutated model_path.

Includes unit tests for torn/truncated/missing-blob sources.

Upstreaming: sgl-project#30367
nanjiangwill added a commit to modal-projects/sglang that referenced this pull request Jul 20, 2026
…-local checkpoint

Add a disaggregated weight-sync receiver so an inference engine can bring its own
host-local checkpoint up to a published version while it keeps serving, decoupled
from the trainer.

- POST /pull_weights {local_checkpoint_dir, source_dir, target_version}: each host
  walks the published weight_v{N:06d}/ chain back to the nearest full anchor and
  replays the deltas forward (xor + zstd, xxh3-128 checksum) into a host-local
  checkpoint that /update_weights_from_disk then reloads. New
  weight_sync/local_checkpoint.py plus wiring through io_struct, scheduler,
  weight_updater, tokenizer_control_mixin, http_server, and server_args
  (custom_pull_weights_pre_read_hook lets the deployment refresh its volume view
  before a read).

- Robust to an eventually-consistent volume mount whose view can change under a
  running engine: read each delta blob whole-file into memory and size-verify it
  against its own safetensors header before the xor, so the apply is immune to the
  mount shifting mid-read; a short read (source not yet materialized) raises
  rather than applying a partial delta, and the caller reloads + retries. One
  reload per host (a per-host file lock collapses co-located ranks; only the lock
  winner reloads). Reseed from the pristine boot checkpoint captured on the first
  pull, never the mutated model_path.

Includes unit tests for torn/truncated/missing-blob sources.

Upstreaming: sgl-project#30367
…host-local checkpoint

Add the disaggregated receiver used by Stitch: POST /pull_weights fans out across the deployment, walks the published weight_v{N} chain from the nearest full anchor, and replays zstd-compressed XOR deltas with per-tensor checksums into a host-local checkpoint for update_weights_from_disk.

Harden eventual-consistency and recovery semantics by reading and size-verifying each source blob in memory before mutation, distinguishing incomplete sources from corrupt local state, syncing dirty pages before advancing the version marker, deduplicating work under a per-host lock, and reseeding from the latched pristine boot checkpoint after a torn apply. Seed shards copy in parallel with 16 MiB streaming reads and progress logging.

Ported from the Stitch production fork. Upstreaming keeps the engine-side receiver independent of trainer framework and omits the separate partial-reload optimization tier.
nanjiangwill added a commit to modal-projects/sglang that referenced this pull request Jul 20, 2026
…-local checkpoint

Add a disaggregated weight-sync receiver so an inference engine can bring its own
host-local checkpoint up to a published version while it keeps serving, decoupled
from the trainer.

- POST /pull_weights {local_checkpoint_dir, source_dir, target_version}: each host
  walks the published weight_v{N:06d}/ chain back to the nearest full anchor and
  replays the deltas forward (xor + zstd, xxh3-128 checksum) into a host-local
  checkpoint that /update_weights_from_disk then reloads. New
  weight_sync/local_checkpoint.py plus wiring through io_struct, scheduler,
  weight_updater, tokenizer_control_mixin, http_server, and server_args
  (custom_pull_weights_pre_read_hook lets the deployment refresh its volume view
  before a read).

- Robust to an eventually-consistent volume mount whose view can change under a
  running engine: read each delta blob whole-file into memory and size-verify it
  against its own safetensors header before the xor, so the apply is immune to the
  mount shifting mid-read; a short read (source not yet materialized) raises
  rather than applying a partial delta, and the caller reloads + retries. One
  reload per host (a per-host file lock collapses co-located ranks; only the lock
  winner reloads). Reseed from the pristine boot checkpoint captured on the first
  pull, never the mutated model_path.

Includes unit tests for torn/truncated/missing-blob sources.

Upstreaming: sgl-project#30367
nanjiangwill added a commit to modal-projects/sglang that referenced this pull request Jul 21, 2026
…-local checkpoint

Add a disaggregated weight-sync receiver so an inference engine can bring its own
host-local checkpoint up to a published version while it keeps serving, decoupled
from the trainer.

- POST /pull_weights {local_checkpoint_dir, source_dir, target_version}: each host
  walks the published weight_v{N:06d}/ chain back to the nearest full anchor and
  replays the deltas forward (xor + zstd, xxh3-128 checksum) into a host-local
  checkpoint that /update_weights_from_disk then reloads. New
  weight_sync/local_checkpoint.py plus wiring through io_struct, scheduler,
  weight_updater, tokenizer_control_mixin, http_server, and server_args
  (custom_pull_weights_pre_read_hook lets the deployment refresh its volume view
  before a read).

- Catch-up is chosen per host, all in _pull_locked: a steady-state host applies the
  one new delta in place (mmap, only the delta's dirty pages, O(delta)); a host
  several versions behind folds the whole range in one pass per tensor — one buffered
  read + XOR the range in RAM + one write — instead of N mmap read-modify-writes, each
  faulting the whole tensor in; and a fresh mid-run joiner, when an aggregated delta
  aggregate/ (base xor vM as one delta, base_version 0) has been published, applies it
  once on the base seed to reach vM and then folds only the d(M+1)..target tail. The
  aggregate is a separate writer's single in-place dir on an eventually-consistent
  source, so a reader can observe a partial (mid-update) state; every shard stamps its
  version and a reader that sees a disagreement (a torn read whose per-shard checksums
  still self-verify) skips it and folds from base.

- Robust to an eventually-consistent volume mount whose view can change under a
  running engine: read each delta blob whole-file into memory and size-verify it
  against its own safetensors header before the xor, so the apply is immune to the
  mount shifting mid-read; a short read (source not yet materialized) raises
  rather than applying a partial delta, and the caller reloads + retries. One
  reload per host (a per-host file lock collapses co-located ranks; only the lock
  winner reloads). Reseed from the pristine boot checkpoint captured on the first
  pull, never the mutated model_path.

Includes unit tests for torn/truncated/missing-blob sources, the multi-delta fold,
and the aggregate fast-forward.

Upstreaming: sgl-project#30367
nanjiangwill added a commit to modal-projects/sglang that referenced this pull request Jul 21, 2026
…-local checkpoint

Add a disaggregated weight-sync receiver so an inference engine can bring its own
host-local checkpoint up to a published version while it keeps serving, decoupled
from the trainer.

- POST /pull_weights {local_checkpoint_dir, source_dir, target_version}: each host
  walks the published weight_v{N:06d}/ chain back to the nearest full anchor and
  replays the deltas forward (xor + zstd, xxh3-128 checksum) into a host-local
  checkpoint that /update_weights_from_disk then reloads. New
  weight_sync/local_checkpoint.py plus wiring through io_struct, scheduler,
  weight_updater, tokenizer_control_mixin, http_server, and server_args
  (custom_pull_weights_pre_read_hook lets the deployment refresh its volume view
  before a read).

- Catch-up is chosen per host, all in _pull_locked: a steady-state host applies the
  one new delta in place (mmap, only the delta's dirty pages, O(delta)); a host
  several versions behind folds the whole range in one pass per tensor — one buffered
  read + XOR the range in RAM + one write — instead of N mmap read-modify-writes, each
  faulting the whole tensor in. Pure xor/zstd ranges only; a non-xor version in the
  range falls back to the per-version apply.

- Robust to an eventually-consistent volume mount whose view can change under a
  running engine: read each delta blob whole-file into memory and size-verify it
  against its own safetensors header before the xor, so the apply is immune to the
  mount shifting mid-read; a short read (source not yet materialized) raises
  rather than applying a partial delta, and the caller reloads + retries. One
  reload per host (a per-host file lock collapses co-located ranks; only the lock
  winner reloads). Reseed from the pristine boot checkpoint captured on the first
  pull, never the mutated model_path.

Includes unit tests for torn/truncated/missing-blob sources and the multi-delta fold.

Upstreaming: sgl-project#30367
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant