How-to: Use virtual actors in Dapr

Learn more about the actor pattern

The Dapr actor runtime provides support for virtual actors through following capabilities:

Actor method invocation

You can interact with Dapr to invoke the actor method by calling HTTP/gRPC endpoint.

  1. POST/GET/PUT/DELETE http://localhost:3500/v1.0/actors/<actorType>/<actorId>/method/<method>

You can provide any data for the actor method in the request body and the response for the request is in response body which is data from actor method call.

Refer api spec for more details.

Alternatively, you can use the Dapr SDK in .NET, Java, or Python.

Actor state management

Actors can save state reliably using state management capability. You can interact with Dapr through HTTP/gRPC endpoints for state management.

To use actors, your state store must support multi-item transactions. This means your state store component must implement the TransactionalStore interface. The list of components that support transactions/actors can be found here: supported state stores. Only a single state store component can be used as the statestore for all actors.

Actor timers and reminders

Actors can schedule periodic work on themselves by registering either timers or reminders.

The functionality of timers and reminders is very similar. The main difference is that Dapr actor runtime is not retaining any information about timers after deactivation, while persisting the information about reminders using Dapr actor state provider.

This distinction allows users to trade off between light-weight but stateless timers vs. more resource-demanding but stateful reminders.

The scheduling configuration of timers and reminders is identical, as summarized below:


dueTime is an optional parameter that sets time at which or time interval before the callback is invoked for the first time. If dueTime is omitted, the callback is invoked immediately after timer/reminder registration.

Supported formats:

  • RFC3339 date format, e.g. 2020-10-02T15:00:00Z
  • time.Duration format, e.g. 2h30m
  • ISO 8601 duration format, e.g. PT2H30M

period is an optional parameter that sets time interval between two consecutive callback invocations. When specified in ISO 8601-1 duration format, you can also configure the number of repetition in order to limit the total number of callback invocations. If period is omitted, the callback will be invoked only once.

Supported formats:


ttl is an optional parameter that sets time at which or time interval after which the timer/reminder will be expired and deleted. If ttl is omitted, no restrictions are applied.

Supported formats:

  • RFC3339 date format, e.g. 2020-10-02T15:00:00Z
  • time.Duration format, e.g. 2h30m
  • ISO 8601 duration format. Example: PT2H30M

The actor runtime validates correctness of the scheduling configuration and returns error on invalid input.

When you specify both the number of repetitions in period as well as ttl, the timer/reminder will be stopped when either condition is met.

Actor timers

You can register a callback on actor to be executed based on a timer.

The Dapr actor runtime ensures that the callback methods respect the turn-based concurrency guarantees. This means that no other actor methods or timer/reminder callbacks will be in progress until this callback completes execution.

The Dapr actor runtime saves changes made to the actor’s state when the callback finishes. If an error occurs in saving the state, that actor object is deactivated and a new instance will be activated.

All timers are stopped when the actor is deactivated as part of garbage collection. No timer callbacks are invoked after that. Also, the Dapr actor runtime does not retain any information about the timers that were running before deactivation. It is up to the actor to register any timers that it needs when it is reactivated in the future.

You can create a timer for an actor by calling the HTTP/gRPC request to Dapr as shown below, or via Dapr SDK.

  1. POST/PUT http://localhost:3500/v1.0/actors/<actorType>/<actorId>/timers/<name>

Examples

The timer parameters are specified in the request body.

The following request body configures a timer with a dueTime of 9 seconds and a period of 3 seconds. This means it will first fire after 9 seconds, then every 3 seconds after that.

  1. {
  2. "dueTime":"0h0m9s0ms",
  3. "period":"0h0m3s0ms"
  4. }

The following request body configures a timer with a period of 3 seconds (in ISO 8601 duration format). It also limits the number of invocations to 10. This means it will fire 10 times: first, immediately after registration, then every 3 seconds after that.

  1. {
  2. "period":"R10/PT3S",
  3. }

The following request body configures a timer with a period of 3 seconds (in ISO 8601 duration format) and a ttl of 20 seconds. This means it fires immediately after registration, then every 3 seconds after that for the duration of 20 seconds.

  1. {
  2. "period":"PT3S",
  3. "ttl":"20s"
  4. }

The following request body configures a timer with a dueTime of 10 seconds, a period of 3 seconds, and a ttl of 10 seconds. It also limits the number of invocations to 4. This means it will first fire after 10 seconds, then every 3 seconds after that for the duration of 10 seconds, but no more than 4 times in total.

  1. {
  2. "dueTime":"10s",
  3. "period":"R4/PT3S",
  4. "ttl":"10s"
  5. }

You can remove the actor timer by calling

  1. DELETE http://localhost:3500/v1.0/actors/<actorType>/<actorId>/timers/<name>

Refer api spec for more details.

Actor reminders

Reminders are a mechanism to trigger persistent callbacks on an actor at specified times. Their functionality is similar to timers. But unlike timers, reminders are triggered under all circumstances until the actor explicitly unregisters them or the actor is explicitly deleted or the number in invocations is exhausted. Specifically, reminders are triggered across actor deactivations and failovers because the Dapr actor runtime persists the information about the actors’ reminders using Dapr actor state provider.

You can create a persistent reminder for an actor by calling the HTTP/gRPC request to Dapr as shown below, or via Dapr SDK.

  1. POST/PUT http://localhost:3500/v1.0/actors/<actorType>/<actorId>/reminders/<name>

The request structure for reminders is identical to those of actors. Please refer to the actor timers examples.

Retrieve actor reminder

You can retrieve the actor reminder by calling

  1. GET http://localhost:3500/v1.0/actors/<actorType>/<actorId>/reminders/<name>

Remove the actor reminder

You can remove the actor reminder by calling

  1. DELETE http://localhost:3500/v1.0/actors/<actorType>/<actorId>/reminders/<name>

Refer api spec for more details.

Actor runtime configuration

You can configure the Dapr actor runtime configuration to modify the default runtime behavior.

Configuration parameters

  • entities - The actor types supported by this host.
  • actorIdleTimeout - The timeout before deactivating an idle actor. Checks for timeouts occur every actorScanInterval interval. Default: 60 minutes
  • actorScanInterval - The duration which specifies how often to scan for actors to deactivate idle actors. Actors that have been idle longer than actor_idle_timeout will be deactivated. Default: 30 seconds
  • drainOngoingCallTimeout - The duration when in the process of draining rebalanced actors. This specifies the timeout for the current active actor method to finish. If there is no current actor method call, this is ignored. Default: 60 seconds
  • drainRebalancedActors - If true, Dapr will wait for drainOngoingCallTimeout duration to allow a current actor call to complete before trying to deactivate an actor. Default: true
  • reentrancy (ActorReentrancyConfig) - Configure the reentrancy behavior for an actor. If not provided, reentrancy is disabled. Default: disabled Default: false
  • remindersStoragePartitions - Configure the number of partitions for actor’s reminders. If not provided, all reminders are saved as a single record in actor’s state store. Default: 0
  • entitiesConfig - Configure each actor type individually with an array of configurations. Any entity specified in the individual entity configurations must also be specified in the top level entities field. Default: None

  • Java

  • Dotnet
  • Python
  • Go
  1. // import io.dapr.actors.runtime.ActorRuntime;
  2. // import java.time.Duration;
  3. ActorRuntime.getInstance().getConfig().setActorIdleTimeout(Duration.ofMinutes(60));
  4. ActorRuntime.getInstance().getConfig().setActorScanInterval(Duration.ofSeconds(30));
  5. ActorRuntime.getInstance().getConfig().setDrainOngoingCallTimeout(Duration.ofSeconds(60));
  6. ActorRuntime.getInstance().getConfig().setDrainBalancedActors(true);
  7. ActorRuntime.getInstance().getConfig().setActorReentrancyConfig(false, null);
  8. ActorRuntime.getInstance().getConfig().setRemindersStoragePartitions(7);

See this example

  1. // In Startup.cs
  2. public void ConfigureServices(IServiceCollection services)
  3. {
  4. // Register actor runtime with DI
  5. services.AddActors(options =>
  6. {
  7. // Register actor types and configure actor settings
  8. options.Actors.RegisterActor<MyActor>();
  9. // Configure default settings
  10. options.ActorIdleTimeout = TimeSpan.FromMinutes(60);
  11. options.ActorScanInterval = TimeSpan.FromSeconds(30);
  12. options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(60);
  13. options.DrainRebalancedActors = true;
  14. options.RemindersStoragePartitions = 7;
  15. options.ReentrancyConfig = new() { Enabled = false };
  16. // Add a configuration for a specific actor type.
  17. // This actor type must have a matching value in the base level 'entities' field. If it does not, the configuration will be ignored.
  18. // If there is a matching entity, the values here will be used to overwrite any values specified in the root configuration.
  19. // In this example, `ReentrantActor` has reentrancy enabled; however, 'MyActor' will not have reentrancy enabled.
  20. options.Actors.RegisterActor<ReentrantActor>(typeOptions: new()
  21. {
  22. ReentrancyConfig = new()
  23. {
  24. Enabled = true,
  25. }
  26. });
  27. });
  28. // Register additional services for use with actors
  29. services.AddSingleton<BankService>();
  30. }

See the .NET SDK documentation.

  1. from datetime import timedelta
  2. from dapr.actor.runtime.config import ActorRuntimeConfig, ActorReentrancyConfig
  3. ActorRuntime.set_actor_config(
  4. ActorRuntimeConfig(
  5. actor_idle_timeout=timedelta(hours=1),
  6. actor_scan_interval=timedelta(seconds=30),
  7. drain_ongoing_call_timeout=timedelta(minutes=1),
  8. drain_rebalanced_actors=True,
  9. reentrancy=ActorReentrancyConfig(enabled=False),
  10. remindersStoragePartitions=7
  11. )
  12. )
  1. const (
  2. defaultActorType = "basicType"
  3. reentrantActorType = "reentrantType"
  4. )
  5. type daprConfig struct {
  6. Entities []string `json:"entities,omitempty"`
  7. ActorIdleTimeout string `json:"actorIdleTimeout,omitempty"`
  8. ActorScanInterval string `json:"actorScanInterval,omitempty"`
  9. DrainOngoingCallTimeout string `json:"drainOngoingCallTimeout,omitempty"`
  10. DrainRebalancedActors bool `json:"drainRebalancedActors,omitempty"`
  11. Reentrancy config.ReentrancyConfig `json:"reentrancy,omitempty"`
  12. EntitiesConfig []config.EntityConfig `json:"entitiesConfig,omitempty"`
  13. }
  14. var daprConfigResponse = daprConfig{
  15. Entities: []string{defaultActorType, reentrantActorType},
  16. ActorIdleTimeout: actorIdleTimeout,
  17. ActorScanInterval: actorScanInterval,
  18. DrainOngoingCallTimeout: drainOngoingCallTimeout,
  19. DrainRebalancedActors: drainRebalancedActors,
  20. Reentrancy: config.ReentrancyConfig{Enabled: false},
  21. EntitiesConfig: []config.EntityConfig{
  22. {
  23. // Add a configuration for a specific actor type.
  24. // This actor type must have a matching value in the base level 'entities' field. If it does not, the configuration will be ignored.
  25. // If there is a matching entity, the values here will be used to overwrite any values specified in the root configuration.
  26. // In this example, `reentrantActorType` has reentrancy enabled; however, 'defaultActorType' will not have reentrancy enabled.
  27. Entities: []string{reentrantActorType},
  28. Reentrancy: config.ReentrancyConfig{
  29. Enabled: true,
  30. MaxStackDepth: &maxStackDepth,
  31. },
  32. },
  33. },
  34. }
  35. func configHandler(w http.ResponseWriter, r *http.Request) {
  36. w.Header().Set("Content-Type", "application/json")
  37. w.WriteHeader(http.StatusOK)
  38. json.NewEncoder(w).Encode(daprConfigResponse)
  39. }

Refer to the documentation and examples of the Dapr SDKs for more details.

Partitioning reminders

Actor reminders are persisted and continue to be triggered after sidecar restarts. Applications with multiple reminders registered can experience the following issues:

  • Low throughput on reminders registration and de-registration
  • Limit on the total number of reminders registered based on the single record size limit on the state store

Applications can enable partitioning of actor reminders while data is distributed in multiple keys in the state store.

  1. A metadata record in actors\|\|<actor type>\|\|metadata is used to store persisted configuration for a given actor type.
  2. Multiple records store subsets of the reminders for the same actor type.
KeyValue
actors||<actor type>||metadata{ “id”: <actor metadata identifier>, “actorRemindersMetadata”: { “partitionCount”: <number of partitions for reminders> } }
actors||<actor type>||<actor metadata identifier>||reminders||1[ <reminder 1-1>, <reminder 1-2>, … , <reminder 1-n> ]
actors||<actor type>||<actor metadata identifier>||reminders||2[ <reminder 1-1>, <reminder 1-2>, … , <reminder 1-m> ]

If you need to change the number of partitions, Dapr’s sidecar will automatically redistribute the reminders’s set.

Enabling actor reminders partitioning

Actor runtime configuration for actor reminders partitioning

Similar to other actor configuration elements, the actor runtime provides the appropriate configuration to partition actor reminders via the actor’s endpoint for GET /dapr/config.

  1. // import io.dapr.actors.runtime.ActorRuntime;
  2. // import java.time.Duration;
  3. ActorRuntime.getInstance().getConfig().setActorIdleTimeout(Duration.ofMinutes(60));
  4. ActorRuntime.getInstance().getConfig().setActorScanInterval(Duration.ofSeconds(30));
  5. ActorRuntime.getInstance().getConfig().setRemindersStoragePartitions(7);

For more information, see the Java actors example

  1. // In Startup.cs
  2. public void ConfigureServices(IServiceCollection services)
  3. {
  4. // Register actor runtime with DI
  5. services.AddActors(options =>
  6. {
  7. // Register actor types and configure actor settings
  8. options.Actors.RegisterActor<MyActor>();
  9. // Configure default settings
  10. options.ActorIdleTimeout = TimeSpan.FromMinutes(60);
  11. options.ActorScanInterval = TimeSpan.FromSeconds(30);
  12. options.RemindersStoragePartitions = 7;
  13. });
  14. // Register additional services for use with actors
  15. services.AddSingleton<BankService>();
  16. }

See the .NET SDK documentation for registering actors.

  1. from datetime import timedelta
  2. ActorRuntime.set_actor_config(
  3. ActorRuntimeConfig(
  4. actor_idle_timeout=timedelta(hours=1),
  5. actor_scan_interval=timedelta(seconds=30),
  6. remindersStoragePartitions=7
  7. )
  8. )
  1. type daprConfig struct {
  2. Entities []string `json:"entities,omitempty"`
  3. ActorIdleTimeout string `json:"actorIdleTimeout,omitempty"`
  4. ActorScanInterval string `json:"actorScanInterval,omitempty"`
  5. DrainOngoingCallTimeout string `json:"drainOngoingCallTimeout,omitempty"`
  6. DrainRebalancedActors bool `json:"drainRebalancedActors,omitempty"`
  7. RemindersStoragePartitions int `json:"remindersStoragePartitions,omitempty"`
  8. }
  9. var daprConfigResponse = daprConfig{
  10. []string{defaultActorType},
  11. actorIdleTimeout,
  12. actorScanInterval,
  13. drainOngoingCallTimeout,
  14. drainRebalancedActors,
  15. 7,
  16. }
  17. func configHandler(w http.ResponseWriter, r *http.Request) {
  18. w.Header().Set("Content-Type", "application/json")
  19. w.WriteHeader(http.StatusOK)
  20. json.NewEncoder(w).Encode(daprConfigResponse)
  21. }

The following is an example of a valid configuration for reminder partitioning:

  1. {
  2. "entities": [ "MyActorType", "AnotherActorType" ],
  3. "remindersStoragePartitions": 7
  4. }

Handling configuration changes

To configure actor reminders partitioning, Dapr persists the actor type metadata in the actor’s state store. This allows the configuration changes to be applied globally, not just in a single sidecar instance.

Also, you can only increase the number of partitions, not decrease. This allows Dapr to automatically redistribute the data on a rolling restart where one or more partition configurations might be active.

Demo

Watch this video for a demo of actor reminder partitioning:

Last modified September 6, 2022: fix some typos (#2765) (46748172)