From ca0ecc94f4d49bbceb0365aa79f804706cbcde19 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Wed, 20 Jul 2022 13:58:25 -0700 Subject: [PATCH 1/3] partition --- .../Lru/CapacityPartitionExtensionsTests.cs | 54 +++++++++++++++++++ .../Lru/ConcurrentLruTests.cs | 9 ++++ .../Lru/TestCapacityPartition.cs | 18 +++++++ .../Lru/Builder/LruBuilderBase.cs | 12 +++++ BitFaster.Caching/Lru/Builder/LruInfo.cs | 2 +- .../Lru/CapacityPartitionExtensions.cs | 30 +++++++++++ .../Lru/TemplateConcurrentLru.cs | 1 + 7 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 BitFaster.Caching.UnitTests/Lru/CapacityPartitionExtensionsTests.cs create mode 100644 BitFaster.Caching.UnitTests/Lru/TestCapacityPartition.cs create mode 100644 BitFaster.Caching/Lru/CapacityPartitionExtensions.cs diff --git a/BitFaster.Caching.UnitTests/Lru/CapacityPartitionExtensionsTests.cs b/BitFaster.Caching.UnitTests/Lru/CapacityPartitionExtensionsTests.cs new file mode 100644 index 00000000..9b2e755d --- /dev/null +++ b/BitFaster.Caching.UnitTests/Lru/CapacityPartitionExtensionsTests.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BitFaster.Caching.Lru; +using FluentAssertions; +using Xunit; + +namespace BitFaster.Caching.UnitTests.Lru +{ + public class CapacityPartitionExtensionsTests + { + [Fact] + public void WhenCapacityIsValidDoesNotThrow() + { + var p = new TestCapacityPartition { Cold = 2, Warm = 2, Hot = 2 }; + + Action validate = () => { p.Validate(); }; + + validate.Should().NotThrow(); + } + + [Fact] + public void WhenColdIsZeroThrows() + { + var p = new TestCapacityPartition { Cold = 0, Warm = 2, Hot = 2 }; + + Action validate = () => { p.Validate(); }; + + validate.Should().Throw(); + } + + [Fact] + public void WhenWarmIsZeroThrows() + { + var p = new TestCapacityPartition { Cold = 2, Warm = 0, Hot = 2 }; + + Action validate = () => { p.Validate(); }; + + validate.Should().Throw(); + } + + [Fact] + public void WhenHotIsZeroThrows() + { + var p = new TestCapacityPartition { Cold = 2, Warm = 2, Hot = 0 }; + + Action validate = () => { p.Validate(); }; + + validate.Should().Throw(); + } + } +} diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs index c349748d..7830ded5 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs @@ -59,6 +59,15 @@ public void WhenPartitionIsNullCtorThrows() constructor.Should().Throw(); } + [Fact] + public void WhenPartitionIsInvalidThrows() + { + var p = new TestCapacityPartition { Cold = 2, Warm = 0, Hot = 2 }; + Action constructor = () => { var x = new ConcurrentLru(1, p, EqualityComparer.Default); }; + + constructor.Should().Throw(); + } + [Fact] public void WhenComparerIsNullCtorThrows() { diff --git a/BitFaster.Caching.UnitTests/Lru/TestCapacityPartition.cs b/BitFaster.Caching.UnitTests/Lru/TestCapacityPartition.cs new file mode 100644 index 00000000..1036eaea --- /dev/null +++ b/BitFaster.Caching.UnitTests/Lru/TestCapacityPartition.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BitFaster.Caching.Lru; + +namespace BitFaster.Caching.UnitTests.Lru +{ + public class TestCapacityPartition : ICapacityPartition + { + public int Cold { get; set; } + + public int Warm { get; set; } + + public int Hot { get; set; } + } +} diff --git a/BitFaster.Caching/Lru/Builder/LruBuilderBase.cs b/BitFaster.Caching/Lru/Builder/LruBuilderBase.cs index bfd8c253..cf9d6b14 100644 --- a/BitFaster.Caching/Lru/Builder/LruBuilderBase.cs +++ b/BitFaster.Caching/Lru/Builder/LruBuilderBase.cs @@ -25,6 +25,18 @@ protected LruBuilderBase(LruInfo info) /// The maximum number of values to keep in the cache. /// A ConcurrentLruBuilder public TBuilder WithCapacity(int capacity) + { + this.info.Capacity = new FavorFrequencyPartition(capacity); + return this as TBuilder; + } + + /// + /// Set the maximum number of values to keep in the cache. If more items than this are added, + /// the cache eviction policy will determine which values to remove. + /// + /// The capacity partition scheme to use. + /// A ConcurrentLruBuilder + public TBuilder WithCapacity(ICapacityPartition capacity) { this.info.Capacity = capacity; return this as TBuilder; diff --git a/BitFaster.Caching/Lru/Builder/LruInfo.cs b/BitFaster.Caching/Lru/Builder/LruInfo.cs index 42581963..17c61afc 100644 --- a/BitFaster.Caching/Lru/Builder/LruInfo.cs +++ b/BitFaster.Caching/Lru/Builder/LruInfo.cs @@ -8,7 +8,7 @@ namespace BitFaster.Caching.Lru.Builder { public sealed class LruInfo { - public int Capacity { get; set; } = 128; + public ICapacityPartition Capacity { get; set; } = new FavorFrequencyPartition(128); public int ConcurrencyLevel { get; set; } = Defaults.ConcurrencyLevel; diff --git a/BitFaster.Caching/Lru/CapacityPartitionExtensions.cs b/BitFaster.Caching/Lru/CapacityPartitionExtensions.cs new file mode 100644 index 00000000..16a0f840 --- /dev/null +++ b/BitFaster.Caching/Lru/CapacityPartitionExtensions.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching.Lru +{ + public static class CapacityPartitionExtensions + { + public static void Validate(this ICapacityPartition capacity) + { + if (capacity.Cold < 1) + { + throw new ArgumentOutOfRangeException(nameof(capacity.Cold)); + } + + if (capacity.Warm < 1) + { + throw new ArgumentOutOfRangeException(nameof(capacity.Warm)); + } + + if (capacity.Hot < 1) + { + throw new ArgumentOutOfRangeException(nameof(capacity.Hot)); + } + } + } +} diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index 3462764b..0190072e 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -70,6 +70,7 @@ public TemplateConcurrentLru( throw new ArgumentNullException(nameof(comparer)); } + capacity.Validate(); this.capacity = capacity; this.hotQueue = new ConcurrentQueue(); From fcf061fb388c52f7a94264b27d38c808576dd2ff Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Wed, 20 Jul 2022 14:10:16 -0700 Subject: [PATCH 2/3] tests --- .../Lru/{LruBuilderTests.cs => ConcurrentLruBuilderTests.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename BitFaster.Caching.UnitTests/Lru/{LruBuilderTests.cs => ConcurrentLruBuilderTests.cs} (100%) diff --git a/BitFaster.Caching.UnitTests/Lru/LruBuilderTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs similarity index 100% rename from BitFaster.Caching.UnitTests/Lru/LruBuilderTests.cs rename to BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs From a0a246039664680b34fe8f49a2c253d2e758be74 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Wed, 20 Jul 2022 14:18:54 -0700 Subject: [PATCH 3/3] tests --- .../Lru/ConcurrentLruBuilderTests.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs index 95d7f601..48d6eeb2 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs @@ -9,7 +9,7 @@ namespace BitFaster.Caching.UnitTests.Lru { - public class LruBuilderTests + public class ConcurrentLruBuilderTests { [Fact] public void TestFastLru() @@ -86,5 +86,25 @@ public void TestConcurrencyLevel() constructor.Should().Throw(); } + + [Fact] + public void TestIntCapacity() + { + var lru = new ConcurrentLruBuilder() + .WithCapacity(3) + .Build(); + + lru.Capacity.Should().Be(3); + } + + [Fact] + public void TestPartitionCapacity() + { + var lru = new ConcurrentLruBuilder() + .WithCapacity(new FavorFrequencyPartition(6)) + .Build(); + + lru.Capacity.Should().Be(6); + } } }