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)
{