Unseen
Unseen

Reputation: 79

CancellationTokenSource+CallBackNode Memory Leaks (10 MB/minutes)

I have console application for quartz.net operations.

Its basically settings serilog options, loading IJob implemented business logics dll's, creating token source then starting host environment:


class Program
{
    const string DllFolderName = "SchDlls";

    private static ISchedulerService _schedulerService { get; set; }
    private static CancellationTokenSource _tokenSource { get; set; }

    static async Task Main(string[] args)
    {
        SetSerilogConfiguration();
        Logger.Info("Scheduler Service Started!!", "Program.Main()");

        LoadAllAssemblies();

        _tokenSource = new();


        await Microsoft.Extensions.Hosting.Host
            .CreateDefaultBuilder(args)
            .UseWindowsService(options =>
                options.ServiceName = "Test")
            .ConfigureServices((hostContext, services) =>
        {
            services.AddSingleton<ISchedulerService, QuartzScheduler>();
            services.AddHostedService<QuartzWorker>();
        }).Build().RunAsync(_tokenSource.Token);
    }
}

I am registering the services. Scheduler interface is basically abstracts Init, Start, Stop operations.

public interface ISchedulerService
{
    Task Init(CancellationToken cancellationToken);
    Task Start(CancellationToken cancellationToken);
    Task Stop(CancellationToken cancellationToken);
}

Implementation of ISchedulerService is Quartz:

public sealed class QuartzScheduler : ISchedulerService
{
    IScheduler _scheduler = null;

    private static async Task<IScheduler> GetScheduler(CancellationToken cancellationToken)
        => await GetSchedulerFactory().GetScheduler(cancellationToken);

    private static StdSchedulerFactory GetSchedulerFactory() => new();

    public async Task Init(CancellationToken cancellationToken)
    {
        _scheduler ??= await GetScheduler(cancellationToken).ConfigureAwait(true);
    }

    public async Task Start(CancellationToken cancellationToken)
    {
        if (_scheduler is not null && !_scheduler.IsStarted)
            await _scheduler.Start(cancellationToken).ConfigureAwait(true);            
    }

    public async Task Stop(CancellationToken cancellationToken)
    {
        if (_scheduler is not null && !_scheduler.IsShutdown)
        {
            await _scheduler.Shutdown(true, cancellationToken).ConfigureAwait(true);
            _scheduler = null;
        }
    }
}

And the hosted service worker is doing start stop operations for .net console application.

public class QuartzWorker(ISchedulerService schedulerService) : IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await schedulerService.Init(cancellationToken);
        await schedulerService.Start(cancellationToken);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await schedulerService.Stop(cancellationToken);
    }
}

There is the leak snapshot screenshot i got:

When i run the program for 1-2 hours its raises the 3-4 GB ram usage.

Its horrible for us,

I will take your advices or reasons to that and fixes.

Thanks for answers.

I have tested ASP.NET Core API to look for cancellation token and async operations leak. But ASP.NET Core api works perfect with managemenet.

But console application to use quartz operations not working perfect.

Let me know.

UPDATES:

There is sample of my DLLs doing jobs:

public abstract class JobBase : IJob

I am generating a library derived from IJob

In this way, by adding this library to the scheduler, I will write to the Quartz database and enable the triggers to run.

In this way, the desired things will be triggered at certain times.

In the constructor
I'm just initialising the Logger and Configs. There is no leak there.

I'm using a get response async that gets API results, but since it doesn't use instances, it said to mark static, so I marked static:
protected static async Task<T> GetResponseAsync<T>(string url, object body = null, string parameterName = "", object parameter = null)

This is a message generator for httpClient called in the parent method:
 private static HttpRequestMessage GetRequestMessage(string url, object body, string parameterName, object parameter)

A method that gets the report parameters from the api according to the type of the report and keeps them in the field, also overrideable for additional fields:
protected virtual async Task GetReportParametersAsync(string uri, AutoReportType autoReportType)

I mean, it makes sense and I don't see any leaks, please let me know if there are any.

Another method that extracts lists such as cc bcc and to from email send types:
protected void SetMailListBySendType(List<NotificationEmailSendTypeSurrogate> notificationEmailSendTypes)


static async Task<string> GetJwtTokenAsync()

This is a method that receives jwt tokens for api requests. It gets it once for each request.

Now I think I have explained the base class.

Let's look at other classes derived from it.

public class DailyUpsellingReportJob : JobBase
I defined a class that reports daily sales derive from base.

I override the Execute operation:
public async override Task Execute(IJobExecutionContext context)

So I did how it will work here. I returned the hosts foreachle and got the parameters for each host, got the mail types, calculated the attachments (I returned the report with the creation stream object.)

Example:

 async Task<NotificationAttachment> GetAttachment(string url)
 {
UpsellingReport upsellingReport = null;
Notif = new(){};

Notif.Document = new MemoryStream();
var dataFetched = api.GetData(blabla);

upsellingReport = new()
{
    DataSource = dataFetched 
};

await upsellingReport.CreateDocumentAsync();
upsellingReport.ExportToPdf(notif.Document);
notif.Document.Seek(0, SeekOrigin.Begin);

return notif;

also in the finally block => upsellingReport?.Dispose();

This is how the work is done. There are many jobs like this.

I this memory stream side is leaking memory.

Now where is the part that leaks here? Thank you.

Upvotes: 0

Views: 89

Answers (0)

Related Questions