Repository navigation
Conversation
Signed-off-by: 0z5a <0z5a@users.noreply.github.com>
Documentation build overview
48 files changed ·
|
There was a problem hiding this comment.
Code Review
This pull request introduces the TransferQueue streaming scope documentation, a local coordinator state contract (GroupLedger and associated dataclasses), and unit tests to validate lease acquisition, preparation, training, and expiration behaviors. The review feedback suggests automatically triggering lease expiration checks during acquire and _check operations to prevent stale states, along with adding a unit test to verify this auto-expiration logic.
| def acquire(self, owner: str, token: str, now: float, ttl: float) -> Lease: | ||
| if self.phase != Phase.READY or not owner or not token or ttl <= 0: | ||
| raise ValueError("group is unavailable or lease arguments are invalid") | ||
| self.generation += 1 | ||
| self.lease = Lease(owner, token, self.generation, now + ttl) | ||
| self.phase = Phase.LEASED | ||
| return self.lease |
There was a problem hiding this comment.
To prevent the ledger from getting stuck in a stale LEASED or PREPARED state when a lease expires, we should automatically trigger self.expire(now) at the beginning of acquire. This ensures that any expired lease is reclaimed immediately when a new worker attempts to acquire the group, without requiring the coordinator to explicitly call expire beforehand.
| def acquire(self, owner: str, token: str, now: float, ttl: float) -> Lease: | |
| if self.phase != Phase.READY or not owner or not token or ttl <= 0: | |
| raise ValueError("group is unavailable or lease arguments are invalid") | |
| self.generation += 1 | |
| self.lease = Lease(owner, token, self.generation, now + ttl) | |
| self.phase = Phase.LEASED | |
| return self.lease | |
| def acquire(self, owner: str, token: str, now: float, ttl: float) -> Lease: | |
| self.expire(now) | |
| if self.phase != Phase.READY or not owner or not token or ttl <= 0: | |
| raise ValueError("group is unavailable or lease arguments are invalid") | |
| self.generation += 1 | |
| self.lease = Lease(owner, token, self.generation, now + ttl) | |
| self.phase = Phase.LEASED | |
| return self.lease |
| def _check(self, lease: Lease, now: float) -> None: | ||
| if self.lease != lease or now >= lease.expires_at: | ||
| raise ValueError("lease is stale or expired") |
There was a problem hiding this comment.
Similarly, calling self.expire(now) at the beginning of _check ensures that the ledger state is synchronized and transitioned to Phase.READY or Phase.UNKNOWN immediately when an expired lease is presented during operations like prepare, start_training, or commit.
| def _check(self, lease: Lease, now: float) -> None: | |
| if self.lease != lease or now >= lease.expires_at: | |
| raise ValueError("lease is stale or expired") | |
| def _check(self, lease: Lease, now: float) -> None: | |
| self.expire(now) | |
| if self.lease != lease or now >= lease.expires_at: | |
| raise ValueError("lease is stale or expired") |
| def test_training_start_is_not_a_commit_boundary(): | ||
| ledger = GroupLedger(_group()) | ||
| lease = ledger.acquire("worker-a", "token-a", now=0, ttl=5) | ||
| ledger.prepare(lease, "batch-1", now=1) | ||
| ledger.start_training(lease, now=2) | ||
| with pytest.raises(ValueError, match="not finished"): | ||
| ledger.commit(lease, "early", now=3) | ||
| ledger.finish_training(lease, now=4) | ||
| ledger.expire(now=5) | ||
| assert ledger.phase == Phase.UNKNOWN |
There was a problem hiding this comment.
Add a unit test to verify that the ledger automatically expires stale leases during acquire and other operations without requiring an explicit expire() call.
| def test_training_start_is_not_a_commit_boundary(): | |
| ledger = GroupLedger(_group()) | |
| lease = ledger.acquire("worker-a", "token-a", now=0, ttl=5) | |
| ledger.prepare(lease, "batch-1", now=1) | |
| ledger.start_training(lease, now=2) | |
| with pytest.raises(ValueError, match="not finished"): | |
| ledger.commit(lease, "early", now=3) | |
| ledger.finish_training(lease, now=4) | |
| ledger.expire(now=5) | |
| assert ledger.phase == Phase.UNKNOWN | |
| def test_training_start_is_not_a_commit_boundary(): | |
| ledger = GroupLedger(_group()) | |
| lease = ledger.acquire("worker-a", "token-a", now=0, ttl=5) | |
| ledger.prepare(lease, "batch-1", now=1) | |
| ledger.start_training(lease, now=2) | |
| with pytest.raises(ValueError, match="not finished"): | |
| ledger.commit(lease, "early", now=3) | |
| ledger.finish_training(lease, now=4) | |
| ledger.expire(now=5) | |
| assert ledger.phase == Phase.UNKNOWN | |
| def test_auto_expiration_on_acquire_and_check(): | |
| ledger = GroupLedger(_group()) | |
| old = ledger.acquire("worker-a", "token-a", now=0, ttl=5) | |
| ledger.prepare(old, "batch-1", now=4) | |
| new = ledger.acquire("worker-b", "token-b", now=5, ttl=10) | |
| assert ledger.phase == Phase.LEASED | |
| assert new.generation == old.generation + 1 |
Signed-off-by: 0z5a <0z5a@users.noreply.github.com>
Signed-off-by: 0z5a <0z5a@users.noreply.github.com>
Signed-off-by: 0z5a <0z5a@users.noreply.github.com>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Retain both TransferQueue and upstream draft-feature collection documentation and argument tests. Keep the direct pytest entrypoint after both test groups so the CI script exercises all configuration validation cases. Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
Stream accepted complete text-GRPO groups through real TransferQueue 0.1.9 CPU SimpleStorage before VIME's existing fixed-round training plan. Unique job/epoch/group partitions prevent initial puts from overwriting other groups. Generation, reward/filter and Sample conversion retain their existing ownership.
Review this feature: incremental diff (20 files, +1,855/-33). Prerequisite: maintenance #460 at
a5b3e0b1. Target:0z5a-branchat3e569314(merged #459); headb6f6c30a. The Files changed tab includes maintenance. History is preserved without force-push. #242/#265/#320 are references; this covers a limited part of #227.The CI follow-up changes two production files (+24/-11). It restores the public four-parameter
generate_rolloutplugin contract; the queue callback uses a separate entry point only when TransferQueue is enabled.The driver seals its batch, pauses admission/engines and confirms idle before training. READY→LEASED→PREPARED→TRAINING→TRAINED→COMMITTED is owned by VIME; native storage reads are not optimizer acknowledgments. A separate heartbeat covers the whole training plan. Owned partitions are deleted before capacity release; the whole serving cohort acknowledges a changed version before the next round. Checkpoints combine native model/optimizer/data state, acknowledged publication and a quiescent queue cursor.
Supported: standard synchronous text GRPO, TP=PP=CP=DP=1, no VPP, checkpointable global dataset and bounded deadlines/capacity. Default off imports no TQ and starts no extra services. Unsupported/debug/partial/critic/multimodal/MoE paths fail early. Sample telemetry, masks, logprobs, IDs and ragged top-p data round-trip unchanged. Restarts require the same job, common cursor/configuration and a larger epoch. Expired uncertain training becomes UNKNOWN and stops; no automatic optimizer replay or cross-crash exactly-once claim. Failed committed deletion retains GC without retraining. Working-set limits include transport copies and decoded samples, not the whole process RSS.
Thor backend checks use existing Python 3.12.3, Ray 2.58.0 and TransferQueue 0.1.9. The same real SDK/controller/storage/client check also passed on A100 (101 rounds × 2 groups), reusing the existing Ray service with voluntary actor exits. Its optimizer/publication events are fixtures. The A100 task environment includes router, TMS, TransferQueue/TensorDict and compiled Transformer Engine; shared Python/CUDA packages and drivers are unchanged. Qwen has not completed a native optimizer update: the initial NCCL failure showed different cuMem settings in the manual external engine and trainer. An aligned configuration is queued for validation under the shared GPU lock. Current Ouro collector/native results do not validate this queue path. The rollout-state field uses its concrete
DataSourcetype.CPU put/get spans are transport measurements, not training throughput. No model speedup is claimed.
The expiry-on-acquire, expiry-on-operation and regression review cases are covered. Remains Draft until model E0–E3, matched timings and CI pass. Raw logs, model files and coordination records stay outside the diff.