Skip to content

Add Dask Gateway support to lc run (JupyterHub deployments)#159

Draft
EiffL wants to merge 1 commit into
mainfrom
gateway-cluster-support
Draft

Add Dask Gateway support to lc run (JupyterHub deployments)#159
EiffL wants to merge 1 commit into
mainfrom
gateway-cluster-support

Conversation

@EiffL

@EiffL EiffL commented Jul 12, 2026

Copy link
Copy Markdown
Member

Summary

lc run now works on Dask Gateway deployments (our JupyterHub-on-GKE playground) with zero user configuration: on a hub pod, cd /shared/<project> && lc run requests an adaptive Gateway cluster, runs the pipeline on the spot pool, and shuts the cluster down afterwards. Setting LIGHTCONE_GATEWAY_CLUSTER=<name> instead attaches to an existing cluster (e.g. one created from the JupyterLab Dask sidebar, dashboard panels docked) and leaves it running on exit.

Why the rendezvous changes shape

Gateway scheduler addresses are not dialable: they use a custom gateway:// comm scheme (traefik multiplexing + per-cluster TLS + SNI) that only the dask-gateway client library understands — verified on the staging cluster (unknown address scheme 'gateway' from a bare Client). Since lc run and the executor plugin live in different processes, the fix is the same rendezvous-by-ambient-context pattern the SLURM branch already uses: the parent passes the cluster name through the env, and the executor rejoins by name through the authenticated Gateway API.

Concretely, cluster_for_run now yields the env overlay the child snakemake needs ({"DASK_SCHEDULER_ADDRESS": ...} on existing branches, {"LIGHTCONE_GATEWAY_CLUSTER": name} on the new one) instead of a bare address string — making the actual parent→child contract explicit.

Changes

  • engine/dask_cluster.py — fourth branch (priority: explicit address → gateway → SLURM → local): creates an adaptive cluster (minimum=1, bounded by --jobs) or attaches by name; owned clusters are shut down on exit, attached ones untouched — mirroring the address-branch convention. Fails fast when workers don't advertise the cpus/memory/gpus resource contract (otherwise every task hangs with no error).
  • snakemake_executor_plugin_dask/executor.py — client resolution extracted to _connect_client(): Gateway name → rejoin via Gateway API; else Client(DASK_SCHEDULER_ADDRESS) as before.
  • engine/site_registry.pylightcone-hub site entry detected via env markers (JUPYTERHUB_USER + DASK_GATEWAY__ADDRESS; pods have generated hostnames), checked ahead of hostname patterns in detect_current_site.
  • pyproject.toml — optional extra lightcone-cli[gateway]; mypy override for dask_gateway.
  • Docs — api/user/contributing pages updated; full design rationale + GCP deployment procedure + staging findings in docs/design/jupyterhub-dask-gateway-gcp.md.

Testing

  • 346 passed (11 new: branch routing/precedence, owned-vs-attached lifecycle, missing-dependency and resource-contract failure modes, executor rejoin, site env-marker detection); ruff and strict mypy clean.
  • The deployment-side wiring (worker resource injection, /shared RWX + flock, hub-proxied gateway) was validated end to end on the lightcone-hub GKE staging cluster. End-to-end lc run on the hub additionally needs the worker image (lc + snakemake baked in) — tracked in lightcone-hub.

🤖 Generated with Claude Code

cluster_for_run gains a fourth branch, selected when a Gateway
environment is ambient (DASK_GATEWAY__ADDRESS, as on a lightcone
JupyterHub pod) or a cluster is named via LIGHTCONE_GATEWAY_CLUSTER.
Gateway scheduler addresses use a gateway:// comm scheme that a bare
distributed.Client cannot dial, so the parent/child rendezvous changes
shape: cluster_for_run now yields the env overlay the child snakemake
needs ({DASK_SCHEDULER_ADDRESS: ...} on existing branches,
{LIGHTCONE_GATEWAY_CLUSTER: name} on the new one), and the executor
rejoins the cluster by name through the authenticated Gateway API —
the same rendezvous-by-ambient-context pattern as the SLURM branch.

Ownership mirrors the address branch: clusters we create are adaptive
(minimum=1, bounded by --jobs) and shut down on exit; clusters named
by the user (e.g. created from the JupyterLab Dask sidebar) are left
running with their scaling untouched. Startup fails fast when gateway
workers don't advertise the cpus/memory/gpus resource contract, which
otherwise hangs every task silently.

Also: a lightcone-hub site entry detected via env markers (pods have
generated hostnames, so detect_current_site now checks declared env
markers before hostname patterns), and a [gateway] optional extra for
the dask-gateway client.

Validated against the lightcone-hub GKE staging deployment; design
notes and the deployment procedure live in
docs/design/jupyterhub-dask-gateway-gcp.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 12, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
lightcone-cli da571bc Commit Preview URL

Branch Preview URL
Jul 12 2026, 12:21 PM

@github-actions

Copy link
Copy Markdown

✅ Eval Results

Metric Value
Score 1.00
Build complete
Cost $2.30
Turns 84
Duration 1049s
lightcone-cli 0.3.8.dev3+g924c833cc (924c833c)
Results Download

Graders

✅ spec_valid (1.00)
✅ all_materialized (1.00)

Full output
 pygments-2.20.0 pyparsing-3.3.2 pypdf-6.14.2 pystow-0.8.21 pytest-9.1.1 pytest-logging-2015.11.4 pyyaml-6.0.3 rapidfuzz-3.14.5 rdflib-7.6.0 referencing-0.37.0 requests-2.34.2 rich-15.0.0 rpds-py-2026.6.3 smart-open-7.7.1 smmap-5.0.3 snakemake-9.23.1 snakemake-interface-common-1.23.0 snakemake-interface-executor-plugins-9.4.0 snakemake-interface-logger-plugins-2.1.0 snakemake-interface-report-plugins-1.3.0 snakemake-interface-scheduler-plugins-2.0.2 snakemake-interface-storage-plugins-4.4.1 sortedcontainers-2.4.0 sqlmodel-0.0.37 tabulate-0.10.0 tblib-3.2.2 tenacity-9.1.4 throttler-1.2.3 toolz-1.1.0 tornado-6.5.7 tqdm-4.68.4 traitlets-5.15.1 typing-extensions-4.16.0 typing-inspection-0.4.2 urllib3-2.7.0 wrapt-2.2.2 yte-1.9.4 zict-3.0.0
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
12:21:21 lightcone.eval.sandbox [sandbox build] #13 [10/11] WORKDIR /home/evaluser
12:21:21 lightcone.eval.sandbox [sandbox build] #14 [11/11] RUN mkdir -p ~/.claude && echo '{"hasCompletedOnboarding": true}' > ~/.claude.json
12:21:21 lightcone.eval.sandbox [sandbox build] #15 exporting to image
12:21:45 lightcone.eval.sandbox Created sandbox a3696165-44cd-45aa-847a-d6932dd8635d for trial build-snae-0
12:21:45 httpx HTTP Request: POST https://proxy.app.daytona.io/toolbox/a3696165-44cd-45aa-847a-d6932dd8635d/files/bulk-upload "HTTP/1.1 200 OK"
12:21:46 lightcone.eval.sandbox Installed wheels: ['lightcone_cli-0.3.8.dev3+g924c833cc-py3-none-any.whl']
12:21:46 httpx HTTP Request: POST https://proxy.app.daytona.io/toolbox/a3696165-44cd-45aa-847a-d6932dd8635d/files/bulk-upload "HTTP/1.1 200 OK"
12:21:46 httpx HTTP Request: POST https://proxy.app.daytona.io/toolbox/a3696165-44cd-45aa-847a-d6932dd8635d/files/bulk-upload "HTTP/1.1 200 OK"
12:21:47 httpx HTTP Request: POST https://proxy.app.daytona.io/toolbox/a3696165-44cd-45aa-847a-d6932dd8635d/files/bulk-upload "HTTP/1.1 200 OK"
12:21:47 httpx HTTP Request: POST https://proxy.app.daytona.io/toolbox/a3696165-44cd-45aa-847a-d6932dd8635d/files/bulk-upload "HTTP/1.1 200 OK"
12:39:18 lightcone.eval.sandbox Deleted sandbox for trial build-snae-0
  snae trial 0: score=1.00 complete

lightcone-cli: 0.3.8.dev3+g924c833cc (HEAD 924c833c)
ASTRA: 0.2.10

  Eval Results: Scores  
┏━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Task ┃     Score     ┃
┡━━━━━━╇━━━━━━━━━━━━━━━┩
│ snae │ 1.00 +/- 0.00 │
│      │ pass@k: 100%  │
└──────┴───────────────┘

   Eval Results: Cost &   
         Duration         
┏━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃ Task ┃ Cost / Duration ┃
┡━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ snae │      $2.31      │
│      │      1049s      │
└──────┴─────────────────┘

Total: 1 trials, $2.31, 1049s

Results saved to: eval-results/build-924c833c/results.json

@EiffL EiffL marked this pull request as draft July 12, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant