Skip to content

Commit 840fc4a

Browse files
authored
Optimize Microsoft.Extensions.Caching.Memory (#111050)
* Optimize Microsoft.Extensions.Caching.Memory * AbsoluteExpiration opts * Address PR feedback * Fix merge conflicts * Address PR feedback
1 parent d6cd577 commit 840fc4a

File tree

8 files changed

+119
-121
lines changed

8 files changed

+119
-121
lines changed

src/libraries/Microsoft.Extensions.Caching.Abstractions/src/CacheEntryExtensions.cs

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,8 @@ public static ICacheEntry SetSlidingExpiration(
9191
/// <param name="entry">The <see cref="ICacheEntry"/>.</param>
9292
/// <param name="callback">The callback to run after the entry is evicted.</param>
9393
/// <returns>The <see cref="ICacheEntry"/> for chaining.</returns>
94-
public static ICacheEntry RegisterPostEvictionCallback(
95-
this ICacheEntry entry,
96-
PostEvictionDelegate callback)
97-
{
98-
ThrowHelper.ThrowIfNull(callback);
99-
100-
return entry.RegisterPostEvictionCallbackNoValidation(callback, state: null);
101-
}
94+
public static ICacheEntry RegisterPostEvictionCallback(this ICacheEntry entry, PostEvictionDelegate callback)
95+
=> RegisterPostEvictionCallback(entry, callback, state: null);
10296

10397
/// <summary>
10498
/// Fires the given callback after the cache entry is evicted from the cache.
@@ -114,14 +108,6 @@ public static ICacheEntry RegisterPostEvictionCallback(
114108
{
115109
ThrowHelper.ThrowIfNull(callback);
116110

117-
return entry.RegisterPostEvictionCallbackNoValidation(callback, state);
118-
}
119-
120-
private static ICacheEntry RegisterPostEvictionCallbackNoValidation(
121-
this ICacheEntry entry,
122-
PostEvictionDelegate callback,
123-
object? state)
124-
{
125111
entry.PostEvictionCallbacks.Add(new PostEvictionCallbackRegistration()
126112
{
127113
EvictionCallback = callback,
@@ -179,18 +165,24 @@ public static ICacheEntry SetOptions(this ICacheEntry entry, MemoryCacheEntryOpt
179165
entry.Priority = options.Priority;
180166
entry.Size = options.Size;
181167

182-
foreach (IChangeToken expirationToken in options.ExpirationTokens)
168+
if (options.ExpirationTokensDirect is { } expirationTokens)
183169
{
184-
entry.AddExpirationToken(expirationToken);
170+
foreach (IChangeToken expirationToken in expirationTokens)
171+
{
172+
entry.AddExpirationToken(expirationToken);
173+
}
185174
}
186175

187-
for (int i = 0; i < options.PostEvictionCallbacks.Count; i++)
176+
if (options.PostEvictionCallbacksDirect is { } postEvictionCallbacks)
188177
{
189-
PostEvictionCallbackRegistration postEvictionCallback = options.PostEvictionCallbacks[i];
190-
if (postEvictionCallback.EvictionCallback is null)
191-
ThrowNullCallback(i, nameof(options));
192-
193-
entry.RegisterPostEvictionCallbackNoValidation(postEvictionCallback.EvictionCallback, postEvictionCallback.State);
178+
for (int i = 0; i < postEvictionCallbacks.Count; i++)
179+
{
180+
PostEvictionCallbackRegistration postEvictionCallback = postEvictionCallbacks[i];
181+
if (postEvictionCallback.EvictionCallback is null)
182+
ThrowNullCallback(i, nameof(options));
183+
184+
entry.PostEvictionCallbacks.Add(postEvictionCallback);
185+
}
194186
}
195187

196188
return entry;

src/libraries/Microsoft.Extensions.Caching.Abstractions/src/DistributedCacheEntryOptions.cs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,29 @@ namespace Microsoft.Extensions.Caching.Distributed
1010
/// </summary>
1111
public class DistributedCacheEntryOptions
1212
{
13-
private DateTimeOffset? _absoluteExpiration;
14-
private TimeSpan? _absoluteExpirationRelativeToNow;
15-
private TimeSpan? _slidingExpiration;
13+
private DateTimeOffset _absoluteExpiration;
14+
private bool _absoluteExpirationSet;
15+
private TimeSpan _absoluteExpirationRelativeToNow;
16+
private bool _absoluteExpirationRelativeToNowSet;
17+
private TimeSpan _slidingExpiration;
18+
private bool _slidingExpirationSet;
19+
private bool _frozen;
1620

1721
/// <summary>
1822
/// Gets or sets an absolute expiration date for the cache entry.
1923
/// </summary>
2024
public DateTimeOffset? AbsoluteExpiration
2125
{
22-
get
23-
{
24-
return _absoluteExpiration;
25-
}
26-
set
27-
{
28-
_absoluteExpiration = value;
29-
}
26+
get => _absoluteExpirationSet ? _absoluteExpiration : null;
27+
set => Set(ref _absoluteExpiration, ref _absoluteExpirationSet, value);
3028
}
3129

3230
/// <summary>
3331
/// Gets or sets an absolute expiration time, relative to now.
3432
/// </summary>
3533
public TimeSpan? AbsoluteExpirationRelativeToNow
3634
{
37-
get
38-
{
39-
return _absoluteExpirationRelativeToNow;
40-
}
35+
get => _absoluteExpirationRelativeToNowSet ? _absoluteExpirationRelativeToNow : null;
4136
set
4237
{
4338
if (value <= TimeSpan.Zero)
@@ -48,7 +43,7 @@ public TimeSpan? AbsoluteExpirationRelativeToNow
4843
"The relative expiration value must be positive.");
4944
}
5045

51-
_absoluteExpirationRelativeToNow = value;
46+
Set(ref _absoluteExpirationRelativeToNow, ref _absoluteExpirationRelativeToNowSet, value);
5247
}
5348
}
5449

@@ -58,10 +53,7 @@ public TimeSpan? AbsoluteExpirationRelativeToNow
5853
/// </summary>
5954
public TimeSpan? SlidingExpiration
6055
{
61-
get
62-
{
63-
return _slidingExpiration;
64-
}
56+
get => _slidingExpirationSet ? _slidingExpiration : null;
6557
set
6658
{
6759
if (value <= TimeSpan.Zero)
@@ -71,8 +63,27 @@ public TimeSpan? SlidingExpiration
7163
value,
7264
"The sliding expiration value must be positive.");
7365
}
74-
_slidingExpiration = value;
66+
Set(ref _slidingExpiration, ref _slidingExpirationSet, value);
7567
}
7668
}
69+
70+
internal DistributedCacheEntryOptions Freeze()
71+
{
72+
_frozen = true;
73+
return this;
74+
}
75+
76+
private void Set<T>(ref T field, ref bool isSet, in T? value) where T : struct
77+
{
78+
if (_frozen)
79+
{
80+
ThrowFrozen();
81+
}
82+
83+
field = value.GetValueOrDefault();
84+
isSet = value.HasValue;
85+
86+
static void ThrowFrozen() => throw new InvalidOperationException("This instance has been frozen and cannot be mutated");
87+
}
7788
}
7889
}

src/libraries/Microsoft.Extensions.Caching.Abstractions/src/DistributedCacheExtensions.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ namespace Microsoft.Extensions.Caching.Distributed
1313
/// </summary>
1414
public static class DistributedCacheExtensions
1515
{
16+
private static readonly DistributedCacheEntryOptions DefaultOptions = new DistributedCacheEntryOptions().Freeze();
17+
1618
/// <summary>
1719
/// Sets a sequence of bytes in the specified cache with the specified key.
1820
/// </summary>
@@ -25,7 +27,7 @@ public static void Set(this IDistributedCache cache, string key, byte[] value)
2527
ThrowHelper.ThrowIfNull(key);
2628
ThrowHelper.ThrowIfNull(value);
2729

28-
cache.Set(key, value, new DistributedCacheEntryOptions());
30+
cache.Set(key, value, DefaultOptions);
2931
}
3032

3133
/// <summary>
@@ -42,7 +44,7 @@ public static void Set(this IDistributedCache cache, string key, byte[] value)
4244
ThrowHelper.ThrowIfNull(key);
4345
ThrowHelper.ThrowIfNull(value);
4446

45-
return cache.SetAsync(key, value, new DistributedCacheEntryOptions(), token);
47+
return cache.SetAsync(key, value, DefaultOptions, token);
4648
}
4749

4850
/// <summary>
@@ -54,7 +56,7 @@ public static void Set(this IDistributedCache cache, string key, byte[] value)
5456
/// <exception cref="System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
5557
public static void SetString(this IDistributedCache cache, string key, string value)
5658
{
57-
cache.SetString(key, value, new DistributedCacheEntryOptions());
59+
cache.SetString(key, value, DefaultOptions);
5860
}
5961

6062
/// <summary>
@@ -84,7 +86,7 @@ public static void SetString(this IDistributedCache cache, string key, string va
8486
/// <exception cref="System.ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> is null.</exception>
8587
public static Task SetStringAsync(this IDistributedCache cache, string key, string value, CancellationToken token = default(CancellationToken))
8688
{
87-
return cache.SetStringAsync(key, value, new DistributedCacheEntryOptions(), token);
89+
return cache.SetStringAsync(key, value, DefaultOptions, token);
8890
}
8991

9092
/// <summary>

src/libraries/Microsoft.Extensions.Caching.Abstractions/src/MemoryCacheEntryOptions.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ public class MemoryCacheEntryOptions
1616
private TimeSpan? _absoluteExpirationRelativeToNow;
1717
private TimeSpan? _slidingExpiration;
1818
private long? _size;
19+
private List<IChangeToken>? _expirationTokens;
20+
private List<PostEvictionCallbackRegistration>? _postEvictionCallbacks;
1921

2022
/// <summary>
2123
/// Gets or sets an absolute expiration date for the cache entry.
@@ -81,13 +83,16 @@ public TimeSpan? SlidingExpiration
8183
/// <summary>
8284
/// Gets the <see cref="IChangeToken"/> instances which cause the cache entry to expire.
8385
/// </summary>
84-
public IList<IChangeToken> ExpirationTokens { get; } = new List<IChangeToken>();
86+
public IList<IChangeToken> ExpirationTokens => _expirationTokens ??= [];
87+
88+
internal List<IChangeToken>? ExpirationTokensDirect => _expirationTokens;
8589

8690
/// <summary>
8791
/// Gets or sets the callbacks will be fired after the cache entry is evicted from the cache.
8892
/// </summary>
89-
public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; }
90-
= new List<PostEvictionCallbackRegistration>();
93+
public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks => _postEvictionCallbacks ??= [];
94+
95+
internal List<PostEvictionCallbackRegistration>? PostEvictionCallbacksDirect => _postEvictionCallbacks;
9196

9297
/// <summary>
9398
/// Gets or sets the priority for keeping the cache entry in the cache during a

src/libraries/Microsoft.Extensions.Caching.Abstractions/src/MemoryCacheExtensions.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public static class CacheExtensions
3333
/// <returns>The value associated with this key, or <c>default(TItem)</c> if the key is not present.</returns>
3434
public static TItem? Get<TItem>(this IMemoryCache cache, object key)
3535
{
36-
return (TItem?)(cache.Get(key) ?? default(TItem));
36+
cache.TryGetValue(key, out object? value);
37+
return value is null ? default : (TItem)value;
3738
}
3839

3940
/// <summary>
@@ -75,8 +76,9 @@ public static bool TryGetValue<TItem>(this IMemoryCache cache, object key, out T
7576
/// <returns>The value that was set.</returns>
7677
public static TItem Set<TItem>(this IMemoryCache cache, object key, TItem value)
7778
{
78-
using ICacheEntry entry = cache.CreateEntry(key);
79+
ICacheEntry entry = cache.CreateEntry(key);
7980
entry.Value = value;
81+
entry.Dispose();
8082

8183
return value;
8284
}

src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,11 @@ internal long AbsoluteExpirationTicks
8484
{
8585
DateTimeOffset expiration = value.GetValueOrDefault();
8686
_absoluteExpirationTicks = expiration.UtcTicks;
87+
#if NET
88+
_absoluteExpirationOffsetMinutes = (short)expiration.TotalOffsetMinutes;
89+
#else
8790
_absoluteExpirationOffsetMinutes = (short)(expiration.Offset.Ticks / TimeSpan.TicksPerMinute);
91+
#endif
8892
}
8993
}
9094
}

0 commit comments

Comments
 (0)