From a2c4695557313663e4675b8f6e14ff780bb049a7 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 13 Sep 2017 03:12:40 +0200 Subject: [PATCH] Global custom reactions now use redis pub/sub --- .../CustomReactions/CustomReactions.cs | 6 +-- .../Services/CustomReactionsService.cs | 39 ++++++++++++++++++- .../Database/Models/CustomReaction.cs | 6 ++- src/NadekoBot/Services/IDataCache.cs | 4 +- src/NadekoBot/Services/Impl/RedisCache.cs | 7 ++-- 5 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index 9867dbcb..33af0699 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -58,8 +58,7 @@ namespace NadekoBot.Modules.CustomReactions if (channel == null) { - Array.Resize(ref _service.GlobalReactions, _service.GlobalReactions.Length + 1); - _service.GlobalReactions[_service.GlobalReactions.Length - 1] = cr; + await _service.AddGcr(cr).ConfigureAwait(false); } else { @@ -237,8 +236,7 @@ namespace NadekoBot.Modules.CustomReactions if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null) { uow.CustomReactions.Remove(toDelete); - //todo 91 i can dramatically improve performance of this, if Ids are ordered. - _service.GlobalReactions = _service.GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray(); + await _service.DelGcr(toDelete.Id); success = true; } else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId) diff --git a/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs b/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs index 2944ab6c..bcb36784 100644 --- a/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs +++ b/src/NadekoBot/Modules/CustomReactions/Services/CustomReactionsService.cs @@ -15,6 +15,7 @@ using NadekoBot.Modules.CustomReactions.Extensions; using NadekoBot.Modules.Permissions.Common; using NadekoBot.Modules.Permissions.Services; using NadekoBot.Services.Impl; +using Newtonsoft.Json; namespace NadekoBot.Modules.CustomReactions.Services { @@ -32,9 +33,11 @@ namespace NadekoBot.Modules.CustomReactions.Services private readonly CommandHandler _cmd; private readonly IBotConfigProvider _bc; private readonly NadekoStrings _strings; + private readonly IDataCache _cache; public CustomReactionsService(PermissionService perms, DbService db, NadekoStrings strings, - DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow) + DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow, + IDataCache cache) { _log = LogManager.GetCurrentClassLogger(); _db = db; @@ -43,10 +46,42 @@ namespace NadekoBot.Modules.CustomReactions.Services _cmd = cmd; _bc = bc; _strings = strings; - + _cache = cache; + + var sub = _cache.Redis.GetSubscriber(); + sub.Subscribe("gcr.added", (ch, msg) => + { + Array.Resize(ref GlobalReactions, GlobalReactions.Length + 1); + GlobalReactions[GlobalReactions.Length - 1] = JsonConvert.DeserializeObject(msg); + }, StackExchange.Redis.CommandFlags.FireAndForget); + sub.Subscribe("gcr.deleted", (ch, msg) => + { + var id = int.Parse(msg); + GlobalReactions = GlobalReactions.Where(cr => cr?.Id != id).ToArray(); + }, StackExchange.Redis.CommandFlags.FireAndForget); + var items = uow.CustomReactions.GetAll(); + GuildReactions = new ConcurrentDictionary(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray())); GlobalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray(); + foreach (var item in items) + { + _log.Info(item.Id); + _log.Info(item.Trigger); + _log.Info(item.GuildId); + } + } + + public Task AddGcr(CustomReaction cr) + { + var sub = _cache.Redis.GetSubscriber(); + return sub.PublishAsync("gcr.added", JsonConvert.SerializeObject(cr)); + } + + public Task DelGcr(int id) + { + var sub = _cache.Redis.GetSubscriber(); + return sub.PublishAsync("gcr.deleted", id); } public void ClearStats() => ReactionStats.Clear(); diff --git a/src/NadekoBot/Services/Database/Models/CustomReaction.cs b/src/NadekoBot/Services/Database/Models/CustomReaction.cs index 2f268849..cd57bc35 100644 --- a/src/NadekoBot/Services/Database/Models/CustomReaction.cs +++ b/src/NadekoBot/Services/Database/Models/CustomReaction.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations.Schema; using System.Text.RegularExpressions; namespace NadekoBot.Services.Database.Models @@ -6,7 +7,9 @@ namespace NadekoBot.Services.Database.Models public class CustomReaction : DbEntity { public ulong? GuildId { get; set; } + [NotMapped] + [JsonIgnore] public Regex Regex { get; set; } public string Response { get; set; } public string Trigger { get; set; } @@ -16,6 +19,7 @@ namespace NadekoBot.Services.Database.Models public bool AutoDeleteTrigger { get; set; } public bool DmResponse { get; set; } + [JsonIgnore] public bool IsGlobal => !GuildId.HasValue; public bool ContainsAnywhere { get; set; } diff --git a/src/NadekoBot/Services/IDataCache.cs b/src/NadekoBot/Services/IDataCache.cs index 67223dfe..b708a66b 100644 --- a/src/NadekoBot/Services/IDataCache.cs +++ b/src/NadekoBot/Services/IDataCache.cs @@ -1,4 +1,5 @@ -using System; +using StackExchange.Redis; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,6 +9,7 @@ namespace NadekoBot.Services { public interface IDataCache { + ConnectionMultiplexer Redis { get; } Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key); Task SetImageDataAsync(string key, byte[] data); } diff --git a/src/NadekoBot/Services/Impl/RedisCache.cs b/src/NadekoBot/Services/Impl/RedisCache.cs index b4d9a89d..5e54979d 100644 --- a/src/NadekoBot/Services/Impl/RedisCache.cs +++ b/src/NadekoBot/Services/Impl/RedisCache.cs @@ -5,13 +5,14 @@ namespace NadekoBot.Services.Impl { public class RedisCache : IDataCache { - private readonly ConnectionMultiplexer _redis; + public ConnectionMultiplexer Redis { get; } private readonly IDatabase _db; public RedisCache() { - _redis = ConnectionMultiplexer.Connect("localhost"); - _db = _redis.GetDatabase(); + Redis = ConnectionMultiplexer.Connect("localhost"); + Redis.PreserveAsyncOrder = false; + _db = Redis.GetDatabase(); } public async Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key)