Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1300,11 +1300,14 @@ public class ConcurrentLruIntegrityChecker<K, V, I, P, T>
where T : struct, ITelemetryPolicy<K, V>
{
private readonly ConcurrentLruCore<K, V, I, P, T> cache;


private readonly ConcurrentDictionary<K, I> dictionary;
private readonly ConcurrentQueue<I> hotQueue;
private readonly ConcurrentQueue<I> warmQueue;
private readonly ConcurrentQueue<I> coldQueue;

private static FieldInfo dictionaryField = typeof(ConcurrentLruCore<K, V, I, P, T>).GetField("dictionary", BindingFlags.NonPublic | BindingFlags.Instance);

private static FieldInfo hotQueueField = typeof(ConcurrentLruCore<K, V, I, P, T>).GetField("hotQueue", BindingFlags.NonPublic | BindingFlags.Instance);
private static FieldInfo warmQueueField = typeof(ConcurrentLruCore<K, V, I, P, T>).GetField("warmQueue", BindingFlags.NonPublic | BindingFlags.Instance);
private static FieldInfo coldQueueField = typeof(ConcurrentLruCore<K, V, I, P, T>).GetField("coldQueue", BindingFlags.NonPublic | BindingFlags.Instance);
Expand All @@ -1314,6 +1317,7 @@ public ConcurrentLruIntegrityChecker(ConcurrentLruCore<K, V, I, P, T> cache)
this.cache = cache;

// get queues via reflection
this.dictionary = (ConcurrentDictionary<K, I>)dictionaryField.GetValue(cache);
this.hotQueue = (ConcurrentQueue<I>)hotQueueField.GetValue(cache);
this.warmQueue = (ConcurrentQueue<I>)warmQueueField.GetValue(cache);
this.coldQueue = (ConcurrentQueue<I>)coldQueueField.GetValue(cache);
Expand Down Expand Up @@ -1344,7 +1348,7 @@ private void ValidateQueue(ConcurrentLruCore<K, V, I, P, T> cache, ConcurrentQue
// It is possible for the queues to contain 2 (or more) instances of the same key/item. One that was removed,
// and one that was added after the other was removed.
// In this case, the dictionary may contain the value only if the queues contain an entry for that key marked as WasRemoved == false.
if (cache.TryGet(item.Key, out var value))
if (dictionary.TryGetValue(item.Key, out var value))
{
hotQueue.Union(warmQueue).Union(coldQueue)
.Any(i => i.Key.Equals(item.Key) && !i.WasRemoved)
Expand All @@ -1353,7 +1357,7 @@ private void ValidateQueue(ConcurrentLruCore<K, V, I, P, T> cache, ConcurrentQue
}
else
{
cache.TryGet(item.Key, out var value).Should().BeTrue($"{queueName} item {item.Key} was not present");
dictionary.TryGetValue(item.Key, out var value).Should().BeTrue($"{queueName} item {item.Key} was not present");
}
}
}
Expand Down
51 changes: 51 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/ConcurrentTLruSoakTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using BitFaster.Caching.Lru;
using Xunit;
using Xunit.Abstractions;

namespace BitFaster.Caching.UnitTests.Lru
{
[Collection("Soak")]
public class ConcurrentTLruSoakTests
{
private readonly ITestOutputHelper testOutputHelper;
private const int hotCap = 33;
private const int warmCap = 33;
private const int coldCap = 33;
private static readonly ICapacityPartition capacity = new EqualCapacityPartition(hotCap + warmCap + coldCap);

private ConcurrentTLru<int, string> lru = new ConcurrentTLru<int, string>(1, capacity, EqualityComparer<int>.Default, TimeSpan.FromMilliseconds(10));

public ConcurrentTLruSoakTests(ITestOutputHelper testOutputHelper)
{
this.testOutputHelper = testOutputHelper;
}

[Theory]
[Repeat(10)]
public async Task WhenSoakConcurrentGetCacheEndsInConsistentState(int iteration)
{
await Threaded.Run(4, () => {
for (int j = 0; j < 100000; j++)
{
for (int i = 0; i < lru.Capacity; i++)
{
lru.GetOrAdd(i + 1, i => i.ToString());
}
}
});

this.testOutputHelper.WriteLine($"iteration{iteration}: {lru.HotCount} {lru.WarmCount} {lru.ColdCount}");
this.testOutputHelper.WriteLine(string.Join(" ", lru.Keys));

RunIntegrityCheck();
}

private void RunIntegrityCheck()
{
new ConcurrentLruIntegrityChecker<int, string, LongTickCountLruItem<int, string>, TLruLongTicksPolicy<int, string>, TelemetryPolicy<int, string>>(this.lru).Validate();
}
}
}