MemoryCache evict now correctly handles the ArrayPool

This commit is contained in:
Carl Johnsen
2025-02-27 10:54:47 +01:00
parent 7109155127
commit bdfdc7e92a
@@ -111,6 +111,7 @@ namespace Duplicati.Library.Main.Operation.Restore
private long m_returncount = 0;
private long m_autoevictcount = 0;
private MemoryCacheEntryOptions m_entry_options = new();
private ConcurrentDictionary<long, long> m_active_readers = [];
/// <summary>
/// Initializes a new instance of the <see cref="SleepableDictionary"/> class.
@@ -124,18 +125,21 @@ namespace Duplicati.Library.Main.Operation.Restore
m_volume_request = volume_request;
if (m_options.RestoreCacheMax > 0)
{
var cache_options = new MemoryCacheOptions();
m_block_cache = new MemoryCache(cache_options);
m_entry_options.RegisterPostEvictionCallback((key, value, reason, state) =>
while (true)
{
m_block_cache.Remove(key);
lock (m_blockcount_lock)
{
m_returncount++;
if (m_active_readers.TryGetValue((long)key, out var ac) && ac == 0)
{
m_block_cache.Remove(key);
m_returncount++;
break;
}
}
ArrayPool<byte>.Shared.Return((byte[])value);
});
}
await Task.Delay(10).ConfigureAwait(false);
}
ArrayPool<byte>.Shared.Return((byte[])value);
});
this.readers = readers;
sw_cacheevict = options.InternalProfiling ? new() : null;
sw_checkcounts = options.InternalProfiling ? new() : null;
@@ -170,6 +174,8 @@ namespace Duplicati.Library.Main.Operation.Restore
{
sw_checkcounts?.Start();
m_active_readers[blockRequest.BlockID] = m_active_readers.TryGetValue(blockRequest.BlockID, out var ac) ? ac - 1 : 0;
var block_count = m_blockcount.TryGetValue(blockRequest.BlockID, out var c) ? c - 1 : 0;
if (block_count > 0)
@@ -240,6 +246,11 @@ namespace Duplicati.Library.Main.Operation.Restore
/// <returns>A `Task` holding the data block.</returns>
public Task<byte[]> Get(BlockRequest block_request)
{
lock (m_blockcount_lock)
{
m_active_readers[block_request.BlockID] = m_active_readers.TryGetValue(block_request.BlockID, out var c) ? c + 1 : 1;
}
// Check if the block is already in the cache, and return it if it is.
if (m_block_cache != null && m_block_cache.TryGetValue(block_request.BlockID, out byte[] value))
{
@@ -281,11 +292,10 @@ namespace Duplicati.Library.Main.Operation.Restore
}
sw_cacheevict?.Start();
// TODO cache is broken due to the arraypool
//if (m_block_cache?.Count > m_options.RestoreCacheMax)
//{
// m_block_cache?.Compact(m_options.RestoreCacheEvict);
//}
if (m_block_cache?.Count > m_options.RestoreCacheMax)
{
m_block_cache?.Compact(m_options.RestoreCacheEvict);
}
sw_cacheevict?.Stop();
}
@@ -332,7 +342,7 @@ namespace Duplicati.Library.Main.Operation.Restore
Logging.Log.WriteProfilingMessage(LOGTAG, "InternalTimings", $"Sleepable dictionary - CheckCounts: {sw_checkcounts.ElapsedMilliseconds}ms, Get wait: {sw_get_wait.ElapsedMilliseconds}ms, Cache evict: {sw_cacheevict.ElapsedMilliseconds}ms, returned blocks: {m_returncount}, total blocks: {m_blockcount_total}, auto evicted: {m_autoevictcount}");
}
if (m_block_cache.Count > 0)
if (m_block_cache?.Count > 0)
{
Logging.Log.WriteWarningMessage(LOGTAG, "BlockCacheError", null, $"Internal Block cache is not empty: {m_block_cache.Count}");
}