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
138 changes: 138 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,113 @@ public void WhenItemIsRemovedRemovedEventIsFired()
removedItems[0].Reason.Should().Be(ItemRemovedReason.Removed);
}

[Fact]
public void WhenItemRemovedFromHotDuringWarmupItIsEagerlyCycledOut()
{
lru.GetOrAdd(1, valueFactory.Create);

lru.TryRemove(1);
Print(); // Hot [1] Warm [] Cold []

lru.GetOrAdd(1, valueFactory.Create);
lru.GetOrAdd(2, valueFactory.Create);
lru.GetOrAdd(3, valueFactory.Create);
Print(); // Hot [1,2,3] Warm [] Cold []

lru.WarmCount.Should().Be(0);
lru.ColdCount.Should().Be(0);
}

[Fact]
public void WhenItemRemovedFromHotAfterWarmupItIsEagerlyCycledOut()
{
for (int i = 0; i < lru.Capacity; i++)
{
lru.GetOrAdd(i, valueFactory.Create);
}

Print(); // Hot [6,7,8] Warm [1,2,3] Cold [0,4,5]
lru.Metrics.Value.Evicted.Should().Be(0);

lru.GetOrAdd(-1, valueFactory.Create);

lru.TryRemove(-1);
Print(); // Hot[7, 8, -1] Warm[1, 2, 3] Cold[4, 5, 6]

// fully cycle hot, which is 3 items
lru.GetOrAdd(-2, valueFactory.Create);
lru.GetOrAdd(-3, valueFactory.Create);
lru.GetOrAdd(-4, valueFactory.Create);

Print(); // Hot [-2,-3,-4] Warm [1,2,3] Cold [6,7,8]

// without eager eviction as -1 is purged from hot, a 4th item will pushed out since hot queue is full
lru.Metrics.Value.Evicted.Should().Be(3);
}

[Fact]
public void WhenItemRemovedFromWarmDuringWarmupItIsEagerlyCycledOut()
{
lru.GetOrAdd(1, valueFactory.Create);
lru.GetOrAdd(2, valueFactory.Create);
lru.GetOrAdd(3, valueFactory.Create);
lru.GetOrAdd(4, valueFactory.Create);
Print(); // Hot [2,3,4] Warm [1] Cold []

lru.TryRemove(1);

lru.GetOrAdd(5, valueFactory.Create);
lru.GetOrAdd(6, valueFactory.Create);
lru.GetOrAdd(7, valueFactory.Create);
Print(); // Hot [5,6,7] Warm [2,3,4] Cold []

lru.WarmCount.Should().Be(3);
lru.ColdCount.Should().Be(0);
}


[Fact]
public void WhenItemRemovedFromWarmAfterWarmupItIsEagerlyCycledOut()
{
for (int i = 0; i < lru.Capacity; i++)
{
lru.GetOrAdd(i, valueFactory.Create);
}

Print(); // Hot [6,7,8] Warm [1,2,3] Cold [0,4,5]
lru.Metrics.Value.Evicted.Should().Be(0);

lru.TryRemove(1);

lru.GetOrAdd(6, valueFactory.Create); // 6 -> W
lru.GetOrAdd(9, valueFactory.Create);

Print(); // Hot [7,8,9] Warm [2,3,6] Cold [0,4,5]

lru.Metrics.Value.Evicted.Should().Be(0);
}

[Fact]
public void WhenItemRemovedFromColdAfterWarmupItIsEagerlyCycledOut()
{
for (int i = 0; i < lru.Capacity; i++)
{
lru.GetOrAdd(i, valueFactory.Create);
}

Print(); // Hot [6,7,8] Warm [1,2,3] Cold [0,4,5]
lru.Metrics.Value.Evicted.Should().Be(0);

lru.GetOrAdd(0, valueFactory.Create);
lru.TryRemove(0);

lru.GetOrAdd(9, valueFactory.Create);

Print(); // Hot [7,8,9] Warm [1,2,3] Cold [4,5,6]

lru.Metrics.Value.Evicted.Should().Be(0);
}

[Fact]
public void WhenKeyDoesNotExistTryRemoveReturnsFalse()
{
Expand All @@ -686,6 +793,29 @@ public void WhenKeyDoesNotExistTryRemoveReturnsFalse()
lru.TryRemove(2).Should().BeFalse();
}

[Fact]
public void WhenItemsAreRemovedTrimRemovesDeletedItemsFromQueues()
{
for (int i = 0; i < lru.Capacity; i++)
{
lru.GetOrAdd(i, valueFactory.Create);
}

Print(); // Hot [6,7,8] Warm [1,2,3] Cold [0,4,5]

lru.TryRemove(0);
lru.TryRemove(1);
lru.TryRemove(6);

lru.Policy.Eviction.Value.Trim(1);

Print(); // Hot [7,8] Warm [2,3] Cold [5]

lru.HotCount.Should().Be(2);
lru.WarmCount.Should().Be(2);
lru.ColdCount.Should().Be(1);
}

[Fact]
public void WhenRepeatedlyAddingAndRemovingSameValueLruRemainsInConsistentState()
{
Expand Down Expand Up @@ -1305,6 +1435,14 @@ private void Warmup()
lru.GetOrAdd(-8, valueFactory.Create);
lru.GetOrAdd(-9, valueFactory.Create);
}


private void Print()
{
#if DEBUG
this.testOutputHelper.WriteLine(this.lru.FormatLruString());
#endif
}
}

public class ConcurrentLruIntegrityChecker<K, V, I, P, T>
Expand Down
37 changes: 36 additions & 1 deletion BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
using Xunit;
using System.Runtime.InteropServices;
using BitFaster.Caching.UnitTests.Retry;
using Xunit.Abstractions;

namespace BitFaster.Caching.UnitTests.Lru
{
public class ConcurrentTLruTests
{
private readonly ITestOutputHelper testOutputHelper;
private readonly TimeSpan timeToLive = TimeSpan.FromMilliseconds(10);
private readonly ICapacityPartition capacity = new EqualCapacityPartition(9);
private ConcurrentTLru<int, string> lru;
Expand All @@ -31,8 +33,9 @@ public ConcurrentTLru<K, V> CreateTLru<K, V>(ICapacityPartition capacity, TimeSp
return new ConcurrentTLru<K, V>(1, capacity, EqualityComparer<K>.Default, timeToLive);
}

public ConcurrentTLruTests()
public ConcurrentTLruTests(ITestOutputHelper testOutputHelper)
{
this.testOutputHelper = testOutputHelper;
lru = CreateTLru<int, string>(capacity, timeToLive);
}

Expand Down Expand Up @@ -315,6 +318,31 @@ public void WhenItemsAreExpiredEnumerateFiltersExpiredItems()
);
}

[Fact]
public void WhenItemsAreRemovedTrimExpiredRemovesDeletedItemsFromQueues()
{
lru = CreateTLru<int, string>(capacity, TimeSpan.FromMinutes(1));

for (int i = 0; i < lru.Capacity; i++)
{
lru.GetOrAdd(i, valueFactory.Create);
}

Print(); // Hot [6,7,8] Warm [1,2,3] Cold [0,4,5]

lru.TryRemove(0);
lru.TryRemove(1);
lru.TryRemove(6);

lru.Policy.ExpireAfterWrite.Value.TrimExpired();

Print(); // Hot [7,8] Warm [2,3] Cold [4,5]

lru.HotCount.Should().Be(2);
lru.WarmCount.Should().Be(2);
lru.ColdCount.Should().Be(2);
}

[Fact]
public void ConstructWithDefaultCtorReturnsCapacity()
{
Expand All @@ -338,5 +366,12 @@ public void ConstructPartitionCtorReturnsCapacity()

x.Capacity.Should().Be(3);
}

private void Print()
{
#if DEBUG
this.testOutputHelper.WriteLine(this.lru.FormatLruString());
#endif
}
}
}
52 changes: 32 additions & 20 deletions BitFaster.Caching.UnitTests/Lru/DiscretePolicyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,48 +114,60 @@ public void CanDiscardIsTrue()
this.policy.CanDiscard().Should().BeTrue();
}


[Theory]
[InlineData(false, true, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Cold)]
public void RouteHot(bool wasAccessed, bool isExpired, ItemDestination expectedDestination)
[InlineData(false, false, true, ItemDestination.Remove)]
[InlineData(true, false, true, ItemDestination.Remove)]
[InlineData(true, false, false, ItemDestination.Warm)]
[InlineData(false, false, false, ItemDestination.Cold)]
[InlineData(false, true, true, ItemDestination.Remove)]
[InlineData(true, true, true, ItemDestination.Remove)]
[InlineData(true, true, false, ItemDestination.Remove)]
[InlineData(false, true, false, ItemDestination.Remove)]
public void RouteHot(bool wasAccessed, bool wasRemoved, bool isExpired, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed, isExpired);
var item = CreateItem(wasAccessed, wasRemoved, isExpired);

this.policy.RouteHot(item).Should().Be(expectedDestination);
}

[Theory]
[InlineData(false, true, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Cold)]
public void RouteWarm(bool wasAccessed, bool isExpired, ItemDestination expectedDestination)
[InlineData(false, false, true, ItemDestination.Remove)]
[InlineData(true, false, true, ItemDestination.Remove)]
[InlineData(true, false, false, ItemDestination.Warm)]
[InlineData(false, false, false, ItemDestination.Cold)]
[InlineData(false, true, true, ItemDestination.Remove)]
[InlineData(true, true, true, ItemDestination.Remove)]
[InlineData(true, true, false, ItemDestination.Remove)]
[InlineData(false, true, false, ItemDestination.Remove)]
public void RouteWarm(bool wasAccessed, bool wasRemoved, bool isExpired, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed, isExpired);
var item = CreateItem(wasAccessed, wasRemoved, isExpired);

this.policy.RouteWarm(item).Should().Be(expectedDestination);
}

[Theory]
[InlineData(false, true, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Remove)]
public void RouteCold(bool wasAccessed, bool isExpired, ItemDestination expectedDestination)
[InlineData(false, false, true, ItemDestination.Remove)]
[InlineData(true, false, true, ItemDestination.Remove)]
[InlineData(true, false, false, ItemDestination.Warm)]
[InlineData(false, false, false, ItemDestination.Remove)]
[InlineData(false, true, true, ItemDestination.Remove)]
[InlineData(true, true, true, ItemDestination.Remove)]
[InlineData(true, true, false, ItemDestination.Remove)]
[InlineData(false, true, false, ItemDestination.Remove)]
public void RouteCold(bool wasAccessed, bool wasRemoved, bool isExpired, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed, isExpired);
var item = CreateItem(wasAccessed, wasRemoved, isExpired);

this.policy.RouteCold(item).Should().Be(expectedDestination);
}

private LongTickCountLruItem<int, int> CreateItem(bool wasAccessed, bool isExpired)
private LongTickCountLruItem<int, int> CreateItem(bool wasAccessed, bool wasRemoved, bool isExpired)
{
var item = this.policy.CreateItem(1, 2);

item.WasAccessed = wasAccessed;
item.WasRemoved = wasRemoved;

if (isExpired)
{
Expand Down
33 changes: 20 additions & 13 deletions BitFaster.Caching.UnitTests/Lru/LruPolicyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,40 +69,47 @@ public void CanDiscardIsFalse()
}

[Theory]
[InlineData(true, ItemDestination.Warm)]
[InlineData(false, ItemDestination.Cold)]
public void RouteHot(bool wasAccessed, ItemDestination expectedDestination)
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Cold)]
[InlineData(false, true, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
public void RouteHot(bool wasAccessed, bool wasRemoved, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed);
var item = CreateItem(wasAccessed, wasRemoved);

this.policy.RouteHot(item).Should().Be(expectedDestination);
}

[Theory]
[InlineData(true, ItemDestination.Warm)]
[InlineData(false, ItemDestination.Cold)]
public void RouteWarm(bool wasAccessed, ItemDestination expectedDestination)
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Cold)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(false, true, ItemDestination.Remove)]
public void RouteWarm(bool wasAccessed, bool wasRemoved, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed);
var item = CreateItem(wasAccessed, wasRemoved);

this.policy.RouteWarm(item).Should().Be(expectedDestination);
}

[Theory]
[InlineData(true, ItemDestination.Warm)]
[InlineData(false, ItemDestination.Remove)]
public void RouteCold(bool wasAccessed, ItemDestination expectedDestination)
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(false, true, ItemDestination.Remove)]
public void RouteCold(bool wasAccessed, bool wasRemoved, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed);
var item = CreateItem(wasAccessed, wasRemoved);

this.policy.RouteCold(item).Should().Be(expectedDestination);
}

private LruItem<int, int> CreateItem(bool wasAccessed)
private LruItem<int, int> CreateItem(bool wasAccessed, bool wasRemoved)
{
var item = this.policy.CreateItem(1, 2);

item.WasAccessed = wasAccessed;
item.WasRemoved = wasRemoved;

return item;
}
Expand Down
Loading
Loading