WIP: osu: asyncify caches

This commit is contained in:
Natsu Kagami 2020-09-06 22:09:56 -04:00
parent bd5f4f0fd2
commit 07a8bc5579
No known key found for this signature in database
GPG key ID: F17543D4B9424B94
2 changed files with 83 additions and 71 deletions

View file

@ -22,15 +22,25 @@ use youmubot_prelude::*;
/// osu! announcer's unique announcer key. /// osu! announcer's unique announcer key.
pub const ANNOUNCER_KEY: &'static str = "osu"; pub const ANNOUNCER_KEY: &'static str = "osu";
/// Announce osu! top scores. /// The announcer struct implementing youmubot_prelude::Announcer
pub fn updates(c: Arc<CacheAndHttp>, d: AppData, channels: MemberToChannels) -> CommandResult { pub struct Announcer;
let osu = d.get_cloned::<OsuClient>();
let cache = d.get_cloned::<BeatmapMetaCache>(); #[async_trait]
let oppai = d.get_cloned::<BeatmapCache>(); impl youmubot_prelude::Announcer for Announcer {
async fn updates(
&mut self,
c: Arc<CacheAndHttp>,
d: AppData,
channels: MemberToChannels,
) -> Result<()> {
let d = d.read().await;
let osu = d.get::<OsuClient>().unwrap();
let cache = d.get::<BeatmapMetaCache>().unwrap();
let oppai = d.get::<BeatmapCache>().unwrap();
// For each user... // For each user...
let mut data = OsuSavedUsers::open(&*d.read()).borrow()?.clone(); let mut data = OsuSavedUsers::open(&*d).borrow()?.clone();
for (user_id, osu_user) in data.iter_mut() { for (user_id, osu_user) in data.iter_mut() {
let channels = channels.channels_of(c.clone(), *user_id); let channels = channels.channels_of(c.clone(), *user_id).await;
if channels.is_empty() { if channels.is_empty() {
continue; // We don't wanna update an user without any active server continue; // We don't wanna update an user without any active server
} }
@ -46,7 +56,7 @@ pub fn updates(c: Arc<CacheAndHttp>, d: AppData, channels: MemberToChannels) ->
*user_id, *user_id,
&channels[..], &channels[..],
*m, *m,
d.clone(), &*d,
) )
}) })
.collect::<Result<_, _>>() .collect::<Result<_, _>>()
@ -63,9 +73,10 @@ pub fn updates(c: Arc<CacheAndHttp>, d: AppData, channels: MemberToChannels) ->
*OsuSavedUsers::open(&*d.read()).borrow_mut()? = data; *OsuSavedUsers::open(&*d.read()).borrow_mut()? = data;
Ok(()) Ok(())
} }
}
/// Handles an user/mode scan, announces all possible new scores, return the new pp value. /// Handles an user/mode scan, announces all possible new scores, return the new pp value.
fn handle_user_mode( async fn handle_user_mode(
c: Arc<CacheAndHttp>, c: Arc<CacheAndHttp>,
osu: &Osu, osu: &Osu,
cache: &BeatmapMetaCache, cache: &BeatmapMetaCache,
@ -74,15 +85,16 @@ fn handle_user_mode(
user_id: UserId, user_id: UserId,
channels: &[ChannelId], channels: &[ChannelId],
mode: Mode, mode: Mode,
d: AppData, d: &TypeMap,
) -> Result<Option<f64>, Error> { ) -> Result<Option<f64>, Error> {
let scores = scan_user(osu, osu_user, mode)?; let scores = scan_user(osu, osu_user, mode)?;
let user = osu let user = osu
.user(UserID::ID(osu_user.id), |f| f.mode(mode))? .user(UserID::ID(osu_user.id), |f| f.mode(mode))
.await?
.ok_or(Error::from("user not found"))?; .ok_or(Error::from("user not found"))?;
scores scores
.into_par_iter() .into_iter()
.map(|(rank, score)| -> Result<_, Error> { .map(|(rank, score)| -> Result<_> {
let beatmap = cache.get_beatmap_default(score.beatmap_id)?; let beatmap = cache.get_beatmap_default(score.beatmap_id)?;
let content = oppai.get_beatmap(beatmap.beatmap_id)?; let content = oppai.get_beatmap(beatmap.beatmap_id)?;
Ok((rank, score, BeatmapWithMode(beatmap, mode), content)) Ok((rank, score, BeatmapWithMode(beatmap, mode), content))

View file

@ -3,16 +3,14 @@ use crate::{
Client, Client,
}; };
use dashmap::DashMap; use dashmap::DashMap;
use serenity::framework::standard::CommandError; use youmubot_prelude::*;
use std::sync::Arc;
use youmubot_prelude::TypeMapKey;
/// BeatmapMetaCache intercepts beatmap-by-id requests and caches them for later recalling. /// BeatmapMetaCache intercepts beatmap-by-id requests and caches them for later recalling.
/// Does not cache non-Ranked beatmaps. /// Does not cache non-Ranked beatmaps.
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct BeatmapMetaCache { pub struct BeatmapMetaCache {
client: Client, client: Client,
cache: Arc<DashMap<(u64, Mode), Beatmap>>, cache: DashMap<(u64, Mode), Beatmap>,
} }
impl TypeMapKey for BeatmapMetaCache { impl TypeMapKey for BeatmapMetaCache {
@ -24,10 +22,10 @@ impl BeatmapMetaCache {
pub fn new(client: Client) -> Self { pub fn new(client: Client) -> Self {
BeatmapMetaCache { BeatmapMetaCache {
client, client,
cache: Arc::new(DashMap::new()), cache: DashMap::new(),
} }
} }
fn insert_if_possible(&self, id: u64, mode: Option<Mode>) -> Result<Beatmap, CommandError> { async fn insert_if_possible(&self, id: u64, mode: Option<Mode>) -> Result<Beatmap> {
let beatmap = self let beatmap = self
.client .client
.beatmaps(crate::BeatmapRequestKind::Beatmap(id), |f| { .beatmaps(crate::BeatmapRequestKind::Beatmap(id), |f| {
@ -36,35 +34,37 @@ impl BeatmapMetaCache {
} }
f f
}) })
.and_then(|v| { .await
v.into_iter() .and_then(|v| v.into_iter().next().ok_or(Error::msg("beatmap not found")))?;
.next()
.ok_or(CommandError::from("beatmap not found"))
})?;
if let ApprovalStatus::Ranked(_) = beatmap.approval { if let ApprovalStatus::Ranked(_) = beatmap.approval {
self.cache.insert((id, beatmap.mode), beatmap.clone()); self.cache.insert((id, beatmap.mode), beatmap.clone());
}; };
Ok(beatmap) Ok(beatmap)
} }
/// Get the given beatmap /// Get the given beatmap
pub fn get_beatmap(&self, id: u64, mode: Mode) -> Result<Beatmap, CommandError> { pub async fn get_beatmap(&self, id: u64, mode: Mode) -> Result<Beatmap> {
self.cache match self.cache.get(&(id, mode)).map(|v| v.clone()) {
.get(&(id, mode)) Some(v) => Ok(v),
.map(|b| Ok(b.clone())) None => self.insert_if_possible(id, Some(mode)).await,
.unwrap_or_else(|| self.insert_if_possible(id, Some(mode))) }
} }
/// Get a beatmap without a mode... /// Get a beatmap without a mode...
pub fn get_beatmap_default(&self, id: u64) -> Result<Beatmap, CommandError> { pub async fn get_beatmap_default(&self, id: u64) -> Result<Beatmap> {
(&[Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania]) Ok(
match (&[Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania])
.iter() .iter()
.filter_map(|&mode| { .filter_map(|&mode| {
self.cache self.cache
.get(&(id, mode)) .get(&(id, mode))
.filter(|b| b.mode == mode) .filter(|b| b.mode == mode)
.map(|b| Ok(b.clone())) .map(|b| b.clone())
}) })
.next() .next()
.unwrap_or_else(|| self.insert_if_possible(id, None)) {
Some(v) => v,
None => self.insert_if_possible(id, None).await?,
},
)
} }
} }