diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj index 9b31ae6f..0e69d7b6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj +++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -37,8 +37,7 @@ - - + diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/CacheType.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/CacheType.cs new file mode 100644 index 00000000..53165851 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/CacheType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BotSharp.Abstraction.Infrastructures.Enums +{ + public enum CacheType + { + MemoryCache, + RedisCache + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/ICacheService.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/ICacheService.cs index ef9c7d8e..af510da3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/ICacheService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/ICacheService.cs @@ -6,4 +6,5 @@ public interface ICacheService Task GetAsync(string key, Type type); Task SetAsync(string key, T value, TimeSpan? expiry); Task RemoveAsync(string key); + Task ClearCacheAsync(string prefix); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs index c8f6a674..301c6b83 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheAttribute.cs @@ -6,33 +6,37 @@ using Rougamo.Context; namespace BotSharp.Core.Infrastructures; -public class SharpCacheAttribute : MoAttribute +public class SharpCacheAttribute : AsyncMoAttribute { public static IServiceProvider Services { get; set; } = null!; + private static readonly object NullMarker = new { __is_null = "$_is_null" }; - private int _minutes; + private readonly int _minutes; + private readonly bool _perInstanceCache; + private readonly ICacheService _cache; + private readonly SharpCacheSettings _settings; - public SharpCacheAttribute(int minutes = 60) + public SharpCacheAttribute(int minutes = 60, bool perInstanceCache = false) { _minutes = minutes; + _perInstanceCache = perInstanceCache; + _cache = Services.GetRequiredService(); + _settings = Services.GetRequiredService(); } - public override void OnEntry(MethodContext context) + public override async ValueTask OnEntryAsync(MethodContext context) { - var settings = Services.GetRequiredService(); - if (!settings.Enabled) + if (!_settings.Enabled) { return; } - var cache = Services.GetRequiredService(); - - var key = GetCacheKey(settings, context); - var value = cache.GetAsync(key, context.TaskReturnType).Result; + var key = GetCacheKey(context); + var value = await _cache.GetAsync(key, context.TaskReturnType); if (value != null) { // check if the cache is out of date - var isOutOfDate = IsOutOfDate(context, value).Result; + var isOutOfDate = await IsOutOfDate(context, value); if (!isOutOfDate) { @@ -41,10 +45,9 @@ public class SharpCacheAttribute : MoAttribute } } - public override void OnSuccess(MethodContext context) + public override async ValueTask OnSuccessAsync(MethodContext context) { - var settings = Services.GetRequiredService(); - if (!settings.Enabled) + if (!_settings.Enabled) { return; } @@ -57,12 +60,10 @@ public class SharpCacheAttribute : MoAttribute return; } - var cache = Services.GetRequiredService(); - if (context.ReturnValue != null) { - var key = GetCacheKey(settings, context); - cache.SetAsync(key, context.ReturnValue, new TimeSpan(0, _minutes, 0)).Wait(); + var key = GetCacheKey(context); + await _cache.SetAsync(key, context.ReturnValue, new TimeSpan(0, _minutes, 0)); } } @@ -71,25 +72,45 @@ public class SharpCacheAttribute : MoAttribute return Task.FromResult(false); } - private string GetCacheKey(SharpCacheSettings settings, MethodContext context) - { - var key = settings.Prefix + ":" + context.Method.Name; - foreach (var arg in context.Arguments) - { - if (arg is null) - { - key += "-" + ""; - } - else if (arg is ICacheKey withCacheKey) - { - key += "-" + withCacheKey.GetCacheKey(); - } - else - { - key += "-" + arg.ToString(); - } - } - return key; + private string GetCacheKey(MethodContext context) + { + var prefixKey = GetPrefixKey(context.Method.Name); + var argsKey = string.Join("_", context.Arguments.Select(arg => GetCacheKeyByArg(arg))); + + if (_perInstanceCache && context.Target != null) + { + return $"{prefixKey}-{context.Target.GetHashCode()}_{argsKey}"; + } + else + { + return $"{prefixKey}_{argsKey}"; + } + } + + private string GetPrefixKey(string name) + { + return _settings.Prefix + ":" + name; + } + + private string GetCacheKeyByArg(object? arg) + { + if (arg is null) + { + return NullMarker.GetHashCode().ToString(); + } + else if (arg is ICacheKey withCacheKey) + { + return withCacheKey.GetCacheKey(); + } + else + { + return arg.GetHashCode().ToString(); + } + } + + public async Task ClearCacheAsync() + { + await _cache.ClearCacheAsync(_settings.Prefix); } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheSettings.cs index 2fc266d2..7f42f1ca 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/SharpCacheSettings.cs @@ -2,6 +2,7 @@ namespace BotSharp.Abstraction.Infrastructures; public class SharpCacheSettings { - public bool Enabled { get; set; } = false; - public string Prefix { get; set; } = "cache"; + public bool Enabled { get; set; } = true; + public CacheType CacheType { get; set; } = Enums.CacheType.MemoryCache; + public string Prefix { get; set; } = "cache"; } diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAspect.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAspect.cs deleted file mode 100644 index 83bc4e78..00000000 --- a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAspect.cs +++ /dev/null @@ -1,180 +0,0 @@ -using AspectInjector.Broker; -using BotSharp.Abstraction.Shared; -using Microsoft.Extensions.DependencyInjection; -using System.Reflection; - -namespace BotSharp.Abstraction.SideCar.Attributes; - -[Aspect(Scope.PerInstance)] -public class SideCarAspect -{ - [Advice(Kind.Around)] - public object Handle( - [Argument(Source.Target)] Func target, - [Argument(Source.Arguments)] object[] args, - [Argument(Source.Instance)] object instance, - [Argument(Source.ReturnType)] Type retType, - [Argument(Source.Name)] string name, - [Argument(Source.Metadata)] MethodBase metaData, - [Argument(Source.Triggers)] Attribute[] triggers) - { - object value; - var serviceProvider = ((IHaveServiceProvider)instance).ServiceProvider; - - if (typeof(Task).IsAssignableFrom(retType)) - { - var syncResultType = retType.IsConstructedGenericType ? retType.GenericTypeArguments[0] : typeof(void); - value = CallAsyncMethod(serviceProvider, syncResultType, name, target, args); - } - else - { - value = CallSyncMethod(serviceProvider, retType, name, target, args); - } - - return value; - } - - - private static MethodInfo GetMethod(string name) - { - return typeof(SideCarAspect).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static); - } - - - private (IConversationSideCar?, MethodInfo?) GetSideCarMethod(IServiceProvider serviceProvider, string methodName, object[] args) - { - var sidecar = serviceProvider.GetService(); - var argTypes = args.Select(x => x.GetType()).ToArray(); - var sidecarMethod = sidecar?.GetType()?.GetMethods(BindingFlags.Public | BindingFlags.Instance) - .FirstOrDefault(x => x.Name == methodName - && x.GetParameters().Length == argTypes.Length - && x.GetParameters().Select(p => p.ParameterType) - .Zip(argTypes, (paramType, argType) => paramType.IsAssignableFrom(argType)).All(y => y)); - - return (sidecar, sidecarMethod); - } - - private object CallAsyncMethod(IServiceProvider serviceProvider, Type retType, string methodName, Func target, object[] args) - { - object value; - - var (sidecar, sidecarMethod) = GetSideCarMethod(serviceProvider, methodName, args); - var enabled = sidecar != null && sidecar.IsEnabled() && sidecarMethod != null; - - if (retType == typeof(void)) - { - if (enabled) - { - - value = GetMethod(nameof(CallAsync)).Invoke(this, [sidecar, sidecarMethod, args]); - } - else - { - value = GetMethod(nameof(WrapAsync)).Invoke(this, [target, args]); - } - } - else - { - if (enabled) - { - value = GetMethod(nameof(CallGenericAsync)).MakeGenericMethod(retType).Invoke(this, [sidecar, sidecarMethod, args]); - } - else - { - value = GetMethod(nameof(WrapGenericAsync)).MakeGenericMethod(retType).Invoke(this, [target, args]); - } - } - - return value; - } - - private object CallSyncMethod(IServiceProvider serviceProvider, Type retType, string methodName, Func target, object[] args) - { - object value; - - var (sidecar, sidecarMethod) = GetSideCarMethod(serviceProvider, methodName, args); - var enabled = sidecar != null && sidecar.IsEnabled() && sidecarMethod != null; - - if (retType == typeof(void)) - { - if (enabled) - { - value = GetMethod(nameof(CallSync)).Invoke(this, [sidecar, sidecarMethod, args]); - } - else - { - value = GetMethod(nameof(WrapSync)).Invoke(this, [target, args]); - } - } - else - { - if (enabled) - { - value = GetMethod(nameof(CallGenericSync)).MakeGenericMethod(retType).Invoke(this, [sidecar, sidecarMethod, args]); - } - else - { - value = GetMethod(nameof(WrapGenericSync)).MakeGenericMethod(retType).Invoke(this, [target, args]); - } - } - - return value; - } - - - #region Call Side car method - private static async Task CallGenericAsync(object instance, MethodInfo method, object[] args) - { - var res = await (Task)method.Invoke(instance, args); - return res; - } - - private static async Task CallAsync(object instance, MethodInfo method, object[] args) - { - await (Task)method.Invoke(instance, args); - return; - } - - private static T CallGenericSync(object instance, MethodInfo method, object[] args) - { - var res = (T)method.Invoke(instance, args); - return res; - } - - private static void CallSync(object instance, MethodInfo method, object[] args) - { - method.Invoke(instance, args); - return; - } - #endregion - - - #region Call original method - private static T WrapGenericSync(Func target, object[] args) - { - T res; - res = (T)target(args); - return res; - } - - private static async Task WrapGenericAsync(Func target, object[] args) - { - T res; - res = await (Task)target(args); - return res; - } - - - private static void WrapSync(Func target, object[] args) - { - target(args); - return; - } - - private static async Task WrapAsync(Func target, object[] args) - { - await (Task)target(args); - return; - } - #endregion -} diff --git a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs b/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs index 2c5c259f..f4bb238d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs +++ b/src/Infrastructure/BotSharp.Abstraction/SideCar/Attributes/SideCarAttribute.cs @@ -1,13 +1,145 @@ -using AspectInjector.Broker; +using System.Reflection; +using Rougamo; +using Rougamo.Context; +using Microsoft.Extensions.DependencyInjection; +using BotSharp.Abstraction.Shared; + namespace BotSharp.Abstraction.SideCar.Attributes; [AttributeUsage(AttributeTargets.Method, Inherited = true)] -[Injection(typeof(SideCarAspect))] -public class SideCarAttribute : Attribute +public class SideCarAttribute : AsyncMoAttribute { public SideCarAttribute() { - + } + + public override async ValueTask OnEntryAsync(MethodContext context) + { + object? value = null; + var isHandled = false; + var methodName = context.Method.Name; + var methodArgs = context.Arguments ?? []; + var instance = context.Target; + var retType = context.ReturnType; + + var serviceProvider = ((IHaveServiceProvider)instance).ServiceProvider; + var (sidecar, sidecarMethod) = GetSideCarMethod(serviceProvider, methodName, retType, methodArgs); + if (sidecar == null || sidecarMethod == null) + { + return; + } + + if (typeof(Task).IsAssignableFrom(retType)) + { + var syncResultType = retType.IsConstructedGenericType ? retType.GenericTypeArguments[0] : typeof(void); + (isHandled, value) = CallAsyncMethod(sidecar, sidecarMethod, syncResultType, methodArgs); + } + else + { + (isHandled, value) = CallSyncMethod(sidecar, sidecarMethod, retType, methodArgs); + } + + if (isHandled) + { + context.ReplaceReturnValue(this, value); + } + } + + private static MethodInfo GetMethod(string name) + { + return typeof(SideCarAttribute).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static); + } + + + private (IConversationSideCar?, MethodInfo?) GetSideCarMethod(IServiceProvider serviceProvider, string methodName, Type retType, object[] args) + { + var sidecar = serviceProvider.GetService(); + var argTypes = args.Select(x => x.GetType()).ToArray(); + var sidecarMethod = sidecar?.GetType()?.GetMethods(BindingFlags.Public | BindingFlags.Instance) + .FirstOrDefault(x => x.Name == methodName + && x.ReturnType == retType + && x.GetParameters().Length == argTypes.Length + && x.GetParameters().Select(p => p.ParameterType) + .Zip(argTypes, (paramType, argType) => paramType.IsAssignableFrom(argType)).All(y => y)); + + return (sidecar, sidecarMethod); + } + + private (bool, object?) CallAsyncMethod(IConversationSideCar instance, MethodInfo method, Type retType, object[] args) + { + object? value = null; + var isHandled = false; + + var enabled = instance != null && instance.IsEnabled() && method != null; + if (!enabled) + { + return (isHandled, value); + } + + isHandled = true; + if (retType == typeof(void)) + { + value = GetMethod(nameof(CallAsync)).Invoke(this, [instance, method, args]); + } + else + { + var task = GetMethod(nameof(CallGenericAsync)).MakeGenericMethod(retType).Invoke(this, [instance, method, args]); + value = task?.GetType().GetProperty("Result")?.GetValue(task); + } + + return (isHandled, value); + } + + private (bool, object?) CallSyncMethod(IConversationSideCar instance, MethodInfo method, Type retType, object[] args) + { + object? value = null; + var isHandled = false; + + var enabled = instance != null && instance.IsEnabled() && method != null; + if (!enabled) + { + return (isHandled, value); + } + + isHandled = true; + if (retType == typeof(void)) + { + value = GetMethod(nameof(CallSync)).Invoke(this, [instance, method, args]); + } + else + { + value = GetMethod(nameof(CallGenericSync)).MakeGenericMethod(retType).Invoke(this, [instance, method, args]); + } + + return (isHandled, value); + } + + + #region Call Side car method + private static async Task CallGenericAsync(object instance, MethodInfo method, object[] args) + { + var res = await (Task)method.Invoke(instance, args); + return res; + } + + private static async Task CallAsync(object instance, MethodInfo method, object[] args) + { + await (Task)method.Invoke(instance, args); + return; + } + + private static T CallGenericSync(object instance, MethodInfo method, object[] args) + { + var res = (T)method.Invoke(instance, args); + return res; + } + + private static void CallSync(object instance, MethodInfo method, object[] args) + { + method.Invoke(instance, args); + return; + } + #endregion } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 04b5c22d..95018a2d 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -5,7 +5,7 @@ namespace BotSharp.Core.Agents.Services; public partial class AgentService { #if !DEBUG - [MemoryCache(10 * 60, perInstanceCache: true)] + [SharpCache(10, perInstanceCache: true)] #endif public async Task> GetAgents(AgentFilter filter) { @@ -27,7 +27,7 @@ public partial class AgentService } #if !DEBUG - [MemoryCache(10 * 60, perInstanceCache: true)] + [SharpCache(10, perInstanceCache: true)] #endif public async Task GetAgent(string id) { diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs index 9e6ad8ee..15ba26a1 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs @@ -7,7 +7,7 @@ public partial class AgentService { public static ConcurrentDictionary> AgentParameterTypes = new(); - [MemoryCache(10 * 60, perInstanceCache: true)] + [SharpCache(10, perInstanceCache: true)] public async Task LoadAgent(string id, bool loadUtility = true) { if (string.IsNullOrEmpty(id) || id == Guid.Empty.ToString()) diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index d1232c5e..37eee3ac 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -188,13 +188,13 @@ - + diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index f588b4d1..d801e943 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -14,6 +14,7 @@ using BotSharp.Core.Infrastructures.Events; using BotSharp.Core.Roles.Services; using BotSharp.Abstraction.Templating; using BotSharp.Core.Templating; +using BotSharp.Abstraction.Infrastructures.Enums; namespace BotSharp.Core; @@ -34,15 +35,9 @@ public static class BotSharpCoreExtensions services.AddScoped(); services.AddScoped(); - // Register cache service - var cacheSettings = new SharpCacheSettings(); - config.Bind("SharpCache", cacheSettings); - services.AddSingleton(x => cacheSettings); - services.AddSingleton(); - AddRedisEvents(services, config); - - services.AddMemoryCache(); + // Register cache service + AddCacheServices(services, config); RegisterPlugins(services, config); AddBotSharpOptions(services, configOptions); @@ -50,6 +45,20 @@ public static class BotSharpCoreExtensions return services; } + private static void AddCacheServices(IServiceCollection services, IConfiguration config) + { + services.AddMemoryCache(); + var cacheSettings = new SharpCacheSettings(); + config.Bind("SharpCache", cacheSettings); + services.AddSingleton(x => cacheSettings); + + services.AddSingleton(sp => cacheSettings.CacheType switch + { + CacheType.RedisCache => ActivatorUtilities.CreateInstance(sp), + _ => ActivatorUtilities.CreateInstance(sp), + }); + } + public static IServiceCollection UsingSqlServer(this IServiceCollection services, IConfiguration config) { services.AddScoped(sp => diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/MemoryCacheService.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/Cache/MemoryCacheService.cs similarity index 72% rename from src/Infrastructure/BotSharp.Core/Infrastructures/MemoryCacheService.cs rename to src/Infrastructure/BotSharp.Core/Infrastructures/Cache/MemoryCacheService.cs index a775ab6a..f5bae36a 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/MemoryCacheService.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/Cache/MemoryCacheService.cs @@ -1,18 +1,15 @@ using BotSharp.Abstraction.Infrastructures; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; namespace BotSharp.Core.Infrastructures; public class MemoryCacheService : ICacheService { - private static IMemoryCache _cache = new MemoryCache(new MemoryCacheOptions - { - }); - private readonly BotSharpDatabaseSettings _settings; + private static readonly MemoryCache _cache = new MemoryCache(new OptionsWrapper(new MemoryCacheOptions())); - public MemoryCacheService(BotSharpDatabaseSettings settings) + public MemoryCacheService() { - _settings = settings; } public async Task GetAsync(string key) @@ -37,4 +34,9 @@ public class MemoryCacheService : ICacheService { _cache.Remove(key); } + + public async Task ClearCacheAsync(string prefix) + { + _cache.Compact(1.0); + } } diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/Cache/RedisCacheService.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/Cache/RedisCacheService.cs new file mode 100644 index 00000000..1e49bc75 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/Cache/RedisCacheService.cs @@ -0,0 +1,68 @@ +using BotSharp.Abstraction.Infrastructures; +using Newtonsoft.Json; +using StackExchange.Redis; + +namespace BotSharp.Core.Infrastructures; + +public class RedisCacheService : ICacheService +{ + private readonly IConnectionMultiplexer _redis; + + public RedisCacheService(IConnectionMultiplexer redis) + { + _redis = redis; + } + + public async Task GetAsync(string key) + { + var db = _redis.GetDatabase(); + var value = await db.StringGetAsync(key); + + if (value.HasValue) + { + return JsonConvert.DeserializeObject(value); + } + + return default; + } + + public async Task GetAsync(string key, Type type) + { + var db = _redis.GetDatabase(); + var value = await db.StringGetAsync(key); + + if (value.HasValue) + { + return JsonConvert.DeserializeObject(value, type); + } + + return default; + } + + + public async Task SetAsync(string key, T value, TimeSpan? expiry) + { + var db = _redis.GetDatabase(); + await db.StringSetAsync(key, JsonConvert.SerializeObject(value), expiry); + } + + public async Task RemoveAsync(string key) + { + var db = _redis.GetDatabase(); + await db.KeyDeleteAsync(key); + } + + public async Task ClearCacheAsync(string prefix) + { + var db = _redis.GetDatabase(); + var server = _redis.GetServer(_redis.GetEndPoints().First()); + const int pageSize = 1000; + var keys = server.Keys(pattern: $"{prefix}*", pageSize: pageSize).ToList(); + + for (int i = 0; i < keys.Count; i += pageSize) + { + var batch = keys.Skip(i).Take(pageSize).ToArray(); + await db.KeyDeleteAsync(batch); + } + } +} diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/RedisCacheService.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/RedisCacheService.cs deleted file mode 100644 index bde2708e..00000000 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/RedisCacheService.cs +++ /dev/null @@ -1,95 +0,0 @@ -using BotSharp.Abstraction.Infrastructures; -using Newtonsoft.Json; -using StackExchange.Redis; - -namespace BotSharp.Core.Infrastructures; - -public class RedisCacheService : ICacheService -{ - private readonly BotSharpDatabaseSettings _settings; - private static ConnectionMultiplexer redis = null!; - - public RedisCacheService(BotSharpDatabaseSettings settings) - { - _settings = settings; - } - - public async Task GetAsync(string key) - { - if (string.IsNullOrEmpty(_settings.Redis)) - { - return default; - } - - if (redis == null) - { - redis = ConnectionMultiplexer.Connect(_settings.Redis); - } - - var db = redis.GetDatabase(); - var value = await db.StringGetAsync(key); - - if (value.HasValue) - { - return JsonConvert.DeserializeObject(value); - } - - return default; - } - - public async Task GetAsync(string key, Type type) - { - if (string.IsNullOrEmpty(_settings.Redis)) - { - return default; - } - - if (redis == null) - { - redis = ConnectionMultiplexer.Connect(_settings.Redis); - } - - var db = redis.GetDatabase(); - var value = await db.StringGetAsync(key); - - if (value.HasValue) - { - return JsonConvert.DeserializeObject(value, type); - } - - return default; - } - - - public async Task SetAsync(string key, T value, TimeSpan? expiry) - { - if (string.IsNullOrEmpty(_settings.Redis)) - { - return; - } - - if (redis == null) - { - redis = ConnectionMultiplexer.Connect(_settings.Redis); - } - - var db = redis.GetDatabase(); - await db.StringSetAsync(key, JsonConvert.SerializeObject(value), expiry); - } - - public async Task RemoveAsync(string key) - { - if (string.IsNullOrEmpty(_settings.Redis)) - { - return; - } - - if (redis == null) - { - redis = ConnectionMultiplexer.Connect(_settings.Redis); - } - - var db = redis.GetDatabase(); - await db.KeyDeleteAsync(key); - } -} diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/Utilities.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/Utilities.cs index db5a9a7d..6293c1f6 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/Utilities.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/Utilities.cs @@ -48,10 +48,8 @@ public static class Utilities public static void ClearCache() { // Clear whole cache. - if (new MemoryCacheAttribute(0).Cache is MemoryCache memcache) - { - memcache.Compact(100); - } + var sharpCache = new SharpCacheAttribute(0); + sharpCache.ClearCacheAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } public static string HideMiddleDigits(string input, bool isEmail = false) diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index ef32f018..075307b5 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Routing.Settings; +using BotSharp.Core.Infrastructures; namespace BotSharp.Core.Routing; @@ -74,7 +75,7 @@ public partial class RoutingService : IRoutingService } #if !DEBUG - [MemoryCache(10 * 60)] + [SharpCache(10)] #endif protected RoutingRule[] GetRoutingRecords() { @@ -99,7 +100,7 @@ public partial class RoutingService : IRoutingService } #if !DEBUG - [MemoryCache(10 * 60)] + [SharpCache(10)] #endif public RoutableAgent[] GetRoutableAgents(List profiles) { diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 0af66b30..5035a33b 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -377,7 +377,7 @@ public class UserService : IUserService return await _cacheService.GetAsync(GetUserTokenExpiresCacheKey(_user.Id)); } - [MemoryCache(10 * 60, perInstanceCache: true)] + [SharpCache(10, perInstanceCache: true)] public async Task GetMyProfile() { var db = _services.GetRequiredService(); @@ -398,7 +398,7 @@ public class UserService : IUserService return user; } - [MemoryCache(10 * 60, perInstanceCache: true)] + [SharpCache(10, perInstanceCache: true)] public async Task GetUser(string id) { var db = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs index 4a74f1d0..0aaaf01a 100644 --- a/src/Infrastructure/BotSharp.Core/Using.cs +++ b/src/Infrastructure/BotSharp.Core/Using.cs @@ -42,5 +42,4 @@ global using BotSharp.Core.Agents.Services; global using BotSharp.Core.Conversations.Services; global using BotSharp.Core.Infrastructures; global using BotSharp.Core.Users.Services; -global using Aspects.Cache; global using BotSharp.Abstraction.Infrastructures.Events; \ No newline at end of file