feat(worker): sync app source per-file from Hypha instead of Ray GCS#148
Merged
Conversation
Replace the ephemeral Ray-GCS app-source package with durable per-file Hypha
downloads keyed on each file's remote last_modified. This fixes three problems,
all hit in production on 2026-07-22:
- Head-restart outage: a Ray head restart wiped the in-memory GCS, dangling the
cached built_app.app_source_uri package ("package prematurely deleted from
GCS"); redeploys wedged. Source now lives on durable Hypha.
- Head-node OOM: a package accumulated per app*version in the head's GCS.
- Redeploy stale-code footgun: a redeploy reusing a cached/stale package served
old code.
Hypha is already a hard dependency (an app can't register its service without
it), so pulling source from Hypha at replica start adds no new availability
surface — on failure the deployment fails and auto_redeploy retries.
- replica_init: per-file incremental sync (_list_source_files, _download_file,
_sync_source_from_hypha) with a .source_snapshot.json; download changed/new,
delete removed, reconcile the whole tree. The sys.meta_path finder is now the
sole source path (app source is no longer shipped as a Ray py_module).
- bootstrap/builder: drop _package_source_to_ray_gcs and all app_source_uri
plumbing; 30-day read token so replicas can pull for their lifetime (public
artifacts stay anonymous). The bioengine framework package stays on Ray
py_modules (it is the bootstrap that runs the sync).
- Keep the head-restart in-place recovery (Serve-client reset on reconnect,
controller-loss redeploy, is_ready backstop, exc_info diagnostics) and the
stop_app pop-before-serve.delete race fix.
Validated on a persistent-Ray dev image (external-cluster + single-machine):
monolithic-entry and composed-app boot with the finder as sole path, redeploy
code cutover, and in-place head-restart recovery with zero "package prematurely
deleted from GCS". 83 unit tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nilsmechtel
marked this pull request as ready for review
July 22, 2026 12:24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Move BioEngine app-source transport off the ephemeral Ray GCS onto durable Hypha per-file download with remote-timestamp incremental sync. Replicas now materialise
<app_dir>/source/by syncing files directly from the Hypha artifact (keyed on each file'slast_modified), instead of pulling agcs://_ray_pkg_<hash>.zippackage the introspect task uploaded.Why
The in-memory Ray GCS intermediary was the root of three problems, all hit in production on 2026-07-22:
built_app.app_source_uripackage dangles (package prematurely deleted from GCS) and redeploys wedge (both prod apps were down until a manual pod kill).Hypha is already a hard dependency (a BioEngine app can't register its service without it), so pulling source from Hypha at replica start adds no new availability surface — if Hypha is unreachable the deployment fails and
auto_redeployretries.What changed
bioengine/_app/replica_init.py— per-file incremental sync:_list_source_files(recursive/fileslisting withlast_modified),_download_file(streamed per-file GET),_sync_source_from_hypha(snapshot diff via.source_snapshot.json→ download changed/new, delete removed, reconcile the whole tree so migrated app_dirs shed GCS-era orphans). Thesys.meta_pathfinder is now the sole source path — the app source is no longer shipped as a Ray py_module.bioengine/_app/bootstrap.py/bioengine/apps/builder.py— deleted_package_source_to_ray_gcs; introspect returns{spec}only; removed allapp_source_uri/BIOENGINE_APP_SOURCE_URIplumbing. Read token TTL → 30 days (replicas pull for their lifetime; public artifacts stay anonymous). The bioengine framework package stays on Ray py_modules — it's the bootstrap that runs the sync (chicken-egg), is re-uploaded on every reconnect, and is content-hash-dedup'd (not the OOM driver).bioengine/apps/manager.py/cluster/ray_cluster.py/worker/worker.py— kept the head-restart in-place recovery this uncovered: Serve-client reset on reconnect, controller-loss detect+redeploy with cooldown,is_readyliveness backstop,exc_infodiagnostics, and thestop_apppop-before-serve.deleterace fix. (An earlier rebuild-on-redeploy approach was reverted — durable Hypha source makes the cachedbuilt_appre-submittable, so it's unnecessary.)Validation
83 unit tests pass. End-to-end on a persistent standalone Ray head (dev image
0.11.31-dev6), both external-cluster and single-machine modes:import bioengine), py_modules channel removed_ray_pkgon headKnown limitation / follow-ups
upload_appre-uploads every file, so all get freshlast_modifiedon a commit → a version bump re-downloads all files. Same-version re-syncs (replica restart / autoscale / head-restart) are incremental (0 downloads). Full "big weights not re-fetched on a small change" needs an upload-side change (skip unchanged files) — tracked as a follow-up. Moot for model-runner (weights come from HF, not the artifact).Rollout
Draft until model-runner is validated on a dev image (worker change; even-clam drives the redeploy-cutover check on an isolated dev worker).
pyproject.toml/_version.pyversion bump lands as the final commit beforegh pr ready.🤖 Generated with Claude Code