pass the engine weight version from the trainer instead of polling the router - #2244
Conversation
| if args.max_weight_staleness is not None: | ||
| oldest = group_oldest_weight_version(group) | ||
| current = await self._weight_version.get(args) | ||
| current = input.weight_version |
There was a problem hiding this comment.
[P1] Stamp LoRA updates before trusting the trainer counter
input.weight_version is only comparable with Sample.weight_versions when both are stamped from the same counter. However, the supported --fully-async single-LoRA path with the default broadcast transport calls load_lora_adapter_from_distributed without any weight_version (the tensor path is the same, and the actor version-equality check explicitly skips LoRA), while the trainer counter still increments. Samples can therefore retain "default" or a fixed base version: oldest_weight_version then either returns None and silently disables the configured --max-weight-staleness, or remains fixed until every group is recycled as stale. Please stamp this counter on the engine after a successful LoRA load, or reject this configuration, and add coverage for the LoRA path.
|
[cc] [P2] Cell failover regresses the published weight version; the staleness filter goes silently inert
Under FT ( To be fair, the counter reset itself predates this PR (the same reset used to reach the filter via engine stamps + router polling), so this is inherited rather than introduced. But the new channel carries it forward, and the PR does add one new case: a RolloutManager actor restart resets Minimal ask: make the regression observable — warn (or assert) in |
…arn when it regresses
…ression under --ci-test
…t on regression under --ci-test" This reverts commit 48c4d78.
FullyAsyncRolloutFnneeds the current engine weight version to measure and bound sample staleness. Today it rediscovers it over HTTP:_CachedWeightVersionpolls the router's/model_infoonce per drained group, behind a 1s TTL cache that also throttles failures so an unreachable router does not cost one 2s timeout per group.That number is ours. The trainer stamps it onto the engines (
update_weight_version(weight_version=str(self.weight_version))), so polling reads back what miles just wrote.Change
weight_updater.weight_version→RolloutManager.set_weight_version→RolloutFnTrainInput.weight_version→ the drain.self.rollout_managerand already call it at rank 0 insideupdate_weights(clear_updatable_has_new_engines); this adds one call right after the push completes, with the same precondition.RolloutFnTrainInputgainsweight_version: int | None, mirroringRolloutFnEvalInput._CachedWeightVersionand its HTTP/timeout/TTL machinery are deleted.Same numbering on both sides: engines are stamped from this counter and
sample.weight_versionscome back from enginemeta_info, socurrent - group_oldest_weight_versioncompares like with like — that is what theci_testversion assertion in the actors already checks.Noneuntil the first weight update, which turns the staleness filter off rather than reporting staleness 0.train_async.pypushes weights once before the loop, so a real run has a version before its first drain.Notes
update_weights()returns, i.e. after engines resume, so a brief window under-reports staleness. Smaller than the 1s TTL it replaces.Noneon router flakiness, and no longer needs to special-case the engines' pre-update"default"string.Motivated by #2030, which moves staleness control into the fully-async data buffer and pays this HTTP round trip on every consumed group. Independent of it — this lands on main and #2030 picks it up on the next merge.
Tests
test_stale_group_recyclednow passes the version on the input;test_weight_version_throttles_failed_queriesis replaced bytest_staleness_filter_off_before_the_first_weight_update, covering theNonecase.py_compileandpre-commit run --all-filesonly. Please runtests/fast/rollout/test_fully_async_rollout.pybefore merging.🤖 Generated with Claude Code