dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.16k stars 4.71k forks source link

adding a GetExpirationTime(Time-to-Live) method to the IMemoryCache #108626

Open MDshirzad opened 2 weeks ago

MDshirzad commented 2 weeks ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

I would like to suggest adding a GetExpirationTime(Time-to-Live) method to the IMemoryCache interface in Microsoft.Extensions.Caching.Memory. This method would allow developers to retrieve the remaining time before a cached item expires, which is currently not possible using the existing API.

At present, the IMemoryCache API allows us to add, retrieve, and remove items from the cache, but it doesn't expose any direct mechanism to retrieve the remaining expiration time (TTL) for a cached item. This feature is often useful in scenarios where cache invalidation and monitoring is important.

Developers currently have no way to determine how long a cache entry will last before it expires. This can be particularly limiting when we want to implement cache-aware features such as refreshing an entry before it expires or logging cache expiration diagnostics or if you want to update a value without changing the time.

Describe the solution you'd like

public interface IMemoryCache
{
    // Existing methods...

    TimeSpan? GetExpirationTime(string key);
}

I used redis for this option because the ttl was crucial for me and this inteface does not provide it,but the thing is that there are some points that I would rather to use IMemoryCache instead of redis:

Additional context

This will be the usage:

public class MyService
{
    private readonly IMemoryCache _memoryCache;

    public MyService(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public void UpdateCachedItem(string key, int valueChange, bool addFlag)
    {
        var currentTTL = _memoryCache.GetExpirationTime(key);

        if (currentTTL.HasValue && _memoryCache.TryGetValue(key, out int cachedValue))
        {
            if (addFlag)
            {
                cachedValue += valueChange;
                Console.WriteLine($"Adding {valueChange} to the cached value. New value: {cachedValue}");
            }
            else
            {
                cachedValue -= valueChange;
                Console.WriteLine($"Subtracting {valueChange} from the cached value. New value: {cachedValue}");
            }

            var cacheOptions = new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = currentTTL.Value
            };

            _memoryCache.Set(key, cachedValue, cacheOptions);
            Console.WriteLine($"Cache item '{key}' updated with the same TTL of {currentTTL.Value.TotalMinutes} minutes.");
        }
        else
        {
            Console.WriteLine($"Cache item '{key}' does not exist or has no expiration time.");
        }
    }
}
dotnet-policy-service[bot] commented 2 weeks ago

Tagging subscribers to this area: @dotnet/area-extensions-caching See info in area-owners.md if you want to be subscribed.

MDshirzad commented 1 week ago

@mgravell @sebastienros @dotnet/area-extensions-caching

KalleOlaviNiemitalo commented 1 week ago

Adding a GetExpirationTime method to interface IMemoryCache would be a breaking change. Other new features like https://github.com/dotnet/runtime/issues/45593 were added only to class MemoryCache and not to the interface.

If the method were added, then the parameter should be object key, like in the existing bool TryGetValue(object key, out object? value).

MemoryCache supports "linked entries" that can expire together with other entries, before their own expiration times. I suppose GetExpirationTime would ignore that feature.

There are plans to replace IMemoryCache and MemoryCache, in https://github.com/dotnet/runtime/issues/48567. That may make the team less willing to add features to MemoryCache. However, the planned replacement RCache\<TKey, TValue> in https://github.com/dotnet/extensions/issues/4766#issuecomment-2341808266 doesn't have a GetExpirationTime method either.

mgravell commented 1 week ago

What @KalleOlaviNiemitalo said :)

MDshirzad commented 1 week ago

Adding a GetExpirationTime method to interface IMemoryCache would be a breaking change. Other new features like #45593 were added only to class MemoryCache and not to the interface.

If the method were added, then the parameter should be object key, like in the existing bool TryGetValue(object key, out object? value).

MemoryCache supports "linked entries" that can expire together with other entries, before their own expiration times. I suppose GetExpirationTime would ignore that feature.

There are plans to replace IMemoryCache and MemoryCache, in #48567. That may make the team less willing to add features to MemoryCache. However, the planned replacement RCache<TKey, TValue> in dotnet/extensions#4766 (comment) doesn't have a GetExpirationTime method either.

So what you suggest for the scenario that you wanna update an object without changing ttl in .net?

KalleOlaviNiemitalo commented 1 week ago

You can store a mutable object, and update its fields while it's referenced by the cache.

If the actual values you want to store are instances of a value type, then I don't think this indirection will even cost any extra CPU time or memory: the values would have to be boxed anyway, because MemoryCache is not generic and stores values as object references.