The Rust shim (ffi/ -> librestatemojo.dylib) embeds the Restate Rust SDK: it
owns the HTTP/2 endpoint, the event loop, and the journal. Mojo owns the
business logic, driven by a synchronous loop:
from restate import App
def main() raises:
var stats = Stats(0)
_ = App.run[Stats, __functions_in_module()]("Counter", stats)
That loop is single-threaded, and a handler that calls another handler served
by the same process therefore deadlocks. App.serve is the concurrent form —
the same loop on N threads, via threads-mojo's TypedPool:
from restate import App, Invocation, OpaquePtr
def handle(app: App, inv: Invocation, worker: Int, ctx: OpaquePtr) raises:
app.complete(inv, "ok")
def main() raises:
var app = App("Counter", ["add", "get"], object=True)
_ = app.serve[handle](num_workers=4) # until app.stop()
With two or more workers one thread can wait for a call while another runs the
callee, so self-calls complete. See HandlerFn for the handler's obligations
and App.stop for how a worker parked in next() is released.
Semantics: on retry or resume, Restate re-invokes the handler and replays the
journal — ctx operations (sleep, call, get_state, run_*, ...) return
their journaled results instantly, so handler logic must be deterministic
between them; wrap non-deterministic work in run_enter/run_exit. When
Restate suspends an invocation (e.g. a long sleep), the pending operation
raises a suspension error: abandon the invocation and keep serving — Restate
re-delivers it later.
Payloads are raw bytes on the wire; the String helpers below treat them as
UTF-8 (use the _bytes variants for binary data).