From 5b093dc309989dbafb92d050be65ccddd3876c6a Mon Sep 17 00:00:00 2001
From: InCerryGit <36690780+InCerryGit@users.noreply.github.com>
Date: Thu, 17 Nov 2022 23:11:02 +0800
Subject: [PATCH] feat(FasterKv): Add FasterKv support (#418)
* feat(FasterKv): add FasterKv caching provider support.
* fix wrong summary
---
EasyCaching.sln | 7 +
.../Controllers/ValuesController.cs | 2 +-
.../EasyCaching.Demo.Providers.csproj | 1 +
sample/EasyCaching.Demo.Providers/Startup.cs | 10 +-
.../appsettings.json | 4 +
.../Internal/CachingProviderType.cs | 1 +
.../Internal/EasyCachingConstValue.cs | 36 +-
.../ClientSessionPooledObjectPolicy.cs | 27 ++
.../Configurations/FasterKvCachingOptions.cs | 74 ++++
.../FasterKvCachingOptionsExtensions.cs | 68 ++++
.../FasterKvOptionsExtension.cs | 61 +++
.../DefaultFasterKvCachingProvider.Async.cs | 223 +++++++++++
.../DefaultFasterKvCachingProvider.cs | 377 ++++++++++++++++++
.../EasyCaching.FasterKv.csproj | 18 +
src/EasyCaching.FasterKv/StoreFunctions.cs | 34 ++
.../EasyCaching.PerformanceTests.csproj | 6 +-
.../FasterKvBenchmark.cs | 141 +++++++
test/EasyCaching.PerformanceTests/Program.cs | 3 +-
.../CachingTests/BaseCachingProviderTest.cs | 8 +-
.../FasterKvCachingProviderTest.cs | 164 ++++++++
.../EasyCaching.UnitTests.csproj | 3 +-
21 files changed, 1245 insertions(+), 23 deletions(-)
create mode 100644 src/EasyCaching.FasterKv/ClientSessionPooledObjectPolicy.cs
create mode 100644 src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptions.cs
create mode 100644 src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptionsExtensions.cs
create mode 100644 src/EasyCaching.FasterKv/Configurations/FasterKvOptionsExtension.cs
create mode 100644 src/EasyCaching.FasterKv/DefaultFasterKvCachingProvider.Async.cs
create mode 100644 src/EasyCaching.FasterKv/DefaultFasterKvCachingProvider.cs
create mode 100644 src/EasyCaching.FasterKv/EasyCaching.FasterKv.csproj
create mode 100644 src/EasyCaching.FasterKv/StoreFunctions.cs
create mode 100644 test/EasyCaching.PerformanceTests/FasterKvBenchmark.cs
create mode 100644 test/EasyCaching.UnitTests/CachingTests/FasterKvCachingProviderTest.cs
diff --git a/EasyCaching.sln b/EasyCaching.sln
index ff571815..3f2504a3 100644
--- a/EasyCaching.sln
+++ b/EasyCaching.sln
@@ -74,6 +74,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyCaching.Bus.ConfluentKa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Bus.Zookeeper", "bus\EasyCaching.Bus.Zookeeper\EasyCaching.Bus.Zookeeper.csproj", "{5E488583-391E-4E15-83C1-7301B4FE79AE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.FasterKv", "src\EasyCaching.FasterKv\EasyCaching.FasterKv.csproj", "{7191E567-38DF-4879-82E1-73EC618AFCAC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -196,6 +198,10 @@ Global
{5E488583-391E-4E15-83C1-7301B4FE79AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E488583-391E-4E15-83C1-7301B4FE79AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E488583-391E-4E15-83C1-7301B4FE79AE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7191E567-38DF-4879-82E1-73EC618AFCAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -230,6 +236,7 @@ Global
{4FCF16BF-5E21-4B74-AB45-3C121ADF1485} = {15070C49-A507-4844-BCFE-D319CFBC9A63}
{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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {63A57886-054B-476C-AAE1-8D7C8917682E}
diff --git a/sample/EasyCaching.Demo.Providers/Controllers/ValuesController.cs b/sample/EasyCaching.Demo.Providers/Controllers/ValuesController.cs
index d08da6a7..d7f8878a 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
+ //1. InMemory,Memcached,Redis,SQLite,FasterKv
private readonly IEasyCachingProvider _provider;
public ValuesController(IEasyCachingProvider provider)
diff --git a/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj b/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
index 0fb6da6f..116c4206 100644
--- a/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
+++ b/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
@@ -7,6 +7,7 @@
+
diff --git a/sample/EasyCaching.Demo.Providers/Startup.cs b/sample/EasyCaching.Demo.Providers/Startup.cs
index 9f9e30f5..5372e2ae 100644
--- a/sample/EasyCaching.Demo.Providers/Startup.cs
+++ b/sample/EasyCaching.Demo.Providers/Startup.cs
@@ -61,6 +61,14 @@ public void ConfigureServices(IServiceCollection services)
});
option.UseMemcached(Configuration);
+
+ //use fasterKv
+ option.UseFasterKv(config =>
+ {
+ // fasterKv must be set SerializerName
+ config.SerializerName = "msg";
+ })
+ .WithMessagePack("msg");
});
}
@@ -84,4 +92,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
});
}
}
-}
+}
diff --git a/sample/EasyCaching.Demo.Providers/appsettings.json b/sample/EasyCaching.Demo.Providers/appsettings.json
index 85b08a94..9184d1a9 100644
--- a/sample/EasyCaching.Demo.Providers/appsettings.json
+++ b/sample/EasyCaching.Demo.Providers/appsettings.json
@@ -76,6 +76,10 @@
"GroupId": "MyGroupId"
},
"ConsumerCount":2
+ },
+ "fasterKv": {
+ "MemorySizeBit": 16,
+ "PageSizeBit": 15
}
}
}
diff --git a/src/EasyCaching.Core/Internal/CachingProviderType.cs b/src/EasyCaching.Core/Internal/CachingProviderType.cs
index c7ff8d1b..ed1d8f27 100644
--- a/src/EasyCaching.Core/Internal/CachingProviderType.cs
+++ b/src/EasyCaching.Core/Internal/CachingProviderType.cs
@@ -13,5 +13,6 @@ public enum CachingProviderType
Ext1,
Ext2,
LiteDB,
+ FasterKv,
}
}
diff --git a/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs b/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
index 4e471456..342a0ae4 100644
--- a/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
+++ b/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
@@ -53,13 +53,13 @@ public class EasyCachingConstValue
///
/// The rabbitMQ Bus section.
///
- public const string RabbitMQBusSection = "easycaching:rabbitmqbus";
-
+ public const string RabbitMQBusSection = "easycaching:rabbitmqbus";
+
///
/// The kafka bus section.
///
- public const string KafkaBusSection = "easycaching:kafkabus";
-
+ public const string KafkaBusSection = "easycaching:kafkabus";
+
///
/// The zookeeper bus section.
///
@@ -104,19 +104,29 @@ public class EasyCachingConstValue
///
/// The default name of the serializer.
///
- public const string DefaultSerializerName = "binary";
-
+ public const string DefaultSerializerName = "binary";
+
///
/// The default name of the LiteDB.
///
- public const string DefaultLiteDBName = "DefaultLiteDB";
+ public const string DefaultLiteDBName = "DefaultLiteDB";
///
/// The LiteDB Bus section.
- ///
- public const string LiteDBSection= "easycaching:litedb";
-
- public const string NotFoundCliExceptionMessage = "Can not find the matched client instance, client name is {0}";
-
- public const string NotFoundSerExceptionMessage = "Can not find the matched serializer instance, serializer name is {0}";
+ ///
+ public const string LiteDBSection= "easycaching:litedb";
+
+ ///
+ /// The default name of the FasterKv
+ ///
+ public const string DefaultFasterKvName = "DefaultFasterKvName";
+
+ ///
+ /// The FasterKv section.
+ ///
+ public const string FasterKvSection= "easycaching:fasterKv";
+
+ public const string NotFoundCliExceptionMessage = "Can not find the matched client instance, client name is {0}";
+
+ public const string NotFoundSerExceptionMessage = "Can not find the matched serializer instance, serializer name is {0}";
}
}
diff --git a/src/EasyCaching.FasterKv/ClientSessionPooledObjectPolicy.cs b/src/EasyCaching.FasterKv/ClientSessionPooledObjectPolicy.cs
new file mode 100644
index 00000000..75cd7e7b
--- /dev/null
+++ b/src/EasyCaching.FasterKv/ClientSessionPooledObjectPolicy.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Concurrent;
+using FASTER.core;
+
+namespace EasyCaching.FasterKv
+{
+ public class ClientSessionWrap : IDisposable
+ {
+ public ClientSession Session { get; }
+
+ private readonly ConcurrentQueue> _innerPool;
+
+ public ClientSessionWrap(
+ ClientSession clientSession,
+ ConcurrentQueue> innerPool)
+ {
+ Session = clientSession;
+ _innerPool = innerPool;
+ }
+
+ public void Dispose()
+ {
+ Session.CompletePending(true);
+ _innerPool.Enqueue(Session);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptions.cs b/src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptions.cs
new file mode 100644
index 00000000..325ff15f
--- /dev/null
+++ b/src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptions.cs
@@ -0,0 +1,74 @@
+using System;
+using System.IO;
+using EasyCaching.Core.Configurations;
+using FASTER.core;
+
+namespace EasyCaching.FasterKv.Configurations
+{
+ ///
+ /// FasterKvCachingOptions
+ /// for details, see https://microsoft.github.io/FASTER/docs/fasterkv-basics/#fasterkvsettings
+ ///
+ public class FasterKvCachingOptions : BaseProviderOptions
+ {
+ ///
+ /// FasterKv index count, Must be power of 2
+ ///
+ /// For example: 1024(2^10) 2048(2^11) 65536(2^16) 131072(2^17)
+ /// Each index is 64 bits. So this define 131072 keys. Used 1024Kb memory
+ public long IndexCount { get; set; } = 131072;
+
+ ///
+ /// FasterKv used memory size (default: 16MB)
+ ///
+ public int MemorySizeBit { get; set; } = 24;
+
+ ///
+ /// FasterKv page size (default: 1MB)
+ ///
+ public int PageSizeBit { get; set; } = 20;
+
+ ///
+ /// FasterKv read cache used memory size (default: 16MB)
+ ///
+ public int ReadCacheMemorySizeBit { get; set; } = 24;
+
+ ///
+ /// FasterKv read cache page size (default: 16MB)
+ ///
+ public int ReadCachePageSizeBit { get; set; } = 20;
+
+ ///
+ /// FasterKv commit logs path
+ ///
+ public string LogPath { get; set; } =
+#if (NET6_0 || NET7_0)
+ Path.Combine(Environment.CurrentDirectory, $"EasyCaching-FasterKv-{Environment.ProcessId}");
+#else
+ Path.Combine(Environment.CurrentDirectory, $"EasyCaching-FasterKv-{System.Diagnostics.Process.GetCurrentProcess().Id}");
+#endif
+
+
+ ///
+ /// Set Custom Store
+ ///
+ public FasterKV? CustomStore { get; set; }
+
+ internal LogSettings GetLogSettings(string name)
+ {
+ return new LogSettings
+ {
+ LogDevice = Devices.CreateLogDevice(Path.Combine(LogPath, name),
+ preallocateFile: true,
+ deleteOnClose: true),
+ PageSizeBits = PageSizeBit,
+ MemorySizeBits = MemorySizeBit,
+ ReadCacheSettings = new ReadCacheSettings
+ {
+ MemorySizeBits = ReadCacheMemorySizeBit,
+ PageSizeBits = ReadCachePageSizeBit,
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptionsExtensions.cs b/src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptionsExtensions.cs
new file mode 100644
index 00000000..d1d770da
--- /dev/null
+++ b/src/EasyCaching.FasterKv/Configurations/FasterKvCachingOptionsExtensions.cs
@@ -0,0 +1,68 @@
+using System;
+using EasyCaching.Core;
+using EasyCaching.Core.Configurations;
+using EasyCaching.FasterKv;
+using EasyCaching.FasterKv.Configurations;
+using Microsoft.Extensions.Configuration;
+// ReSharper disable CheckNamespace
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static class FasterKvCachingOptionsExtensions
+{
+ ///
+ /// Uses the FasterKv provider (specify the config via hard code).
+ ///
+ /// Options.
+ /// Configure provider settings.
+ /// The name of this provider instance.
+ public static EasyCachingOptions UseFasterKv(
+ this EasyCachingOptions options,
+ Action configure,
+ string name = EasyCachingConstValue.DefaultFasterKvName
+ )
+ {
+ ArgumentCheck.NotNull(configure, nameof(configure));
+
+ options.RegisterExtension(new FasterKvOptionsExtension(name, configure));
+ return options;
+ }
+
+ ///
+ /// Uses the FasterKv 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 UseFasterKv(
+ this EasyCachingOptions options,
+ IConfiguration configuration,
+ string name = EasyCachingConstValue.DefaultFasterKvName,
+ string sectionName = EasyCachingConstValue.FasterKvSection
+ )
+ {
+ var dbConfig = configuration.GetSection(sectionName);
+ var fasterKvOptions = new FasterKvCachingOptions();
+ dbConfig.Bind(fasterKvOptions);
+
+ void Configure(FasterKvCachingOptions x)
+ {
+ x.EnableLogging = fasterKvOptions.EnableLogging;
+ x.MaxRdSecond = fasterKvOptions.MaxRdSecond;
+ x.LockMs = fasterKvOptions.LockMs;
+ x.SleepMs = fasterKvOptions.SleepMs;
+ x.SerializerName = fasterKvOptions.SerializerName;
+ x.CacheNulls = fasterKvOptions.CacheNulls;
+ x.IndexCount = fasterKvOptions.IndexCount;
+ x.MemorySizeBit = fasterKvOptions.MemorySizeBit;
+ x.PageSizeBit = fasterKvOptions.PageSizeBit;
+ x.ReadCacheMemorySizeBit = fasterKvOptions.ReadCacheMemorySizeBit;
+ x.ReadCachePageSizeBit = fasterKvOptions.ReadCachePageSizeBit;
+ x.LogPath = fasterKvOptions.LogPath;
+ }
+
+ options.RegisterExtension(new FasterKvOptionsExtension(name, Configure));
+ return options;
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCaching.FasterKv/Configurations/FasterKvOptionsExtension.cs b/src/EasyCaching.FasterKv/Configurations/FasterKvOptionsExtension.cs
new file mode 100644
index 00000000..5f956666
--- /dev/null
+++ b/src/EasyCaching.FasterKv/Configurations/FasterKvOptionsExtension.cs
@@ -0,0 +1,61 @@
+using System;
+using EasyCaching.Core;
+using EasyCaching.Core.Configurations;
+using EasyCaching.Core.Serialization;
+using EasyCaching.FasterKv.Configurations;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace EasyCaching.FasterKv
+{
+ ///
+ /// FasterKv options extension.
+ ///
+ internal sealed class FasterKvOptionsExtension : IEasyCachingOptionsExtension
+ {
+ ///
+ /// The name.
+ ///
+ private readonly string _name;
+
+ ///
+ /// The configure.
+ ///
+ private readonly Action _configure;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Name.
+ /// Configure.
+ public FasterKvOptionsExtension(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 DefaultFasterKvCachingProvider(_name, options, serializers, factory);
+ });
+ }
+ }
+}
diff --git a/src/EasyCaching.FasterKv/DefaultFasterKvCachingProvider.Async.cs b/src/EasyCaching.FasterKv/DefaultFasterKvCachingProvider.Async.cs
new file mode 100644
index 00000000..80393de0
--- /dev/null
+++ b/src/EasyCaching.FasterKv/DefaultFasterKvCachingProvider.Async.cs
@@ -0,0 +1,223 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using EasyCaching.Core;
+using Microsoft.Extensions.Logging;
+
+namespace EasyCaching.FasterKv
+{
+ public partial class DefaultFasterKvCachingProvider
+ {
+ public override async Task BaseExistsAsync(string cacheKey, CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey));
+ using var session = GetSession();
+ var result = (await session.Session.ReadAsync(GetSpanByte(cacheKey), token: cancellationToken)).Complete();
+ return result.status.Found;
+ }
+
+
+ public override async Task BaseFlushAsync(CancellationToken cancellationToken = default)
+ {
+ EnsureNotDispose();
+ using var session = GetSession();
+ using var iter = session.Session.Iterate();
+ while (iter.GetNext(out var recordInfo))
+ {
+ await session.Session.DeleteAsync(ref iter.GetKey(), token: cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+
+ public override async Task>> BaseGetAllAsync(IEnumerable cacheKeys,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullAndCountGTZero(cacheKeys, nameof(cacheKeys));
+ EnsureNotDispose();
+
+ using var session = GetSession();
+ var dic = new Dictionary>();
+ foreach (var cacheKey in cacheKeys)
+ {
+ dic[cacheKey] = await BaseGetInternalAsync(session, cacheKey, cancellationToken);
+ }
+
+ return dic;
+ }
+
+ public override async Task> BaseGetAsync(string cacheKey, Func> dataRetriever,
+ TimeSpan expiration,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey));
+ ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration));
+ EnsureNotDispose();
+
+ using var session = GetSession();
+ var result = await BaseGetInternalAsync(session, cacheKey, cancellationToken);
+ if (result.HasValue)
+ {
+ return result;
+ }
+
+ var item = await dataRetriever();
+ if (item is not null || _options.CacheNulls)
+ {
+ await SetAsync(cacheKey, item, expiration, cancellationToken);
+ return new CacheValue(item, true);
+ }
+
+ return CacheValue.NoValue;
+ }
+
+ public override async Task
-
+
@@ -40,6 +40,7 @@
+