fix: keep async rollouts version-consistent

- serialize shared-model optimizer updates with generation
- reject future or over-lagged rollout results after asynchronous scoring
- close cache publication races
- persist policy versions in online checkpoints
This commit is contained in:
0z5a
2026-09-03 12:01:24 +08:00
parent ce2f9d13b3
commit 587b0ee046
16 changed files with 531 additions and 49 deletions
+72
View File
@@ -561,6 +561,78 @@ def test_scheduler_weight_versions_are_monotonic_and_acknowledged(device):
scheduler.stop()
def test_scheduler_applies_weight_mutation_and_version_atomically(device):
scheduler, _tok, model = _make_real_scheduler(device)
before = next(model.parameters()).detach().clone()
def mutate():
with torch.no_grad():
next(model.parameters()).add_(1)
return "updated"
try:
assert scheduler.apply_weight_update(1, mutate) == "updated"
assert scheduler.policy_version == 1
assert not torch.equal(next(model.parameters()), before)
with pytest.raises(ValueError, match="must advance"):
scheduler.apply_weight_update(1, mutate)
def failed_mutation():
raise RuntimeError("optimizer failed")
with pytest.raises(RuntimeError, match="optimizer failed"):
scheduler.apply_weight_update(2, failed_mutation)
assert scheduler.policy_version == 1
finally:
scheduler.stop()
def test_scheduler_serializes_policy_snapshot_and_direct_update(device):
scheduler, _tok, _model = _make_real_scheduler(device)
snapshot_started = threading.Event()
release_snapshot = threading.Event()
update_finished = threading.Event()
errors = []
def inspect(version):
assert version == 0
snapshot_started.set()
assert release_snapshot.wait(timeout=5)
def take_snapshot():
try:
scheduler.with_policy_snapshot(inspect)
except BaseException as exc:
errors.append(exc)
def update():
try:
scheduler.update_weights(1)
update_finished.set()
except BaseException as exc:
errors.append(exc)
snapshot_thread = threading.Thread(target=take_snapshot)
update_thread = threading.Thread(target=update)
try:
snapshot_thread.start()
assert snapshot_started.wait(timeout=5)
update_thread.start()
assert not update_finished.wait(timeout=0.1)
release_snapshot.set()
snapshot_thread.join(timeout=5)
update_thread.join(timeout=5)
assert not snapshot_thread.is_alive()
assert not update_thread.is_alive()
assert errors == []
assert scheduler.policy_version == 1
finally:
release_snapshot.set()
snapshot_thread.join(timeout=5)
update_thread.join(timeout=5)
scheduler.stop()
def test_scheduler_rejects_weight_update_with_queued_tasks(device):
scheduler, _tok, _model = _make_real_scheduler(device)
task_id = scheduler.add_task("queued")