diff --git a/.github/workflows/buildandtest.yml b/.github/workflows/buildandtest.yml
index 355aa58a..d93c59bb 100644
--- a/.github/workflows/buildandtest.yml
+++ b/.github/workflows/buildandtest.yml
@@ -35,7 +35,15 @@ jobs:
image: bitnami/memcached
ports:
- 11212:11211
-
+ etcd:
+ image: quay.io/coreos/etcd:v3.5.18
+ ports:
+ - 2379:2379
+ env:
+ ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
+ ETCD_ADVERTISE_CLIENT_URLS: "http://127.0.0.1:2379"
+ ETCDCTL_API: 3
+ ALLOW_NONE_AUTHENTICATION: "yes"
steps:
- uses: actions/checkout@v4
- name: Setup .NET SDK 8.0.x
diff --git a/EasyCaching.sln b/EasyCaching.sln
index 9ae4c577..cd8fb48e 100644
--- a/EasyCaching.sln
+++ b/EasyCaching.sln
@@ -83,6 +83,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Serialization.M
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Demo.Locks", "sample\EasyCaching.Demo.Locks\EasyCaching.Demo.Locks.csproj", "{9B15A0A0-BD6B-40B0-90D4-848BC3E4AF98}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Etcd", "src\EasyCaching.Etcd\EasyCaching.Etcd.csproj", "{BA59F594-423A-4667-B6A0-980619AED44E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -209,18 +211,22 @@ Global
{7191E567-38DF-4879-82E1-73EC618AFCAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7191E567-38DF-4879-82E1-73EC618AFCAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7191E567-38DF-4879-82E1-73EC618AFCAC}.Release|Any CPU.Build.0 = Release|Any CPU
- {EEF22C21-F380-4980-B72C-F14488369333}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {EEF22C21-F380-4980-B72C-F14488369333}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {EEF22C21-F380-4980-B72C-F14488369333}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {EEF22C21-F380-4980-B72C-F14488369333}.Release|Any CPU.Build.0 = Release|Any CPU
{3C9D5E40-B3A5-4649-8B40-08094644B0FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C9D5E40-B3A5-4649-8B40-08094644B0FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C9D5E40-B3A5-4649-8B40-08094644B0FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C9D5E40-B3A5-4649-8B40-08094644B0FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EEF22C21-F380-4980-B72C-F14488369333}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EEF22C21-F380-4980-B72C-F14488369333}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EEF22C21-F380-4980-B72C-F14488369333}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EEF22C21-F380-4980-B72C-F14488369333}.Release|Any CPU.Build.0 = Release|Any CPU
{9B15A0A0-BD6B-40B0-90D4-848BC3E4AF98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B15A0A0-BD6B-40B0-90D4-848BC3E4AF98}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B15A0A0-BD6B-40B0-90D4-848BC3E4AF98}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B15A0A0-BD6B-40B0-90D4-848BC3E4AF98}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BA59F594-423A-4667-B6A0-980619AED44E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA59F594-423A-4667-B6A0-980619AED44E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA59F594-423A-4667-B6A0-980619AED44E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA59F594-423A-4667-B6A0-980619AED44E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -256,9 +262,10 @@ Global
{F7FBADEB-D766-4595-949A-07104B52692C} = {B337509B-75F9-4851-821F-9BBE87C4E4BC}
{5E488583-391E-4E15-83C1-7301B4FE79AE} = {B337509B-75F9-4851-821F-9BBE87C4E4BC}
{7191E567-38DF-4879-82E1-73EC618AFCAC} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
- {EEF22C21-F380-4980-B72C-F14488369333} = {15070C49-A507-4844-BCFE-D319CFBC9A63}
{3C9D5E40-B3A5-4649-8B40-08094644B0FB} = {B337509B-75F9-4851-821F-9BBE87C4E4BC}
+ {EEF22C21-F380-4980-B72C-F14488369333} = {15070C49-A507-4844-BCFE-D319CFBC9A63}
{9B15A0A0-BD6B-40B0-90D4-848BC3E4AF98} = {F88D727A-9F9C-43D9-90B1-D4A02BF8BC98}
+ {BA59F594-423A-4667-B6A0-980619AED44E} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {63A57886-054B-476C-AAE1-8D7C8917682E}
diff --git a/sample/EasyCaching.Demo.ConsoleApp/EasyCaching.Demo.ConsoleApp.csproj b/sample/EasyCaching.Demo.ConsoleApp/EasyCaching.Demo.ConsoleApp.csproj
index 1d45c7c8..b711660a 100644
--- a/sample/EasyCaching.Demo.ConsoleApp/EasyCaching.Demo.ConsoleApp.csproj
+++ b/sample/EasyCaching.Demo.ConsoleApp/EasyCaching.Demo.ConsoleApp.csproj
@@ -14,6 +14,7 @@
+
diff --git a/sample/EasyCaching.Demo.ConsoleApp/Program.cs b/sample/EasyCaching.Demo.ConsoleApp/Program.cs
index ccb3f2e1..037c8cd2 100644
--- a/sample/EasyCaching.Demo.ConsoleApp/Program.cs
+++ b/sample/EasyCaching.Demo.ConsoleApp/Program.cs
@@ -9,7 +9,9 @@ namespace EasyCaching.Demo.ConsoleApp
using MemoryPack;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
+ using Newtonsoft.Json;
using System;
+ using System.Collections.Generic;
using System.IO;
class Program
@@ -56,6 +58,17 @@ static void Main(string[] args)
.WithJson("json")
.WithSystemTextJson("sysjson")
.WithMessagePack("msgpack");
+
+ option.UseEtcd(options =>
+ {
+ options.Address = "http://127.0.0.1:2379";
+ options.Timeout = 30000;
+ options.SerializerName = "json";
+ }, "e1").WithJson(jsonSerializerSettingsConfigure: x =>
+ {
+ x.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.None;
+ x.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
+ }, "json");
});
IServiceProvider serviceProvider = services.BuildServiceProvider();
@@ -104,6 +117,19 @@ static void Main(string[] args)
var diskVal = diskCache.Get("diskkey");
Console.WriteLine($"disk cache get value, {diskVal.HasValue} {diskVal.IsNull} {diskVal.Value} ");
+ //etcd cache
+ var etcdCache = factory.GetCachingProvider("e1");
+ var re11 = etcdCache.GetAllKeysByPrefix("emk");
+ var re12 = etcdCache.GetByPrefix("emk");
+ etcdCache.Set("emkey3", prod, TimeSpan.FromSeconds(2000));
+ var re13 = etcdCache.Get("emkey3");
+ var re14 = etcdCache.GetAll(new List()
+ {
+ "emkey3"
+ });
+ etcdCache.Remove("emkey3");
+ Console.WriteLine($"etcd cache get value, {re13.HasValue} {re13.IsNull} {re13.Value} ");
+
Console.ReadKey();
}
}
diff --git a/sample/EasyCaching.Demo.Locks/Controllers/LocksController.cs b/sample/EasyCaching.Demo.Locks/Controllers/LocksController.cs
index b6400a70..400c8942 100644
--- a/sample/EasyCaching.Demo.Locks/Controllers/LocksController.cs
+++ b/sample/EasyCaching.Demo.Locks/Controllers/LocksController.cs
@@ -67,4 +67,33 @@ public async Task MemoryLockingOperation(int millisecondsTimeout)
await memoryLock.ReleaseAsync();
}
}
+
+ [HttpPost("etcd-locking")]
+ public async Task EtcdLockingOperation(int millisecondsTimeout)
+ {
+ using var distributedLock = _distributedLockFactory.CreateLock("DefaultEtcd", "YourKey");
+
+ try
+ {
+ if (await distributedLock.LockAsync(millisecondsTimeout))
+ {
+ // Simulate operation
+ Thread.Sleep(2000);
+ }
+ else
+ {
+ // Proper error
+ }
+ }
+ catch (Exception ex)
+ {
+ // log error
+ throw new Exception("Exception", ex);
+ }
+ finally
+ {
+ // release lock at the end
+ await distributedLock.ReleaseAsync();
+ }
+ }
}
diff --git a/sample/EasyCaching.Demo.Locks/EasyCaching.Demo.Locks.csproj b/sample/EasyCaching.Demo.Locks/EasyCaching.Demo.Locks.csproj
index 0e6245ee..acf448fe 100644
--- a/sample/EasyCaching.Demo.Locks/EasyCaching.Demo.Locks.csproj
+++ b/sample/EasyCaching.Demo.Locks/EasyCaching.Demo.Locks.csproj
@@ -12,6 +12,7 @@
+
diff --git a/sample/EasyCaching.Demo.Locks/GlobalUsings.cs b/sample/EasyCaching.Demo.Locks/GlobalUsings.cs
index 838d0c76..152016b7 100644
--- a/sample/EasyCaching.Demo.Locks/GlobalUsings.cs
+++ b/sample/EasyCaching.Demo.Locks/GlobalUsings.cs
@@ -1,4 +1,5 @@
global using EasyCaching.Core.Configurations;
global using EasyCaching.Core.DistributedLock;
global using Microsoft.AspNetCore.Mvc;
-global using EasyCaching.Redis.DistributedLock;
\ No newline at end of file
+global using EasyCaching.Redis.DistributedLock;
+global using EasyCaching.Etcd.DistributedLock;
\ No newline at end of file
diff --git a/sample/EasyCaching.Demo.Locks/Program.cs b/sample/EasyCaching.Demo.Locks/Program.cs
index c2d00cdb..2de6f09f 100644
--- a/sample/EasyCaching.Demo.Locks/Program.cs
+++ b/sample/EasyCaching.Demo.Locks/Program.cs
@@ -1,3 +1,5 @@
+using Newtonsoft.Json;
+
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
@@ -23,6 +25,19 @@
})
.WithJson()//with josn serialization
.UseRedisLock(); // use distributed lock
+
+ // use etcd cache
+ option.UseEtcd(options =>
+ {
+ options.Address = "http://127.0.0.1:2379";
+ options.Timeout = 3000;
+ options.LockMs = 10000;
+ options.SerializerName = "json";
+ }).WithJson(jsonSerializerSettingsConfigure: x =>
+ {
+ x.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.None;
+ x.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
+ }, "json").UseEtcdLock();
});
#region How Inject Distributed and Memory lock
@@ -33,6 +48,9 @@
// inject to use memory lock
builder.Services.AddSingleton();
+// inject to use memory lock
+builder.Services.AddSingleton();
+
#endregion
var app = builder.Build();
diff --git a/sample/EasyCaching.Demo.Providers/Controllers/ValuesController.cs b/sample/EasyCaching.Demo.Providers/Controllers/ValuesController.cs
index d7f8878a..b6eee99e 100644
--- a/sample/EasyCaching.Demo.Providers/Controllers/ValuesController.cs
+++ b/sample/EasyCaching.Demo.Providers/Controllers/ValuesController.cs
@@ -8,7 +8,7 @@
[Route("api/[controller]")]
public class ValuesController : Controller
{
- //1. InMemory,Memcached,Redis,SQLite,FasterKv
+ //1. InMemory,Memcached,Redis,SQLite,FasterKv,Etcd
private readonly IEasyCachingProvider _provider;
public ValuesController(IEasyCachingProvider provider)
@@ -38,6 +38,9 @@ public string Get(string str)
case "set" :
_provider.Set("demo", "123", TimeSpan.FromMinutes(1));
return "seted";
+ case "getexpiretime":
+ var timeSpanData = _provider.GetExpiration("demo");
+ return $"{timeSpanData.TotalSeconds}";
case "remove" :
_provider.Remove("demo");
return "removed";
@@ -64,6 +67,9 @@ public async Task GetAsync(string str)
case "set":
await _provider.SetAsync("demo", "123", TimeSpan.FromMinutes(1));
return "seted";
+ case "getexpiretime":
+ var timeSpanData = _provider.GetExpiration("demo");
+ return $"{timeSpanData.TotalSeconds}";
case "remove":
await _provider.RemoveAsync("demo");
return "removed";
diff --git a/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj b/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
index aac47298..0ded0074 100644
--- a/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
+++ b/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
@@ -7,6 +7,8 @@
+
+
diff --git a/sample/EasyCaching.Demo.Providers/Startup.cs b/sample/EasyCaching.Demo.Providers/Startup.cs
index 5372e2ae..e84e1b76 100644
--- a/sample/EasyCaching.Demo.Providers/Startup.cs
+++ b/sample/EasyCaching.Demo.Providers/Startup.cs
@@ -8,6 +8,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+ using Newtonsoft.Json;
public class Startup
{
@@ -69,6 +70,18 @@ public void ConfigureServices(IServiceCollection services)
config.SerializerName = "msg";
})
.WithMessagePack("msg");
+
+ // use etcd cache
+ option.UseEtcd(options =>
+ {
+ options.Address = "http://127.0.0.1:2379";
+ options.Timeout = 30000;
+ options.SerializerName = "json";
+ }, "e1").WithJson(jsonSerializerSettingsConfigure: x =>
+ {
+ x.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.None;
+ x.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
+ }, "json");
});
}
diff --git a/src/EasyCaching.Core/Internal/CachingProviderType.cs b/src/EasyCaching.Core/Internal/CachingProviderType.cs
index ed1d8f27..cb8a2223 100644
--- a/src/EasyCaching.Core/Internal/CachingProviderType.cs
+++ b/src/EasyCaching.Core/Internal/CachingProviderType.cs
@@ -14,5 +14,6 @@ public enum CachingProviderType
Ext2,
LiteDB,
FasterKv,
+ Etcd,
}
}
diff --git a/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs b/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
index 342a0ae4..21ce93cf 100644
--- a/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
+++ b/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
@@ -119,7 +119,17 @@ public class EasyCachingConstValue
/// The default name of the FasterKv
///
public const string DefaultFasterKvName = "DefaultFasterKvName";
-
+
+ ///
+ /// The default name of the etcd.
+ ///
+ public const string DefaultEtcdName = "DefaultEtcd";
+
+ ///
+ /// The etcd section.
+ ///
+ public const string EtcdSection = "easycaching:etcd";
+
///
/// The FasterKv section.
///
diff --git a/src/EasyCaching.Etcd/Configurations/EtcdCachingOptions.cs b/src/EasyCaching.Etcd/Configurations/EtcdCachingOptions.cs
new file mode 100644
index 00000000..3af52d62
--- /dev/null
+++ b/src/EasyCaching.Etcd/Configurations/EtcdCachingOptions.cs
@@ -0,0 +1,31 @@
+using EasyCaching.Core.Configurations;
+
+namespace EasyCaching.Etcd
+{
+ ///
+ /// EasyCaching options extensions of Etcd.
+ ///
+ public class EtcdCachingOptions : BaseProviderOptions
+ {
+ ///
+ /// Etcd address
+ /// cluster:like "http://localhost:23790,http://localhost:23791,http://localhost:23792"
+ ///
+ public string Address { get; set; }
+
+ ///
+ /// Etcd access UserName
+ ///
+ public string UserName { get; set; }
+
+ ///
+ /// Etcd access Pwd
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// Etcd timeout with Milliseconds
+ ///
+ public long Timeout { get; set; } = 3000;
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCaching.Etcd/Configurations/EtcdCachingOptionsExtensions.cs b/src/EasyCaching.Etcd/Configurations/EtcdCachingOptionsExtensions.cs
new file mode 100644
index 00000000..f2076771
--- /dev/null
+++ b/src/EasyCaching.Etcd/Configurations/EtcdCachingOptionsExtensions.cs
@@ -0,0 +1,78 @@
+using System;
+using EasyCaching.Core;
+using EasyCaching.Core.Configurations;
+using EasyCaching.Core.DistributedLock;
+using EasyCaching.Etcd;
+using EasyCaching.Etcd.DistributedLock;
+using Microsoft.Extensions.Configuration;
+// ReSharper disable CheckNamespace
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class EtcdCachingOptionsExtensions
+{
+ ///
+ /// Uses the Etcd provider (specify the config via hard code).
+ ///
+ /// Options.
+ /// Configure provider settings.
+ /// The name of this provider instance.
+ public static EasyCachingOptions UseEtcd(
+ this EasyCachingOptions options,
+ Action configure,
+ string name = EasyCachingConstValue.DefaultEtcdName
+ )
+ {
+ ArgumentCheck.NotNull(configure, nameof(configure));
+
+ options.RegisterExtension(new EtcdOptionsExtension(name, configure));
+ return options;
+ }
+
+ ///
+ /// Uses the Etcd provider (read config from configuration file).
+ ///
+ /// Options.
+ /// The configuration.
+ /// The name of this provider instance.
+ /// The section name in the configuration file.
+ public static EasyCachingOptions UseEtcd(
+ this EasyCachingOptions options,
+ IConfiguration configuration,
+ string name = EasyCachingConstValue.DefaultEtcdName,
+ string sectionName = EasyCachingConstValue.EtcdSection
+ )
+ {
+ var dbConfig = configuration.GetSection(sectionName);
+ var EtcdOptions = new EtcdCachingOptions();
+ dbConfig.Bind(EtcdOptions);
+
+ void Configure(EtcdCachingOptions x)
+ {
+ x.EnableLogging = EtcdOptions.EnableLogging;
+ x.MaxRdSecond = EtcdOptions.MaxRdSecond;
+ x.LockMs = EtcdOptions.LockMs;
+ x.SleepMs = EtcdOptions.SleepMs;
+ x.SerializerName = EtcdOptions.SerializerName;
+ x.CacheNulls = EtcdOptions.CacheNulls;
+ x.Address = EtcdOptions.Address;
+ x.UserName = EtcdOptions.UserName;
+ x.Password = EtcdOptions.Password;
+ x.Timeout= EtcdOptions.Timeout;
+ }
+
+ options.RegisterExtension(new EtcdOptionsExtension(name, Configure));
+ return options;
+ }
+
+ ///
+ /// Uses the Etcd lock.
+ ///
+ /// Options.
+ public static EasyCachingOptions UseEtcdLock(this EasyCachingOptions options)
+ {
+ options.UseDistributedLock();
+
+ return options;
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCaching.Etcd/Configurations/EtcdOptionsExtension.cs b/src/EasyCaching.Etcd/Configurations/EtcdOptionsExtension.cs
new file mode 100644
index 00000000..7eb8adc5
--- /dev/null
+++ b/src/EasyCaching.Etcd/Configurations/EtcdOptionsExtension.cs
@@ -0,0 +1,70 @@
+using System;
+using EasyCaching.Core;
+using EasyCaching.Core.Configurations;
+using EasyCaching.Core.Serialization;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace EasyCaching.Etcd
+{
+ ///
+ /// Etcd options extension.
+ ///
+ internal sealed class EtcdOptionsExtension : IEasyCachingOptionsExtension
+ {
+ ///
+ /// The name.
+ ///
+ private readonly string _name;
+
+ ///
+ /// The configure.
+ ///
+ private readonly Action _configure;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Name.
+ /// Configure.
+ public EtcdOptionsExtension(string name, Action configure)
+ {
+ _name = name;
+ _configure = configure;
+ }
+
+ ///
+ /// Adds the services.
+ ///
+ /// Services.
+ public void AddServices(IServiceCollection services)
+ {
+ services.AddOptions();
+
+ services.Configure(_name, _configure);
+
+ services.TryAddSingleton();
+
+ services.AddSingleton(x =>
+ {
+ var optionsMon = x.GetRequiredService>();
+ var options = optionsMon.Get(_name);
+ var factory = x.GetService();
+ var serializers = x.GetServices();
+ return new EtcdCaching(_name, options,serializers,factory);
+ });
+
+ services.AddSingleton(x =>
+ {
+ var mCache = x.GetServices();
+ var optionsMon = x.GetRequiredService>();
+ var options = optionsMon.Get(_name);
+ var factory = x.GetService();
+ var serializers = x.GetServices();
+ return new DefaultEtcdCachingProvider(_name,mCache, options, serializers, factory);
+ });
+ }
+ }
+}
diff --git a/src/EasyCaching.Etcd/DefaultEtcdCachingProvider.Async.cs b/src/EasyCaching.Etcd/DefaultEtcdCachingProvider.Async.cs
new file mode 100644
index 00000000..5fdc7048
--- /dev/null
+++ b/src/EasyCaching.Etcd/DefaultEtcdCachingProvider.Async.cs
@@ -0,0 +1,397 @@
+using EasyCaching.Core;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace EasyCaching.Etcd
+{
+
+ ///
+ /// EtcdCaching provider.
+ ///
+ public partial class DefaultEtcdCachingProvider : EasyCachingAbstractProvider
+ {
+ ///
+ /// Gets the specified cacheKey, dataRetriever and expiration async.
+ ///
+ /// The async.
+ /// Cache key.
+ /// Data retriever.
+ /// Expiration.
+ /// CancellationToken
+ /// The 1st type parameter.
+ public override async Task> BaseGetAsync(string cacheKey, Func> dataRetriever, TimeSpan expiration, CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey));
+ ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration));
+
+ var result = await _etcdClient.GetAsync(cacheKey);
+ if (result.HasValue)
+ {
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"Cache Hit : cachekey = {cacheKey}");
+
+ CacheStats.OnHit();
+
+ return result;
+ }
+
+ CacheStats.OnMiss();
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"Cache Missed : cachekey = {cacheKey}");
+
+ if (!await _etcdClient.SetAsync($"{cacheKey}_Lock", "1", TimeSpan.FromMilliseconds(_options.LockMs)))
+ {
+ //wait for some ms
+ await Task.Delay(_options.SleepMs, cancellationToken);
+ return await GetAsync(cacheKey, dataRetriever, expiration);
+ }
+
+ try
+ {
+ var res = await dataRetriever();
+
+ if (res != null || _options.CacheNulls)
+ {
+ await SetAsync(cacheKey, res, expiration);
+ //remove mutex key
+ await _etcdClient.DeleteAsync($"{cacheKey}_Lock");
+
+ return new CacheValue(res, true);
+ }
+ else
+ {
+ //remove mutex key
+ await _etcdClient.DeleteAsync($"{cacheKey}_Lock");
+ return CacheValue.NoValue;
+ }
+ }
+ catch
+ {
+ //remove mutex key
+ await _etcdClient.DeleteAsync($"{cacheKey}_Lock");
+ throw;
+ }
+ }
+
+ ///
+ /// Gets the specified cacheKey async.
+ ///
+ /// The async.
+ /// Cache key.
+ /// CancellationToken
+ /// The 1st type parameter.
+ public override async Task> BaseGetAsync(string cacheKey, CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey));
+
+ var result = await _etcdClient.GetAsync(cacheKey);
+
+ if (result.HasValue)
+ {
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"Cache Hit : cachekey = {cacheKey}");
+
+ CacheStats.OnHit();
+
+ return result;
+ }
+ else
+ {
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"Cache Missed : cachekey = {cacheKey}");
+
+ CacheStats.OnMiss();
+
+ return CacheValue.NoValue;
+ }
+ }
+
+ ///
+ /// Gets the count.
+ ///
+ /// The count.
+ /// Prefix.
+ /// CancellationToken
+ public override async Task BaseGetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
+ {
+ var dicData = await _etcdClient.GetAllAsync(prefix);
+ return dicData != null ? dicData.Count : 0;
+ }
+
+ ///
+ /// Gets the specified cacheKey async.
+ ///
+ /// The async.
+ /// Cache key.
+ /// Object Type.
+ /// CancellationToken
+ public override async Task