Skip to content

Commit 93863ce

Browse files
authored
Merge pull request #9 from PandaTechAM/development
Total redesign by implementing hybrid cache.
2 parents d197d53 + f00aa5e commit 93863ce

34 files changed

+687
-626
lines changed

Readme.md

Lines changed: 207 additions & 184 deletions
Large diffs are not rendered by default.

src/DistributedCache/DistributedCache.csproj

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
<PackageReadmeFile>Readme.md</PackageReadmeFile>
99
<Authors>Pandatech</Authors>
1010
<Copyright>MIT</Copyright>
11-
<Version>3.0.1</Version>
11+
<Version>4.0.0</Version>
1212
<PackageId>Pandatech.DistributedCache</PackageId>
1313
<Title>Pandatech Distributed Cache</Title>
14-
<PackageTags>Pandatech, library, redis, distributed locks, cache</PackageTags>
15-
<Description>Pandatech.DistributedCache is a comprehensive caching library designed for .NET applications, leveraging the power of Redis. It provides easy-to-use and highly configurable caching mechanisms, including support for tagged cache entries, customizable expiration policies, and robust health check services. The library also features built-in distributed lock mechanisms to ensure data consistency and prevent cache stampedes. This ensures high performance, scalability, and reliability, making it an ideal choice for enterprise-level distributed caching needs.</Description>
14+
<PackageTags>pandatech;cache;hybrid-cache;distributed-cahce;message-pack;rate-limiting;redis-lock;redis;fusion-cache</PackageTags>
15+
<Description>Pandatech.DistributedCache is a lightweight .NET library that extends the new HybridCache abstraction for seamless distributed caching on top of Redis. It provides strongly typed caching with MessagePack serialization, distributed locking, rate limiting, and stampede protection. With fewer than 500 lines of code, it is both easy to understand and simple to adopt, while still offering robust features for production environments.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-distributed-cache</RepositoryUrl>
17-
<PackageReleaseNotes>Added healthcheck</PackageReleaseNotes>
17+
<PackageReleaseNotes>Changed ICacheService with HybridCache interface. Not backward compatible with previous version</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>
@@ -23,11 +23,12 @@
2323
</ItemGroup>
2424

2525
<ItemGroup>
26-
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="8.0.1" />
27-
<PackageReference Include="MessagePack" Version="2.5.192" />
28-
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
29-
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="10.2.0"/>
30-
<PackageReference Include="StackExchange.Redis.Extensions.MsgPack" Version="10.2.0"/>
26+
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="9.0.0"/>
27+
<PackageReference Include="MessagePack" Version="3.1.2"/>
28+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.1"/>
29+
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.1"/>
30+
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="11.0.0"/>
31+
<PackageReference Include="StackExchange.Redis.Extensions.MsgPack" Version="11.0.0"/>
3132
</ItemGroup>
3233

3334
</Project>

src/DistributedCache/Extensions/HealthCheckExtension.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ namespace DistributedCache.Extensions;
55

66
internal static class HealthCheckExtension
77
{
8-
internal static WebApplicationBuilder AddRedisHealthCheck(this WebApplicationBuilder builder, string connectionString)
8+
internal static WebApplicationBuilder AddRedisHealthCheck(this WebApplicationBuilder builder,
9+
string connectionString)
910
{
1011
builder.Services
1112
.AddHealthChecks()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using Microsoft.Extensions.Caching.Hybrid;
2+
3+
namespace DistributedCache.Extensions;
4+
5+
public static class HybridCacheExtensions
6+
{
7+
public static async ValueTask<TValue> GetOrDefaultAsync<TValue>(this HybridCache cache,
8+
string key,
9+
TValue defaultValue,
10+
CancellationToken ct = default)
11+
{
12+
return await cache.GetOrCreateAsync<TValue>(
13+
key,
14+
_ => new ValueTask<TValue>(defaultValue),
15+
new HybridCacheEntryOptions
16+
{
17+
Flags = HybridCacheEntryFlags.DisableLocalCacheWrite | HybridCacheEntryFlags.DisableDistributedCacheWrite
18+
},
19+
cancellationToken: ct
20+
);
21+
}
22+
23+
public static async ValueTask<(bool Exists, TValue? Value)> TryGetAsync<TValue>(this HybridCache cache,
24+
string key,
25+
CancellationToken ct = default)
26+
{
27+
var exists = true;
28+
var value = await cache.GetOrCreateAsync<TValue>(
29+
key,
30+
_ =>
31+
{
32+
exists = false;
33+
return new ValueTask<TValue>(default(TValue)!);
34+
},
35+
new HybridCacheEntryOptions
36+
{
37+
Flags = HybridCacheEntryFlags.DisableLocalCacheWrite | HybridCacheEntryFlags.DisableDistributedCacheWrite
38+
},
39+
cancellationToken: ct
40+
);
41+
return (exists, value);
42+
}
43+
44+
public static async ValueTask<bool> ExistsAsync<TValue>(this HybridCache cache,
45+
string key,
46+
CancellationToken ct = default)
47+
{
48+
var (exists, _) = await cache.TryGetAsync<TValue>(key, ct);
49+
return exists;
50+
}
51+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Reflection;
2+
3+
namespace DistributedCache.Extensions;
4+
5+
public static class StringExtensions
6+
{
7+
public static string PrefixWith(this string value, string prefix)
8+
{
9+
return $"{prefix}{value}";
10+
}
11+
12+
public static string PrefixWith(this string value, Assembly assembly)
13+
{
14+
return $"{assembly.GetName().Name}:{value}";
15+
}
16+
}

src/DistributedCache/Extensions/WebApplicationBuilderExtension.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using DistributedCache.Services.Implementations;
44
using DistributedCache.Services.Interfaces;
55
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.Extensions.Caching.Hybrid;
67
using Microsoft.Extensions.DependencyInjection;
78
using Microsoft.Extensions.Options;
89
using StackExchange.Redis;
@@ -34,9 +35,9 @@ public static WebApplicationBuilder AddDistributedCache(this WebApplicationBuild
3435

3536
builder.Services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(redisOptions));
3637

37-
builder.Services.AddSingleton(typeof(ICacheService<>), typeof(RedisCacheService<>));
38-
builder.Services.AddSingleton(typeof(IRateLimitService<>), typeof(RedisRateLimitService<>));
39-
builder.Services.AddSingleton<RedisLockService>();
38+
builder.Services.AddSingleton<IRateLimitService, RedisRateLimitService>();
39+
builder.Services.AddSingleton<IDistributedLockService, RedisLockService>();
40+
builder.Services.AddSingleton<HybridCache, RedisDistributedCache>();
4041

4142
var redisConfiguration = new RedisConfiguration
4243
{
@@ -75,7 +76,7 @@ private static void ValidateOptions(WebApplicationBuilder builder)
7576
throw new ArgumentException("AddCacheService options: SyncTimeout must be greater than 0.");
7677
}
7778

78-
if (options.DistributedLockDuration <= TimeSpan.FromSeconds(1))
79+
if (options.DistributedLockMaxDuration <= TimeSpan.FromSeconds(1))
7980
{
8081
throw new ArgumentException(
8182
"AddCacheService options: DistributedLockDuration must be greater or equal 1.");
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using DistributedCache.Options;
2+
3+
namespace DistributedCache.Helpers;
4+
5+
internal static class CacheKeyFormatter
6+
{
7+
private const string DefaultCachePrefix = "DistributedCache";
8+
9+
internal static string BuildPrefixedKey(string key,
10+
CacheConfigurationOptions options)
11+
{
12+
return options.ChannelPrefix is not null
13+
? $"{DefaultCachePrefix}:{options.ChannelPrefix}:{key}"
14+
: $"{DefaultCachePrefix}:{key}";
15+
}
16+
17+
internal static IEnumerable<string> BuildPrefixedKeys(IEnumerable<string> keys,
18+
CacheConfigurationOptions options)
19+
{
20+
return keys.Select(k => BuildPrefixedKey(k, options));
21+
}
22+
23+
internal static string BuildTagKey(string tagName,
24+
CacheConfigurationOptions options)
25+
{
26+
return options.ChannelPrefix is not null
27+
? $"{DefaultCachePrefix}:{options.ChannelPrefix}:tag:{tagName}"
28+
: $"{DefaultCachePrefix}:tag:{tagName}";
29+
}
30+
31+
internal static string BuildLockKey(string baseKey)
32+
{
33+
return $"{baseKey}:lock";
34+
}
35+
}

src/DistributedCache/Helpers/CacheTag.cs

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

src/DistributedCache/Helpers/KeyFormatHelper.cs

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using DistributedCache.Services.Interfaces;
2+
using MessagePack;
3+
4+
namespace DistributedCache.Models;
5+
6+
[MessagePackObject]
7+
public class CacheStore<T> : ICacheEntity
8+
{
9+
[Key(0)]
10+
public required T Data { get; set; }
11+
12+
[Key(1)]
13+
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
14+
15+
[Key(2)]
16+
public required List<string> Tags { get; set; }
17+
}

0 commit comments

Comments
 (0)