Akka.Persistence.Reminders 0.7.0-beta1

This is a prerelease version of Akka.Persistence.Reminders.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Akka.Persistence.Reminders --version 0.7.0-beta1                
NuGet\Install-Package Akka.Persistence.Reminders -Version 0.7.0-beta1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Akka.Persistence.Reminders" Version="0.7.0-beta1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Akka.Persistence.Reminders --version 0.7.0-beta1                
#r "nuget: Akka.Persistence.Reminders, 0.7.0-beta1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Akka.Persistence.Reminders as a Cake Addin
#addin nuget:?package=Akka.Persistence.Reminders&version=0.7.0-beta1&prerelease

// Install Akka.Persistence.Reminders as a Cake Tool
#tool nuget:?package=Akka.Persistence.Reminders&version=0.7.0-beta1&prerelease                

Akka.Persistence.Reminders

An Akka.NET scheduler designed to work with long running tasks. When compared to standard ActorSystem scheduler, there are two major differences:

  • Standard scheduler state is stored in memory. That means, it's going to be lost after ActorSystem or machine restart. Reminder state is backed by an Akka.Persistence eventsourcing engine, which means that it's able to survive between restarts, making it good choice for tasks designed to be fired hours, days or many weeks in the future.
  • Unlike standard scheduler, reminder is not designed to work with sub-second latencies. If this is your case, don't use reminders.

Basic example

var config = ConfigurationFactory.Load().WithFallback(Reminder.DefaultConfig);
using (var system = ActorSystem.Create("system", config))
{
	// create a reminder
	var reminder = system.ActorOf(Reminder.Props(), "reminder");
	
	var taskId = Guid.NewGuid().ToString();

	// setup a message to be send to a recipient in the future
	var task = new Reminder.Schedule(taskId, recipient.Path, "message", DateTime.UtcNow.AddDays(1));
	// setup a message to be send in hour intervals
	var task = new Reminder.ScheduleRepeatedly(taskId, recipient.Path, "message", DateTime.UtcNow.AddDays(1), TimeSpan.FromHours(1));
	// setup a message to be send at 10:15am on the third Friday of every month
	var task = new Reminder.ScheduleCron(taskId, recipient.Path, "message", DateTime.UtcNow.AddDays(1), "15 10 ? * 6#3");
	reminder.Tell(task);
	
	// get scheduled entries
	var state = await reminder.Ask<Reminder.State>(Reminder.GetState.Instance);

	// cancel previously scheduled entity - if ack was defined it will be returned to sender after completion
	reminder.Tell(new Reminder.Cancel(taskId))
}

You can also define a reply message, that will be send back once a scheduled task has been persisted.

var task = new Reminder.Schedule(Guid.NewGuid().ToString(), recipient.Path, "message", DateTime.UtcNow.AddDays(1), ack: "reply");
var ack = await reminder.Ask<string>(task); // ack should be "reply"

Cron Expressions

You can setup schedule to repeat itself accordingly to following cron expressions format:

                                       Allowed values    Allowed special characters 

 ┌───────────── minute                0-59              * , - /                      
 │ ┌───────────── hour                0-23              * , - /                     
 │ │ ┌───────────── day of month      1-31              * , - / L W ?               
 │ │ │ ┌───────────── month           1-12 or JAN-DEC   * , - /                     
 │ │ │ │ ┌───────────── day of week   0-6  or SUN-SAT   * , - / # L ?               
 │ │ │ │ │
 * * * * *

Other characteristics (supported by a Cronos library used by this plugin):

  • Supports non-standard characters like L, W, # and their combinations.
  • Supports reversed ranges, like 23-01 (equivalent to 23,00,01) or DEC-FEB (equivalent to DEC,JAN,FEB).
  • Supports time zones, and performs all the date/time conversions for you.
  • Does not skip occurrences, when the clock jumps forward to Daylight saving time (known as Summer time).
  • Does not skip interval-based occurrences, when the clock jumps backward from Summer time.
  • Does not retry non-interval based occurrences, when the clock jumps backward from Summer time.

Using reminder in cluster

Since a reminder is a persistent actor, it's crucial, that only one instance of it may be active in the entire cluster at the same time. In order to avoid risk of having multiple instances or rellying on a single preconfigured machine, you may combine reminder together with Akka.NET cluster singleton from Akka.Cluster.Tools package.

system.ActorOf(ClusterSingletonManager.Props(
    singletonProps: Reminder.Props(),
    terminationMessage: PoisonPill.Instance,
	// use role to limit reminder actor placement only to some subset of nodes
    settings: ClusterSingletonManagerSettings.Create(system).WithRole("reminder")), 
    name: "reminder");

Using reminders together with Akka.Cluster.Sharding

Since sharded actors don't have a single ActorPath as they can move between different nodes of the cluster, it's not reliable to set that actor's path as a reminder recipient. In this case a recipient should be the shard region living on a current cluster node, while the message itself should be a shard envelope having an information necessary to route the payload to target sharded actor.

var region = ClusterSharding.Get(system).Start(
    typeName: "my-actor",
    entityProps: Props.Create<MyActor>(),
    settings: ClusterShardingSettings.Create(system),
    messageExtractor: new MessageExtractor());

var envelope = new Envelope(shardId: 1, entityId: 1, message: "hello");
var task = new Reminder.Schedule(taskId, region.Path, envelope, DateTime.UtcNow.AddDays(1));
reminder.Tell(task);

Configuration

akka.persistence.reminder {

	# Persistent identifier for event stream produced by correlated reminder.
	persistence-id = "reminder"

	# Identifier of a event journal used by correlated reminder.
	journal-plugin-id = ""

	# Identifer of a snapshot store used by correlated reminder.
	snapshot-plugin-id = ""

	# Unlike standard akka.net scheduler, reminders work in much lower frequency.
	# Reason for this is that they are designed for a long running tasks (think of
	# seconds, minutes, hours, days or weeks).
	tick-inverval = 10s

	# Reminder uses standard akka.persistence eventsourcing for maintaining scheduler
	# internal state. In order to make a stream of events shorter (and ultimately 
	# allowing for a faster recovery in case of failure), a reminder state snapshot is
	# performed after a series of consecutive events have been stored.
	snapshot-interval = 100

        # Reminder periodically saves its state in Snapshot store, so old EventJournal entries can be removed from the database.
        # Default: true. (Default is set to true to match Reminder's behavior when this settings was not configurable)
        cleanup-old-messages = true
    
        # Current state of Reminder is stored in the most recent snapshot, so older SnapshotStore entries can be removed from the database.
        # Before enabling snapshot cleanup make sure the SnapshotStore table doesn't contain large number of entries for the given PersistenceId.
        # Deletion of large snapshots may take long time, so it may lead to timeout exception when deleting multiple snapshots. 
        # Default: false. (Default is set to false to match Reminder's behavior when this settings was not configurable)
        cleanup-old-snapshots = false
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.7.0 5,300 5/22/2023
0.7.0-beta1 180 5/15/2023
0.6.0 183 5/15/2023
0.5.0 286 5/4/2023
0.4.0 341 3/15/2023
0.3.0 4,488 3/14/2020
0.2.0 1,131 2/6/2019
0.1.0 870 11/28/2018
0.0.1 1,138 11/27/2017

Removed Fake from the project.