Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
19d3ddb
fix(vllm): suppress health check canary during RL weight transfers
glamr-agent Sep 11, 2026
3077579
fix(runtime): scope canary maintenance windows to a lease
glamr-agent Sep 11, 2026
11828c0
fix(vllm): make weight-transfer maintenance termination idempotent
glamr-agent Sep 11, 2026
c673064
fix(vllm): release the maintenance lease when finish_weight_update fails
glamr-agent Sep 11, 2026
ea216f0
test(vllm): assert the released maintenance lease
glamr-agent Sep 11, 2026
5a7c353
Merge remote-tracking branch 'upstream/main' into dyn-4383-rl-weight-…
glamr-agent Sep 11, 2026
b7b2e76
test(vllm): preserve watchdog maintenance lease mock
glamr-agent Sep 11, 2026
8bb253d
Merge remote-tracking branch 'upstream/main' into dyn-4383-rl-weight-…
glamr-agent Sep 12, 2026
5581ffa
test(vllm): consolidate maintenance coverage
glamr-agent Sep 12, 2026
638d0ab
fix(runtime): prune expired canary maintenance leases
glamr-agent Sep 12, 2026
04120cb
fix(vllm): release maintenance after rejected finish
glamr-agent Sep 12, 2026
16c049c
test(vllm): bound cancellation maintenance test
glamr-agent Sep 12, 2026
1367c72
style: remove redundant maintenance comments
glamr-agent Sep 12, 2026
e414b00
fix(runtime): extend RL canary deadlines without suppressing probes
glamr-agent Sep 16, 2026
d8d4cfd
fix(ci): pass required framework to Triton tests
glamr-agent Sep 17, 2026
2ad9fc2
fix(runtime): reject overflowing health check deadlines
glamr-agent Sep 17, 2026
76e4eb6
fix(ci): include networkx for test collection
glamr-agent Sep 17, 2026
5b8f1c1
fix(vllm): bound RL rendezvous timeout before maintenance
MatejKosec Sep 18, 2026
0b68f17
chore(vllm): cross-reference maintenance timeout limits
glamr-agent Sep 18, 2026
f48bf26
test(runtime): cover in-flight canary maintenance deadlines
glamr-agent Sep 18, 2026
529008b
test(runtime): run canary deadline regression in unit CI
glamr-agent Sep 18, 2026
7420c78
fix(vllm): release maintenance on invalid finish options
glamr-agent Sep 18, 2026
5981e77
test(vllm): preserve maintenance after rejected weight pushes
glamr-agent Sep 18, 2026
6a8703d
fix(vllm): release maintenance during worker cleanup
glamr-agent Sep 18, 2026
cc21062
merge: resolve main weight-version initialization conflict
glamr-agent Sep 19, 2026
2b89a57
merge: resolve main conflict in RL maintenance initialization
glamr-agent Sep 30, 2026
48af018
Merge main to rerun CI after an infrastructure failure
glamr-agent Oct 3, 2026
e7ac3e1
Merge main to resolve runtime binding declarations conflict
glamr-agent Oct 21, 2026
4e1185d
Merge main to rerun CI
glamr-agent Oct 21, 2026
a002b9c
Merge main to rerun CI
glamr-agent Oct 22, 2026
70bfc9b
Merge main to rerun CI
glamr-agent Oct 22, 2026
6b76631
Merge main to rerun CI
glamr-agent Oct 26, 2026
9b7e685
Merge main to rerun CI
glamr-agent Oct 26, 2026
54f7a74
Merge main to rerun CI
glamr-agent Oct 26, 2026
aae95c5
Merge main to rerun CI
glamr-agent Oct 26, 2026
eb97ea5
Merge main to resolve Python runtime stub conflict
glamr-agent Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ jobs:
uses: $/.github/workflows/shared-test.yml
with:
test_suite_name: triton
framework: triton
test_type: CPU Test
amd_runner: prod-tester-amd-v2
target_tag_plain: ${{ needs.triton-build.outputs.target_tag_plain }}
Expand Down
66 changes: 64 additions & 2 deletions components/src/dynamo/vllm/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
_DELTA_REQUEST_OUTPUT_KIND = RequestOutputKind.DELTA
_RL_INIT_WEIGHTS_TIMEOUT_ENV = "DYN_RL_INIT_WEIGHTS_TIMEOUT_S"
_RL_INIT_WEIGHTS_TIMEOUT_DEFAULT_S = 30.0
_RL_INIT_WEIGHTS_TIMEOUT_MAX_S = 86_400.0
Comment thread
glamr-agent marked this conversation as resolved.
Resolved
# Ceiling on the Ray GCS round-trips behind get_ep_capacity. The reconciler polls
# that endpoint, so an unbounded wait on a degraded GCS would pile up control
# requests; a capacity read is advisory and stale-or-absent beats slow.
Expand Down Expand Up @@ -165,12 +166,22 @@ def build_prompt_tokens_details(


def _rl_init_weights_timeout_s() -> float:
return float(
timeout_s = float(
os.environ.get(
_RL_INIT_WEIGHTS_TIMEOUT_ENV,
str(_RL_INIT_WEIGHTS_TIMEOUT_DEFAULT_S),
)
)
# Keep the rendezvous watchdog within the runtime maintenance API's bound.
if math.isfinite(timeout_s) and timeout_s > _RL_INIT_WEIGHTS_TIMEOUT_MAX_S:
logger.warning(
"%s=%s exceeds the maximum of %s seconds; using the maximum",
_RL_INIT_WEIGHTS_TIMEOUT_ENV,
timeout_s,
_RL_INIT_WEIGHTS_TIMEOUT_MAX_S,
)
return _RL_INIT_WEIGHTS_TIMEOUT_MAX_S
return timeout_s


class _DeferredAbort:
Expand Down Expand Up @@ -1201,6 +1212,11 @@ def __init__(
self._lora_capacity_guard = asyncio.Lock()
self._paused: bool = False
self._weight_version: str = "initial"
# Canary maintenance lease for the transfer this worker is running, if
# any. A worker has one weight-update group, so one lease is enough and
# holding it in a single slot is what makes the terminators idempotent —
# see _begin_rl_maintenance and _end_rl_maintenance.
self._rl_maintenance_lease: int | None = None

embedding_loader = self.init_embedding_loader(config, encode_worker_client)

Expand Down Expand Up @@ -2103,8 +2119,13 @@ async def update_weights_from_distributed(self, body: dict) -> dict:
"Set 'reset_prefix_cache' to false or pause generation first."
),
}
rpc = body.get("engine_rpc", "update_weights_from_path")
async with self._pause_lock:
if not self._paused and not allow_unpaused:
if rpc == "finish_weight_update":
# A rejected finish still terminates its transfer. Do not
# leave the canary timeout extended until the lease expires.
self._end_rl_maintenance()
Comment thread
MatejKosec marked this conversation as resolved.
Resolved
return {
"status": "error",
"message": (
Expand All @@ -2114,7 +2135,6 @@ async def update_weights_from_distributed(self, body: dict) -> dict:
),
}
version = body.get("weight_version", "unknown")
rpc = body.get("engine_rpc", "update_weights_from_path")
rpc_kwargs = {
k: v
for k, v in body.items()
Expand All @@ -2137,6 +2157,12 @@ async def update_weights_from_distributed(self, body: dict) -> dict:
except Exception as e:
logger.error(f"[RL] update_weights_from_distributed failed: {e}")
return {"status": "error", "message": str(e)}
finally:
Comment thread
glamr-agent marked this conversation as resolved.
Resolved
if rpc == "finish_weight_update":
Comment thread
glamr-agent marked this conversation as resolved.
Resolved
# The other terminator of a weight-transfer transaction: a
# controller may end here and never call destroy. A finish
# that failed still ends it and restores the normal timeout.
self._end_rl_maintenance()

async def update_weights_from_tensor(self, body: dict) -> dict:
"""Not implemented: in-process tensor transfer is not yet supported."""
Expand All @@ -2152,6 +2178,31 @@ async def update_weights_from_tensor(self, body: dict) -> dict:
"message": "update_weights_from_tensor is not implemented",
}

def _begin_rl_maintenance(self, timeout_s: float) -> None:
"""Take the lease for a new transfer, superseding any lease still held.

A worker has one weight-update group, so an init means the previous
transfer is over however it ended. Releasing its lease here keeps a
transfer that never reached a terminator from holding a window until
its deadline.
"""
self._end_rl_maintenance()
self._rl_maintenance_lease = self.runtime.begin_health_check_maintenance(
timeout_s, self.config.endpoint
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

def _end_rl_maintenance(self) -> None:
"""Release this worker's lease, once.

Clearing the slot before releasing makes the terminators idempotent: a
transfer that reaches both `finish_weight_update` and
`destroy_weights_update_group` releases its lease on the first and does
nothing on the second, rather than releasing a lease it does not own.
"""
lease, self._rl_maintenance_lease = self._rl_maintenance_lease, None
if lease is not None:
self.runtime.end_health_check_maintenance(lease)

async def init_weights_update_group(self, body: dict) -> dict:
"""Initialize the distributed weight-update communication group."""
if body is None:
Expand All @@ -2166,6 +2217,8 @@ async def init_weights_update_group(self, body: dict) -> dict:
async with self._pause_lock:
try:
timeout_s = _rl_init_weights_timeout_s()
# Give the canary the same bound as the rendezvous watchdog.
self._begin_rl_maintenance(timeout_s)
rpc_task = asyncio.create_task(
self.engine_client.collective_rpc(rpc, kwargs=kwargs)
)
Expand All @@ -2174,10 +2227,12 @@ async def init_weights_update_group(self, body: dict) -> dict:
except asyncio.CancelledError:
rpc_task.cancel()
await asyncio.gather(rpc_task, return_exceptions=True)
self._end_rl_maintenance()
raise
if rpc_task not in done:
rpc_task.cancel()
await asyncio.gather(rpc_task, return_exceptions=True)
self._end_rl_maintenance()
logger.error(
f"[RL] init_weights_update_group timed out after "
f"{timeout_s:.1f} seconds (rpc={rpc}); terminating the "
Expand All @@ -2186,11 +2241,15 @@ async def init_weights_update_group(self, body: dict) -> dict:
self._shutdown_worker()

await rpc_task
# The lease expires timeout_s seconds after initialization began.
# Later transfers do not renew it; finish or destroy releases it
# early if the transaction ends before that absolute deadline.
logger.info(f"[RL] Weight update group initialized (rpc={rpc})")
return {"status": "ok", "message": "Weight update group initialized"}
except EngineDeadError as e:
self._shutdown_on_engine_dead(e)
except Exception as e:
self._end_rl_maintenance()
logger.error(f"[RL] init_weights_update_group failed: {e}")
return {"status": "error", "message": str(e)}

Expand All @@ -2215,6 +2274,9 @@ async def destroy_weights_update_group(self, body: dict) -> dict:
except Exception as e:
logger.error(f"[RL] destroy_weights_update_group failed: {e}")
return {"status": "error", "message": str(e)}
finally:
# Restore the normal timeout even when teardown fails.
self._end_rl_maintenance()

@abstractmethod
def generate(self, request: RequestT, context: Context) -> AsyncIterator[ResponseT]:
Expand Down
Loading
Loading