From 423d23faf793d8f7d44827b6fa57a8a4466ab1ff Mon Sep 17 00:00:00 2001 From: Ske Date: Mon, 12 Aug 2019 03:48:08 +0200 Subject: [PATCH] Cache account lookup in memory when proxying --- PluralKit.Bot/Bot.cs | 3 + PluralKit.Bot/Commands/MemberCommands.cs | 4 + PluralKit.Bot/PluralKit.Bot.csproj | 2 +- PluralKit.Bot/Services/ProxyCacheService.cs | 82 +++++++++++++++++++++ PluralKit.Bot/Services/ProxyService.cs | 23 ++---- 5 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 PluralKit.Bot/Services/ProxyCacheService.cs diff --git a/PluralKit.Bot/Bot.cs b/PluralKit.Bot/Bot.cs index aeb0a46b..67979666 100644 --- a/PluralKit.Bot/Bot.cs +++ b/PluralKit.Bot/Bot.cs @@ -106,6 +106,7 @@ namespace PluralKit.Bot .AddTransient() .AddTransient() + .AddSingleton() .AddSingleton() .AddTransient() @@ -130,6 +131,8 @@ namespace PluralKit.Bot .AddSingleton(svc => new LoggerProvider(svc.GetRequiredService(), "bot")) .AddScoped(svc => svc.GetRequiredService().RootLogger.ForContext("EventId", svc.GetRequiredService().EventId)) + .AddMemoryCache() + .BuildServiceProvider(); } class Bot diff --git a/PluralKit.Bot/Commands/MemberCommands.cs b/PluralKit.Bot/Commands/MemberCommands.cs index fa16ee93..2a58b7a3 100644 --- a/PluralKit.Bot/Commands/MemberCommands.cs +++ b/PluralKit.Bot/Commands/MemberCommands.cs @@ -15,6 +15,8 @@ namespace PluralKit.Bot.Commands public SystemStore Systems { get; set; } public MemberStore Members { get; set; } public EmbedService Embeds { get; set; } + + public ProxyCacheService ProxyCache { get; set; } public override string Prefix => "member"; public override string ContextNoun => "member"; @@ -170,6 +172,8 @@ namespace PluralKit.Bot.Commands ContextEntity.Suffix = prefixAndSuffix[1].Length > 0 ? prefixAndSuffix[1] : null; await Members.Save(ContextEntity); await Context.Channel.SendMessageAsync($"{Emojis.Success} Member proxy tags changed to `{ContextEntity.ProxyString.Sanitize()}`. Try proxying now!"); + + ProxyCache.InvalidateResultsForSystem(Context.SenderSystem); } [Command("delete")] diff --git a/PluralKit.Bot/PluralKit.Bot.csproj b/PluralKit.Bot/PluralKit.Bot.csproj index b40772c1..eb9a6af2 100644 --- a/PluralKit.Bot/PluralKit.Bot.csproj +++ b/PluralKit.Bot/PluralKit.Bot.csproj @@ -14,8 +14,8 @@ + - diff --git a/PluralKit.Bot/Services/ProxyCacheService.cs b/PluralKit.Bot/Services/ProxyCacheService.cs new file mode 100644 index 00000000..b80a4260 --- /dev/null +++ b/PluralKit.Bot/Services/ProxyCacheService.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Dapper; +using Microsoft.Extensions.Caching.Memory; +using Serilog; + +namespace PluralKit.Bot +{ + public class ProxyCacheService + { + public class ProxyDatabaseResult + { + public PKSystem System; + public PKMember Member; + } + + private DbConnectionFactory _conn; + private IMemoryCache _cache; + private ILogger _logger; + + public ProxyCacheService(DbConnectionFactory conn, IMemoryCache cache, ILogger logger) + { + _conn = conn; + _cache = cache; + _logger = logger; + } + + public Task> GetResultsFor(ulong account) + { + _logger.Debug("Looking up members for account {Account} in cache...", account); + return _cache.GetOrCreateAsync(GetKey(account), (entry) => FetchResults(account, entry)); + } + + public void InvalidateResultsFor(ulong account) + { + _logger.Information("Invalidating proxy cache for account {Account}", account); + _cache.Remove(GetKey(account)); + } + + public async Task InvalidateResultsForSystem(PKSystem system) + { + _logger.Information("Invalidating proxy cache for system {System}", system.Id); + using (var conn = await _conn.Obtain()) + foreach (var accountId in await conn.QueryAsync("select uid from accounts where system = @Id", system)) + _cache.Remove(GetKey(accountId)); + } + + private async Task> FetchResults(ulong account, ICacheEntry entry) + { + _logger.Information("Members for account {Account} not in cache, fetching", account); + using (var conn = await _conn.Obtain()) + { + var results = (await conn.QueryAsync( + "select members.*, systems.* from members, systems, accounts where members.system = systems.id and accounts.system = systems.id and accounts.uid = @Uid", + (member, system) => + new ProxyDatabaseResult {Member = member, System = system}, new {Uid = account})).ToList(); + + if (results.Count == 0) + { + // Long expiry for accounts with no system registered + entry.SetSlidingExpiration(TimeSpan.FromMinutes(5)); + entry.SetAbsoluteExpiration(TimeSpan.FromHours(1)); + } + else + { + // Shorter expiry if they already have a system + entry.SetSlidingExpiration(TimeSpan.FromMinutes(1)); + entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(5)); + } + + return results; + } + } + + private object GetKey(ulong account) + { + return $"_proxy_account_{account}"; + } + } +} \ No newline at end of file diff --git a/PluralKit.Bot/Services/ProxyService.cs b/PluralKit.Bot/Services/ProxyService.cs index 289f7b64..8744c1ea 100644 --- a/PluralKit.Bot/Services/ProxyService.cs +++ b/PluralKit.Bot/Services/ProxyService.cs @@ -10,16 +10,11 @@ using Discord; using Discord.Net; using Discord.Webhook; using Discord.WebSocket; +using Microsoft.Extensions.Caching.Memory; using Serilog; namespace PluralKit.Bot { - class ProxyDatabaseResult - { - public PKSystem System; - public PKMember Member; - } - class ProxyMatch { public PKMember Member; public PKSystem System; @@ -35,10 +30,11 @@ namespace PluralKit.Bot private EmbedService _embeds; private IMetrics _metrics; private ILogger _logger; + private ProxyCacheService _cache; private HttpClient _httpClient; - public ProxyService(IDiscordClient client, WebhookCacheService webhookCache, DbConnectionFactory conn, LogChannelService logChannel, MessageStore messageStorage, EmbedService embeds, IMetrics metrics, ILogger logger) + public ProxyService(IDiscordClient client, WebhookCacheService webhookCache, DbConnectionFactory conn, LogChannelService logChannel, MessageStore messageStorage, EmbedService embeds, IMetrics metrics, ILogger logger, ProxyCacheService cache) { _client = client; _webhookCache = webhookCache; @@ -47,12 +43,13 @@ namespace PluralKit.Bot _messageStorage = messageStorage; _embeds = embeds; _metrics = metrics; + _cache = cache; _logger = logger.ForContext(); _httpClient = new HttpClient(); } - private ProxyMatch GetProxyTagMatch(string message, IEnumerable potentials) + private ProxyMatch GetProxyTagMatch(string message, IEnumerable potentials) { // If the message starts with a @mention, and then proceeds to have proxy tags, // extract the mention and place it inside the inner message @@ -89,14 +86,7 @@ namespace PluralKit.Bot // Bail early if this isn't in a guild channel if (!(message.Channel is IGuildChannel)) return; - IEnumerable results; - using (var conn = await _conn.Obtain()) - { - results = await conn.QueryAsync( - "select members.*, systems.* from members, systems, accounts where members.system = systems.id and accounts.system = systems.id and accounts.uid = @Uid", - (member, system) => - new ProxyDatabaseResult {Member = member, System = system}, new {Uid = message.Author.Id}); - } + var results = await _cache.GetResultsFor(message.Author.Id); // Find a member with proxy tags matching the message var match = GetProxyTagMatch(message.Content, results); @@ -134,7 +124,6 @@ namespace PluralKit.Bot await message.DeleteAsync(); } catch (HttpException) {} // If it's already deleted, we just swallow the exception } - private static string SanitizeEveryoneMaybe(IMessage message, string messageContents) { var senderPermissions = ((IGuildUser) message.Author).GetPermissions(message.Channel as IGuildChannel);