How to: Enable partitioning of actor reminders

Enable actor reminders partitioning for your application

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
  • Limited number of reminders registered based on the single record size limit on the state store

To sidestep these issues, 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 the 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’ set.

Configure the actor runtime to partition actor reminders

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. Select your preferred language for an actor runtime configuration 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 on registring actors.

  1. import { CommunicationProtocolEnum, DaprClient, DaprServer } from "@dapr/dapr";
  2. // Configure the actor runtime with the DaprClientOptions.
  3. const clientOptions = {
  4. actor: {
  5. remindersStoragePartitions: 0,
  6. },
  7. };
  8. const actor = builder.build(new ActorId("my-actor"));
  9. // Register a reminder, it has a default callback: `receiveReminder`
  10. await actor.registerActorReminder(
  11. "reminder-id", // Unique name of the reminder.
  12. Temporal.Duration.from({ seconds: 2 }), // DueTime
  13. Temporal.Duration.from({ seconds: 1 }), // Period
  14. Temporal.Duration.from({ seconds: 1 }), // TTL
  15. 100, // State to be sent to reminder callback.
  16. );
  17. // Delete the reminder
  18. await actor.unregisterActorReminder("reminder-id");

See the documentation on writing actors with the JavaScript SDK.

  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. )

See the documentation on running actors with the Python SDK

  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);

See the documentation on writing actors with the Java SDK.

  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. }

See an example for using actors with the Go SDK.

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

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

Handle 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.

In addition, 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:

Next steps

Interact with virtual actors >>

Last modified October 12, 2023: Update config.toml (#3826) (0ffc2e7)