Skip to content

feat(relay-tunnel): embed the relay receiver as a native library (WIP) - #644

Draft
slinkydeveloper wants to merge 1 commit into
mainfrom
tunnel-client
Draft

feat(relay-tunnel): embed the relay receiver as a native library (WIP)#644
slinkydeveloper wants to merge 1 commit into
mainfrom
tunnel-client

Conversation

@slinkydeveloper

Copy link
Copy Markdown
Contributor

WIP / draft. Java side of the embedded relay-tunnel receiver. Depends on restatedev/sdk-shared-core#94 (consumed as a git dependency on branch relay); swap back to a crates.io release once the tunnel feature ships.

What

Let a Restate Java service register with the relay (M:N HTTP/2 reverse-tunnel broker) with no service-code change and no separate ops artifact, by embedding the relay receiver as native code in the JVM. The Rust engine does all relay-facing work on its own tokio runtime and bridges each forwarded request to the SDK's own HTTP/2 server over a loopback socket; the FFI boundary is control-plane only (start / status / stop).

Rust (sdk-core/src/main/rust — one cdylib, alongside vm_*)

  • Depend on restate-sdk-shared-core at the relay branch with features = ["tunnel"] (the engine — relay stack + loopback — lives in shared-core).
  • src/relay_tunnel.rs: the relay_tunnel_{start,status,stop} C ABI over the engine's relay::{Config, Engine, Handle}. Reuses the existing FFI conventions — ForeignSlice in, a VmNewResult-style tagged-union result, an owned Slice out freed via free_buffer — rather than a bespoke const char*/free_string.

Java

  • dev.restate.sdk.core.RelayTunnel (+ RelayTunnelException): transport-neutral control class in the Java-17 base source set (so sdk-http-vertx can see it); builds the JSON config, parses status, reflectively loads the FFM impl (mirrors StateMachineFactory). No pure-Java fallback — the tunnel requires JDK 23+ (FFM).
  • dev.restate.sdk.core.statemachine.ffm.FfmRelayTunnel (java23 overlay): the FFM downcalls. Loads the native lib but deliberately does not call SharedCoreNative.init(...) — that once-only tracing install is FfmStateMachine's, and a tunnel may start before any state machine exists.
  • dev.restate.sdk.http.vertx.RestateRelayServer.listen(endpoint, config): binds the existing Vert.x h2c server on 127.0.0.1:0 and points the tunnel at it; a shutdown hook drains the tunnel then closes the server. Swap RestateHttpServer.listen(endpoint) for this — dispatch is untouched (the forwarded :path is the SDK-relative tail).

Verification

The cdylib builds, cbindgen emits the relay_tunnel_* symbols + tagged-union, jextract binds them, and :sdk-core:compileJava + :compileJava23Java + :sdk-http-vertx:compileJava all pass. The loopback round-trip is proven in shared-core's tests/relay_loopback.rs.

Not in this PR

Live run against a real relay (the prior-knowledge h2c-into-Vert.x path is used with default HttpServerOptions, as the plan assumed, but not yet exercised against a live relay). Design/rationale in development/relay-receiver-plan.md.

🤖 Generated with Claude Code

Let a Restate Java service register with the relay (M:N HTTP/2 reverse-tunnel
broker) with no service-code change and no separate ops artifact, by embedding
the relay receiver as native code inside the JVM. The Rust engine does all
relay-facing work on its own tokio runtime and bridges each forwarded request
to the SDK's own HTTP/2 server over a loopback socket; the FFI boundary is
control-plane only (start / status / stop).

Rust (sdk-core/src/main/rust — one cdylib, alongside vm_*):
  * Depend on restate-sdk-shared-core at the `relay` branch with the `tunnel`
    feature (the engine — relay stack + loopback — now lives in shared-core).
  * src/relay_tunnel.rs: the relay_tunnel_{start,status,stop} C ABI over the
    engine's relay::{Config, Engine, Handle}. Reuses the existing FFI
    conventions — ForeignSlice in, a VmNewResult-style tagged-union result, an
    owned Slice out freed via free_buffer — rather than a bespoke free_string.

Java:
  * dev.restate.sdk.core.RelayTunnel (+ RelayTunnelException): transport-neutral
    control class in the Java-17 base source set (so sdk-http-vertx can see it);
    builds the JSON config, parses status, and reflectively loads the FFM impl
    (mirrors StateMachineFactory). No pure-Java fallback — the tunnel requires
    JDK 23+ (FFM).
  * dev.restate.sdk.core.statemachine.ffm.FfmRelayTunnel (java23 overlay): the
    FFM downcalls. Loads the native lib but deliberately does NOT call
    SharedCoreNative.init(...) — that once-only tracing install is
    FfmStateMachine's, and a tunnel may start before any state machine exists.
  * dev.restate.sdk.http.vertx.RestateRelayServer.listen(endpoint, config):
    binds the existing Vert.x h2c server on 127.0.0.1:0 and points the tunnel at
    it; a shutdown hook drains the tunnel then closes the server. Swap
    RestateHttpServer.listen(endpoint) for this; dispatch is untouched (the
    forwarded :path is the SDK-relative tail, so request-identity holds).

Verified: the cdylib builds, cbindgen emits the relay_tunnel_* symbols +
tagged-union, jextract binds them, and the base + java23 + sdk-http-vertx
sources compile clean. The loopback round-trip is proven in shared-core's
tests/relay_loopback.rs. Design/rationale in development/relay-receiver-plan.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Test Results

  8 files  ±0    8 suites  ±0   3m 25s ⏱️ +17s
 59 tests ±0   59 ✅ ±0  0 💤 ±0  0 ❌ ±0 
266 runs  ±0  266 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 491f063. ± Comparison against base commit d533e91.

♻️ This comment has been updated with latest results.

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