Skip to content

Commit

Permalink
feat: improved rate of fetching gacha records
Browse files Browse the repository at this point in the history
  • Loading branch information
lgou2w committed Jan 27, 2024
1 parent 5b5a7d7 commit 18201e3
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 12 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tauri-build = { version = "1.5.1", features = [] }
anyhow = "1.0.79"
async-trait = "0.1.77"
byteorder = "1.5.0"
exponential-backoff = "1.2.0"
form_urlencoded = "1.2.1"
futures = { version = "0.3.30", default-features = false, features = ["std", "async-await"] }
once_cell = "1.19.0"
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ pub enum Error {
#[error("Timeoutd Gacha Url")]
TimeoutdGachaUrl,

#[error("Visit too frequently Gacha Url")]
VisitTooFrequentlyGachaUrl,

#[error("Gacha record response: {retcode:?} {message:?}")]
GachaRecordRetcode { retcode: i32, message: String },

Expand Down Expand Up @@ -88,6 +91,7 @@ impl_error_identifiers! {
IllegalGachaUrl => ILLEGAL_GACHA_URL,
VacantGachaUrl => VACANT_GACHA_URL,
TimeoutdGachaUrl => TIMEOUTD_GACHA_URL,
VisitTooFrequentlyGachaUrl => VISIT_TOO_FREQUENTLY_GACHA_URL,
GachaRecordRetcode => GACHA_RECORD_RETCODE,
GachaRecordFetcherChannelSend => GACHA_RECORD_FETCHER_CHANNEL_SEND,
GachaRecordFetcherChannelJoin => GACHA_RECORD_FETCHER_CHANNEL_JOIN,
Expand Down
8 changes: 4 additions & 4 deletions src-tauri/src/gacha/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ pub trait GachaRecordFetcherChannel<T: GachaRecord + Sized + Serialize + Send +
gacha_type: &str,
last_end_id: Option<&str>,
) -> Result<()> {
const SLEEPING: u64 = 3;
const SLEEPING_MILLIS: u64 = 500;

sender
.send(GachaRecordFetcherChannelFragment::Ready(
gacha_type.to_owned(),
))
.await
.map_err(|_| Error::GachaRecordFetcherChannelSend)?;
tokio::time::sleep(tokio::time::Duration::from_secs(SLEEPING)).await;
tokio::time::sleep(tokio::time::Duration::from_millis(SLEEPING_MILLIS)).await;

let mut end_id = String::from("0");
let mut pagination: u32 = 1;
Expand All @@ -120,7 +120,7 @@ pub trait GachaRecordFetcherChannel<T: GachaRecord + Sized + Serialize + Send +
.send(GachaRecordFetcherChannelFragment::Sleeping)
.await
.map_err(|_| Error::GachaRecordFetcherChannelSend)?;
tokio::time::sleep(tokio::time::Duration::from_secs(SLEEPING)).await;
tokio::time::sleep(tokio::time::Duration::from_millis(SLEEPING_MILLIS)).await;
}

sender
Expand Down Expand Up @@ -159,7 +159,7 @@ pub trait GachaRecordFetcherChannel<T: GachaRecord + Sized + Serialize + Send +
if should_break {
break;
} else {
tokio::time::sleep(tokio::time::Duration::from_secs(SLEEPING)).await;
tokio::time::sleep(tokio::time::Duration::from_millis(SLEEPING_MILLIS)).await;
continue;
}
}
Expand Down
70 changes: 62 additions & 8 deletions src-tauri/src/gacha/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use crate::constants;
use crate::disk_cache::{BlockFile, EntryStore, IndexFile};
use crate::error::{Error, Result};
use crate::storage::entity_account::AccountFacet;
use exponential_backoff::Backoff;
use futures::future::BoxFuture;
use futures::FutureExt;
use once_cell::sync::Lazy;
use reqwest::Client as Reqwest;
use serde::de::DeserializeOwned;
Expand All @@ -13,9 +16,11 @@ use std::fs::File;
use std::io::{prelude::BufRead, BufReader};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::Duration;
use time::{OffsetDateTime, UtcOffset};
use tokio::sync::Mutex;
use tracing::debug;
use tokio::time::sleep;
use tracing::{debug, warn};
use url::Url;

pub(super) fn create_default_reqwest() -> Result<reqwest::Client> {
Expand Down Expand Up @@ -246,7 +251,7 @@ pub(super) struct GachaResponse<T> {
pub data: Option<T>,
}

pub(super) async fn fetch_gacha_records<T: Sized + DeserializeOwned>(
pub(super) async fn fetch_gacha_records<T: Sized + DeserializeOwned + Send>(
reqwest: &Reqwest,
endpoint: &str,
gacha_url: &str,
Expand Down Expand Up @@ -286,22 +291,71 @@ pub(super) async fn fetch_gacha_records<T: Sized + DeserializeOwned>(
url.query_pairs_mut().append_pair("end_id", end_id);
}

let response: GachaResponse<T> = reqwest.get(url).send().await?.json().await?;
let response: GachaResponse<T> = request_gacha_url_with_retry(reqwest, url, None).await?;
Ok(response)
}

async fn request_gacha_url<T: Sized + DeserializeOwned>(
reqwest: &Reqwest,
url: Url,
) -> Result<GachaResponse<T>> {
let response: GachaResponse<T> = reqwest.get(url).send().await?.json().await?;
if response.retcode != 0 {
if response.retcode == -101 {
Err(Error::TimeoutdGachaUrl)
} else {
Err(Error::GachaRecordRetcode {
match response.retcode {
-101 => Err(Error::TimeoutdGachaUrl),
-110 => Err(Error::VisitTooFrequentlyGachaUrl),
_ => Err(Error::GachaRecordRetcode {
retcode: response.retcode,
message: response.message,
})
}),
}
} else {
Ok(response)
}
}

fn request_gacha_url_with_retry<T: Sized + DeserializeOwned + Send>(
reqwest: &Reqwest,
url: Url,
retries: Option<u8>,
) -> BoxFuture<'_, Result<GachaResponse<T>>> {
// HACK: Default maximum 5 attempts
const RETRIES: u8 = 5;

let min = Duration::from_millis(200); // Min: 0.2s
let max = Duration::from_millis(10_000); // Max: 10s

let retries = retries.unwrap_or(RETRIES);
let backoff = Backoff::new(retries as u32, min, max);

async move {
for duration in &backoff {
match request_gacha_url(reqwest, url.clone()).await {
// okay
Ok(response) => return Ok(response),

// Wait and retry only if the error is VisitTooFrequently.
Err(Error::VisitTooFrequentlyGachaUrl) => {
warn!(
"Requesting gacha url visit too frequently, wait({}s) and retry...",
duration.as_secs_f32()
);
sleep(duration).await;
continue;
}

// Other errors are returned
Err(error) => return Err(error),
}
}

// Maximum number of retries reached
warn!("Maximum number of retries exceeded: {retries}");
Err(Error::VisitTooFrequentlyGachaUrl)
}
.boxed()
}

//- Find the Gacha url and validate consistency
// Hashmap<String, GachaUrl> GACHA_URL_CACHED
// key: facet + uid + addr
Expand Down
1 change: 1 addition & 0 deletions src/components/gacha/GachaLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const KnownErrorIdentifiers: Record<string, string> = {
ILLEGAL_GACHA_URL: '无效的抽卡链接!',
VACANT_GACHA_URL: '未找到有效的抽卡链接。请尝试在游戏内打开抽卡历史记录界面!',
TIMEOUTD_GACHA_URL: '抽卡链接已经过期失效。请重新在游戏内打开抽卡历史记录界面!',
VISIT_TOO_FREQUENTLY_GACHA_URL: '请求获取抽卡记录 API 速率过快!请稍等几秒后再次重试!',
UIGF_OR_SRGF_MISMATCHED_UID: '待导入的 UIGF 或 SRGF 数据 UID 与当前账号不匹配!',
UIGF_OR_SRGF_INVALID_FIELD: '待导入的 UIGF 或 SRGF 数据中存在无效的字段!'
}

0 comments on commit 18201e3

Please sign in to comment.