Repository navigation
[KV Offload] Reject secondary tiers on engines that span nodes - #62930
Conversation
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request; loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the mirror stops. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace unless rows are topology-agnostic. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
|
Documentation preview: https://vllm--62930.org.readthedocs.build/en/62930/ |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 32d4fecf19
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request; loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the mirror stops. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace unless rows are topology-agnostic. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
32d4fec to
aeffb52
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aeffb52f01
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request; loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the mirror stops. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace, topology-agnostic rows included: rows stored across nodes before the mirror lack the remote slots. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
aeffb52 to
580209c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 580209cbb1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
580209c to
2d40017
Compare
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request; loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the mirror stops. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace, topology-agnostic rows included: rows stored across nodes before the mirror lack the remote slots. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2d40017588
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
2d40017 to
4ac5adf
Compare
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request; loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts, also those of a replaced client, are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the mirror stops. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace, topology-agnostic rows included: rows stored across nodes before the mirror lack the remote slots. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ac5adf2b1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
4ac5adf to
e0db005
Compare
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request; loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts, also those of a replaced client, are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the mirror stops. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace, topology-agnostic rows included: rows stored across nodes before the mirror lack the remote slots. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3151044a46
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
3151044 to
c3a7d6a
Compare
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request (or one slot, if larger); loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts, also those of a replaced client, are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the worker raises instead of reporting it, as a late attempt could overwrite a reused chunk. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace, topology-agnostic rows included: rows stored across nodes before the mirror lack the remote slots. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. The kvcr tier is rejected across nodes: its keys carry no slot_layout namespace. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
Depends on vllm-project#52838, vllm-project#62930 and vllm-project#62929. With TieringOffloadingSpec, the scheduler and its secondary tiers run in the engine core next to worker rank 0 and read only that node's /dev/shm region, so workers on other nodes write their slots into a region nobody reads (vllm-project#54415). vllm-project#62930 rejects such engines at startup; this lifts the rejection for the multiprocessing executor with nnodes > 1 by mirroring remote slots into the head region. With secondary tiers and workers on several nodes (mp executor, nnodes > 1): - Each worker off the head node keeps a compact region with only its slot. A background thread pushes the slot of each stored chunk to the head region before the store is reported, and pulls it before a load's DMA. Copies move at most 32 MiB per request (or one slot, if larger); loads go first, and a waiting store runs after at most four load requests. - Transport: a ZMQ ROUTER on worker rank 0 bound to VLLM_HOST_IP. ZAP admits only the remote workers' addresses; requests carry a random per-engine token and a per-client epoch and sequence, so delayed puts, also those of a replaced client, are rejected. None of it is cryptographic. - A copy that fails after its retries (mirror_attempts x mirror_timeout_s) is reported through vllm-project#52838: failed_jobs drops a store and failed_recving recomputes a request whose load failed. Before a failed push is reported, a fence makes the head reject its late attempts; if the fence fails, the worker raises instead of reporting it, as a late attempt could overwrite a reused chunk. - Worker creation gathers errors, addresses, the server endpoint and the client handshake from every rank, so a failure raises on all. A remote worker's compact region is unlinked as soon as it is mapped. - FileMapper tiers (fs, obj, p2p) of multi-node engines store under their own slot_layout namespace, topology-agnostic rows included: rows stored across nodes before the mirror lack the remote slots. - The cache-reset promotion hold of vllm-project#62929 also covers a remote node's late slot push: a remote worker reports a store only after its push, and fences a failed push's late attempts before reporting it. - The rejections of vllm-project#62930 for external_launcher, an unknown node count (Ray), canonical_layout and replicated_layout stay. The kvcr tier, under any name that resolves to its class, is rejected across nodes: its keys carry no slot_layout namespace. Without secondary tiers, regions stay node-local as before. Signed-off-by: Javier López <jlopez@semark.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011NBbUxmYzpDb5p1ScTziyw
|
Thanks @javieratomic ! |
c3a7d6a to
4185ec5
Compare
|
Done in 4185ec5: it is down to the minimum, +84/-0 including the test. With secondary tiers and more than one worker, Checked on 2× DGX Spark (TP=2, |
TieringOffloadingSpec secondary tiers run in the engine core and read only its node's CPU region. Workers on other nodes write their slots into their own node's region, so the secondary tiers store rows without them and later load stale or uninitialized KV into those ranks (vllm-project#54415). Reject the setup when the workers are created instead: with secondary tiers and more than one worker, each worker checks with in_the_same_node_as() whether its TP, PP and PCP groups share a node. An engine's workers form a grid of those groups, so they all share a node if each group does. Single-node engines and engines without secondary tiers are unchanged. Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8SKmHUi4aeTtP6qyf8Xpe
4185ec5 to
daf1a7e
Compare
|
Pushed daf1a7e with one more fix to the same check. The previous revision raised at the first group that spans nodes, so a rank could skip the PP or PCP collective that other ranks still entered. With an uneven placement, e.g. TP=2 × PP=2 with three workers on one node and one on another, the ranks of the spanning TP group raised at once while the ranks of the node-local TP group blocked in the PP collective, waiting for them, until the executor noticed the dead workers or the gloo timeout expired, instead of failing with the same error right away. Now every rank runs all three collectives and only then raises. The unit test also checks that all three groups are queried when the first one already spans nodes; it fails on the previous revision. |
TieringOffloadingSpec's scheduler and secondary tiers run in the engine core, which shares a node with worker rank 0 (the head node), and read only that node's shared CPU region. Workers on other nodes wrote their slots into their own node's region, so secondary tiers stored rows without those slots and promotions never filled them (vllm-project#54415); vllm-project#62930 rejects such engines at startup. This lifts that rejection. Each worker on another node moves its own slot of every chunk between its GPU and the head region with one-sided NIXL transfers (RDMA WRITE and READ over UCX). The head only registers its region; it serves no requests. A store is reported once its slots are in the head region, and a load reads them before the request runs. Loads go ahead of stores on a background copy thread. mirror_source picks what a remote worker transfers from: - cpu: a compact node-local region holding only its slot, filled by the regular offloading DMA and mirrored in transfers of at most 256 MiB; - gpu: its GPU blocks, straight to and from the head region, with the CPU offloading worker's copy plan mapped onto the head slot. It needs GPUDirect RDMA; without it UCX emulates the transfers through host memory, several times slower than cpu; - auto (default): gpu when the driver reports GPUDirect RDMA or dma-buf support for the GPU, cpu otherwise (e.g. GB10). A transfer that ends in an error is reported as a failed transfer. One that reaches no terminal state within mirror_timeout_s may still land on a reused chunk or block, so the worker raises and the engine stops. Placement: secondary tiers on an engine whose TP, PP or PCP groups span nodes get the mirror; the scheduler must join worker rank 0's region. Data parallelism, canonical_layout, replicated_layout and the kvcr tier stay rejected across nodes. FileMapper-keyed tiers use their own namespace on multi-node engines, since rows stored before the mirror lack the remote slots. Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8SKmHUi4aeTtP6qyf8Xpe
Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com>
TieringOffloadingSpec's scheduler and secondary tiers run in the engine core, which shares a node with worker rank 0 (the head node), and read only that node's shared CPU region. Workers on other nodes wrote their slots into their own node's region, so secondary tiers stored rows without those slots and promotions never filled them (vllm-project#54415); vllm-project#62930 rejects such engines at startup. This lifts that rejection. Each worker on another node moves its own slot of every chunk between its GPU and the head region with one-sided NIXL transfers (RDMA WRITE and READ over UCX). The head only registers its region; it serves no requests. A store is reported once its slots are in the head region, and a load reads them before the request runs. Loads go ahead of stores on a background copy thread. mirror_source picks what a remote worker transfers from: - cpu: a compact node-local region holding only its slot, filled by the regular offloading DMA and mirrored in transfers of at most 256 MiB; - gpu: its GPU blocks, straight to and from the head region, with the CPU offloading worker's copy plan mapped onto the head slot. It needs GPUDirect RDMA; without it UCX emulates the transfers through host memory, several times slower than cpu; - auto (default): gpu when the driver reports GPUDirect RDMA or dma-buf support for the GPU, cpu otherwise (e.g. GB10). A transfer that ends in an error is reported as a failed transfer. One that reaches no terminal state within mirror_timeout_s may still land on a reused chunk or block, so the worker raises and the engine stops. Placement: secondary tiers on an engine whose TP, PP or PCP groups span nodes get the mirror; the scheduler must join worker rank 0's region. Data parallelism, canonical_layout, replicated_layout and the kvcr tier stay rejected across nodes. FileMapper-keyed tiers use their own namespace on multi-node engines, since rows stored before the mirror lack the remote slots. Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8SKmHUi4aeTtP6qyf8Xpe
Etelis
left a comment
There was a problem hiding this comment.
The check is in the right place, before allocating the CPU region. One small test change below to make sure we keep that ordering.
Etelis
left a comment
There was a problem hiding this comment.
Other than that it LGTM!
Thanks!
Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lxs9D9vjV9HGQmKEUGZW8U
|
/ci run |
|
✅ @javieratomic, CI is now available for this PR.
|
|
✅ Triggered Buildkite CI #98286 for commit ℹ️ This PR is 1 commit behind upstream |
Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com>
TieringOffloadingSpec's scheduler and secondary tiers run in the engine core, which shares a node with worker rank 0 (the head node), and read only that node's shared CPU region. Workers on other nodes wrote their slots into their own node's region, so secondary tiers stored rows without those slots and promotions never filled them (vllm-project#54415); vllm-project#62930 rejects such engines at startup. This lifts that rejection. Each worker on another node moves its own slot of every chunk between its GPU and the head region with one-sided NIXL transfers (RDMA WRITE and READ over UCX). The head only registers its region; it serves no requests. A store is reported once its slots are in the head region, and a load reads them before the request runs. Loads go ahead of stores on a background copy thread. mirror_source picks what a remote worker transfers from: - cpu: a compact node-local region holding only its slot, filled by the regular offloading DMA and mirrored in transfers of at most 256 MiB; - gpu: its GPU blocks, straight to and from the head region, with the CPU offloading worker's copy plan mapped onto the head slot. It needs GPUDirect RDMA; without it UCX emulates the transfers through host memory, several times slower than cpu; - auto (default): gpu when the driver reports GPUDirect RDMA or dma-buf support for the GPU, cpu otherwise (e.g. GB10). A transfer that ends in an error is reported as a failed transfer. One that reaches no terminal state within mirror_timeout_s may still land on a reused chunk or block, so the worker raises and the engine stops. Placement: secondary tiers on an engine whose TP, PP or PCP groups span nodes get the mirror; the scheduler must join worker rank 0's region. Data parallelism, canonical_layout, replicated_layout and the kvcr tier stay rejected across nodes. FileMapper-keyed tiers use their own namespace on multi-node engines, since rows stored before the mirror lack the remote slots. Signed-off-by: Javier López <59578422+javieratomic@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Overview
This PR makes
TieringOffloadingSpecfail at startup, instead of serving wrong KV, when secondary tiers run on an engine whose workers span several nodes (#54415).It applies to
mainon its own and depends on no other PR. #62933 builds on it to support the multiprocessing executor with--nnodes> 1.Claims
NotImplementedErrorif they do not. This covers every way to place workers on several nodes (mp with--nnodes, Ray,external_launcher) without looking at the executor.Validation
Multi-node startup on 2× DGX Spark (GB10),
vllm/vllm-openai:nightly-00b6c4c565with this branch's changed Python files on top, TP=2, mp executor with--nnodes 2,facebook/opt-125m:TieringOffloadingSpecwith anfstier: both workers fail at startup withNotImplementedError: TieringOffloadingSpec secondary tiers are not supported on engines whose workers span several nodes.TieringOffloadingSpecwithout secondary tiers: starts and serves; the same prompt sent twice gives the same output.Unit test:
tests/v1/kv_offload/test_factory.py::test_tiering_spec_rejects_secondary_tiers_across_nodesfakes the TP, PP and PCP groups andin_the_same_node_as(): it rejects a TP, PP or PCP group that spans nodes, accepts groups that each share a node, queries all three groups even after one spans nodes, and queries none without secondary tiers. Without the fix, four of its five cases fail. CI provides one host only, so it cannot run a real multi-node rejection.Suites (CPU-only container):
pytest tests/v1/kv_offload/tiering tests/v1/kv_offload/test_factory.py \ tests/v1/kv_offload/test_file_mapper.py \ tests/v1/kv_connector/unit/offloading_connector # 1286 passed, 1 failed (on 71294c2e9a)The one failure,
test_worker.py::test_register_packed_kv_caches_skips_scratch_group, needs a GPU and fails the same way onmain; it passes on GPU.ruff checkandruff format --checkpass on the changed files.Details
Root cause. The tiering scheduler and its secondary tiers run in the engine core and address only its node's
/dev/shmregion. Since #58497 every worker writes the slot of its rank, but workers on other nodes write it into their own node's region. Secondary tiers therefore persist rows without those slots, and promotions never fill them.Detection. An engine's workers form a grid of its TP, PP and PCP groups (DCP lives inside TP), so they all share one node exactly when each of those groups does.
create_worker()therefore callsin_the_same_node_as()on each group with more than one rank. It is a collective over that group, which every worker of the group reaches during KV cache registration. Every rank runs all three before any of them raises, so a rank whose own TP group spans nodes cannot leave the ranks of a node-local TP group waiting in the PP or PCP collective. It runs only with secondary tiers, since without them each node's region serves its own workers.Not a duplicate. Searched open PRs for "54415 in:body", "multi-node offload", "secondary tiers nodes", "local_world_size offload" and "shared region node". #53474 also sizes node-local regions and rejects multi-node secondary tiers from the config; #46556 moves rank-local shards to a shared filesystem through worker transfers instead; #62058 and #53073 deal with
/dev/shmcapacity and region lifecycle, not with nodes.Docs:
docs/features/kv_offloading_usage.mdstates the restriction.AI assistance. AI assistance (Claude) was used to write this change, its tests and this description. I reviewed every changed line and ran the tests above.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code