Skip to content

Latest commit

 

History

History
102 lines (80 loc) · 2.98 KB

AK1007.md

File metadata and controls

102 lines (80 loc) · 2.98 KB
uid title
AK1007
Akka.Analyzers Rule AK1007 - "`Timers.StartSingleTimer()` and `Timers.StartPeriodicTimer()` must not be used inside AroundPreRestart() or PreRestart()"

AK1007 - Error

Creating timer registration using Timers.StartSingleTimer() or `` in Timers.StartPeriodicTimer() will not be honored because they will be cleared immediately. Move timer creation to `PostRestart()` instead.

Cause

When implementing the IWithTimers interface in your actor, a TimerScheduler instance will be injected into your actor and you can access it through the public ITimerScheduler Timers { get; set; } property. This TimerScheduler manages all the timers by tracking them using a dictionary when its StartSingleTimer() and StartPeriodicTimer() methods were invoked, and automatically removing them when the actor is being restarted or stopped.

Any calls to Timers.StartSingleTimer() or Timers.StartPeriodicTimer() inside the actor lifecycle methods AroundPreRestart() or the PreRestart() will not work because the TimerScheduler state will be cleared immediately after.

Example:

using System;
using Akka.Actor;

public sealed class MyActor : ReceiveActor, IWithTimers
{
    private sealed class TimerKey
    {
        public static readonly TimerKey Instance = new();
        private TimerKey() { }
    }
    
    private sealed class TimerMessage
    {
        public static readonly TimerMessage Instance = new();
        private TimerMessage() { }
    }

    public MyActor()
    {
        Receive<TimerMessage>(_ =>
        {
            // Timer callback code will never be executed
        });
    }
    
    public ITimerScheduler Timers { get; set; }
    
    protected override void PreRestart(Exception reason, object message)
    {
        base.PreRestart(reason, message);
        
        // This timer will never be fired
        Timers.StartSingleTimer(
            key: TimerKey.Instance, 
            msg: TimerMessage.Instance, 
            timeout: TimeSpan.FromSeconds(3));
    }
}

Resolution

Move the timer registration to the actor PostRestart() lifecycle method instead:

using System;
using Akka.Actor;

public sealed class MyActor : ReceiveActor, IWithTimers
{
    private sealed class TimerKey
    {
        public static readonly TimerKey Instance = new();
        private TimerKey() { }
    }
    
    private sealed class TimerMessage
    {
        public static readonly TimerMessage Instance = new();
        private TimerMessage() { }
    }

    public MyActor()
    {
        Receive<TimerMessage>(_ =>
        {
            // Timer callback code
        });
    }
    
    public ITimerScheduler Timers { get; set; }
    
    protected override void PostRestart(Exception reason)
    {
        base.PostRestart(reason);
        
        // Timer registration moved to here
        Timers.StartSingleTimer(
            key: TimerKey.Instance, 
            msg: TimerMessage.Instance, 
            timeout: TimeSpan.FromSeconds(3));
    }
}