Microsoft Orleans — Reminders and grains calling grains
Tags: msorleans, dotnetcore, csharp, microsoftorleans
Published: 2018-11-26 18:03:38

---
title: Microsoft Orleans — Reminders and grains calling grains
published: true
tags: msOrleans,dotnet-core,csharp,microsoft orleans
canonical_url: https://medium.com/@kritner/microsoft-orleans-reminders-and-grains-calling-grains-6ad58ad104a2
series: Microsoft Orleans
cover_image: https://cdn-images-1.medium.com/max/1024/0*on51Hi-gzcJVPav3
---

### Microsoft Orleans — Reminders and grains calling grains

cover image by <a href="https://unsplash.com/@linper?utm_source=medium&amp;utm_medium=referral">Linda Perez Johannessen</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption>

Orleans is an actor model framework — a framework used for easily creating distributed systems across a cluster of machines. In this post we’ll explore the “Reminders” feature of Orleans.

First off, the docs:

[Timers and Reminders | Microsoft Orleans Documentation](https://dotnet.github.io/orleans/Documentation/grains/timers_and_reminders.html)

Reminders can be used in Orleans to perform tasks on a “schedule” more or less. I’m having trouble thinking of a simple example that actually makes sense, so, we’re going to go with an everything’s ok alarm:

{% youtube Yj7XA3tiGUg %}

#### To build the everything’s ok alarm, we’ll need to do a few things:

- Enable a reminder service within the `ISiloHostBuilder `— we’ll use the in memory one just for simplicity's sake, and to not have to rely on additional infrastructure
- A new reminder grain
- Something for the reminder grain to do

I figured we could use the `FakeEmailSender` introduced in:

[Microsoft Orleans — Dependency Injection](https://dev.to/kritner/microsoft-orleansdependency-injection-4p41)

in order to send “Fake email” everything’s ok notifications.

### Enable the Reminder service

Starting from [https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.40](https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.40), we’ll enable the in-memory reminder service by adding the following line to our ISiloHostBuilder.

```cs
.UseInMemoryReminderService()
```

The full method is:

```cs
private static async Task<ISiloHost> StartSilo()
{
 // define the cluster configuration
 var builder = new SiloHostBuilder()
  .UseLocalhostClustering()
  .Configure<ClusterOptions>(options =>
  {
   options.ClusterId = "dev";
   options.ServiceId = "HelloWorldApp";
  })
  .Configure<EndpointOptions>(options => options.AdvertisedIPAddress = IPAddress.Loopback)
  .AddMemoryGrainStorage(Constants.OrleansMemoryProvider)
  .ConfigureApplicationParts(parts =>
  {
    parts.AddApplicationPart(typeof(IGrainMarker).Assembly).WithReferences();
  })
  .ConfigureServices(DependencyInjectionHelper.IocContainerRegistration)
  .UseDashboard(options => { })
  .UseInMemoryReminderService()
  .ConfigureLogging(logging => logging.AddConsole());

 var host = builder.Build();
 await host.StartAsync();
 return host;
}
```

Some other possible reminder services to use include `AzureTableReminderService` and `AdoNetReminderService`.

### Reminder Grain

Let’s create our everything’s ok alarm grain! I discovered in writing this, that there is a 1 minute minimum on the amount of time between “reminds”, so unfortunately, we’ll not be going with the originally planned 3 seconds :(

#### Anyway… our grain interface:

```cs
public interface IEverythingIsOkGrain : IGrainWithStringKey, IRemindable
{
 Task Start();
 Task Stop();
}
```

In the above, we’re doing a pretty standard grain interface, with the additional (to be) implemented `IRemindable`. Two methods are attached to the interface, one to start the reminder, one to stop it. Note that the `IRemindable` interface requires the implementing class to implement:

```
Task ReceiveReminder(string reminderName, TickStatus status);
```

#### The Grain Implementation — also Grainception!

As I mentioned previously, we’ll be using the `FakeEmailSender` created from a previous post, as well as having our to be created grain utilize other grains (grainception)!

That could look like:

```cs
[StorageProvider(ProviderName = Constants.OrleansMemoryProvider)]
public class EverythingIsOkGrain : Grain, IEverythingIsOkGrain
{
 IGrainReminder _reminder = null;

 public async Task ReceiveReminder(string reminderName, TickStatus status)
 {
  // Grain-ception!
  var emailSenderGrain = GrainFactory
   .GetGrain<IEmailSenderGrain>(Guid.Empty);

  await emailSenderGrain.SendEmail(
   "homer@anykey.com",
   new[] 
   {
    "marge@anykey.com",
    "bart@anykey.com",
    "lisa@anykey.com",
    "maggie@anykey.com"
   },
   "Everything's ok!",
   "This alarm will sound every 1 minute, as long as everything is ok!"
  );
 }

 public async Task Start()
 {
  if (_reminder != null)
  {
   return;
  }

  _reminder = await RegisterOrUpdateReminder(
   this.GetPrimaryKeyString(),
   TimeSpan.FromSeconds(3),
   TimeSpan.FromMinutes(1) // apparently the minimum
  );
 }

public async Task Stop()
 {
  if (_reminder == null)
  {
   return;
  }

  await UnregisterReminder(_reminder);
  _reminder = null;
 }
}
```

A few things of note from the above:

- `[StorageProvider(ProviderName = Constants.OrleansMemoryProvider)]` — we’re making the grain stateful so (theoretically) the reminder will persist on shutdown. Note, it will not in our case because of using in memory storage, I *think* it would otherwise.
- `IGrainReminder _reminder = null;` — holds reference to our started reminder, used for stopping the reminder.
- `Task ReceiveReminder(string reminderName, TickStatus status)` — this is the method where we actually define what happens when the reminder occurs.
- `var emailSenderGrain = GrainFactory.GetGrain<IEmailSenderGrain>(Guid.Empty);` — here we’re using a slightly different means of retrieving a grain, since we’re actually doing it from the `SiloHost`, rather than `Client`. Note that this grain being pulled also makes use of dependency injection, but its dependency is only injected into the grain that actually needs it, not this reminder grain.

### New grain menu option

as per usual, we’re going to create a new `IOrleansFunction` concretion for use in our menu system; that new grain will also be added to be returned from our `IOrleansFunctionProvider`.

```cs
public class EverythingIsOkReminder : IOrleansFunction
{
 public string Description => "Demonstrates a reminder service, notifying the user that everything is ok... every three seconds...";

 public async Task PerformFunction(IClusterClient clusterClient)
 {
  var grain = clusterClient.GetGrain<IEverythingIsOkGrain>(
   $"{nameof(IEverythingIsOkGrain)}-{Guid.NewGuid()}"
  );

  Console.WriteLine("Starting everything's ok alerm after key press.");
  Console.ReadKey();

  Console.WriteLine("Starting everything's ok reminder...");
  await grain.Start();

  Console.WriteLine("Reminder started. Press any key to stop reminder.");
  Console.ReadKey();

  await grain.Stop();

  ConsoleHelpers.ReturnToMenu();
 }
}
```

### Trying it out

As per the norm, we’ll be starting the `SiloHost`, the `Client`, and trying the new grain out.

![](https://cdn-images-1.medium.com/max/1024/1*ohX0WbFAIMozjHwyBvElHA.png)<figcaption>The everything’s ok reminder</figcaption>

In the above, you can see that our “FakeEmail” went out to the Orleans log, stating that everything’s ok.

One other cool thing we can see due to adding the [Orleans Dashboard](https://medium.com/@kritner/microsoft-orleans-reporting-dashboard-16465d255199) in a previous post is:

![](https://cdn-images-1.medium.com/max/1024/1*jIRcL58X2ZgDvqG2QDgASg.png)<figcaption>Reminder shows up on dashboard</figcaption>

Neat!

In this post we learned a little bit about another Orleans feature — Reminders! You can find the code as of this post at:

[Kritner-Blogs/OrleansGettingStarted](https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.45)

#### Related:

- [Getting Started with Microsoft Orleans](https://medium.com/@kritner/getting-started-with-microsoft-orleans-882cdac4307f?source=friends_link&sk=1fc3451d71a19dcb49f2c8bbeb6b079e)
- [Microsoft Orleans — Reusing Grains and Grain State](https://medium.com/@kritner/microsoft-orleans-reusing-grains-and-grain-state-136977facd42?source=friends_link&sk=f19cfa3f17665c3d700bfe0df56e27a9)
- [Microsoft Orleans — Reporting Dashboard](https://medium.com/@kritner/microsoft-orleans-reporting-dashboard-16465d255199)
- [Using polymorphism to update Orleans Project to be ready for new Orleans Examples!](https://medium.com/@kritner/updating-orleans-project-to-be-more-ready-for-new-orleans-examples-2105b29a46fd?source=friends_link&sk=61d1f591e5b5c498688439db50ad310e)
- [Microsoft Orleans — Dependency Injection](https://medium.com/@kritner/microsoft-orleans-dependency-injection-6379d52a7169?source=friends_link&sk=6c3883a5213d65eb251b56c717e0e4f2)
- Code as of post — [v0.45](https://github.com/Kritner-Blogs/OrleansGettingStarted/releases/tag/v0.45)
- Microsoft Orleans Docs — [Reminders and Timers](https://dotnet.github.io/orleans/Documentation/grains/timers_and_reminders.html)