From 485536675a1b5fca6f8807e71c615d8cfed97956 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Mon, 25 Jul 2022 14:26:24 -0700 Subject: [PATCH 1/5] outline --- BitFaster.Caching/ICachePolicy.cs | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 BitFaster.Caching/ICachePolicy.cs diff --git a/BitFaster.Caching/ICachePolicy.cs b/BitFaster.Caching/ICachePolicy.cs new file mode 100644 index 00000000..398524f9 --- /dev/null +++ b/BitFaster.Caching/ICachePolicy.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching +{ + public interface ICachePolicy + { + BoundedPolicy Eviction { get; } + + TimePolicy ExpireAfterWrite { get; } + + bool IsWriteAtomic { get; } + } + + public class BoundedPolicy + { + public static readonly BoundedPolicy None = new BoundedPolicy(i => { }); + + private readonly Action trimCallback; + + public BoundedPolicy(Action trimCallback) + { + this.trimCallback = trimCallback; + } + + public void Trim(int itemCount) + { + trimCallback(itemCount); + } + } + + public class TimePolicy + { + public static readonly TimePolicy None = new TimePolicy(); + + public TimeSpan TimeToLive { get; } + + public TimeSpan? AgeOf(K key) + { + return null; + } + + public void TrimExpired() + { + } + } +} From 1169d84670b0609d2e05c3a8b001d96c6dd9b579 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Tue, 26 Jul 2022 11:34:47 -0700 Subject: [PATCH 2/5] policy --- .../Atomic/AtomicFactoryAsyncCacheTests.cs | 2 +- .../Atomic/AtomicFactoryCacheTests.cs | 2 +- .../Lru/ConcurrentLruBuilderTests.cs | 6 +- .../ScopedAsyncCacheTestBase.cs | 4 +- .../ScopedCacheTestBase.cs | 2 +- .../Atomic/AtomicFactoryAsyncCache.cs | 7 +- .../Atomic/AtomicFactoryCache.cs | 7 +- .../Atomic/AtomicFactoryScopedAsyncCache.cs | 7 +- .../Atomic/AtomicFactoryScopedCache.cs | 7 +- BitFaster.Caching/IAsyncCache.cs | 13 +--- BitFaster.Caching/ICache.cs | 8 +-- BitFaster.Caching/ICachePolicy.cs | 64 ++++++++++++------- BitFaster.Caching/IScopedAsyncCache.cs | 13 +--- BitFaster.Caching/IScopedCache.cs | 8 +-- BitFaster.Caching/Lru/ClassicLru.cs | 6 +- BitFaster.Caching/Lru/IItemPolicy.cs | 2 + BitFaster.Caching/Lru/LruPolicy.cs | 2 + .../Lru/TemplateConcurrentLru.cs | 20 +++++- BitFaster.Caching/Lru/TlruDateTimePolicy.cs | 2 + BitFaster.Caching/Lru/TlruLongTicksPolicy.cs | 7 ++ BitFaster.Caching/Lru/TlruTicksPolicy.cs | 2 + BitFaster.Caching/ScopedAsyncCache.cs | 11 +--- BitFaster.Caching/ScopedCache.cs | 8 +-- 23 files changed, 108 insertions(+), 102 deletions(-) diff --git a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryAsyncCacheTests.cs b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryAsyncCacheTests.cs index bd55a1e6..41f82bc5 100644 --- a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryAsyncCacheTests.cs +++ b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryAsyncCacheTests.cs @@ -114,7 +114,7 @@ public void WhenCacheContainsValuesTrim1RemovesColdestValue() this.cache.AddOrUpdate(1, 1); this.cache.AddOrUpdate(2, 2); - this.cache.Trim(1); + this.cache.Policy.Eviction.Trim(1); this.cache.TryGet(0, out var value).Should().BeFalse(); } diff --git a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryCacheTests.cs b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryCacheTests.cs index 18605a76..ba244339 100644 --- a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryCacheTests.cs +++ b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryCacheTests.cs @@ -114,7 +114,7 @@ public void WhenCacheContainsValuesTrim1RemovesColdestValue() this.cache.AddOrUpdate(1, 1); this.cache.AddOrUpdate(2, 2); - this.cache.Trim(1); + this.cache.Policy.Eviction.Trim(1); this.cache.TryGet(0, out var value).Should().BeFalse(); } diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs index af387a62..8c096c35 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruBuilderTests.cs @@ -95,7 +95,7 @@ public void AsAsyncTestMetricsTLru() .Build(); lru.Should().BeOfType>(); - lru.Capacity.Should().Be(128); + lru.Policy.Eviction.Capacity.Should().Be(128); } @@ -268,7 +268,7 @@ public void AsAsyncWithScoped() lru.Should().BeAssignableTo>(); - lru.Capacity.Should().Be(3); + lru.Policy.Eviction.Capacity.Should().Be(3); } // 7 @@ -282,7 +282,7 @@ public void WithScopedAsAsync() .Build(); lru.Should().BeAssignableTo>(); - lru.Capacity.Should().Be(3); + lru.Policy.Eviction.Capacity.Should().Be(3); } // 8 diff --git a/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs b/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs index 02cbd16a..ab2ff0f3 100644 --- a/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs +++ b/BitFaster.Caching.UnitTests/ScopedAsyncCacheTestBase.cs @@ -24,7 +24,7 @@ protected ScopedAsyncCacheTestBase(IScopedAsyncCache cache) [Fact] public void WhenCreatedCapacityPropertyWrapsInnerCache() { - this.cache.Capacity.Should().Be(capacity); + this.cache.Policy.Eviction.Capacity.Should().Be(capacity); } [Fact] @@ -138,7 +138,7 @@ public void WhenCacheContainsValuesTrim1RemovesColdestValue() this.cache.AddOrUpdate(1, new Disposable()); this.cache.AddOrUpdate(2, new Disposable()); - this.cache.Trim(1); + this.cache.Policy.Eviction.Trim(1); this.cache.ScopedTryGet(0, out var lifetime).Should().BeFalse(); } diff --git a/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs b/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs index 1ac22135..1cfef06a 100644 --- a/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs +++ b/BitFaster.Caching.UnitTests/ScopedCacheTestBase.cs @@ -138,7 +138,7 @@ public void WhenCacheContainsValuesTrim1RemovesColdestValue() this.cache.AddOrUpdate(1, new Disposable()); this.cache.AddOrUpdate(2, new Disposable()); - this.cache.Trim(1); + this.cache.Policy.Eviction.Trim(1); this.cache.ScopedTryGet(0, out var lifetime).Should().BeFalse(); } diff --git a/BitFaster.Caching/Atomic/AtomicFactoryAsyncCache.cs b/BitFaster.Caching/Atomic/AtomicFactoryAsyncCache.cs index f93bbfbb..51346e41 100644 --- a/BitFaster.Caching/Atomic/AtomicFactoryAsyncCache.cs +++ b/BitFaster.Caching/Atomic/AtomicFactoryAsyncCache.cs @@ -33,6 +33,8 @@ public AtomicFactoryAsyncCache(ICache> cache) public ICollection Keys => this.cache.Keys; + public CachePolicy Policy => this.cache.Policy; + public void AddOrUpdate(K key, V value) { cache.AddOrUpdate(key, new AsyncAtomicFactory(value)); @@ -49,11 +51,6 @@ public ValueTask GetOrAddAsync(K key, Func> valueFactory) return synchronized.GetValueAsync(key, valueFactory); } - public void Trim(int itemCount) - { - cache.Trim(itemCount); - } - public bool TryGet(K key, out V value) { AsyncAtomicFactory output; diff --git a/BitFaster.Caching/Atomic/AtomicFactoryCache.cs b/BitFaster.Caching/Atomic/AtomicFactoryCache.cs index 54c1639d..32fea74f 100644 --- a/BitFaster.Caching/Atomic/AtomicFactoryCache.cs +++ b/BitFaster.Caching/Atomic/AtomicFactoryCache.cs @@ -33,6 +33,8 @@ public AtomicFactoryCache(ICache> cache) public ICollection Keys => this.cache.Keys; + public CachePolicy Policy => this.cache.Policy; + public void AddOrUpdate(K key, V value) { this.cache.AddOrUpdate(key, new AtomicFactory(value)); @@ -49,11 +51,6 @@ public V GetOrAdd(K key, Func valueFactory) return atomicFactory.GetValue(key, valueFactory); } - public void Trim(int itemCount) - { - this.cache.Trim(itemCount); - } - public bool TryGet(K key, out V value) { AtomicFactory output; diff --git a/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs b/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs index 605bdc72..6a5f93d9 100644 --- a/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs +++ b/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs @@ -32,6 +32,8 @@ public AtomicFactoryScopedAsyncCache(ICache> c public ICacheEvents> Events => this.eventProxy; + public CachePolicy Policy => this.cache.Policy; + /// public ICollection Keys => this.cache.Keys; @@ -83,11 +85,6 @@ public bool ScopedTryGet(K key, out Lifetime lifetime) return false; } - public void Trim(int itemCount) - { - this.cache.Trim(itemCount); - } - public bool TryRemove(K key) { return this.cache.TryRemove(key); diff --git a/BitFaster.Caching/Atomic/AtomicFactoryScopedCache.cs b/BitFaster.Caching/Atomic/AtomicFactoryScopedCache.cs index ad5c87c8..59affce7 100644 --- a/BitFaster.Caching/Atomic/AtomicFactoryScopedCache.cs +++ b/BitFaster.Caching/Atomic/AtomicFactoryScopedCache.cs @@ -32,6 +32,8 @@ public AtomicFactoryScopedCache(ICache> cache) public ICacheEvents> Events => this.eventProxy; + public CachePolicy Policy => this.cache.Policy; + /// public ICollection Keys => this.cache.Keys; @@ -81,11 +83,6 @@ public bool ScopedTryGet(K key, out Lifetime lifetime) return false; } - public void Trim(int itemCount) - { - this.cache.Trim(itemCount); - } - public bool TryRemove(K key) { return this.cache.TryRemove(key); diff --git a/BitFaster.Caching/IAsyncCache.cs b/BitFaster.Caching/IAsyncCache.cs index a5d46ecc..970b3ee2 100644 --- a/BitFaster.Caching/IAsyncCache.cs +++ b/BitFaster.Caching/IAsyncCache.cs @@ -13,11 +13,6 @@ namespace BitFaster.Caching /// The type of values in the cache. public interface IAsyncCache : IEnumerable> { - /// - /// Gets the total number of items that can be stored in the cache. - /// - int Capacity { get; } - /// /// Gets the number of items currently held in the cache. /// @@ -33,6 +28,8 @@ public interface IAsyncCache : IEnumerable> /// ICacheEvents Events { get; } + CachePolicy Policy { get; } + /// /// Gets a collection containing the keys in the cache. /// @@ -82,11 +79,5 @@ public interface IAsyncCache : IEnumerable> /// Removes all keys and values from the cache. /// void Clear(); - - /// - /// Trim the specified number of items from the cache. - /// - /// The number of items to remove. - void Trim(int itemCount); } } diff --git a/BitFaster.Caching/ICache.cs b/BitFaster.Caching/ICache.cs index 5ecb804e..5b85b71a 100644 --- a/BitFaster.Caching/ICache.cs +++ b/BitFaster.Caching/ICache.cs @@ -33,6 +33,8 @@ public interface ICache : IEnumerable> /// ICacheEvents Events { get; } + CachePolicy Policy { get; } + /// /// Gets a collection containing the keys in the cache. /// @@ -83,11 +85,5 @@ public interface ICache : IEnumerable> /// Removes all keys and values from the cache. /// void Clear(); - - /// - /// Trim the specified number of items from the cache. - /// - /// The number of items to remove. - void Trim(int itemCount); } } diff --git a/BitFaster.Caching/ICachePolicy.cs b/BitFaster.Caching/ICachePolicy.cs index 398524f9..ffa52e2f 100644 --- a/BitFaster.Caching/ICachePolicy.cs +++ b/BitFaster.Caching/ICachePolicy.cs @@ -6,45 +6,63 @@ namespace BitFaster.Caching { - public interface ICachePolicy + public class CachePolicy { - BoundedPolicy Eviction { get; } + public CachePolicy(IBoundedPolicy eviction, ITimePolicy expireAfterWrite) + { + this.Eviction = eviction; + this.ExpireAfterWrite = expireAfterWrite; + } - TimePolicy ExpireAfterWrite { get; } + public IBoundedPolicy Eviction { get; } - bool IsWriteAtomic { get; } + public ITimePolicy ExpireAfterWrite { get; } } - public class BoundedPolicy + public interface IBoundedPolicy { - public static readonly BoundedPolicy None = new BoundedPolicy(i => { }); + /// + /// Gets the total number of items that can be stored in the cache. + /// + int Capacity { get; } - private readonly Action trimCallback; + /// + /// Trim the specified number of items from the cache. + /// + /// The number of items to remove. + void Trim(int itemCount); + } - public BoundedPolicy(Action trimCallback) - { - this.trimCallback = trimCallback; - } + public interface ITimePolicy + { + /// + /// Gets a value indicating whether the cache can expire items based on time. + /// + bool CanExpire { get; } - public void Trim(int itemCount) - { - trimCallback(itemCount); - } + /// + /// Gets the time to live for items in the cache. + /// + TimeSpan TimeToLive { get; } + + /// + /// Remove all expired items from the cache. + /// + void TrimExpired(); } - public class TimePolicy + public class NoneTimePolicy : ITimePolicy { - public static readonly TimePolicy None = new TimePolicy(); + public static readonly TimeSpan Infinite = new TimeSpan(0, 0, 0, 0, -1); - public TimeSpan TimeToLive { get; } + public static NoneTimePolicy Instance = new NoneTimePolicy(); - public TimeSpan? AgeOf(K key) - { - return null; - } + public bool CanExpire => false; + + public TimeSpan TimeToLive => Infinite; public void TrimExpired() - { + { } } } diff --git a/BitFaster.Caching/IScopedAsyncCache.cs b/BitFaster.Caching/IScopedAsyncCache.cs index ae5e08cd..e02cb887 100644 --- a/BitFaster.Caching/IScopedAsyncCache.cs +++ b/BitFaster.Caching/IScopedAsyncCache.cs @@ -13,11 +13,6 @@ namespace BitFaster.Caching /// The type of values in the cache. public interface IScopedAsyncCache : IEnumerable>> where V : IDisposable { - /// - /// Gets the total number of items that can be stored in the cache. - /// - int Capacity { get; } - /// /// Gets the number of items currently held in the cache. /// @@ -37,6 +32,8 @@ public interface IScopedAsyncCache : IEnumerable /// ICacheEvents> Events { get; } + CachePolicy Policy { get; } + /// /// Gets a collection containing the keys in the cache. /// @@ -87,11 +84,5 @@ public interface IScopedAsyncCache : IEnumerable /// Removes all keys and values from the cache. /// void Clear(); - - /// - /// Trim the specified number of items from the cache. - /// - /// The number of items to remove. - void Trim(int itemCount); } } diff --git a/BitFaster.Caching/IScopedCache.cs b/BitFaster.Caching/IScopedCache.cs index 7c3687d0..69f172e9 100644 --- a/BitFaster.Caching/IScopedCache.cs +++ b/BitFaster.Caching/IScopedCache.cs @@ -37,6 +37,8 @@ public interface IScopedCache : IEnumerable>> wh /// ICacheEvents> Events { get; } + CachePolicy Policy { get; } + /// /// Gets a collection containing the keys in the cache. /// @@ -89,11 +91,5 @@ public interface IScopedCache : IEnumerable>> wh /// Removes all keys and values from the cache. /// void Clear(); - - /// - /// Trim the specified number of items from the cache. - /// - /// The number of items to remove. - void Trim(int itemCount); } } diff --git a/BitFaster.Caching/Lru/ClassicLru.cs b/BitFaster.Caching/Lru/ClassicLru.cs index 0bfc8f62..32b2dd1a 100644 --- a/BitFaster.Caching/Lru/ClassicLru.cs +++ b/BitFaster.Caching/Lru/ClassicLru.cs @@ -18,7 +18,7 @@ namespace BitFaster.Caching.Lru /// /// The type of the key /// The type of the value - public sealed class ClassicLru : ICache, IAsyncCache, IEnumerable> + public sealed class ClassicLru : ICache, IAsyncCache, IBoundedPolicy, IEnumerable> { private readonly int capacity; private readonly ConcurrentDictionary> dictionary; @@ -26,6 +26,7 @@ public sealed class ClassicLru : ICache, IAsyncCache, IEnumera private readonly CacheMetrics metrics = new CacheMetrics(); private readonly CacheEvents events = new CacheEvents(); + private readonly CachePolicy policy; public ClassicLru(int capacity) : this(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default) @@ -46,6 +47,7 @@ public ClassicLru(int concurrencyLevel, int capacity, IEqualityComparer compa this.capacity = capacity; this.dictionary = new ConcurrentDictionary>(concurrencyLevel, this.capacity + 1, comparer); + this.policy = new CachePolicy(this, NoneTimePolicy.Instance); } /// @@ -65,6 +67,8 @@ public ClassicLru(int concurrencyLevel, int capacity, IEqualityComparer compa public ICacheEvents Events => this.events; + public CachePolicy Policy => this.policy; + /// /// Gets a collection containing the keys in the cache. /// diff --git a/BitFaster.Caching/Lru/IItemPolicy.cs b/BitFaster.Caching/Lru/IItemPolicy.cs index ccab48d9..f36b7967 100644 --- a/BitFaster.Caching/Lru/IItemPolicy.cs +++ b/BitFaster.Caching/Lru/IItemPolicy.cs @@ -23,5 +23,7 @@ public interface IItemPolicy where I : LruItem ItemDestination RouteWarm(I item); ItemDestination RouteCold(I item); + + TimeSpan TimeToLive { get; } } } diff --git a/BitFaster.Caching/Lru/LruPolicy.cs b/BitFaster.Caching/Lru/LruPolicy.cs index bf837dab..15842454 100644 --- a/BitFaster.Caching/Lru/LruPolicy.cs +++ b/BitFaster.Caching/Lru/LruPolicy.cs @@ -12,6 +12,8 @@ namespace BitFaster.Caching.Lru /// public readonly struct LruPolicy : IItemPolicy> { + public TimeSpan TimeToLive => NoneTimePolicy.Infinite; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LruItem CreateItem(K key, V value) { diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index 4759222b..172ebeb7 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -28,7 +28,7 @@ namespace BitFaster.Caching.Lru /// 5. When warm is full, warm tail is moved to warm head or cold depending on WasAccessed. /// 6. When cold is full, cold tail is moved to warm head or removed from dictionary on depending on WasAccessed. /// - public class TemplateConcurrentLru : ICache, IAsyncCache, IEnumerable> + public class TemplateConcurrentLru : ICache, IAsyncCache, IBoundedPolicy, ITimePolicy, IEnumerable> where I : LruItem where P : struct, IItemPolicy where T : struct, ITelemetryPolicy @@ -53,6 +53,8 @@ public class TemplateConcurrentLru : ICache, IAsyncCache public ICollection Keys => this.dictionary.Keys; + public CachePolicy Policy => this.policy; + + public bool CanExpire => this.itemPolicy.CanDiscard(); + + public TimeSpan TimeToLive => this.itemPolicy.TimeToLive; + /// Returns an enumerator that iterates through the cache. /// An enumerator for the cache. /// @@ -340,6 +350,14 @@ public void Trim(int itemCount) TrimLiveItems(itemsRemoved, itemCount, capacity); } + public void TrimExpired() + { + if (this.itemPolicy.CanDiscard()) + { + TrimAllDiscardedItems(); + } + } + protected int TrimAllDiscardedItems() { int itemsRemoved = 0; diff --git a/BitFaster.Caching/Lru/TlruDateTimePolicy.cs b/BitFaster.Caching/Lru/TlruDateTimePolicy.cs index e35d64c4..6191918e 100644 --- a/BitFaster.Caching/Lru/TlruDateTimePolicy.cs +++ b/BitFaster.Caching/Lru/TlruDateTimePolicy.cs @@ -15,6 +15,8 @@ namespace BitFaster.Caching.Lru { private readonly TimeSpan timeToLive; + public TimeSpan TimeToLive => timeToLive; + public TLruDateTimePolicy(TimeSpan timeToLive) { this.timeToLive = timeToLive; diff --git a/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs b/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs index aae9216e..41258b10 100644 --- a/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs +++ b/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs @@ -109,9 +109,16 @@ public ItemDestination RouteCold(LongTickCountLruItem item) return ItemDestination.Remove; } + public TimeSpan TimeToLive => FromTicks(timeToLive); + public static long ToTicks(TimeSpan timespan) { return (long)(timespan.Ticks * stopwatchAdjustmentFactor); } + + public static TimeSpan FromTicks(long ticks) + { + return TimeSpan.FromTicks((long)(ticks / stopwatchAdjustmentFactor)); + } } } diff --git a/BitFaster.Caching/Lru/TlruTicksPolicy.cs b/BitFaster.Caching/Lru/TlruTicksPolicy.cs index 412d195a..532fe8c1 100644 --- a/BitFaster.Caching/Lru/TlruTicksPolicy.cs +++ b/BitFaster.Caching/Lru/TlruTicksPolicy.cs @@ -20,6 +20,8 @@ namespace BitFaster.Caching.Lru { private readonly int timeToLive; + public TimeSpan TimeToLive => TimeSpan.FromMilliseconds(timeToLive); + public TLruTicksPolicy(TimeSpan timeToLive) { this.timeToLive = (int)timeToLive.TotalMilliseconds; diff --git a/BitFaster.Caching/ScopedAsyncCache.cs b/BitFaster.Caching/ScopedAsyncCache.cs index 1fd00183..0e4c93d0 100644 --- a/BitFaster.Caching/ScopedAsyncCache.cs +++ b/BitFaster.Caching/ScopedAsyncCache.cs @@ -29,9 +29,6 @@ public ScopedAsyncCache(IAsyncCache> cache) this.cache = cache; } - /// - public int Capacity => this.cache.Capacity; - /// public int Count => this.cache.Count; @@ -41,6 +38,8 @@ public ScopedAsyncCache(IAsyncCache> cache) /// public ICacheEvents> Events => this.cache.Events; + public CachePolicy Policy => this.cache.Policy; + /// public ICollection Keys => this.cache.Keys; @@ -79,12 +78,6 @@ public async ValueTask> ScopedGetOrAddAsync(K key, Func - public void Trim(int itemCount) - { - this.cache.Trim(itemCount); - } - /// public bool ScopedTryGet(K key, out Lifetime lifetime) { diff --git a/BitFaster.Caching/ScopedCache.cs b/BitFaster.Caching/ScopedCache.cs index 2c8476c0..dfaf99ee 100644 --- a/BitFaster.Caching/ScopedCache.cs +++ b/BitFaster.Caching/ScopedCache.cs @@ -41,6 +41,8 @@ public ScopedCache(ICache> cache) /// public ICacheEvents> Events => this.cache.Events; + public CachePolicy Policy => this.cache.Policy; + /// public ICollection Keys => this.cache.Keys; @@ -79,12 +81,6 @@ public Lifetime ScopedGetOrAdd(K key, Func> valueFactory) } } - /// - public void Trim(int itemCount) - { - this.cache.Trim(itemCount); - } - /// public bool ScopedTryGet(K key, out Lifetime lifetime) { From 008993c038c3c294bd82d50254d2ec8292af8c1e Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Tue, 26 Jul 2022 11:42:17 -0700 Subject: [PATCH 3/5] ocpr --- BitFaster.Caching/CachePolicy.cs | 21 +++++++++ BitFaster.Caching/IBoundedPolicy.cs | 22 ++++++++++ BitFaster.Caching/ICachePolicy.cs | 68 ----------------------------- BitFaster.Caching/ITimePolicy.cs | 26 +++++++++++ BitFaster.Caching/NoneTimePolicy.cs | 12 +++++ 5 files changed, 81 insertions(+), 68 deletions(-) create mode 100644 BitFaster.Caching/CachePolicy.cs create mode 100644 BitFaster.Caching/IBoundedPolicy.cs delete mode 100644 BitFaster.Caching/ICachePolicy.cs create mode 100644 BitFaster.Caching/ITimePolicy.cs create mode 100644 BitFaster.Caching/NoneTimePolicy.cs diff --git a/BitFaster.Caching/CachePolicy.cs b/BitFaster.Caching/CachePolicy.cs new file mode 100644 index 00000000..8ec6c672 --- /dev/null +++ b/BitFaster.Caching/CachePolicy.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching +{ + public class CachePolicy + { + public CachePolicy(IBoundedPolicy eviction, ITimePolicy expireAfterWrite) + { + this.Eviction = eviction; + this.ExpireAfterWrite = expireAfterWrite; + } + + public IBoundedPolicy Eviction { get; } + + public ITimePolicy ExpireAfterWrite { get; } + } +} diff --git a/BitFaster.Caching/IBoundedPolicy.cs b/BitFaster.Caching/IBoundedPolicy.cs new file mode 100644 index 00000000..07b423e4 --- /dev/null +++ b/BitFaster.Caching/IBoundedPolicy.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching +{ + public interface IBoundedPolicy + { + /// + /// Gets the total number of items that can be stored in the cache. + /// + int Capacity { get; } + + /// + /// Trim the specified number of items from the cache. + /// + /// The number of items to remove. + void Trim(int itemCount); + } +} diff --git a/BitFaster.Caching/ICachePolicy.cs b/BitFaster.Caching/ICachePolicy.cs deleted file mode 100644 index ffa52e2f..00000000 --- a/BitFaster.Caching/ICachePolicy.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BitFaster.Caching -{ - public class CachePolicy - { - public CachePolicy(IBoundedPolicy eviction, ITimePolicy expireAfterWrite) - { - this.Eviction = eviction; - this.ExpireAfterWrite = expireAfterWrite; - } - - public IBoundedPolicy Eviction { get; } - - public ITimePolicy ExpireAfterWrite { get; } - } - - public interface IBoundedPolicy - { - /// - /// Gets the total number of items that can be stored in the cache. - /// - int Capacity { get; } - - /// - /// Trim the specified number of items from the cache. - /// - /// The number of items to remove. - void Trim(int itemCount); - } - - public interface ITimePolicy - { - /// - /// Gets a value indicating whether the cache can expire items based on time. - /// - bool CanExpire { get; } - - /// - /// Gets the time to live for items in the cache. - /// - TimeSpan TimeToLive { get; } - - /// - /// Remove all expired items from the cache. - /// - void TrimExpired(); - } - - public class NoneTimePolicy : ITimePolicy - { - public static readonly TimeSpan Infinite = new TimeSpan(0, 0, 0, 0, -1); - - public static NoneTimePolicy Instance = new NoneTimePolicy(); - - public bool CanExpire => false; - - public TimeSpan TimeToLive => Infinite; - - public void TrimExpired() - { - } - } -} diff --git a/BitFaster.Caching/ITimePolicy.cs b/BitFaster.Caching/ITimePolicy.cs new file mode 100644 index 00000000..90804fff --- /dev/null +++ b/BitFaster.Caching/ITimePolicy.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching +{ + public interface ITimePolicy + { + /// + /// Gets a value indicating whether the cache can expire items based on time. + /// + bool CanExpire { get; } + + /// + /// Gets the time to live for items in the cache. + /// + TimeSpan TimeToLive { get; } + + /// + /// Remove all expired items from the cache. + /// + void TrimExpired(); + } +} diff --git a/BitFaster.Caching/NoneTimePolicy.cs b/BitFaster.Caching/NoneTimePolicy.cs new file mode 100644 index 00000000..179cff01 --- /dev/null +++ b/BitFaster.Caching/NoneTimePolicy.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching +{ + public class NoneTimePolicy + { + } +} From 962b98ae67d2d7f5f9c48502c6ce9f35cc511236 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Tue, 26 Jul 2022 12:03:36 -0700 Subject: [PATCH 4/5] tests --- .../BitFaster.Caching.UnitTests.csproj | 1 + .../CachePolicyTests.cs | 26 +++++++++++++++ .../Lru/ClassicLruTests.cs | 7 ++++ .../Lru/ConcurrentLruTests.cs | 12 +++++++ .../Lru/ConcurrentTLruTests.cs | 12 +++++++ .../Lru/LruPolicyTests.cs | 6 ++++ .../Lru/TlruDateTimePolicyTests.cs | 6 ++++ .../Lru/TlruLongTicksPolicyTests.cs | 6 ++++ .../Lru/TlruTicksPolicyTests.cs | 6 ++++ .../NoneTimePolicyTests.cs | 33 +++++++++++++++++++ BitFaster.Caching/NoneTimePolicy.cs | 13 +++++++- 11 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 BitFaster.Caching.UnitTests/CachePolicyTests.cs create mode 100644 BitFaster.Caching.UnitTests/NoneTimePolicyTests.cs diff --git a/BitFaster.Caching.UnitTests/BitFaster.Caching.UnitTests.csproj b/BitFaster.Caching.UnitTests/BitFaster.Caching.UnitTests.csproj index 5a51545f..df88509c 100644 --- a/BitFaster.Caching.UnitTests/BitFaster.Caching.UnitTests.csproj +++ b/BitFaster.Caching.UnitTests/BitFaster.Caching.UnitTests.csproj @@ -11,6 +11,7 @@ + all diff --git a/BitFaster.Caching.UnitTests/CachePolicyTests.cs b/BitFaster.Caching.UnitTests/CachePolicyTests.cs new file mode 100644 index 00000000..3631f977 --- /dev/null +++ b/BitFaster.Caching.UnitTests/CachePolicyTests.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Moq; +using Xunit; + +namespace BitFaster.Caching.UnitTests +{ + public class CachePolicyTests + { + [Fact] + public void WhenCtorFieldsAreAssigned() + { + var eviction = new Mock(); + var expire = new Mock(); + + var cp = new CachePolicy(eviction.Object, expire.Object); + + cp.Eviction.Should().Be(eviction.Object); + cp.ExpireAfterWrite.Should().Be(expire.Object); + } + } +} diff --git a/BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs index 791754e2..4ae9d22c 100644 --- a/BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs @@ -183,6 +183,13 @@ private void OnItemRemoved(object sender, ItemRemovedEventArgs e) throw new NotImplementedException(); } + [Fact] + public void ExpireAfterWriteIsDisabled() + { + lru.Policy.ExpireAfterWrite.Should().Be(NoneTimePolicy.Instance); + lru.Policy.ExpireAfterWrite.CanExpire.Should().BeFalse(); + } + [Fact] public void WhenKeyIsRequestedItIsCreatedAndCached() { diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs index 9cb2595e..8fc6d862 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs @@ -224,6 +224,18 @@ public void WhenRefToMetricsIsCapturedResultIsCorrect() m.HitRatio.Should().Be(0.5); } + [Fact] + public void CanExpireIsFalse() + { + this.lru.CanExpire.Should().BeFalse(); + } + + [Fact] + public void TimeToLiveIsInfinite() + { + this.lru.TimeToLive.Should().Be(NoneTimePolicy.Infinite); + } + [Fact] public void WhenKeyIsRequestedItIsCreatedAndCached() { diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs index 2d83a232..b6df69f8 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs @@ -52,6 +52,18 @@ public void ConstructPartitionCtorReturnsCapacity() x.Capacity.Should().Be(3); } + [Fact] + public void CanExpireIsTrue() + { + this.lru.CanExpire.Should().BeTrue(); + } + + [Fact] + public void TimeToLiveIsCtorArg() + { + this.lru.TimeToLive.Should().Be(timeToLive); + } + [Fact] public void WhenItemIsNotExpiredItIsNotRemoved() { diff --git a/BitFaster.Caching.UnitTests/Lru/LruPolicyTests.cs b/BitFaster.Caching.UnitTests/Lru/LruPolicyTests.cs index ec387ab1..d94af4d5 100644 --- a/BitFaster.Caching.UnitTests/Lru/LruPolicyTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/LruPolicyTests.cs @@ -11,6 +11,12 @@ public class LruPolicyTests { private readonly LruPolicy policy = new LruPolicy(); + [Fact] + public void TimeToLiveIsInfinite() + { + this.policy.TimeToLive.Should().Be(NoneTimePolicy.Infinite); + } + [Fact] public void CreateItemInitializesKeyAndValue() { diff --git a/BitFaster.Caching.UnitTests/Lru/TlruDateTimePolicyTests.cs b/BitFaster.Caching.UnitTests/Lru/TlruDateTimePolicyTests.cs index 9e09e829..4cdb7b78 100644 --- a/BitFaster.Caching.UnitTests/Lru/TlruDateTimePolicyTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/TlruDateTimePolicyTests.cs @@ -14,6 +14,12 @@ public class TLruDateTimePolicyTests { private readonly TLruDateTimePolicy policy = new TLruDateTimePolicy(TimeSpan.FromSeconds(10)); + [Fact] + public void TimeToLiveShouldBeTenSecs() + { + this.policy.TimeToLive.Should().Be(TimeSpan.FromSeconds(10)); + } + [Fact] public void CreateItemInitializesKeyAndValue() { diff --git a/BitFaster.Caching.UnitTests/Lru/TlruLongTicksPolicyTests.cs b/BitFaster.Caching.UnitTests/Lru/TlruLongTicksPolicyTests.cs index 61f86eea..38b46664 100644 --- a/BitFaster.Caching.UnitTests/Lru/TlruLongTicksPolicyTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/TlruLongTicksPolicyTests.cs @@ -15,6 +15,12 @@ public class TLruLongTicksPolicyTests { private readonly TLruLongTicksPolicy policy = new TLruLongTicksPolicy(TimeSpan.FromSeconds(10)); + [Fact] + public void TimeToLiveShouldBeTenSecs() + { + this.policy.TimeToLive.Should().Be(TimeSpan.FromSeconds(10)); + } + [Fact] public void CreateItemInitializesKeyAndValue() { diff --git a/BitFaster.Caching.UnitTests/Lru/TlruTicksPolicyTests.cs b/BitFaster.Caching.UnitTests/Lru/TlruTicksPolicyTests.cs index e199f72c..cb282d95 100644 --- a/BitFaster.Caching.UnitTests/Lru/TlruTicksPolicyTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/TlruTicksPolicyTests.cs @@ -14,6 +14,12 @@ public class TLruTicksPolicyTests { private readonly TLruTicksPolicy policy = new TLruTicksPolicy(TimeSpan.FromSeconds(10)); + [Fact] + public void TimeToLiveShouldBeTenSecs() + { + this.policy.TimeToLive.Should().Be(TimeSpan.FromSeconds(10)); + } + [Fact] public void CreateItemInitializesKeyAndValue() { diff --git a/BitFaster.Caching.UnitTests/NoneTimePolicyTests.cs b/BitFaster.Caching.UnitTests/NoneTimePolicyTests.cs new file mode 100644 index 00000000..8c865123 --- /dev/null +++ b/BitFaster.Caching.UnitTests/NoneTimePolicyTests.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; + +namespace BitFaster.Caching.UnitTests +{ + public class NoneTimePolicyTests + { + [Fact] + public void CanExpireIsFalse() + { + NoneTimePolicy.Instance.CanExpire.Should().BeFalse(); + } + + [Fact] + public void TimeToLiveIsInfinite() + { + NoneTimePolicy.Instance.TimeToLive.Should().Be(NoneTimePolicy.Infinite); + } + + [Fact] + public void TrimExpiredIsNoOp() + { + Action trimExpired = () => NoneTimePolicy.Instance.TrimExpired(); + + trimExpired.Should().NotThrow(); + } + } +} diff --git a/BitFaster.Caching/NoneTimePolicy.cs b/BitFaster.Caching/NoneTimePolicy.cs index 179cff01..5a6de95c 100644 --- a/BitFaster.Caching/NoneTimePolicy.cs +++ b/BitFaster.Caching/NoneTimePolicy.cs @@ -6,7 +6,18 @@ namespace BitFaster.Caching { - public class NoneTimePolicy + public class NoneTimePolicy : ITimePolicy { + public static readonly TimeSpan Infinite = new TimeSpan(0, 0, 0, 0, -1); + + public static NoneTimePolicy Instance = new NoneTimePolicy(); + + public bool CanExpire => false; + + public TimeSpan TimeToLive => Infinite; + + public void TrimExpired() + { + } } } From ca88264a8d308360893a1b7b63c22bfda6e15e7d Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Tue, 26 Jul 2022 12:14:14 -0700 Subject: [PATCH 5/5] cleanup --- .../Atomic/AtomicFactoryScopedAsyncCache.cs | 2 -- BitFaster.Caching/Lru/ConcurrentTLru.cs | 9 --------- BitFaster.Caching/Lru/FastConcurrentTLru.cs | 9 --------- 3 files changed, 20 deletions(-) diff --git a/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs b/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs index 6a5f93d9..f98f8603 100644 --- a/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs +++ b/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs @@ -24,8 +24,6 @@ public AtomicFactoryScopedAsyncCache(ICache> c this.eventProxy = new EventProxy(cache.Events); } - public int Capacity => this.cache.Capacity; - public int Count => this.cache.Count; public ICacheMetrics Metrics => this.cache.Metrics; diff --git a/BitFaster.Caching/Lru/ConcurrentTLru.cs b/BitFaster.Caching/Lru/ConcurrentTLru.cs index f1f8ad1f..e6ba5720 100644 --- a/BitFaster.Caching/Lru/ConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentTLru.cs @@ -45,14 +45,5 @@ public ConcurrentTLru(int concurrencyLevel, ICapacityPartition capacity, IEquali : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), default) { } - - /// - /// Remove all expired items from the cache. - /// - /// O(n) where n is the number of items in the cache. - public void TrimExpired() - { - this.TrimAllDiscardedItems(); - } } } diff --git a/BitFaster.Caching/Lru/FastConcurrentTLru.cs b/BitFaster.Caching/Lru/FastConcurrentTLru.cs index d5f30746..889f0c09 100644 --- a/BitFaster.Caching/Lru/FastConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/FastConcurrentTLru.cs @@ -42,14 +42,5 @@ public FastConcurrentTLru(int concurrencyLevel, ICapacityPartition capacity, IEq : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), default) { } - - /// - /// Remove all expired items from the cache. - /// - /// O(n) where n is the number of items in the cache. - public void TrimExpired() - { - this.TrimAllDiscardedItems(); - } } }