Don't use dashmap when you need async updates [deadlocks xd]

This commit is contained in:
Natsu Kagami 2024-03-06 16:14:31 +01:00
parent 0f91c3a22a
commit 01c743f866
Signed by: nki
GPG key ID: 55A032EB38B49ADB

View file

@ -1,19 +1,22 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use dashmap::DashMap;
use serenity::model::{ use serenity::model::{
guild::Member, guild::Member,
id::{GuildId, UserId}, id::{GuildId, UserId},
}; };
use serenity::{http::CacheHttp, prelude::*}; use serenity::{http::CacheHttp, prelude::*};
use std::collections::{hash_map::Entry, HashMap};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex;
const VALID_CACHE_SECONDS: i64 = 15 * 60; // 15 minutes const VALID_CACHE_SECONDS: i64 = 15 * 60; // 15 minutes
type Map<K, V> = Mutex<HashMap<K, V>>;
/// MemberCache resolves `does User belong to Guild` requests, and store them in a cache. /// MemberCache resolves `does User belong to Guild` requests, and store them in a cache.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MemberCache { pub struct MemberCache {
per_user: DashMap<(UserId, GuildId), Expiring<Option<Member>>>, per_user: Map<(UserId, GuildId), Expiring<Option<Member>>>,
per_guild: DashMap<GuildId, Expiring<Arc<[Member]>>>, per_guild: Map<GuildId, Expiring<Arc<[Member]>>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -49,21 +52,31 @@ impl MemberCache {
) -> Option<Member> { ) -> Option<Member> {
let now = Utc::now(); let now = Utc::now();
// Check cache // 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 { 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 // 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(); let t = guild_id.member(&cache_http, user_id).await.ok();
self.per_user.insert( entry
(user_id, guild_id), .or_insert(Expiring::new(
Expiring::new(
t.clone(), t.clone(),
now + chrono::Duration::seconds(VALID_CACHE_SECONDS), now + chrono::Duration::seconds(VALID_CACHE_SECONDS),
), ))
); .value
t .clone()
} }
pub async fn query_members( pub async fn query_members(
@ -72,24 +85,26 @@ impl MemberCache {
guild_id: GuildId, guild_id: GuildId,
) -> crate::Result<Arc<[Member]>> { ) -> crate::Result<Arc<[Member]>> {
let now = Utc::now(); let now = Utc::now();
let mut map = self.per_guild.lock().await;
let entry = map.entry(guild_id);
// Check cache // Check cache
if let Some(r) = self.per_guild.get(&guild_id) { if let Entry::Occupied(oe) = &entry {
if r.timeout > now { if oe.get().timeout > now {
return Ok(r.value.clone()); return Ok(oe.get().value.clone());
} }
} }
// query // query
eprintln!("querying members of {}", guild_id);
let members: Arc<[Member]> = guild_id let members: Arc<[Member]> = guild_id
.members(cache_http.http(), None, None) .members(cache_http.http(), None, None)
.await? .await?
.into(); .into();
self.per_guild.insert( Ok(entry
guild_id, .or_insert(Expiring::new(
Expiring::new(
members.clone(), members.clone(),
now + chrono::Duration::seconds(VALID_CACHE_SECONDS), now + chrono::Duration::seconds(VALID_CACHE_SECONDS),
), ))
); .value
Ok(members) .clone())
} }
} }