feat(tunnel): embedded relay-tunnel receiver via PyO3 + Hypercorn loopback (WIP) - #227
Draft
slinkydeveloper wants to merge 1 commit into
Draft
feat(tunnel): embedded relay-tunnel receiver via PyO3 + Hypercorn loopback (WIP)#227slinkydeveloper wants to merge 1 commit into
slinkydeveloper wants to merge 1 commit into
Conversation
…pback
Serve a Restate Python deployment as a relay *receiver* using the embedded
native engine (restate-sdk-shared-core, `tunnel` feature) — the same Rust engine
the Java (FFM) and TypeScript (napi) SDKs drive. The engine dials the relay on
its own tokio runtime and bridges each forwarded request over a loopback socket
into a local HTTP/2 (h2c) server hosting this SDK's ASGI app. The PyO3 boundary
is control-plane only (start / status / stop); no per-request data crosses it,
so dispatch — and therefore both the promise API and the codegen SDK — is
untouched.
Rust (src/lib.rs, one existing cdylib):
* #[pyclass] RelayTunnel — start(config_json) / status() / stop() — over the
engine's relay::{Config, Engine, Handle}; registered in the _internal module.
stop() releases the GIL while the runtime drains (the engine never calls back
into Python). Cargo.toml repoints restate-sdk-shared-core to the `relay`
branch with the `tunnel` feature (also moving the VM core 7.0.1 -> 7.0.2,
same as the Java branch); adds serde_json.
Python (python/restate/tunnel.py, new `tunnel = [hypercorn]` extra):
* serve_tunnel(services, *, relay, identity_keys=None, protocol=None) builds
the ASGI app via restate.app(...), boots Hypercorn (h2c) on 127.0.0.1:0 in a
background thread (mirrors harness.py), waits until it is listening, then
starts RelayTunnel at that port. Returns a TunnelHandle (status/stop, context
manager). Accepts services from both the promise API and the codegen SDK.
Prior-knowledge h2c into Hypercorn is exactly how Restate talks to this SDK in
production, so the engine's loopback dial is low-risk here.
Verified: cargo build + fmt + clippy clean; ruff clean; maturin develop builds;
a lifecycle smoke test boots the tunnel and confirms the loopback Hypercorn
serves the ASGI app (GET /health -> 200) with status running, then stops. The
forwarded round-trip is the same engine proven in shared-core's
tests/relay_loopback.rs.
A follow-up (captured in development/relay-receiver-plan.md) will make the tunnel
drive the ASGI app directly — no Hypercorn, no socket — via pyo3-async-runtimes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
Serve a Restate Python deployment as a relay receiver using the embedded native engine (
restate-sdk-shared-core,tunnelfeature). The engine dials the relay on its own tokio runtime and bridges each forwarded request over a loopback socket into a local HTTP/2 (h2c) server hosting this SDK's ASGI app. The PyO3 boundary is control-plane only (start / status / stop); no per-request data crosses it, so dispatch — and therefore both the promise API and the codegen SDK — is untouched.The SDK is already PyO3/maturin (
restate._internal) and already ships Hypercorn (test/harness extras), so this is a small addition.Rust (
src/lib.rs, the existing cdylib)#[pyclass] RelayTunnel—start(config_json)/status()/stop()— over the engine'srelay::{Config, Engine, Handle}, registered in the_internalmodule.stop()releases the GIL while the runtime drains (the engine never calls back into Python).Cargo.tomlrepointsrestate-sdk-shared-coreto therelaybranch with thetunnelfeature; addsserde_json. Note: this also moves the VM core 7.0.1 → 7.0.2 (same as the Java branch, where the full suite passed in CI).Python (
python/restate/tunnel.py, newtunnel = [hypercorn]extra)serve_tunnel(services, *, relay, identity_keys=None, protocol=None)builds the ASGI app viarestate.app(...), boots Hypercorn (h2c) on127.0.0.1:0in a background thread (mirrorsharness.py), waits until it is listening, then startsRelayTunnelat that port. Returns aTunnelHandle(status()/stop(), context manager). Accepts services from either programming model.Prior-knowledge h2c into Hypercorn is exactly how Restate talks to this SDK in production, so the engine's loopback dial is low-risk here.
Verification
cargo build+fmt+clippyclean;ruffclean;maturin develop --extras tunnelbuilds. A lifecycle smoke test boots the tunnel and confirms the loopback Hypercorn serves the SDK ASGI app (GET /health→200 {"status":"ok"}) with statusrunning, then stops. The forwarded round-trip is the same engine proven in shared-core'stests/relay_loopback.rs. (Not yet run: the full pytest/testcontainers suite on the 7.0.2 bump — worth a CI run.)Follow-up (captured in
development/relay-receiver-plan.md)The native next step: make the tunnel be the ASGI server — drive
app(scope, receive, send)directly, no Hypercorn/socket — viapyo3-async-runtimes, honouring the SDK's full-duplex teardown (server_context.py:616). Higher-risk cross-runtime build, which is why the proven loopback ships first.Deferred
/_/start-tunnelmode (engine is/whoami-only, same as Java/TS).tunnelships.tunnelfeature's tokio/h2/rustls(ring) on the maturin manylinux/musllinux/macos release matrix before publishing.🤖 Generated with Claude Code