From 01c743f866b04a5de1f301ab44282252caca83ff Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Wed, 6 Mar 2024 16:14:31 +0100 Subject: [PATCH] Don't use dashmap when you need async updates [deadlocks xd] --- youmubot-prelude/src/member_cache.rs | 55 ++++++++++++++++++---------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/youmubot-prelude/src/member_cache.rs b/youmubot-prelude/src/member_cache.rs index dc13acb..a05167d 100644 --- a/youmubot-prelude/src/member_cache.rs +++ b/youmubot-prelude/src/member_cache.rs @@ -1,19 +1,22 @@ use chrono::{DateTime, Utc}; -use dashmap::DashMap; use serenity::model::{ guild::Member, id::{GuildId, UserId}, }; use serenity::{http::CacheHttp, prelude::*}; +use std::collections::{hash_map::Entry, HashMap}; use std::sync::Arc; +use tokio::sync::Mutex; const VALID_CACHE_SECONDS: i64 = 15 * 60; // 15 minutes +type Map = Mutex>; + /// MemberCache resolves `does User belong to Guild` requests, and store them in a cache. #[derive(Debug, Default)] pub struct MemberCache { - per_user: DashMap<(UserId, GuildId), Expiring>>, - per_guild: DashMap>>, + per_user: Map<(UserId, GuildId), Expiring>>, + per_guild: Map>>, } #[derive(Debug)] @@ -49,21 +52,31 @@ impl MemberCache { ) -> Option { let now = Utc::now(); // Check cache - if let Some(r) = self.per_user.get(&(user_id, guild_id)) { + if let Some(r) = self.per_user.lock().await.get(&(user_id, guild_id)) { if r.timeout > now { - return r.clone(); + return r.value.clone(); } } + // Check members cache first if possible + if let Ok(mems) = self.query_members(&cache_http, guild_id).await { + return mems.iter().find(|m| m.user.id == user_id).cloned(); + } // Query + let mut map = self.per_user.lock().await; + let entry = map.entry((user_id, guild_id)); + if let Entry::Occupied(oe) = &entry { + if oe.get().timeout > now { + return oe.get().value.clone(); + } + } let t = guild_id.member(&cache_http, user_id).await.ok(); - self.per_user.insert( - (user_id, guild_id), - Expiring::new( + entry + .or_insert(Expiring::new( t.clone(), now + chrono::Duration::seconds(VALID_CACHE_SECONDS), - ), - ); - t + )) + .value + .clone() } pub async fn query_members( @@ -72,24 +85,26 @@ impl MemberCache { guild_id: GuildId, ) -> crate::Result> { let now = Utc::now(); + let mut map = self.per_guild.lock().await; + let entry = map.entry(guild_id); // Check cache - if let Some(r) = self.per_guild.get(&guild_id) { - if r.timeout > now { - return Ok(r.value.clone()); + if let Entry::Occupied(oe) = &entry { + if oe.get().timeout > now { + return Ok(oe.get().value.clone()); } } // query + eprintln!("querying members of {}", guild_id); let members: Arc<[Member]> = guild_id .members(cache_http.http(), None, None) .await? .into(); - self.per_guild.insert( - guild_id, - Expiring::new( + Ok(entry + .or_insert(Expiring::new( members.clone(), now + chrono::Duration::seconds(VALID_CACHE_SECONDS), - ), - ); - Ok(members) + )) + .value + .clone()) } }