---
uid: AK1004
title: Akka.Analyzers Rule AK1004 - "`ScheduleTellOnce()` and `ScheduleTellRepeatedly()` can cause memory leak if not properly canceled"
---

# AK1004 - Warning

Usage of [`ScheduleTellOnce()`](xref:Akka.Actor.ITellScheduler#Akka_Actor_ITellScheduler_ScheduleTellOnce_System_TimeSpan_Akka_Actor_ICanTell_System_Object_Akka_Actor_IActorRef_) and [`ScheduleTellRepeatedly()`](xref:Akka.Actor.ITellScheduler#Akka_Actor_ITellScheduler_ScheduleTellRepeatedly_System_TimeSpan_System_TimeSpan_Akka_Actor_ICanTell_System_Object_Akka_Actor_IActorRef_) inside an Akka actor, especially the variant that does not accept an [`ICancelable`](xref:Akka.Actor.ICancelable) parameter, can cause memory leak and unnecessary CPU usage if they are not canceled properly inside [`PostStop()`](xref:Akka.Actor.ActorBase#Akka_Actor_ActorBase_PostStop).

## Cause

`ScheduleTellOnce()` and `ScheduleTellRepeatedly()` registers a timer directly inside the `HashedWheelTimerScheduler` that will be fired after or each time a time have elapsed, depending on the method used. These timers are disconnected to the actor lifecycle and are not cleaned up nor replaced when the actors are stopped or restarted and will still run in the background even if the actor were stopped.

Memory leak and increased CPU usage will develop if these methods were called multiple times by the same actor code, especially on actors with short lifespan that are restarted repeatedly.

For example, this code will start a new timer every time the actor is started. If it were short lived and restarted repeatedly, all of the started timers will accumulate in memory and build up over time.

```csharp
using Akka.Actor;
using System.Threading.Tasks;
using System;

public sealed class MyActor : ReceiveActor
{
    private sealed class TimerMessage
    {
        public static readonly TimerMessage Instance = new();
        private TimerMessage() { }
    }
    
    public MyActor()
    {
        Context.System.Scheduler.ScheduleTellRepeatedly(
            initialDelay: TimeSpan.FromSeconds(3), 
            interval: TimeSpan.FromSeconds(3), 
            receiver: Self, 
            message: TimerMessage.Instance, 
            sender: Self);
    }
}
```

## Resolution

Try to convert your actor to implement `IWithTimers` instead. The timers started using `IWithTimers.Timers` methods are tied to the actor life cycle and are automatically removed inside the default `AroundPostStop()` and `AroundPreRestart()` actor lifecycle method.

Here's an example below:

```csharp
using Akka.Actor;
using System.Threading.Tasks;
using System;

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()
    {
        Timers.StartPeriodicTimer(
            key: TimerKey.Instance, 
            msg: TimerMessage.Instance, 
            initialDelay: TimeSpan.FromSeconds(3), 
            interval: TimeSpan.FromSeconds(3));
    }

    public ITimerScheduler Timers { get; set; } = null!;
}
```

If you have to use any of the `ITellScheduler` methods, make sure that you always use the methods that accepts `ICancelable` as a parameter and always call the `ICancelable.Cancel()` method on actor `PostStop()`.

```csharp
using Akka.Actor;
using System.Threading.Tasks;
using System;

public sealed class MyActor : ReceiveActor
{
    private sealed class TimerMessage
    {
        public static readonly TimerMessage Instance = new();
        private TimerMessage() { }
    }
    
    private readonly Cancelable _timerCancelable;
    
    public MyActor()
    {
        _timerCancelable = new Cancelable(Context.System.Scheduler);
        
        Context.System.Scheduler.ScheduleTellRepeatedly(
            initialDelay: TimeSpan.FromSeconds(3), 
            interval: TimeSpan.FromSeconds(3), 
            receiver: Self, 
            message: TimerMessage.Instance, 
            sender: Self
            cancelable: _timerCancelable);
    }
    
    protected override void PostStop()
    {
        _timerCancelable.Cancel();
    }
}
```
