Skip to content

Commit bb12cf4

Browse files
committed
Better logging for eviction events. Changed dictionary cache eviction to be Timer based => Increased minimum netstandard from 1.1 to 1.2 (might be breaking change)
1 parent b41d66d commit bb12cf4

13 files changed

Lines changed: 114 additions & 69 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ Linux, Mono | -
1818

1919
| Package Name | .Net 4.0 | .Net 4.5 | Minimum .NET Platform Standard version *
2020
|--------------| :-------: | :-------: | :-------:
21-
| [CacheManager.Core] [Core.nuget] | x | x | 1.1
21+
| [CacheManager.Core] [Core.nuget] | x | x | 1.2
2222
| [CacheManager.StackExchange.Redis] [Redis.nuget] | x | x | 1.5
2323
| [CacheManager.SystemRuntimeCaching] [SystemRuntimeCaching.nuget] | x | x | -
2424
| [CacheManager.Microsoft.Extensions.Caching.Memory] [MSCache.nuget] | - | \>=4.5.1 | 1.3
25-
| [CacheManager.Microsoft.Extensions.Configuration] [Configuration.nuget] | - | x | 1.3
26-
| [CacheManager.Microsoft.Extensions.Logging] [Logging.nuget] | - | x | 1.3
25+
| [CacheManager.Microsoft.Extensions.Configuration] [Configuration.nuget] | - | x | 1.2
26+
| [CacheManager.Microsoft.Extensions.Logging] [Logging.nuget] | - | x | 1.2
27+
| [CacheManager.Serialization.Bond] [Bond.nuget] | x | x | 1.2
2728
| [CacheManager.Serialization.Json] [Json.nuget] | x | x | 1.2
2829
| [CacheManager.Serialization.ProtoBuf] [ProtoBuf.nuget] | x | x | 1.3
2930
| [CacheManager.Web] [Web.nuget] | - | x | -

src/CacheManager.Core/BaseCacheManager.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,11 @@ private BaseCacheManager(string name, ICacheManagerConfiguration configuration)
7070
var serializer = CacheReflectionHelper.CreateSerializer(configuration, loggerFactory);
7171

7272
Logger = loggerFactory.CreateLogger(this);
73+
7374
_logTrace = Logger.IsEnabled(LogLevel.Trace);
75+
7476
Logger.LogInfo("Cache manager: adding cache handles...");
77+
7578
try
7679
{
7780
_cacheHandles = CacheReflectionHelper.CreateCacheHandles(this, loggerFactory, serializer).ToArray();
@@ -82,9 +85,15 @@ private BaseCacheManager(string name, ICacheManagerConfiguration configuration)
8285
var handleIndex = index;
8386
handle.OnCacheSpecificRemove += (sender, args) =>
8487
{
88+
// base cache handle does logging for this
8589
TriggerOnRemoveByHandle(args.Key, args.Region, args.Reason, handleIndex + 1);
8690
if (Configuration.UpdateMode == CacheUpdateMode.Up)
8791
{
92+
if (_logTrace)
93+
{
94+
Logger.LogTrace("Cleaning handles above '{0}' because of remove event.", handleIndex);
95+
}
96+
8897
EvictFromHandlesAbove(args.Key, args.Region, handleIndex);
8998
}
9099
};

src/CacheManager.Core/CacheManager.Core.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Import Project="..\..\tools\common.props" />
33
<PropertyGroup>
44
<Description>CacheManager is an open source caching abstraction layer for .NET written in C#. It supports various cache providers and implements many advanced features. The Core library contains all base interfaces and tools. You should install at least one other CacheManager package to get cache handle implementations.</Description>
5-
<TargetFrameworks>net40;net45;netstandard1.1</TargetFrameworks>
5+
<TargetFrameworks>net40;net45;netstandard1.2</TargetFrameworks>
66
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
77
<PackageTags>Caching;Cache;CacheManager;Distributed Cache;StackExchange Redis;Azure AppFabric;Memcached</PackageTags>
88
</PropertyGroup>
@@ -12,9 +12,9 @@
1212
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
1313
<Reference Include="System.Configuration" />
1414
</ItemGroup>
15-
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
15+
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.2' ">
1616
<DefineConstants>$(DefineConstants);NETSTANDARD</DefineConstants>
1717
</PropertyGroup>
18-
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
18+
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.2' ">
1919
</ItemGroup>
2020
</Project>

src/CacheManager.Core/Internal/BaseCacheHandle.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using CacheManager.Core.Logging;
23
using static CacheManager.Core.Utility.Guard;
34

45
namespace CacheManager.Core.Internal
@@ -203,6 +204,11 @@ protected void TriggerCacheSpecificRemove(string key, string region, CacheItemRe
203204
{
204205
NotNullOrWhiteSpace(key, nameof(key));
205206

207+
if (Logger.IsEnabled(LogLevel.Debug))
208+
{
209+
Logger.LogDebug("'{0}' triggered remove '{1}:{2}' because '{3}'.", Configuration.Name, region, key, reason);
210+
}
211+
206212
OnCacheSpecificRemove?.Invoke(this, new CacheItemRemovedEventArgs(key, region, reason));
207213
}
208214

src/CacheManager.Core/Internal/CacheEventArgs.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ public CacheItemRemovedEventArgs(string key, string region, CacheItemRemovedReas
8686
/// Gets a value indicating the cache level the event got triggered by.
8787
/// </summary>
8888
public int Level { get; }
89+
90+
/// <inheritdoc />
91+
public override string ToString()
92+
{
93+
return $"CacheItemRemovedEventArgs {Region}:{Key} - {Reason} {Level}";
94+
}
8995
}
9096

9197
/// <summary>
@@ -136,6 +142,12 @@ public CacheActionEventArgs(string key, string region, CacheActionEventArgOrigin
136142
/// Gets the event origin indicating if the event was triggered by a local action or remotly, through the backplane.
137143
/// </summary>
138144
public CacheActionEventArgOrigin Origin { get; } = CacheActionEventArgOrigin.Local;
145+
146+
/// <inheritdoc />
147+
public override string ToString()
148+
{
149+
return $"CacheActionEventArgs {Region}:{Key} - {Origin}";
150+
}
139151
}
140152

141153
/// <summary>
@@ -156,6 +168,12 @@ public CacheClearEventArgs(CacheActionEventArgOrigin origin = CacheActionEventAr
156168
/// Gets the event origin indicating if the event was triggered by a local action or remotly, through the backplane.
157169
/// </summary>
158170
public CacheActionEventArgOrigin Origin { get; }
171+
172+
/// <inheritdoc />
173+
public override string ToString()
174+
{
175+
return $"CacheClearEventArgs {Origin}";
176+
}
159177
}
160178

161179
/// <summary>
@@ -187,5 +205,11 @@ public CacheClearRegionEventArgs(string region, CacheActionEventArgOrigin origin
187205
/// Gets the event origin indicating if the event was triggered by a local action or remotly, through the backplane.
188206
/// </summary>
189207
public CacheActionEventArgOrigin Origin { get; }
208+
209+
/// <inheritdoc />
210+
public override string ToString()
211+
{
212+
return $"CacheClearRegionEventArgs {Region} - {Origin}";
213+
}
190214
}
191215
}

src/CacheManager.Core/Internal/DictionaryCacheHandle.cs

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Concurrent;
33
using System.Linq;
44
using System.Threading;
5-
using System.Threading.Tasks;
65
using CacheManager.Core.Logging;
76
using static CacheManager.Core.Utility.Guard;
87

@@ -14,11 +13,15 @@ namespace CacheManager.Core.Internal
1413
/// <typeparam name="TCacheValue">The type of the cache value.</typeparam>
1514
public class DictionaryCacheHandle<TCacheValue> : BaseCacheHandle<TCacheValue>
1615
{
17-
private const int ScanInterval = 10000;
18-
private ConcurrentDictionary<string, CacheItem<TCacheValue>> _cache;
19-
private long _lastScan = 0L;
20-
private bool _scanRunning;
21-
private object _startScanLock = new object();
16+
private const int ScanInterval = 5000;
17+
private readonly static Random _random = new Random();
18+
private readonly ConcurrentDictionary<string, CacheItem<TCacheValue>> _cache;
19+
private readonly Timer _timer;
20+
21+
//private long _lastScan = 0L;
22+
private int _scanRunning;
23+
24+
//private object _startScanLock = new object();
2225

2326
/// <summary>
2427
/// Initializes a new instance of the <see cref="DictionaryCacheHandle{TCacheValue}"/> class.
@@ -32,6 +35,7 @@ public DictionaryCacheHandle(ICacheManagerConfiguration managerConfiguration, Ca
3235
NotNull(loggerFactory, nameof(loggerFactory));
3336
Logger = loggerFactory.CreateLogger(this);
3437
_cache = new ConcurrentDictionary<string, CacheItem<TCacheValue>>();
38+
_timer = new Timer(TimerLoop, null, _random.Next(100, ScanInterval), ScanInterval);
3539
}
3640

3741
/// <summary>
@@ -63,8 +67,6 @@ public override void ClearRegion(string region)
6367
CacheItem<TCacheValue> val = null;
6468
_cache.TryRemove(item.Key, out val);
6569
}
66-
67-
StartScanExpiredItems();
6870
}
6971

7072
/// <inheritdoc />
@@ -97,7 +99,6 @@ protected override bool AddInternalPrepared(CacheItem<TCacheValue> item)
9799

98100
var key = GetKey(item.Key, item.Region);
99101

100-
StartScanExpiredItems();
101102
return _cache.TryAdd(key, item);
102103
}
103104

@@ -130,7 +131,6 @@ protected override CacheItem<TCacheValue> GetCacheItemInternal(string key, strin
130131
}
131132
}
132133

133-
StartScanExpiredItems();
134134
return result;
135135
}
136136

@@ -145,7 +145,6 @@ protected override void PutInternalPrepared(CacheItem<TCacheValue> item)
145145
NotNull(item, nameof(item));
146146

147147
_cache[GetKey(item.Key, item.Region)] = item;
148-
StartScanExpiredItems();
149148
}
150149

151150
/// <summary>
@@ -207,64 +206,60 @@ private static bool IsExpired(CacheItem<TCacheValue> item, DateTime now)
207206
return false;
208207
}
209208

210-
private static void ScanForExpiredItems(DictionaryCacheHandle<TCacheValue> cacheHandle)
209+
private void TimerLoop(object state)
210+
{
211+
if (_scanRunning > 0)
212+
{
213+
return;
214+
}
215+
216+
if (Interlocked.CompareExchange(ref _scanRunning, 1, 0) == 0)
217+
{
218+
try
219+
{
220+
if (Logger.IsEnabled(LogLevel.Debug))
221+
{
222+
Logger.LogDebug("'{0}' starting eviction scan.", Configuration.Name);
223+
}
224+
225+
ScanForExpiredItems();
226+
}
227+
catch (Exception ex)
228+
{
229+
Logger.LogError(ex, "Error occurred during eviction scan.");
230+
}
231+
finally
232+
{
233+
Interlocked.Exchange(ref _scanRunning, 0);
234+
}
235+
}
236+
}
237+
238+
private int ScanForExpiredItems()
211239
{
212-
cacheHandle._scanRunning = true;
213240
var removed = 0;
214241
var now = DateTime.UtcNow;
215-
foreach (var item in cacheHandle._cache.Values)
242+
foreach (var item in _cache.Values)
216243
{
217244
if (IsExpired(item, now))
218245
{
219-
cacheHandle.RemoveInternal(item.Key, item.Region);
246+
RemoveInternal(item.Key, item.Region);
220247

221248
// trigger global eviction event
222-
cacheHandle.TriggerCacheSpecificRemove(item.Key, item.Region, CacheItemRemovedReason.Expired);
249+
TriggerCacheSpecificRemove(item.Key, item.Region, CacheItemRemovedReason.Expired);
223250

224251
// fix stats
225-
cacheHandle.Stats.OnRemove(item.Region);
252+
Stats.OnRemove(item.Region);
226253
removed++;
227254
}
228255
}
229256

230-
if (removed > 0 && cacheHandle.Logger.IsEnabled(LogLevel.Information))
257+
if (removed > 0 && Logger.IsEnabled(LogLevel.Information))
231258
{
232-
cacheHandle.Logger.LogInfo("Removed {0} expired items.", removed);
259+
Logger.LogInfo("'{0}' removed '{1}' expired items during eviction run.", Configuration.Name, removed);
233260
}
234261

235-
cacheHandle._scanRunning = false;
236-
}
237-
238-
private void StartScanExpiredItems()
239-
{
240-
var currentTicks = Environment.TickCount & int.MaxValue;
241-
if (!_scanRunning && (_lastScan + ScanInterval < currentTicks || _lastScan > currentTicks))
242-
{
243-
lock (_startScanLock)
244-
{
245-
if (!_scanRunning && (_lastScan + ScanInterval < currentTicks || _lastScan > currentTicks))
246-
{
247-
_lastScan = currentTicks;
248-
249-
Logger.LogInfo("Starting scan for expired items. Next scan in {0}sec.", ScanInterval / 1000);
250-
#if NET40
251-
Task.Factory.StartNew(
252-
state => ScanForExpiredItems((DictionaryCacheHandle<TCacheValue>)state),
253-
this,
254-
CancellationToken.None,
255-
TaskCreationOptions.None,
256-
TaskScheduler.Default);
257-
#else
258-
Task.Factory.StartNew(
259-
state => ScanForExpiredItems((DictionaryCacheHandle<TCacheValue>)state),
260-
this,
261-
CancellationToken.None,
262-
TaskCreationOptions.DenyChildAttach,
263-
TaskScheduler.Default);
264-
#endif
265-
}
266-
}
267-
}
262+
return removed;
268263
}
269264
}
270265
}

src/CacheManager.Core/Logging/LoggerExtensions.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace CacheManager.Core.Logging
55
{
66
#pragma warning disable SA1600
77
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
8+
89
public static class LoggerExtensions
910
{
1011
//// Critical
@@ -277,7 +278,14 @@ public override string ToString()
277278
return _format;
278279
}
279280

280-
return string.Format(CultureInfo.CurrentCulture, _format, _args);
281+
try
282+
{
283+
return string.Format(CultureInfo.CurrentCulture, _format, _args);
284+
}
285+
catch
286+
{
287+
return "Failed to format string";
288+
}
281289
}
282290
}
283291
}

src/CacheManager.Microsoft.Extensions.Configuration/CacheManager.Microsoft.Extensions.Configuration.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Import Project="..\..\tools\common.props" />
33
<PropertyGroup>
44
<Description>CacheManager extension package to use Microsoft.Extensions.Configuration to configure the CacheManager instance. CacheManager is an open source caching abstraction layer for .NET written in C#. This is the ASP.NET Core configuration integration package.</Description>
5-
<TargetFrameworks>net45;netstandard1.1</TargetFrameworks>
5+
<TargetFrameworks>net45;netstandard1.2</TargetFrameworks>
66
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageTags>Caching;Cache;CacheManager;Distributed Cache;StackExchange Redis;Azure AppFabric;Memcached</PackageTags>
@@ -17,9 +17,9 @@
1717
</ItemGroup>
1818
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
1919
</ItemGroup>
20-
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
20+
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.2' ">
2121
<DefineConstants>$(DefineConstants);NETSTANDARD</DefineConstants>
2222
</PropertyGroup>
23-
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
23+
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.2' ">
2424
</ItemGroup>
2525
</Project>

src/CacheManager.Microsoft.Extensions.Logging/CacheManager.Microsoft.Extensions.Logging.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Import Project="..\..\tools\common.props" />
33
<PropertyGroup>
44
<Description>CacheManager extension package to work with Microsoft.Extensions.Logging as logging provider. CacheManager is an open source caching abstraction layer for .NET written in C#. The ASP.NET Core logging provides a bridge to the Microsoft.Extensions.Logging framework.</Description>
5-
<TargetFrameworks>net45;netstandard1.1</TargetFrameworks>
5+
<TargetFrameworks>net45;netstandard1.2</TargetFrameworks>
66
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageTags>Caching;Cache;CacheManager;Distributed Cache;StackExchange Redis;Azure AppFabric;Memcached</PackageTags>
@@ -15,6 +15,6 @@
1515
</ItemGroup>
1616
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
1717
</ItemGroup>
18-
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
18+
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.2' ">
1919
</ItemGroup>
2020
</Project>

src/CacheManager.Serialization.Bond/CacheManager.Serialization.Bond.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Import Project="..\..\tools\common.props" />
33
<PropertyGroup>
44
<Description>CacheManager extension package providing Microsoft Bond based serialization for distributed caches. CacheManager is an open source caching abstraction layer for .NET written in C#. It supports various cache providers and implements many advanced features. The Core library contains a Newtonsoft.Json based serializer implementation which can be used instead of the default binary serializer.</Description>
5-
<TargetFrameworks>netstandard1.1;netstandard1.3;net40;net45</TargetFrameworks>
5+
<TargetFrameworks>netstandard1.2;netstandard1.3;net40;net45</TargetFrameworks>
66
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageTags>Caching;Cache;CacheManager;Serialization;Bond</PackageTags>
@@ -15,7 +15,7 @@
1515
</ItemGroup>
1616
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
1717
</ItemGroup>
18-
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
18+
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.2' ">
1919
<DefineConstants>$(DefineConstants);NOUNSAFE</DefineConstants>
2020
</PropertyGroup>
2121
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">

0 commit comments

Comments
 (0)