Ching Wang
Ching Wang

Reputation: 49

Only three of these PostEvictionCallbacks are fired under the same conditions

I have encountered a strange thing. I have a web api project of .net 6. When IServiceProvider starts, it will use FileService to read data and store it in the cache, and set IChangeToken. If the source file changes, the contents of the cache will be reset. I have six classes map from different tables of the same sqlite file, and then there are six different cache keys. However, for some reason, only three of them are being triggered. When I set a breakpoint in the controller, the number of cache keys still shows six. I can't find the reason why this problem happens.

Log info:

2023-07-24 16:12:11.8955|INFO|Weather.FileService|============ Service.SetCache
2023-07-24 16:12:12.8782|INFO|Weather.FileService|set cachekey: Rain
2023-07-24 16:12:13.5386|INFO|Weather.FileService|set cachekey: Humi
2023-07-24 16:12:14.0758|INFO|Weather.FileService|set cachekey: Temp
2023-07-24 16:12:15.0917|INFO|Weather.FileService|set cachekey: Wind
2023-07-24 16:12:15.0917|INFO|Weather.FileService|set cachekey: WeatherInfo
2023-07-24 16:12:15.0979|INFO|Weather.FileService|set cachekey: DataSlice
2023-07-24 16:13:06.6945|INFO|Weather.FileService|============= CacheExpireHandler trigger, key: WeatherInfo
2023-07-24 16:13:08.1085|INFO|Weather.FileService|============= CacheExpireHandler trigger, key: DataSlice
2023-07-24 16:13:09.3240|INFO|Weather.FileService|============= CacheExpireHandler trigger, key: Wind
2023-07-24 16:13:09.3240|INFO|Weather.FileService|set cachekey: DataSlice
2023-07-24 16:13:09.3240|INFO|Weather.FileService|set cachekey: WeatherInfo
2023-07-24 16:13:16.5196|INFO|Weather.FileService|set cachekey: Wind

HostedService:

public void SetCache()
{
    _logger.LogInformation("============ Service.SetCache");
    using (var conn = SQLiteDatabase.OpenConnection(DataFilePath))
    {
        _fileService.SetCache("Rain", conn.GetAll<Rain>());
        _fileService.SetCache("Humi", conn.GetAll<Humi>());
        _fileService.SetCache("Temp", conn.GetAll<Temp>());
        _fileService.SetCache("Wind", conn.GetAll<Wind>());
        _fileService.SetCache("WeatherInfo", conn.GetAll<WeatherInfo>());
        _fileService.SetCache("DataSlice", conn.GetAll<DataSlice>());
    }
}

public Task StartAsync(CancellationToken cancellationToken)
{
    try
    {
        SetCache();
    }
    catch (Exception e)
    {
        _logger.LogError(e.ToString());
    }

    return Task.CompletedTask;
}

public Task StopAsync(CancellationToken cancellationToken)
{
    return Task.CompletedTask;
}

FileService:

public void SetCache<T>(string cacheKey, IEnumerable<T> list)
{
    _logger.LogInformation("========= set cachekey: " + cacheKey);
    var fileProvider = new PhysicalFileProvider(Service.DataFolderPath);
    IChangeToken changeToken = fileProvider.Watch(Service.DataFileName);
    MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions()
        .AddExpirationToken(changeToken)
        .RegisterPostEvictionCallback(CacheExpireHandler);

    _cache.Set(cacheKey, list, cacheEntryOptions);

    if (cacheKey == nameof(DataSlice))
    {
        PreviousDataSlice = (IEnumerable<DataSlice>)list;
    }
}

private void CacheExpireHandler(object key, object oldValue, EvictionReason reason, object state)
{
    _logger.LogInformation("============= CacheExpireHandler trigger, key: " + key);
    if (reason == EvictionReason.TokenExpired && key != null)
    {
        using var conn = SQLiteDatabase.OpenConnection(Service.DataFilePath);
        string cacheKey = key.ToString() ?? string.Empty;

        switch (cacheKey)
        {
            case "Rain":
                SetCache(cacheKey, conn.GetAll<Rain>());
            break;

            case "Temp":
                SetCache(cacheKey, conn.GetAll<Temp>());
                break;

            case "Humi":
                SetCache(cacheKey, conn.GetAll<Humi>());
                break;

            case "Wind":
                SetCache(cacheKey, conn.GetAll<Wind>());
                break;

            case "DataSlice":
                SetCache(cacheKey, conn.GetAll<DataSlice>());
                break;

            case "WeatherInfo":
                SetCache(cacheKey, conn.GetAll<WeatherInfo>());
                break;

            default:
                _logger.LogInformation("=========== ?????  key:" + cacheKey);
                break;
        }
    }
    else
    {
        _logger.LogWarning($"key: {key}, oldValue:{oldValue}, reason: {reason} ,state: {state}");
    }
}

Program.cs:

try
{
    var builder = WebApplication.CreateBuilder(args);

    // NLog: Setup NLog for Dependency injection
    builder.Logging.ClearProviders();
    builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information);
    builder.Host.UseNLog();

    // Add services to the container.
    builder.Services.AddMemoryCache();
    builder.Services.AddHostedService<HostedService>();
    builder.Services.AddSingleton<FileService>();     
    builder.Services.AddControllers();

    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    builder.Services.AddControllers().AddNewtonsoftJson();

    var app = builder.Build();    

    // Configure the HTTP request pipeline.
    //if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    app.UseAuthorization();

    app.UseExceptionHandler("/error");

    app.MapControllers();

    NLog.LogManager.Setup().LoadConfigurationFromAppSettings();
    app.UseElmah();

    app.Run();

}
catch (Exception e)
{
    logger.Error(e, "Stopped program because of exception");
    throw;
}
finally
{
    // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
    NLog.LogManager.Shutdown();
}

Upvotes: 0

Views: 96

Answers (0)

Related Questions