Skip to content

Commit a71aa22

Browse files
committed
Remove abstract CacheEntry
CacheEntry<T> is now a record class with interfaces that expose the expiry property. All expiry dates are now rounded to the second.
1 parent 912a95b commit a71aa22

5 files changed

Lines changed: 165 additions & 190 deletions

File tree

benchmarks/CacheTower.Benchmarks/CacheStackBenchmark.cs

Lines changed: 93 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -31,100 +31,100 @@ public ConfigSettings()
3131
}
3232
}
3333

34-
[Benchmark]
35-
public async Task Set()
36-
{
37-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
38-
{
39-
for (var i = 0; i < WorkIterations; i++)
40-
{
41-
await cacheStack.SetAsync("Set", 15, TimeSpan.FromDays(1));
42-
}
43-
}
44-
}
45-
[Benchmark]
46-
public async Task Set_TwoLayers()
47-
{
48-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
49-
{
50-
for (var i = 0; i < WorkIterations; i++)
51-
{
52-
await cacheStack.SetAsync("Set", 15, TimeSpan.FromDays(1));
53-
}
54-
}
55-
}
56-
[Benchmark]
57-
public async Task Evict()
58-
{
59-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
60-
{
61-
for (var i = 0; i < WorkIterations; i++)
62-
{
63-
await cacheStack.SetAsync("Evict", 15, TimeSpan.FromDays(1));
64-
await cacheStack.EvictAsync("Evict");
65-
}
66-
}
67-
}
68-
[Benchmark]
69-
public async Task Evict_TwoLayers()
70-
{
71-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
72-
{
73-
for (var i = 0; i < WorkIterations; i++)
74-
{
75-
await cacheStack.SetAsync("Evict", 15, TimeSpan.FromDays(1));
76-
await cacheStack.EvictAsync("Evict");
77-
}
78-
}
79-
}
80-
[Benchmark]
81-
public async Task Cleanup()
82-
{
83-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
84-
{
85-
for (var i = 0; i < WorkIterations; i++)
86-
{
87-
await cacheStack.SetAsync("Cleanup", 15, TimeSpan.FromDays(1));
88-
await cacheStack.CleanupAsync();
89-
}
90-
}
91-
}
92-
[Benchmark]
93-
public async Task Cleanup_TwoLayers()
94-
{
95-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
96-
{
97-
for (var i = 0; i < WorkIterations; i++)
98-
{
99-
await cacheStack.SetAsync("Cleanup", 15, TimeSpan.FromDays(1));
100-
await cacheStack.CleanupAsync();
101-
}
102-
}
103-
}
104-
[Benchmark]
105-
public async Task GetMiss()
106-
{
107-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
108-
{
109-
for (var i = 0; i < WorkIterations; i++)
110-
{
111-
await cacheStack.GetAsync<int>("GetMiss");
112-
}
113-
}
114-
}
115-
[Benchmark]
116-
public async Task GetHit()
117-
{
118-
await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
119-
{
120-
await cacheStack.SetAsync("GetHit", 15, TimeSpan.FromDays(1));
34+
//[Benchmark]
35+
//public async Task Set()
36+
//{
37+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
38+
// {
39+
// for (var i = 0; i < WorkIterations; i++)
40+
// {
41+
// await cacheStack.SetAsync("Set", 15, TimeSpan.FromDays(1));
42+
// }
43+
// }
44+
//}
45+
//[Benchmark]
46+
//public async Task Set_TwoLayers()
47+
//{
48+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
49+
// {
50+
// for (var i = 0; i < WorkIterations; i++)
51+
// {
52+
// await cacheStack.SetAsync("Set", 15, TimeSpan.FromDays(1));
53+
// }
54+
// }
55+
//}
56+
//[Benchmark]
57+
//public async Task Evict()
58+
//{
59+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
60+
// {
61+
// for (var i = 0; i < WorkIterations; i++)
62+
// {
63+
// await cacheStack.SetAsync("Evict", 15, TimeSpan.FromDays(1));
64+
// await cacheStack.EvictAsync("Evict");
65+
// }
66+
// }
67+
//}
68+
//[Benchmark]
69+
//public async Task Evict_TwoLayers()
70+
//{
71+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
72+
// {
73+
// for (var i = 0; i < WorkIterations; i++)
74+
// {
75+
// await cacheStack.SetAsync("Evict", 15, TimeSpan.FromDays(1));
76+
// await cacheStack.EvictAsync("Evict");
77+
// }
78+
// }
79+
//}
80+
//[Benchmark]
81+
//public async Task Cleanup()
82+
//{
83+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
84+
// {
85+
// for (var i = 0; i < WorkIterations; i++)
86+
// {
87+
// await cacheStack.SetAsync("Cleanup", 15, TimeSpan.FromDays(1));
88+
// await cacheStack.CleanupAsync();
89+
// }
90+
// }
91+
//}
92+
//[Benchmark]
93+
//public async Task Cleanup_TwoLayers()
94+
//{
95+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer(), new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
96+
// {
97+
// for (var i = 0; i < WorkIterations; i++)
98+
// {
99+
// await cacheStack.SetAsync("Cleanup", 15, TimeSpan.FromDays(1));
100+
// await cacheStack.CleanupAsync();
101+
// }
102+
// }
103+
//}
104+
//[Benchmark]
105+
//public async Task GetMiss()
106+
//{
107+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
108+
// {
109+
// for (var i = 0; i < WorkIterations; i++)
110+
// {
111+
// await cacheStack.GetAsync<int>("GetMiss");
112+
// }
113+
// }
114+
//}
115+
//[Benchmark]
116+
//public async Task GetHit()
117+
//{
118+
// await using (var cacheStack = new CacheStack(new[] { new MemoryCacheLayer() }, Array.Empty<ICacheExtension>()))
119+
// {
120+
// await cacheStack.SetAsync("GetHit", 15, TimeSpan.FromDays(1));
121121

122-
for (var i = 0; i < WorkIterations; i++)
123-
{
124-
await cacheStack.GetAsync<int>("GetHit");
125-
}
126-
}
127-
}
122+
// for (var i = 0; i < WorkIterations; i++)
123+
// {
124+
// await cacheStack.GetAsync<int>("GetHit");
125+
// }
126+
// }
127+
//}
128128
[Benchmark]
129129
public async Task GetOrSet_NeverStale()
130130
{

src/CacheTower.Serializers.Protobuf/ProtobufCacheSerializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ private static class SerializerConfig<T>
3434
{
3535
static SerializerConfig()
3636
{
37-
if (typeof(T).IsSubclassOf(typeof(CacheEntry)))
37+
if (typeof(ICacheEntry).IsAssignableFrom(typeof(T)))
3838
{
3939
RuntimeTypeModel.Default.Add(typeof(T))
4040
.Add(1, nameof(CacheEntry<object>.Expiry))

src/CacheTower/CacheEntry.cs

Lines changed: 64 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,84 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Runtime.CompilerServices;
4-
using System.Text;
53
using CacheTower.Internal;
64

7-
namespace CacheTower
5+
namespace CacheTower;
6+
7+
/// <summary>
8+
/// Container for the cache entry expiry date.
9+
/// </summary>
10+
public interface ICacheEntry
811
{
912
/// <summary>
10-
/// Container for the cache entry expiry date.
13+
/// The expiry date for the cache entry.
1114
/// </summary>
12-
public abstract class CacheEntry
13-
{
14-
/// <summary>
15-
/// The expiry date for the cache entry.
16-
/// </summary>
17-
public DateTime Expiry { get; }
18-
19-
/// <summary>
20-
/// Creates a new <see cref="CacheEntry"/> with the given expiry date.
21-
/// </summary>
22-
/// <param name="expiry">The expiry date of the cache entry. This will be rounded down to the second.</param>
23-
protected CacheEntry(DateTime expiry)
24-
{
25-
//Force the resolution of the expiry date to be to the second
26-
Expiry = new DateTime(
27-
expiry.Year, expiry.Month, expiry.Day, expiry.Hour, expiry.Minute, expiry.Second, DateTimeKind.Utc
28-
);
29-
}
15+
DateTime Expiry { get; }
16+
}
3017

31-
/// <summary>
32-
/// Calculates the stale date for the cache entry using the provided <paramref name="cacheSettings"/>.
33-
/// </summary>
34-
/// <remarks>
35-
/// If <see cref="CacheSettings.StaleAfter"/> is not configured, the stale date is the expiry date.
36-
/// </remarks>
37-
/// <param name="cacheSettings">The cache settings to use for the calculation.</param>
38-
/// <returns>The date that the cache entry can be considered stale.</returns>
39-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
40-
public DateTime GetStaleDate(CacheSettings cacheSettings)
41-
{
42-
if (cacheSettings.StaleAfter.HasValue)
43-
{
44-
return Expiry - cacheSettings.TimeToLive + cacheSettings.StaleAfter!.Value;
45-
}
46-
else
47-
{
48-
return Expiry;
49-
}
50-
}
51-
}
18+
/// <summary>
19+
/// Container for the cached value and its expiry date.
20+
/// </summary>
21+
/// <typeparam name="T"></typeparam>
22+
public interface ICacheEntry<T> : ICacheEntry
23+
{
24+
/// <summary>
25+
/// The cached value.
26+
/// </summary>
27+
T? Value { get; }
28+
}
5229

30+
/// <summary>
31+
/// Extension methods for <see cref="ICacheEntry"/>.
32+
/// </summary>
33+
public static class CacheEntryExtensions
34+
{
5335
/// <summary>
54-
/// Container for both the cached value and its expiry date.
36+
/// Calculates the stale date for an <see cref="ICacheEntry"/> based on the <paramref name="cacheEntry"/>'s expiry and <paramref name="cacheSettings"/>.
5537
/// </summary>
56-
/// <typeparam name="T"></typeparam>
57-
public class CacheEntry<T> : CacheEntry, IEquatable<CacheEntry<T?>?>
38+
/// <remarks>
39+
/// When <see cref="CacheSettings.StaleAfter"/> is <see langword="null"/>, this will return the <paramref name="cacheEntry"/>'s expiry.
40+
/// </remarks>
41+
/// <param name="cacheEntry">The cache entry to get the stale date for.</param>
42+
/// <param name="cacheSettings">The cache settings to use as part of the stale date calculation.</param>
43+
/// <returns></returns>
44+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45+
public static DateTime GetStaleDate(this ICacheEntry cacheEntry, CacheSettings cacheSettings)
5846
{
59-
/// <summary>
60-
/// The cached value.
61-
/// </summary>
62-
public T? Value { get; }
63-
64-
/// <summary>
65-
/// Creates a new <see cref="CacheEntry"/> with the given <paramref name="value"/> and an expiry adjusted to the <paramref name="timeToLive"/>.
66-
/// </summary>
67-
/// <param name="value">The value to cache.</param>
68-
/// <param name="timeToLive">The amount of time before the cache entry expires.</param>
69-
internal CacheEntry(T? value, TimeSpan timeToLive) : this(value, DateTimeProvider.Now + timeToLive) { }
70-
/// <summary>
71-
/// Creates a new <see cref="CacheEntry"/> with the given <paramref name="value"/> and <paramref name="expiry"/>.
72-
/// </summary>
73-
/// <param name="value">The value to cache.</param>
74-
/// <param name="expiry">The expiry date of the cache entry. This will be rounded down to the second.</param>
75-
public CacheEntry(T? value, DateTime expiry) : base(expiry)
47+
if (cacheSettings.StaleAfter.HasValue)
7648
{
77-
Value = value;
49+
return cacheEntry.Expiry - cacheSettings.TimeToLive + cacheSettings.StaleAfter!.Value;
7850
}
79-
80-
/// <inheritdoc/>
81-
public bool Equals(CacheEntry<T?>? other)
51+
else
8252
{
83-
if (other == null)
84-
{
85-
return false;
86-
}
87-
88-
return Equals(Value, other.Value) &&
89-
Expiry == other.Expiry;
53+
return cacheEntry.Expiry;
9054
}
55+
}
56+
}
9157

92-
/// <inheritdoc/>
93-
public override bool Equals(object? obj)
94-
{
95-
if (obj is CacheEntry<T?> objOfType)
96-
{
97-
return Equals(objOfType);
98-
}
58+
/// <summary>
59+
/// Container for both the cached value and its expiry date.
60+
/// </summary>
61+
/// <typeparam name="T"></typeparam>
62+
/// <param name="Value">The value to cache.</param>
63+
/// <param name="Expiry">The expiry date of the cache entry. This will be rounded down to the second.</param>
64+
public sealed record CacheEntry<T>(T? Value, DateTime Expiry) : ICacheEntry<T>
65+
{
66+
/// <summary>
67+
/// The cached value.
68+
/// </summary>
69+
public T? Value { get; } = Value;
9970

100-
return false;
101-
}
71+
/// <summary>
72+
/// The expiry date for the cache entry.
73+
/// </summary>
74+
public DateTime Expiry { get; } = new DateTime(
75+
Expiry.Year, Expiry.Month, Expiry.Day, Expiry.Hour, Expiry.Minute, Expiry.Second, DateTimeKind.Utc
76+
);
10277

103-
/// <inheritdoc/>
104-
public override int GetHashCode()
105-
{
106-
return (Value?.GetHashCode() ?? 1) ^ Expiry.GetHashCode();
107-
}
108-
}
78+
/// <summary>
79+
/// Creates a new <see cref="ICacheEntry"/> with the given <paramref name="value"/> and an expiry adjusted to the <paramref name="timeToLive"/>.
80+
/// </summary>
81+
/// <param name="value">The value to cache.</param>
82+
/// <param name="timeToLive">The amount of time before the cache entry expires.</param>
83+
internal CacheEntry(T? value, TimeSpan timeToLive) : this(value, DateTimeProvider.Now + timeToLive) { }
10984
}

0 commit comments

Comments
 (0)