Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression: Items are not being evicted from MemoryCache in .NET 6 #74676

Open
RobSiklos opened this issue Aug 26, 2022 · 4 comments
Open

Regression: Items are not being evicted from MemoryCache in .NET 6 #74676

RobSiklos opened this issue Aug 26, 2022 · 4 comments

Comments

@RobSiklos
Copy link
Contributor

Description

This works in .NET Framework, but in .NET 6, I'm seeing an issue where items added to a MemoryCache are not evicted based on the cache memory limit.

Likely related, no matter how much I add to the cache, MemoryCache.GetLastSize() always returns 0 in .NET 6.

Reproduction Steps

Run the code below, and notice that items are never evicted from the cache, and that the result of GetLastSize() is always 0.

using System.Collections.Specialized;
using System.Globalization;
using System.Runtime.Caching;

private static readonly TimeSpan CachePollingInterval = TimeSpan.FromSeconds(1);
private const long CacheMemoryLimitMegabytes = 1024;

void Main()
{
    NameValueCollection cacheConfig = new NameValueCollection();
    cacheConfig.Add("cacheMemoryLimitMegabytes", CacheMemoryLimitMegabytes.ToString(CultureInfo.InvariantCulture));
    cacheConfig.Add("physicalMemoryLimitPercentage", "0");
    cacheConfig.Add("pollingInterval", CachePollingInterval.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture));

    using (var cache = new MemoryCache("RobTest", cacheConfig))
    {
        Console.WriteLine($"Memory limit: {cache.CacheMemoryLimit} MB");
        Console.WriteLine($"Polling interval: {cache.PollingInterval}");

        int num = 40; // The number of elements we're going to add to the cache.

        for (int i = 0; i < num; i++)
        {
            // Create an array and initialize it with non-default values so that it actually takes up space in memory.
            var arr = new byte[100_000_000];
            for (int j = 0; j < arr.Length; j++) { arr[j] = 0xF; }

            // Add the item to the cache.
            cache.Add(i.ToString(), arr, DateTimeOffset.Now + TimeSpan.FromMinutes(5));

            Console.WriteLine();
            Console.WriteLine($"Elements in the cache: {cache.GetCount()}");
            Console.WriteLine($"Cache size: {cache.GetLastSize()}%");

            Thread.Sleep(1000);
        }
    }
}


Expected behavior

  1. At some point, elements are evicted from the cache, in consideration of the specified polling interval.
  2. At some point, the result of GetLastSize() is non-zero.

Actual behavior

In .NET 6, items are not evicted from the cache, and the result of GetLastSize() is always zero.

Regression?

Yes, this worked as expected in .NET Framework.

Known Workarounds

No response

Configuration

Running .NET 6.0.8 on Windows 10.0.19044.0 (x64)

Other information

No response

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Aug 26, 2022
@ghost
Copy link

ghost commented Aug 26, 2022

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

Issue Details

Description

This works in .NET Framework, but in .NET 6, I'm seeing an issue where items added to a MemoryCache are not evicted based on the cache memory limit.

Likely related, no matter how much I add to the cache, MemoryCache.GetLastSize() always returns 0 in .NET 6.

Reproduction Steps

Run the code below, and notice that items are never evicted from the cache, and that the result of GetLastSize() is always 0.

using System.Collections.Specialized;
using System.Globalization;
using System.Runtime.Caching;

private static readonly TimeSpan CachePollingInterval = TimeSpan.FromSeconds(1);
private const long CacheMemoryLimitMegabytes = 1024;

void Main()
{
    NameValueCollection cacheConfig = new NameValueCollection();
    cacheConfig.Add("cacheMemoryLimitMegabytes", CacheMemoryLimitMegabytes.ToString(CultureInfo.InvariantCulture));
    cacheConfig.Add("physicalMemoryLimitPercentage", "0");
    cacheConfig.Add("pollingInterval", CachePollingInterval.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture));

    using (var cache = new MemoryCache("RobTest", cacheConfig))
    {
        Console.WriteLine($"Memory limit: {cache.CacheMemoryLimit} MB");
        Console.WriteLine($"Polling interval: {cache.PollingInterval}");

        int num = 40; // The number of elements we're going to add to the cache.

        for (int i = 0; i < num; i++)
        {
            // Create an array and initialize it with non-default values so that it actually takes up space in memory.
            var arr = new byte[100_000_000];
            for (int j = 0; j < arr.Length; j++) { arr[j] = 0xF; }

            // Add the item to the cache.
            cache.Add(i.ToString(), arr, DateTimeOffset.Now + TimeSpan.FromMinutes(5));

            Console.WriteLine();
            Console.WriteLine($"Elements in the cache: {cache.GetCount()}");
            Console.WriteLine($"Cache size: {cache.GetLastSize()}%");

            Thread.Sleep(1000);
        }
    }
}


Expected behavior

  1. At some point, elements are evicted from the cache, in consideration of the specified polling interval.
  2. At some point, the result of GetLastSize() is non-zero.

Actual behavior

In .NET 6, items are not evicted from the cache, and the result of GetLastSize() is always zero.

Regression?

Yes, this worked as expected in .NET Framework.

Known Workarounds

No response

Configuration

Running .NET 6.0.8 on Windows 10.0.19044.0 (x64)

Other information

No response

Author: RobSiklos
Assignees: -
Labels:

area-Extensions-Caching

Milestone: -

@StephenMolloy
Copy link
Member

This may tie in with #62516 which has been labeled as a documentation issue. I know there are some features of MemoryCache from Framework that don't work on Core... but I thought MemoryCache.GetLastSize() was supposed to be non-zero here. I think this probably warrants a closer look.

@StephenMolloy StephenMolloy added this to the 8.0.0 milestone Sep 2, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Sep 2, 2022
@KalleOlaviNiemitalo
Copy link

MemoryCache.GetLastSize() calls MemoryCacheStatistics.GetLastSize(), which reads MemoryMonitor.PressureLast, which reads _pressureHist[_i0], to which the data comes from CacheMemoryMonitor.GetCurrentPressure(), which computes the result from SRefMultiple.ApproximateSize, which is hardcoded to return 0

// until then we provide a stub
internal sealed class SRefMultiple
{
internal SRefMultiple()
{
}
#pragma warning disable CA1822
internal long ApproximateSize => 0;
internal void Dispose()
{
}
#pragma warning restore CA1822
}
because System.SizedReference is not available.

@akoeplinger
Copy link
Member

This depends on some solution being implemented for #24200

@akoeplinger akoeplinger removed this from the 8.0.0 milestone Dec 13, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 13, 2023
@akoeplinger akoeplinger added this to the Future milestone Dec 13, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Dec 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants