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/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/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/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.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..f98f8603 100644 --- a/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs +++ b/BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs @@ -24,14 +24,14 @@ 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; public ICacheEvents> Events => this.eventProxy; + public CachePolicy Policy => this.cache.Policy; + /// public ICollection Keys => this.cache.Keys; @@ -83,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/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/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/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/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/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/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/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/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/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(); - } } } 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/NoneTimePolicy.cs b/BitFaster.Caching/NoneTimePolicy.cs new file mode 100644 index 00000000..5a6de95c --- /dev/null +++ b/BitFaster.Caching/NoneTimePolicy.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching +{ + 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/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) {