From c76a456af5e4acbac6b79d740058e04d91ae46c8 Mon Sep 17 00:00:00 2001 From: jellywx Date: Wed, 24 Mar 2021 13:10:57 +0000 Subject: [PATCH] added cargo feature to cache guild prefixes. dont query language until necessary in framework --- Cargo.lock | 21 +++++++--- Cargo.toml | 8 +++- src/commands/info_cmds.rs | 4 +- src/commands/moderation_cmds.rs | 17 ++++++-- src/commands/reminder_cmds.rs | 73 +++++++++++++++++++++++++-------- src/commands/todo_cmds.rs | 4 +- src/framework.rs | 31 +++++++------- src/main.rs | 18 ++++++++ src/models.rs | 47 ++++++++++++++++++++- 9 files changed, 173 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecb999d..ffc60f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,8 +213,8 @@ dependencies = [ [[package]] name = "command_attr" -version = "0.3.3" -source = "git+https://github.com/Lakelezz/serenity?branch=remove-indirection#6d955638f356b2dbbf7f69a4123c0bfbe218d601" +version = "0.3.4" +source = "git+https://github.com/serenity-rs/serenity?branch=next#943cf037c114ddf3513a3c8c3a2301da41c34f9e" dependencies = [ "proc-macro2", "quote", @@ -292,6 +292,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + [[package]] name = "digest" version = "0.9.0" @@ -1261,11 +1271,12 @@ dependencies = [ [[package]] name = "reminder_rs" -version = "1.4.9" +version = "1.4.10" dependencies = [ "Inflector", "chrono", "chrono-tz", + "dashmap", "dotenv", "env_logger", "humantime", @@ -1483,8 +1494,8 @@ dependencies = [ [[package]] name = "serenity" -version = "0.10.2" -source = "git+https://github.com/Lakelezz/serenity?branch=remove-indirection#6d955638f356b2dbbf7f69a4123c0bfbe218d601" +version = "0.10.4" +source = "git+https://github.com/serenity-rs/serenity?branch=next#943cf037c114ddf3513a3c8c3a2301da41c34f9e" dependencies = [ "async-trait", "async-tungstenite", diff --git a/Cargo.toml b/Cargo.toml index 72e3b15..eac933c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,11 @@ [package] name = "reminder_rs" -version = "1.4.9" +version = "1.4.10" authors = ["jellywx "] edition = "2018" [dependencies] +dashmap = { version = "4.0", optional = true } dotenv = "0.15" humantime = "2.1" tokio = { version = "1", features = ["process", "full"] } @@ -22,8 +23,11 @@ rand = "0.7" Inflector = "0.11" levenshtein = "1.0" # serenity = { version = "0.10", features = ["collector"] } -serenity = { git = "https://github.com/Lakelezz/serenity", branch = "remove-indirection", features = ["collector"] } +serenity = { git = "https://github.com/serenity-rs/serenity", branch = "next", features = ["collector"] } sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]} [dependencies.regex_command_attr] path = "./regex_command_attr" + +[features] +prefix-cache = ["dashmap"] diff --git a/src/commands/info_cmds.rs b/src/commands/info_cmds.rs index 1c55551..c659b59 100644 --- a/src/commands/info_cmds.rs +++ b/src/commands/info_cmds.rs @@ -107,7 +107,7 @@ async fn help(ctx: &Context, msg: &Message, args: String) { let (pool, lm) = get_ctx_data(&ctx).await; let language = UserData::language_of(&msg.author, &pool); - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool); + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx); if !args.is_empty() { let framework = ctx @@ -138,7 +138,7 @@ async fn info(ctx: &Context, msg: &Message, _args: String) { let (pool, lm) = get_ctx_data(&ctx).await; let language = UserData::language_of(&msg.author, &pool); - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool); + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx); let current_user = ctx.cache.current_user(); let footer = footer(ctx).await; diff --git a/src/commands/moderation_cmds.rs b/src/commands/moderation_cmds.rs index f0a1660..a5c675a 100644 --- a/src/commands/moderation_cmds.rs +++ b/src/commands/moderation_cmds.rs @@ -28,6 +28,9 @@ use crate::{ FrameworkCtx, PopularTimezones, }; +#[cfg(feature = "prefix-cache")] +use crate::PrefixCache; + use std::{collections::HashMap, iter, time::Duration}; #[command] @@ -174,7 +177,7 @@ async fn timezone(ctx: &Context, msg: &Message, args: String) { } else { let content = lm.get(&user_data.language, "timezone/no_argument").replace( "{prefix}", - &GuildData::prefix_from_id(msg.guild_id, &pool).await, + &GuildData::prefix_from_id(msg.guild_id, &ctx).await, ); let popular_timezones = ctx @@ -252,7 +255,7 @@ async fn change_meridian(ctx: &Context, msg: &Message, args: String) { }) .await; } else { - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool).await; + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await; command_help(ctx, msg, lm, &prefix, &user_data.language, "meridian").await; } @@ -413,6 +416,12 @@ async fn prefix(ctx: &Context, msg: &Message, args: String) { .await; } else { guild_data.prefix = args; + + #[cfg(feature = "prefix-cache")] + let prefix_cache = ctx.data.read().await.get::().cloned().unwrap(); + #[cfg(feature = "prefix-cache")] + prefix_cache.insert(msg.guild_id.unwrap(), guild_data.prefix.clone()); + guild_data.commit_changes(&pool).await; let content = @@ -548,7 +557,7 @@ WHERE }) .await; } else { - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool).await; + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await; command_help(ctx, msg, lm, &prefix, &language, "restrict").await; } @@ -675,7 +684,7 @@ SELECT command FROM command_aliases WHERE guild_id = (SELECT id FROM guilds WHER } } } else { - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool).await; + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await; command_help(ctx, msg, lm, &prefix, &language, "alias").await; } diff --git a/src/commands/reminder_cmds.rs b/src/commands/reminder_cmds.rs index 3a9a65a..acc44b9 100644 --- a/src/commands/reminder_cmds.rs +++ b/src/commands/reminder_cmds.rs @@ -173,7 +173,7 @@ async fn offset(ctx: &Context, msg: &Message, args: String) { let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap(); if args.is_empty() { - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool).await; + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await; command_help(ctx, msg, lm, &prefix, &user_data.language, "offset").await; } else { @@ -496,10 +496,48 @@ async fn delete(ctx: &Context, msg: &Message, _args: String) { .say(&ctx, lm.get(&user_data.language, "del/listing")) .await; - let reminders = if let Some(guild_id) = msg.guild_id.map(|f| f.as_u64().to_owned()) { - sqlx::query_as_unchecked!( - LookReminder, - " + let reminders = if let Some(guild_id) = msg.guild_id { + let guild_opt = guild_id.to_guild_cached(&ctx).await; + + if let Some(guild) = guild_opt { + let channels = guild + .channels + .keys() + .into_iter() + .map(|k| k.as_u64().to_string()) + .collect::>() + .join(","); + + sqlx::query_as_unchecked!( + LookReminder, + " +SELECT + reminders.id, reminders.time, channels.channel, messages.content, embeds.description +FROM + reminders +LEFT OUTER JOIN + channels +ON + channels.id = reminders.channel_id +INNER JOIN + messages +ON + messages.id = reminders.message_id +LEFT JOIN + embeds +ON + embeds.id = messages.embed_id +WHERE + FIND_IN_SET(channels.channel, ?) + ", + channels + ) + .fetch_all(&pool) + .await + } else { + sqlx::query_as_unchecked!( + LookReminder, + " SELECT reminders.id, reminders.time, channels.channel, messages.content, embeds.description FROM @@ -518,11 +556,12 @@ ON embeds.id = messages.embed_id WHERE channels.guild_id = (SELECT id FROM guilds WHERE guild = ?) - ", - guild_id - ) - .fetch_all(&pool) - .await + ", + guild_id.as_u64() + ) + .fetch_all(&pool) + .await + } } else { sqlx::query_as!( LookReminder, @@ -779,7 +818,7 @@ DELETE FROM timers WHERE owner = ? AND name = ? } _ => { - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool).await; + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await; command_help(ctx, msg, lm, &prefix, &language, "timer").await; } @@ -1110,7 +1149,7 @@ INSERT INTO reminders ( ctx, msg, lm, - &GuildData::prefix_from_id(msg.guild_id, &pool).await, + &GuildData::prefix_from_id(msg.guild_id, &ctx).await, &language, "countdown", ) @@ -1123,7 +1162,7 @@ INSERT INTO reminders ( &ctx, lm.get(&language, "interval/donor").replace( "{prefix}", - &GuildData::prefix_from_id(msg.guild_id, &pool).await, + &GuildData::prefix_from_id(msg.guild_id, &ctx).await, ), ) .await; @@ -1195,7 +1234,7 @@ async fn remind_command(ctx: &Context, msg: &Message, args: String, command: Rem &ctx, lm.get(&language, "interval/donor").replace( "{prefix}", - &GuildData::prefix_from_id(msg.guild_id, &pool).await, + &GuildData::prefix_from_id(msg.guild_id, &ctx).await, ), ) .await; @@ -1347,7 +1386,7 @@ async fn remind_command(ctx: &Context, msg: &Message, args: String, command: Rem } None => { - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool).await; + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await; match command { RemindCommand::Remind => { @@ -1572,7 +1611,7 @@ async fn natural(ctx: &Context, msg: &Message, args: String) { ctx, msg, lm, - &GuildData::prefix_from_id(msg.guild_id, &pool).await, + &GuildData::prefix_from_id(msg.guild_id, &ctx).await, &user_data.language, "natural", ) @@ -1629,7 +1668,7 @@ async fn create_reminder<'a, U: Into, T: TryInto>( match create_webhook(&ctx, guild_channel, "Reminder").await { Ok(webhook) => { channel_data.webhook_id = Some(webhook.id.as_u64().to_owned()); - channel_data.webhook_token = Some(webhook.token); + channel_data.webhook_token = webhook.token; channel_data.commit_changes(&pool).await; } diff --git a/src/commands/todo_cmds.rs b/src/commands/todo_cmds.rs index a454ea0..1906bd1 100644 --- a/src/commands/todo_cmds.rs +++ b/src/commands/todo_cmds.rs @@ -233,7 +233,7 @@ DELETE FROM todos WHERE user_id = (SELECT id FROM users WHERE user = ?) AND guil let (pool, lm) = get_ctx_data(&ctx).await; let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap(); - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool).await; + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx).await; match subcommand { SubCommand::View => { @@ -426,7 +426,7 @@ async fn show_help(ctx: &Context, msg: &Message, target: Option) { let (pool, lm) = get_ctx_data(&ctx).await; let language = UserData::language_of(&msg.author, &pool); - let prefix = GuildData::prefix_from_id(msg.guild_id, &pool); + let prefix = GuildData::prefix_from_id(msg.guild_id, &ctx); let command = match target { None => "todo", diff --git a/src/framework.rs b/src/framework.rs index 3e92d55..ce1a374 100644 --- a/src/framework.rs +++ b/src/framework.rs @@ -335,15 +335,7 @@ impl Framework for RegexFramework { async fn check_prefix(ctx: &Context, guild: &Guild, prefix_opt: Option>) -> bool { if let Some(prefix) = prefix_opt { - let pool = ctx - .data - .read() - .await - .get::() - .cloned() - .expect("Could not get SQLPool from data"); - - let guild_prefix = GuildData::prefix_from_id(Some(guild.id), &pool).await; + let guild_prefix = GuildData::prefix_from_id(Some(guild.id), &ctx).await; guild_prefix.as_str() == prefix.as_str() } else { @@ -369,7 +361,7 @@ impl Framework for RegexFramework { if check_prefix(&ctx, &guild, full_match.name("prefix")).await { let lm = data.get::().unwrap(); - let language = UserData::language_of(&msg.author, &pool).await; + let language = UserData::language_of(&msg.author, &pool); match check_self_permissions(&ctx, &guild, &channel).await { Ok(perms) => match perms { @@ -414,18 +406,25 @@ impl Framework for RegexFramework { { let _ = msg .channel_id - .say(&ctx, lm.get(&language, "no_perms_restricted")) + .say( + &ctx, + lm.get(&language.await, "no_perms_restricted"), + ) .await; } else if command.required_perms == PermissionLevel::Managed { let _ = msg .channel_id .say( &ctx, - lm.get(&language, "no_perms_managed").replace( - "{prefix}", - &GuildData::prefix_from_id(msg.guild_id, &pool) + lm.get(&language.await, "no_perms_managed") + .replace( + "{prefix}", + &GuildData::prefix_from_id( + msg.guild_id, + &ctx, + ) .await, - ), + ), ) .await; } @@ -439,7 +438,7 @@ impl Framework for RegexFramework { manage_messages, ) => { let response = lm - .get(&language, "no_perms_general") + .get(&language.await, "no_perms_general") .replace( "{manage_webhooks}", if manage_webhooks { "✅" } else { "❌" }, diff --git a/src/main.rs b/src/main.rs index 0723e9c..ebcfd23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,13 @@ use log::info; use crate::models::GuildData; use chrono_tz::Tz; +#[cfg(feature = "prefix-cache")] +struct PrefixCache; +#[cfg(feature = "prefix-cache")] +impl TypeMapKey for PrefixCache { + type Value = Arc>; +} + struct SQLPool; impl TypeMapKey for SQLPool { @@ -165,6 +172,11 @@ DELETE FROM channels WHERE channel = ? .cloned() .expect("Could not get SQLPool from data"); + #[cfg(feature = "prefix-cache")] + let prefix_cache = ctx.data.read().await.get::().cloned().unwrap(); + #[cfg(feature = "prefix-cache")] + prefix_cache.remove(&guild.id); + sqlx::query!( " DELETE FROM guilds WHERE guild = ? @@ -262,6 +274,9 @@ async fn main() -> Result<(), Box> { .expect("Error occurred creating client"); { + #[cfg(feature = "prefix-cache")] + let prefix_cache = dashmap::DashMap::new(); + let pool = MySqlPool::connect( &env::var("DATABASE_URL").expect("Missing DATABASE_URL from environment"), ) @@ -287,6 +302,9 @@ async fn main() -> Result<(), Box> { let mut data = client.data.write().await; + #[cfg(feature = "prefix-cache")] + data.insert::(Arc::new(prefix_cache)); + data.insert::(pool); data.insert::(Arc::new(popular_timezones)); data.insert::(Arc::new(reqwest::Client::new())); diff --git a/src/models.rs b/src/models.rs index a13d862..362da10 100644 --- a/src/models.rs +++ b/src/models.rs @@ -17,6 +17,12 @@ use log::error; use crate::consts::{DEFAULT_PREFIX, LOCAL_LANGUAGE, LOCAL_TIMEZONE}; +#[cfg(feature = "prefix-cache")] +use crate::PrefixCache; +use crate::SQLPool; + +use serenity::prelude::Context; + pub struct GuildData { pub id: u32, pub name: Option, @@ -24,10 +30,47 @@ pub struct GuildData { } impl GuildData { + #[cfg(feature = "prefix-cache")] pub async fn prefix_from_id>( guild_id_opt: Option, - pool: &MySqlPool, + ctx: &Context, ) -> String { + let pool = ctx.data.read().await.get::().cloned().unwrap(); + let prefix_cache = ctx.data.read().await.get::().cloned().unwrap(); + + if let Some(guild_id) = guild_id_opt { + let guild_id = guild_id.into(); + + if let Some(prefix) = prefix_cache.get(&guild_id) { + prefix.to_string() + } else { + let row = sqlx::query!( + " +SELECT prefix FROM guilds WHERE guild = ? + ", + guild_id.as_u64().to_owned() + ) + .fetch_one(&pool) + .await; + + let prefix = row.map_or_else(|_| DEFAULT_PREFIX.clone(), |r| r.prefix); + + prefix_cache.insert(guild_id, prefix.clone()); + + prefix + } + } else { + DEFAULT_PREFIX.clone() + } + } + + #[cfg(not(feature = "prefix-cache"))] + pub async fn prefix_from_id>( + guild_id_opt: Option, + ctx: &Context, + ) -> String { + let pool = ctx.data.read().await.get::().cloned().unwrap(); + if let Some(guild_id) = guild_id_opt { let guild_id = guild_id.into().as_u64().to_owned(); @@ -37,7 +80,7 @@ SELECT prefix FROM guilds WHERE guild = ? ", guild_id ) - .fetch_one(pool) + .fetch_one(&pool) .await; row.map_or_else(|_| DEFAULT_PREFIX.clone(), |r| r.prefix)