Skip to content

Setting SlidingExpiration on a CacheEntry has some seemingly undocumented behavior #7830

Open
@VelvetToroyashi

Description

@VelvetToroyashi

Description

👋🏽 Wanted to preface this by saying: First time opening an issue here, please correct me if I misspeak.

This issue stems from the implementation of the Microsoft.Extensions.Caching.Memory package, and from the looks of it, specifically on CacheEntry. While looking into this issue, I wanted to make sure someone else hadn't already reported it, but the closest I could find regarding this exact issue is a question (posted here).

When setting an absolute expiration, it makes sense that when that expiration passes, the object is removed, and this behavior also extends to sliding expirations, of which presumably get reset when the value is retrieved. This is fine, expected, and documented behavior. The documentation seems to be lacking in the nuance of sliding expirations, though.

If a sliding expiration is set, and an absolute expiration is absent, the sliding expiration then acts as an "absolute" expiration (from the time the object is inserted, it seems?). This is slightly confusing, at least to myself, since if the object is never "accessed", the sliding expiration cannot kick in.

Reproduction Steps

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;

namespace MinimalRepro;

public static class Program 
{
    public static async Task Main()
    {
        var services = new ServiceCollection();
        services.AddMemoryCache();

        var provider = services.BuildServiceProvider();
        var cache = provider.GetRequireService<IMemoryCache>();

        var cacheOptions = new new MemoryCacheEntryOptions()
        cacheOptions.SetSlidingExpiration(TimeSpan.FromSeconds(5));
        
        cache.Set("example_key", "example_value", cacheOptions);

        await Task.Delay(TimeSpan.FromSeconds(5));

        var keyExists = cache.TryGetValue("example_key", out _);

        if (!keyExists)
            Console.WriteLine("The cached value has gone missing!");
    }
}

Expected behavior

The expiration timer is started when the value is initially accessed.

Actual behavior

The object will expire from cache regardless of whether it's been "accessed"; this is likely due to the LastAccessed property of a CacheEntry being a non-nullable DateTime, which appears to default to 1/1/0001 12:00:00 AM.

Regression?

I'm unsure, but I'm doubtful of it.

Known Workarounds

No response

Configuration

.NET 6.0.100

Windows 10, 21H1, 64-bit.

This issue should be platform-agnostic.

Other information

The issue is within this code, and I believe is due to the issue regarding the datetime being initialized with a default value that will almost always be greater than the expiration, or so I'd hope 😄.

While I wouldn't say that this is a bug or incorrect behavior, I think at the very least the documentation (MSDN Link) should be updated to reflect this otherwise undocumented behavior.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions