InvalidOperationException When Calling RegisterOrUpdateReminder
- Dominant language
- C#
- Stars
- 10.9k
- Forks
- 2.1k
- Avg merge
- 14h 42m
- Merged PRs (30d)
- 354
Description
**Version is Orleans 7.2.1 on Windows 10.**
I occasionally (several times per day) get an exception on silo startup related to reminder registration, which causes the silo to crash immediately.
I have the following grain implementation:
```csharp
public class PollingGrain : Grain, IPollingGrain, IRemindable
{
private readonly ILogger logger;
private Guid timerEventRoundtripTicket;
public PollingGrain(ILogger logger)
{
this.logger = logger;
this.RegisterTimer(_ => this.ReceiveTimerEvent(), null!, TimeSpan.Zero, TimeSpan.FromSeconds(15));
}
public async Task InitializeAsync()
{
this.logger.LogInformation("Polling loop for {Service} started at {Timestamp}.", this.GetType().Name, DateTimeOffset.Now);
await this.RegisterOrUpdateReminder("Default", TimeSpan.FromSeconds(7), TimeSpan.FromMinutes(1));
}
public Task ReceiveTimerEventRoundtrip(Guid ticket)
{
if (this.timerEventRoundtripTicket != ticket)
{
return Task.CompletedTask;
}
this.logger.LogInformation("Last polling event for {Service} received at {Timestamp}.", this.GetType().Name, DateTimeOffset.Now);
// currently no logic at all, but later it goes here
return Task.CompletedTask;
}
private Task ReceiveTimerEvent()
{
this.timerEventRoundtripTicket = Guid.NewGuid();
return this.AsReference().ReceiveTimerEventRoundtrip(this.timerEventRoundtripTicket);
}
Task IRemindable.ReceiveReminder(string reminderName, TickStatus status)
{
this.logger.LogInformation("Last polling reminder for {Service} received at {Timestamp}.", this.GetType().Name, DateTimeOffset.Now);
return Task.CompletedTask;
}
}
public interface IPollingGrain : IGrainWithIntegerKey
{
Task InitializeAsync();
Task ReceiveTimerEventRoundtrip(Guid ticket);
}
```
`InitializeAsync()` is called from a grain service:
```csharp
[Reentrant]
public class PollingGrainStartService : GrainService, IPollingGrainStartService
{
private readonly IGrainFactory grainFactory;
public PollingGrainStartService(GrainId grainId, Silo silo, ILoggerFactory loggerFactory, IGrainFactory grainFactory)
: base(grainId, silo, loggerFactory)
{
this.grainFactory = grainFactory;
}
public override async Task Start()
{
await base.Start();
// HINT: tried this but doesn't resolve the issue
//
//await Task.Run(this.GetReadyToRumble);
await this.GetReadyToRumble();
}
private Task GetReadyToRumble() => this.grainFactory.GetGrain(0).InitializeAsync();
}
public interface IPollingGrainStartService : IGrainService
{
}
```
Then once in a while I get this exception, but only during startup:
```text
System.InvalidOperationException
HResult=0x80131509
Message=Attempted to access grain from a non-grain context, such as a background thread, which is invalid. Ensure that you are only accessing grain functionality from within the context of a grain.
Source=Orleans.Reminders
StackTrace:
at Orleans.Runtime.ReminderService.ReminderRegistry.ThrowInvalidContext()
at Orleans.Runtime.ReminderService.ReminderRegistry.EnsureReminderServiceRegisteredAndInGrainContext()
at Orleans.Runtime.ReminderService.ReminderRegistry.RegisterOrUpdateReminder(GrainId callingGrainId, String reminderName, TimeSpan dueTime, TimeSpan period)
at Orleans.GrainReminderExtensions.RegisterOrUpdateReminder(Boolean remindable, IGrainContext grainContext, String reminderName, TimeSpan dueTime, TimeSpan period)
at Orleans.GrainReminderExtensions.RegisterOrUpdateReminder(Grain grain, String reminderName, TimeSpan dueTime, TimeSpan period)
at DomainDrivenDesignWithOrleans.Domain.Grains.PollingGrain.d__3.MoveNext()...
```
The idea behind this implementation is as follows: I use a grain service to tickle the polling grain (a simple grain with PK zero so we only have one in the whole cluster which is by intention). The polling grain itself then registers a reminder in order to resurrect from a crash of the silo that holds the activation information for the polling grain.
I don't get the point why this exception is thrown from time to time during startup.
BTW: I do not use `ConfigureAwait(false)` at all.
So, what is wrong here?
Contributor guide
Assessment
This issue has not been assessed yet.