added cargo feature to cache guild prefixes. dont query language until necessary in framework

This commit is contained in:
jellywx 2021-03-24 13:10:57 +00:00
parent 1473ac3bcf
commit c76a456af5
9 changed files with 173 additions and 50 deletions

21
Cargo.lock generated
View File

@ -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",

View File

@ -1,10 +1,11 @@
[package]
name = "reminder_rs"
version = "1.4.9"
version = "1.4.10"
authors = ["jellywx <judesouthworth@pm.me>"]
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"]

View File

@ -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;

View File

@ -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::<PrefixCache>().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;
}

View File

@ -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,7 +496,45 @@ 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()) {
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::<Vec<String>>()
.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,
"
@ -519,10 +557,11 @@ ON
WHERE
channels.guild_id = (SELECT id FROM guilds WHERE guild = ?)
",
guild_id
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<u64>, T: TryInto<i64>>(
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;
}

View File

@ -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<TodoTarget>) {
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",

View File

@ -335,15 +335,7 @@ impl Framework for RegexFramework {
async fn check_prefix(ctx: &Context, guild: &Guild, prefix_opt: Option<Match<'_>>) -> bool {
if let Some(prefix) = prefix_opt {
let pool = ctx
.data
.read()
.await
.get::<SQLPool>()
.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::<LanguageManager>().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,16 +406,23 @@ 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(
lm.get(&language.await, "no_perms_managed")
.replace(
"{prefix}",
&GuildData::prefix_from_id(msg.guild_id, &pool)
&GuildData::prefix_from_id(
msg.guild_id,
&ctx,
)
.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 { "" },

View File

@ -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<dashmap::DashMap<GuildId, String>>;
}
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::<PrefixCache>().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<dyn std::error::Error + Send + Sync>> {
.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<dyn std::error::Error + Send + Sync>> {
let mut data = client.data.write().await;
#[cfg(feature = "prefix-cache")]
data.insert::<PrefixCache>(Arc::new(prefix_cache));
data.insert::<SQLPool>(pool);
data.insert::<PopularTimezones>(Arc::new(popular_timezones));
data.insert::<ReqwestClient>(Arc::new(reqwest::Client::new()));

View File

@ -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<String>,
@ -24,10 +30,47 @@ pub struct GuildData {
}
impl GuildData {
#[cfg(feature = "prefix-cache")]
pub async fn prefix_from_id<T: Into<GuildId>>(
guild_id_opt: Option<T>,
pool: &MySqlPool,
ctx: &Context,
) -> String {
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
let prefix_cache = ctx.data.read().await.get::<PrefixCache>().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<T: Into<GuildId>>(
guild_id_opt: Option<T>,
ctx: &Context,
) -> String {
let pool = ctx.data.read().await.get::<SQLPool>().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)