Skip to content

Commit 64fba78

Browse files
committed
finalized
1 parent abbe491 commit 64fba78

File tree

11 files changed

+32
-48
lines changed

11 files changed

+32
-48
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Deploy NuGet Package
22

33
env:
4-
PROJECT_PATH: './src/PandaNuGet/PandaNuGet.csproj'
4+
PROJECT_PATH: './src/DistributedCache/DistributedCache.csproj'
55
OUTPUT_DIR: 'nupkgs'
66
NUGET_SOURCE: 'https://api.nuget.org/v3/index.json'
77
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}

Readme.md

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ caching solution with advanced features such as typed cache services, distribute
99

1010
- **Typed Cache Service:** Supports strongly-typed caching with MessagePack serialization.
1111
- **Distributed Locking:** Ensures data consistency with distributed locks.
12-
- **Health Checks:** Monitors Redis availability and handles cache reset scenarios.
1312
- **Key Isolation:** Modular monolith support by prefixing keys with assembly names.
1413
- **Stampede Protection:** Protects against cache stampede in the `GetOrCreateAsync` method.
1514
- **No Serializer Override:** Enforces MessagePack serialization for performance and readability.
@@ -40,8 +39,6 @@ builder.AddDistributedCache(options =>
4039
options.DistributedLockDuration = TimeSpan.FromSeconds(5); //Default is 5 seconds
4140
options.DefaultExpiration = TimeSpan.FromMinutes(5); //Default is 15 minutes
4241
options.CacheResetMode = CacheResetMode.ResetFrequentTagsAfterHealthCheckFail; //Default is ResetAllAfterHealthCheckFail
43-
options.HealthCheckInterval = TimeSpan.FromSeconds(1); //Default is 100 milliseconds
44-
options.KeyPrefixForIsolation = KeyPrefix.AssemblyNamePrefix //Default is None
4542
});
4643

4744
var app = builder.Build();
@@ -57,16 +54,6 @@ cross ClassLibrary cache access.
5754
options.KeyPrefixForIsolation = KeyPrefix.AssemblyNamePrefix;
5855
```
5956

60-
**Health Check Service**
61-
62-
The `RedisHealthCheckService` monitors Redis availability and resets cache entries tagged as `Frequent` after a Redis
63-
outage:
64-
65-
```csharp
66-
options.CacheResetMode = CacheResetMode.ResetFrequentTagsAfterHealthCheckFail;
67-
options.HealthCheckInterval = TimeSpan.FromMilliseconds(100);
68-
```
69-
7057
### 2. Cached Entity Preparation
7158

7259
Create your cache entity/model in order to inject it in the actual service:
@@ -99,13 +86,13 @@ public class CacheTestsService(ICacheService<TestCacheEntity> cacheService)
9986
await cacheService.GetOrCreateAsync("test2",
10087
async _ => await GetFromPostgres(token),
10188
TimeSpan.FromMinutes(1),
102-
[CacheTag.Frequent],
89+
["vazgen"],
10390
token);
10491

10592
await cacheService.GetOrCreateAsync("test3",
10693
async _ => await GetFromPostgres(token),
10794
TimeSpan.FromMinutes(1),
108-
["test", CacheTag.Frequent],
95+
["test", "vazgen"],
10996
token);
11097
}
11198

src/DistributedCache/Class1.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/DistributedCache/Extensions/WebApplicationBuilderExtension.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static WebApplicationBuilder AddDistributedCache(this WebApplicationBuild
3737
var redisConfiguration = new RedisConfiguration { ConnectionString = configurations.RedisConnectionString };
3838

3939
builder.Services.AddStackExchangeRedisExtensions<RedisMsgPackObjectSerializer>(redisConfiguration);
40-
builder.Services.AddHostedService<RedisHealthCheckService>();
40+
//builder.Services.AddHostedService<RedisHealthCheckService>(); //Discontinued feature
4141

4242
return builder;
4343
}
@@ -78,7 +78,7 @@ private static void ValidateOptions(WebApplicationBuilder builder)
7878
}
7979

8080
if (options.CacheResetMode == CacheResetMode.ResetFrequentTagsAfterHealthCheckFail &&
81-
options.HealthCheckInterval <= TimeSpan.Zero)
81+
options.HealthCheckInterval <= TimeSpan.Zero) //Discontinued feature
8282
{
8383
throw new ArgumentException(
8484
"AddCacheService options: HealthCheckInterval must be greater than 0 when CacheResetMode is ResetFrequentTagsAfterHealthCheckFail.");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace DistributedCache.Helpers;
22

3-
public static class CacheTag
3+
internal static class CacheTag //Discontinued feature
44
{
55
public const string Frequent = "Frequent";
66
}

src/DistributedCache/Helpers/KeyFormatHelper.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ internal static IEnumerable<string> GetPrefixedKeys(IEnumerable<string> keys, st
1111
internal static IEnumerable<string> GetPrefixedKeys(IEnumerable<string> keys) => keys.Select(GetPrefixedKey);
1212

1313
internal static string GetTagKey(string tag) => $"tags:{tag}";
14+
15+
internal static string GetTagKey(string tag, string moduleName) => $"tags:{moduleName}:{tag}";
1416

15-
internal static string GetTagKey(string tag, string moduleName)
17+
internal static string GetLockKey(string key) => $"{key}:lock";
18+
private static string GetTagKey(string tag, string moduleName, object somethingForNoConflict) //Discontinued feature
1619
{
1720
return tag == CacheTag.Frequent ? $"tags:{tag}" : $"tags:{moduleName}:{tag}";
1821
}

src/DistributedCache/Options/CacheConfigurationOptions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class CacheConfigurationOptions
99
public TimeSpan SyncTimeout { get; set; } = TimeSpan.FromSeconds(5);
1010
public TimeSpan DistributedLockDuration { get; set; } = TimeSpan.FromSeconds(5);
1111
public TimeSpan DefaultExpiration { get; set; } = TimeSpan.FromMinutes(15);
12-
public CacheResetMode CacheResetMode { get; set; } = CacheResetMode.ResetFrequentTagsAfterHealthCheckFail;
13-
public TimeSpan HealthCheckInterval { get; set; } = TimeSpan.FromMilliseconds(100);
12+
13+
internal CacheResetMode CacheResetMode { get; set; } = CacheResetMode.ResetFrequentTagsAfterHealthCheckFail; //Discontinued feature
14+
internal TimeSpan HealthCheckInterval { get; set; } = TimeSpan.FromMilliseconds(100); //Discontinued feature
1415
}

src/DistributedCache/Options/CacheResetMode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace DistributedCache.Options;
22

3-
public enum CacheResetMode
3+
internal enum CacheResetMode //Discontinued feature
44
{
55
None = 1,
66
ResetFrequentTagsAfterHealthCheckFail = 2

src/DistributedCache/Services/Implementations/RedisCacheService.cs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal class RedisCacheService<T>(IRedisClient redisClient, IOptions<CacheConf
1515
private readonly CacheConfigurationOptions _config = options.Value;
1616
private readonly string _moduleName = typeof(T).Assembly.GetName().Name!;
1717
private readonly TimeSpan _lockExpiry = options.Value.DistributedLockDuration;
18-
private readonly TimeSpan _lockRetryDelay = TimeSpan.FromMilliseconds(20);
18+
private readonly TimeSpan _lockRetryDelay = TimeSpan.FromMilliseconds(10);
1919

2020
public async ValueTask<T> GetOrCreateAsync(string key, Func<CancellationToken, ValueTask<T>> factory,
2121
TimeSpan? expiration = null, IReadOnlyCollection<string>? tags = null, CancellationToken token = default)
@@ -24,29 +24,35 @@ public async ValueTask<T> GetOrCreateAsync(string key, Func<CancellationToken, V
2424
? KeyFormatHelper.GetPrefixedKey(key)
2525
: KeyFormatHelper.GetPrefixedKey(key, _moduleName);
2626

27-
var lockKey = $"{prefixedKey}:lock";
27+
var lockKey = KeyFormatHelper.GetLockKey(prefixedKey);
2828
var lockValue = Guid.NewGuid().ToString();
2929

3030
while (true)
3131
{
3232
token.ThrowIfCancellationRequested();
3333

34-
// Acquire lock before getting the value
35-
var lockAcquired = await AcquireLockAsync(lockKey, lockValue);
34+
var isLocked = await _redisDatabase.Database.KeyExistsAsync(lockKey);
3635

37-
if (!lockAcquired)
36+
if (isLocked)
3837
{
3938
await WaitForLockReleaseAsync(lockKey, token);
4039
continue;
4140
}
4241

4342
var cachedValue = await _redisDatabase.GetAsync<T>(prefixedKey);
44-
4543
if (cachedValue != null)
4644
{
47-
await ReleaseLockAsync(lockKey, lockValue);
4845
return cachedValue;
4946
}
47+
48+
var lockAcquired = await AcquireLockAsync(lockKey, lockValue);
49+
50+
if (!lockAcquired)
51+
{
52+
await WaitForLockReleaseAsync(lockKey, token);
53+
continue;
54+
}
55+
5056
break;
5157
}
5258

@@ -58,7 +64,6 @@ public async ValueTask<T> GetOrCreateAsync(string key, Func<CancellationToken, V
5864
}
5965
finally
6066
{
61-
// Release the lock
6267
await ReleaseLockAsync(lockKey, lockValue);
6368
}
6469
}
@@ -119,7 +124,7 @@ public async ValueTask RemoveByTagAsync(string tag, CancellationToken token = de
119124
var tagKey = _config.KeyPrefixForIsolation == KeyPrefix.None
120125
? KeyFormatHelper.GetTagKey(tag)
121126
: KeyFormatHelper.GetTagKey(tag, _moduleName);
122-
127+
123128
var keys = await _redisDatabase.SetMembersAsync<string>(tagKey);
124129
if (keys.Length > 0)
125130
{

src/DistributedCache/Services/Implementations/RedisHealthCheckService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ namespace DistributedCache.Services.Implementations;
1010
public class RedisHealthCheckService(
1111
IRedisClient redisClient,
1212
ILogger<RedisHealthCheckService> logger,
13-
IOptions<CacheConfigurationOptions> options)
13+
IOptions<CacheConfigurationOptions> options) // Discontinued Feature
1414
: BackgroundService
1515
{
1616
private readonly IRedisDatabase _redisDatabase = redisClient.GetDefaultDatabase();
1717
private readonly CacheConfigurationOptions _config = options.Value;
1818
private bool _resetRedis;
1919

20-
protected override async Task ExecuteAsync(CancellationToken stoppingToken) //todo test as not working
20+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
2121
{
2222
if (_config.CacheResetMode == CacheResetMode.None)
2323
{

0 commit comments

Comments
 (0)