GRPC Actors#1081
Conversation
Signed-off-by: Albert Callarisa <albert@diagrid.io>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1081 +/- ##
==========================================
- Coverage 86.63% 82.97% -3.67%
==========================================
Files 84 150 +66
Lines 4473 15326 +10853
==========================================
+ Hits 3875 12716 +8841
- Misses 598 2610 +2012 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR introduces alpha gRPC-native actor hosting for the Dapr Python SDK via an app-initiated SubscribeActorEventsAlpha1 stream, enabling actor callbacks (invoke/reminder/timer/deactivate) to be delivered over a single outbound gRPC connection without HTTP callback endpoints. It also adds a gRPC-based outbound actor client so actor apps can operate without requiring daprd’s HTTP port.
Changes:
- Added
ActorGrpcHostto host actors over theSubscribeActorEventsAlpha1bidirectional stream with reconnect/dispatch behavior. - Added
DaprActorGrpcClientto perform outbound actor operations over unary gRPC actor RPCs (invoke/state/reminders/timers) and wired runtime registration to accept a pluggable actor client. - Added unit + integration + example coverage for gRPC actor hosting, plus shared gRPC channel creation utilities and documentation updates.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
dapr/actor/__init__.py |
Exposes ActorGrpcHost as a public actor API entrypoint. |
dapr/actor/runtime/_grpc_callbacks.py |
Builds stream registration + callback payloads and handles metadata extraction/heuristics. |
dapr/actor/runtime/config.py |
Adds read-only/snapshot properties for actor runtime/type/reentrancy config objects. |
dapr/actor/runtime/grpc_host.py |
Implements the alpha actor hosting service over the gRPC actor stream. |
dapr/actor/runtime/runtime.py |
Adds optional actor_client parameter to registration to support non-HTTP outbound ops. |
dapr/aio/clients/grpc/client.py |
Refactors async gRPC client to use shared channel creation + endpoint resolution. |
dapr/clients/base.py |
Centralizes DAPR_REENTRANCY_ID_HEADER constant. |
dapr/clients/grpc/_channel.py |
Adds shared resolve_grpc_endpoint() and create_aio_channel() helpers. |
dapr/clients/grpc/dapr_actor_grpc_client.py |
Adds gRPC actor client implementing outbound actor operations over unary RPCs. |
dapr/clients/http/dapr_actor_http_client.py |
Switches to the centralized reentrancy header constant. |
docs/actor/actor.runtime.rst |
Documents the new actor.runtime.grpc_host module in API docs. |
examples/demo_actor/README.md |
Adds “Run with gRPC actor hosting (alpha)” instructions. |
examples/demo_actor/demo_actor/demo_actor_grpc_service.py |
Adds gRPC-stream-hosted demo actor service (alpha). |
tests/actor/test_actor_runtime_config.py |
Adds tests validating new read-only config properties/snapshot behavior. |
tests/actor/test_grpc_callbacks.py |
Adds tests for registration message building + callback payload synthesis + heuristics. |
tests/actor/test_grpc_host.py |
Adds scripted fake-sidecar tests for ActorGrpcHost behavior. |
tests/actor_grpc_utils.py |
Adds helper probe to detect sidecar support for SubscribeActorEventsAlpha1. |
tests/clients/fake_dapr_server.py |
Extends fake daprd to support actor stream + unary actor RPCs for tests. |
tests/clients/test_dapr_actor_grpc_client.py |
Adds unit tests for DaprActorGrpcClient request shaping and behavior. |
tests/examples/test_demo_actor_grpc.py |
Adds example test for the gRPC-stream demo actor scenario. |
tests/integration/apps/actor_grpc_host.py |
Adds integration-test actor host subprocess using ActorGrpcHost. |
tests/integration/test_actor_grpc.py |
Adds end-to-end integration tests for stream-hosted actor callbacks and state. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Albert Callarisa <albert@diagrid.io>
Signed-off-by: Albert Callarisa <albert@diagrid.io>
Signed-off-by: Albert Callarisa <albert@diagrid.io>
Description
Adds alpha support for hosting Dapr actors over the app-initiated
SubscribeActorEventsAlpha1gRPC stream (dapr/dapr#9812). The app dials daprd's gRPC port, registers its actor types and runtime config in the stream's first message, and receives all actor callbacks, method invocations, reminders, timers, deactivations, pushed over that single stream. No inbound HTTP or gRPC port is needed for actor callbacks; outbound actor operations (state, reminders, timers, invocations) also go over daprd's gRPC API, so actor apps are fully gRPC-native.The same actor classes,
ActorRuntime, andActorRuntimeConfigwork unchanged on both transports, only the hosting service differs. HTTP actor hosting (FastAPI/Flask extensions) is untouched.What's included
New public API (alpha)
dapr.actor.ActorGrpcHost(dapr/actor/runtime/grpc_host.py), stream lifecycle: registration handshake (with timeout), concurrent callback dispatch with serialized stream writes and request/response correlation byid, automatic reconnect + re-registration after transient drops (UNAVAILABLE/UNKNOWN/INTERNAL, gated onchannel_ready()), graceful shutdown with in-flight task draining. A stale-session guard ensures replies from handlers that outlive a connection are never sent on its successor.DaprActorGrpcClient(dapr/clients/grpc/dapr_actor_grpc_client.py), implementsDaprActorClientBaseover the unary actor RPCs (InvokeActor,GetActorState,ExecuteActorStateTransaction, reminder/timer registration incl. TTL and failure policies), withDapr-Reentrancy-Idpropagation. Closes the long-standing# TODO: We will allow to use gRPC client laterinruntime.py.Protocol semantics
request_failedwithNOT_FOUND(permanent, non-retryable in daprd); application handler exceptions →invoke_response{error=true}carrying the same JSON payload the HTTP extensions return, so callers observe identical errors on both transports.BytesValue-packedAny; fire bodies are synthesized to match daprd's HTTP delivery byte-for-byte soActorRuntimeneeds no changes. Timers registered via the unary gRPC RPC are base64-wrapped by daprd (json.Marshalof raw bytes); the host transparently recovers the original payload (documented edge case in_grpc_callbacks.py).Dapr-Reentrancy-Idandcontent-typemetadata are propagated in both directions.Supporting changes
ActorRuntime.register_actorgains an additiveactor_clientparameter (defaults to the existing HTTP client).ActorRuntimeConfig/ActorTypeConfig/ActorReentrancyConfiggain read-only properties; the registration message is built from typed config objects rather thanas_dict().create_aio_channel()/resolve_grpc_endpoint()(dapr/clients/grpc/_channel.py), single home for grpc.aio channel setup, now also used byDaprGrpcClientAsync(its inline channel construction is replaced).DAPR_REENTRANCY_ID_HEADERconsolidated intodapr/clients/base.py.Example,
examples/demo_actor/demo_actor/demo_actor_grpc_service.pyhosts the existingDemoActorover the stream; README section added. Note: daprd currently requires a gRPC app channel (--app-protocol grpc --app-portwith a listener), so the example runs an emptygrpc.aio.server()on the app port, no actor traffic flows through it.Issue reference
Close: #1078