Skip to content

[Docs] Add resilient Ray training example - #10602

Merged
kevinmingtarja merged 4 commits into
masterfrom
docs/ray-resilient-training
Aug 31, 2026
Merged

[Docs] Add resilient Ray training example#10602
kevinmingtarja merged 4 commits into
masterfrom
docs/ray-resilient-training

Conversation

@kevinmingtarja

Copy link
Copy Markdown
Collaborator

Summary

  • Add a resilient Ray training Job Group with a CPU-only head and two GPU workers.
  • Use Dynamic Node Sets for independent head and worker replacement.
  • Persist Ray GCS metadata with the embedded RocksDB backend and checkpoint driver progress on the same volume.
  • Document setup, launch, worker recovery, head recovery, and application checkpointing boundaries.

Test plan

  • bash format.sh --files examples/ray_resilient_training/train.py examples/ray_resilient_training/wait_for_head.py
  • Parsed the Job Group and volume manifests with PyYAML and asserted the three-document structure, matching two-worker counts, both recovery strategies, and ReadWriteOncePod access mode.
  • Compiled both Python entrypoints.
  • Launched the same workload code on Kubernetes with a CPU head and two H100 workers. Deleted one worker pod and observed Ray reconstruct only the lost actor while the healthy actor kept its original incarnation; training resumed at the next step.
  • Confirmed that the running head wrote GCS RocksDB files and driver state to the mounted volume.

The exact checked-in L4 manifest was not launched. Head replacement and restoration from the persisted GCS state were not exercised in the live run.

@kevinmingtarja
kevinmingtarja marked this pull request as ready for review August 30, 2026 03:02

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

Devin Review found 3 potential issues.

Devin Review

Comment on lines +55 to +59
deadline = time.monotonic() + duration_seconds
batches = 0
while time.monotonic() < deadline:
activations = self.torch.tanh(activations @ self.weight)
batches += 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Recovered rollouts change training results

After worker loss, rollout repeats a wall-clock-dependent number of updates despite reusing its seed. Recovery can change the reward and policy version.

Prompt for agents
Make GPUWorker.rollout deterministic across Ray task retries. The current loop in examples/ray_resilient_training/train.py stops according to elapsed wall time, so a reconstructed actor can perform a different number of matrix updates for the same step and seed. Use a deterministic work count derived from configuration, or checkpoint enough rollout state to reproduce the same result. Preserve the example's ability to keep a rollout active long enough to exercise node recovery.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +55 to +60
deadline = time.monotonic() + duration_seconds
batches = 0
while time.monotonic() < deadline:
activations = self.torch.tanh(activations @ self.weight)
batches += 1
self.torch.cuda.synchronize()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 GPU steps exceed configured duration

Because CUDA operations are asynchronous, rollout enqueues work until the deadline and synchronizes afterward. Healthy steps can greatly exceed STEP_SECONDS and hit the recovery timeout.

Prompt for agents
Make STEP_SECONDS bound actual GPU execution time in GPUWorker.rollout. PyTorch CUDA operations are asynchronous, so the current loop measures CPU enqueue time and cuda.synchronize() drains a potentially large backlog only after the deadline. Synchronize within the timing loop, poll completed CUDA events, or otherwise limit outstanding work while retaining continuous GPU utilization.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +54 to +63
export RAY_gcs_storage=rocksdb
export RAY_gcs_storage_path="${RAY_GCS_ROOT}/rocksdb"

ray start \
--head \
--port=6379 \
--dashboard-host=0.0.0.0 \
--num-cpus=0 \
--num-gpus=0 \
--disable-usage-stats

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Head recovery remains unverified

The central recovery path lacks an end-to-end test. It combines volume reattachment, embedded RocksDB restoration, worker reconnection, and detached-actor discovery.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Devin has a point here — as written, the head can come back and silently create new actors instead of reattaching.

Worth confirming that's not what happens in the head-recovery example, or gating the create branch on last_completed_step == -1 so a failed reattach can't pass as success.

@kevinmingtarja

Copy link
Copy Markdown
Collaborator Author

/build-docs

@github-actions

Copy link
Copy Markdown
Contributor

✅ ReadTheDocs build triggered for branch docs/ray-resilient-training

The documentation will be available at: https://docs.skypilot.co/en/docs-ray-resilient-training/

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Devin Review

# Resilient Ray training on SkyPilot

This example keeps a multi-node Ray training job alive through head and worker
failures. Dynamic Node Sets provide fast failover to warm standby capacity:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Unconfigured warm standby claim

warm standby capacity implies preprovisioned spare resources, but the manifest defines only active nodes. Clarify the wording or document external standby requirements.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@ishankaul1 ishankaul1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

Comment on lines +54 to +63
export RAY_gcs_storage=rocksdb
export RAY_gcs_storage_path="${RAY_GCS_ROOT}/rocksdb"

ray start \
--head \
--port=6379 \
--dashboard-host=0.0.0.0 \
--num-cpus=0 \
--num-gpus=0 \
--disable-usage-stats

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Devin has a point here — as written, the head can come back and silently create new actors instead of reattaching.

Worth confirming that's not what happens in the head-recovery example, or gating the create branch on last_completed_step == -1 so a failed reattach can't pass as success.

@kevinmingtarja
kevinmingtarja merged commit 377d1a3 into master Aug 31, 2026
22 checks passed
@kevinmingtarja
kevinmingtarja deleted the docs/ray-resilient-training branch August 31, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants