~/codewithstu

Timers & Reminders backed by DynamoDB in Microsoft Orleans

Transcript

Hi, my name is Stu. If you're new here, this channel is all about me breaking down different technologies into easily digestible chunks. In this video we're going to take a look at implementing timers and reminders in Microsoft Orleans, building on the previous videos in this series.

As we build out our applications, we often have a requirement to build something that will be invoked either on a schedule or after a specific amount of time. Microsoft Orleans has two options for this that have some subtle differences: timers and reminders.

So what are timers? Timers are used to create a periodic invocation within the context of a single grain activation. This means that every time a grain is deactivated or when the host silo crashes, you will need to set up that timer again. Each grain can set up zero or more timers which are then run in the instance of the grain.

To implement the timers, we're going to be using our HelloWorld grain. The first thing that we're going to need to do is override the OnActivateAsync method. In here we can call a method called RegisterTimer that takes a few arguments. The first one is a function that takes a state and returns a Task, and we're just simply going to make this write to the console. Next is the state property that we want to be passed into the method, and for now this is going to be null for us. Next is the TimeSpan that we want to use as the initial delay property. So for this example I'm going to use a five second initial delay before the first invocation. And then after that we get to specify another TimeSpan, which I'm going to have as a one second interval, meaning that our function that we just declared occurs every second.

Should you need to cancel this timer at any point in time, you can call the Dispose method. But for now I'm just going to assign this to a private variable. All being well, we should be able to hit F5 on our project. As you can see, there was an initial five second delay and now we have "Hello World" printing every second.

There are two important considerations that I think should be called out here. The first is that the length of time the invocation takes directly affects the frequency at which the timer callback is invoked. For example, if this took three seconds then the next invocation will be delayed by three seconds. This is because Microsoft Orleans does not allow overlapping calls and timers. The second consideration is that whilst no overlapping timers are allowed, the callbacks are allowed to be processed alongside other method invocations on the grain. So you will need to consider safe access patterns for your state if you attempt to modify any state in these methods.

So now we can go and take a look at reminders. Reminders can be thought of as persistent timers which are resilient to almost all failure scenarios, including partial or full cluster restarts. One of the first things that we're going to need to do is add a new NuGet package so that we can persist our reminders inside of DynamoDB. To do this, I am going to browse for Orleans Reminders and see the results. We're going to select the Microsoft.Orleans.

Reminders.DynamoDB and install this package. The next thing that we're going to do is register the new reminder. In order to do this, we are going to make the OnActivateAsync method asynchronous. This is because some of the reminder methods are asynchronous in nature.

Next we need to call RegisterOrUpdateReminder, passing in a unique name for our reminder which we're going to call "GrainReminder". Much like timers, we can pass in TimeSpans that represent the initial delay between the first invocation and the period of time that we want to have as an interval. So I'm just going to copy that from the above quickly. It's important to know that we can't have a reminder triggering more frequently than a one minute period. And we need to await this call as well because it's an asynchronous method.

As you can see, we haven't provided any sort of callback function here. This is because we need to implement a new interface on the class called IRemindable. This gives us the ReceiveReminder method and passes in the reminder that is being triggered at the current time and a few details about that reminder. For the purposes of this demo, I'm just going to write a quick if "GrainReminder" equals the reminder name being passed in, then we can write "Hello Reminder". Finally I'm just going to return a completed task here.

The last bit that we need to do in order to get our reminders to work is to go into the Program.

cs and register this in the silo host. To do this, I'm going to call UseDynamoDBReminderService on the builder and configure the options like I have done previously. The only option that I'm interested in at the minute is the service URL, which tells us where to look for the DynamoDB service, and that is on localhost port 4566.

All being well, I should now be able to hit F5 on the application and see our reminder trigger after five seconds. Don't worry, I'm not going to make you wait a full minute before the next invocation.

One thing that I would like to call out is that if the cluster is offline for whatever reason, then you may miss specific invocations of a reminder. This is because Microsoft Orleans does not have the ability to process invocations that may have been missed. The Orleans documentation for reminders states that reminders should not be used for high frequency timers and that the period should be measured in minutes, hours, or days. Reminders are better suited for infrequent tasks that need to survive after a grain is deactivated.

That's it for our quick overview of timers and reminders.

If you enjoyed this video, consider subscribing to the YouTube channel for more content like this.

// share_this