Skip to content

feat: signals API and id-backed InvocationHandle#120

Merged
slinkydeveloper merged 1 commit into
mainfrom
signal
Jul 20, 2026
Merged

feat: signals API and id-backed InvocationHandle#120
slinkydeveloper merged 1 commit into
mainfrom
signal

Conversation

@slinkydeveloper

Copy link
Copy Markdown
Contributor

Summary

Adds signals to the Rust SDK — named, one-shot durable promises scoped to an invocation, matching the feature already shipped in the TypeScript, Python, and Go SDKs. Built on the create_signal_handle / sys_complete_signal / sys_attach_invocation syscalls that were already present (unused) in restate-sdk-shared-core 0.10.0, so no dependency bump.

A handler awaits a signal by name; any other handler completes it by targeting the awaiter's invocation id + signal name — i.e. "awakeables addressed by (invocation_id, name)", with no opaque token to exfiltrate.

// await side (any context; a DurableFuture, so it works in select!)
let approved: bool = ctx.signal::<bool>("approved").await?;

// send side (fluent, sync fire-and-forget)
ctx.invocation_handle(id).signal("approved").resolve(true);
ctx.invocation_handle(id).signal("approved").reject(TerminalError::new("nope"));

InvocationHandle redesign (breaking)

To make the signal send-side ergonomic (and generally cleaner), InvocationHandle becomes an id-backed struct with synchronous accessors:

  • invocation_id() -> &str (was async -> Result<String, _>)
  • cancel() — sync fire-and-forget (was async)
  • attach::<T>()new, awaits the invocation's output (sys_attach_invocation)
  • signal(name)new, returns a SignalHandle with resolve/reject

Consequently:

  • send() / send_after() now return an awaitable SendHandle (impl IntoFutureInvocationHandle). The send is issued eagerly, so bare client.foo().send(); still fire-and-forgets. IntoFuture (rather than a bare impl Future) keeps fire-and-forget free of the must-use lint and keeps the eager send/lazy-id split clean.
  • call() still awaits to the response and gains invocation_handle(&self) (takes &self, does not consume the future); it retains a #[deprecated] invocation_id() to soften the break. CallFuture drops its InvocationHandle supertrait.

Test-services / e2e

  • createSignal command added to VirtualObjectCommandInterpreter.
  • resolveSignal / rejectSignal handlers added to TestUtilsService.

Matches the contract implemented in the other SDKs so the shared e2e suite exercises the flows.

Example

New examples/signals.rs (await / resolve / reject).

Verification

  • cargo build --workspace --all-features --all-targets
  • cargo clippy --workspace --all-features --all-targets -- -D warnings
  • unit + integration + doc tests ✅ (the new # Signals doc examples compile)
  • Docker-based e2e (testcontainers) not run locally.

🤖 Generated with Claude Code

Add signals: named, invocation-scoped durable promises. A handler awaits
a signal by name via `ctx.signal::<T>(name)` (a DurableFuture, usable in
select!); another handler completes it via
`ctx.invocation_handle(id).signal(name).resolve(v)` / `.reject(e)`. Built
on the shared-core create_signal_handle/sys_complete_signal syscalls.

Redesign InvocationHandle from a trait into an id-backed struct with a
synchronous invocation_id() and cancel(), plus attach() (sys_attach_invocation)
and signal(). send()/send_after() now return an awaitable SendHandle (the
send is eager, so fire-and-forget `.send();` still works). CallFuture gains
invocation_handle(&self) and keeps a deprecated invocation_id().

Wire signals into the test-services (createSignal command,
resolveSignal/rejectSignal handlers) and add examples/signals.rs.

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

Copy link
Copy Markdown

Test Results

  8 files  ±0    8 suites  ±0   2m 30s ⏱️ -14s
 43 tests ±0   43 ✅ ±0  0 💤 ±0  0 ❌ ±0 
186 runs  ±0  186 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit a35f67d. ± Comparison against base commit 4dd2de4.

@slinkydeveloper
slinkydeveloper merged commit d02fb91 into main Jul 20, 2026
4 checks passed
@slinkydeveloper
slinkydeveloper deleted the signal branch July 20, 2026 16:12
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant