forked from mirror/Riven
1
0
Fork 0

make generic InsertOnlyCHashMap, remove per-file modules

users/mingwei/surf
Mingwei Samuel 2019-10-17 14:44:26 -07:00
parent dff8eb432d
commit d8d2492c93
8 changed files with 104 additions and 44 deletions

View File

@ -1 +1,3 @@
pub mod region; mod region;
pub use region::*;

View File

@ -3,6 +3,7 @@
pub mod consts; pub mod consts;
mod req; mod req;
mod util;
mod riot_api_config; mod riot_api_config;
#[cfg(test)] #[cfg(test)]

View File

@ -1,5 +1,11 @@
pub mod rate_limit; mod rate_limit;
pub mod rate_limit_type; mod rate_limit_type;
pub mod token_bucket; mod token_bucket;
pub mod regional_requester; mod regional_requester;
pub mod requester_manager; mod requester_manager;
pub use rate_limit::*;
pub use rate_limit_type::*;
pub use token_bucket::*;
pub use regional_requester::*;
pub use requester_manager::*;

View File

@ -8,11 +8,11 @@ use parking_lot::{
RwLock, RwLock,
}; };
use super::token_bucket::{ use super::{
TokenBucket, TokenBucket,
VectorTokenBucket, VectorTokenBucket,
}; };
use super::rate_limit_type::RateLimitType; use super::RateLimitType;
pub struct RateLimit { pub struct RateLimit {
rate_limit_type: RateLimitType, rate_limit_type: RateLimitType,

View File

@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::future::Future; use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
@ -7,15 +6,14 @@ use reqwest::{
Client, Client,
StatusCode, StatusCode,
}; };
use parking_lot::{ use parking_lot::Mutex;
Mutex,
};
use serde::de::DeserializeOwned;
use super::rate_limit::RateLimit;
use super::rate_limit_type::RateLimitType;
use crate::riot_api_config::RiotApiConfig; use crate::riot_api_config::RiotApiConfig;
use crate::consts::region::Region; use crate::consts::Region;
use crate::util::InsertOnlyCHashMap;
use super::RateLimit;
use super::RateLimitType;
pub struct RegionalRequester<'a> { pub struct RegionalRequester<'a> {
/// Configuration settings. /// Configuration settings.
@ -26,7 +24,7 @@ pub struct RegionalRequester<'a> {
/// Represents the app rate limit. /// Represents the app rate limit.
app_rate_limit: RateLimit, app_rate_limit: RateLimit,
/// Represents method rate limits. /// Represents method rate limits.
method_rate_limits: Mutex<HashMap<&'a str, Arc<RateLimit>>>, method_rate_limits: InsertOnlyCHashMap<&'a str, RateLimit>,
} }
impl<'a> RegionalRequester<'a> { impl<'a> RegionalRequester<'a> {
@ -42,23 +40,24 @@ impl<'a> RegionalRequester<'a> {
riot_api_config: riot_api_config, riot_api_config: riot_api_config,
client: client, client: client,
app_rate_limit: RateLimit::new(RateLimitType::Application), app_rate_limit: RateLimit::new(RateLimitType::Application),
method_rate_limits: Mutex::new(HashMap::new()), method_rate_limits: InsertOnlyCHashMap::new(),
} }
} }
pub async fn get<T: DeserializeOwned>( pub async fn get<T: serde::de::DeserializeOwned>(
&mut self, method_id: &'a str, relative_url: &'_ str, &self, method_id: &'a str, region: &'_ Region<'_>, relative_url: &'_ str,
region: &'_ Region<'_>, query: &[(&'_ str, &'_ str)]) -> Result<Option<T>, reqwest::Error> { query: &[(&'_ str, &'_ str)]) -> Result<Option<T>, reqwest::Error>
{
let mut attempts: u8 = 0; let mut attempts: u8 = 0;
for _ in 0..=self.riot_api_config.retries { for _ in 0..=self.riot_api_config.retries {
attempts += 1; attempts += 1;
let method_rate_limit: Arc<RateLimit> = self.method_rate_limits
.get_or_insert_with(method_id, || RateLimit::new(RateLimitType::Method));
// Rate limiting. // Rate limiting.
while let Some(delay) = { while let Some(delay) = RateLimit::get_both_or_delay(&self.app_rate_limit, &*method_rate_limit) {
let method_rate_limit = self.get_method_rate_limit(method_id);
RateLimit::get_both_or_delay(&self.app_rate_limit, &*method_rate_limit)
} {
task::sleep(delay).await; task::sleep(delay).await;
} }
@ -75,11 +74,9 @@ impl<'a> RegionalRequester<'a> {
Ok(r) => r, Ok(r) => r,
}; };
// Update rate limits (if needed). // Maybe update rate limits (based on response headers).
{ self.app_rate_limit.on_response(&response);
self.app_rate_limit.on_response(&response); method_rate_limit.on_response(&response);
self.get_method_rate_limit(method_id).on_response(&response);
}
// Handle response. // Handle response.
let status = response.status(); let status = response.status();
@ -109,16 +106,10 @@ impl<'a> RegionalRequester<'a> {
panic!("FAILED AFTER {} ATTEMPTS!", attempts); panic!("FAILED AFTER {} ATTEMPTS!", attempts);
} }
pub fn get2<T: 'a + DeserializeOwned>(&'a mut self, method_id: &'a str, relative_url: &'a str, pub fn get2<T: 'a + serde::de::DeserializeOwned>(&'a self, method_id: &'a str, region: &'a Region<'_>, relative_url: &'a str,
region: &'a Region<'_>, query: &'a [(&'a str, &'a str)]) -> impl Future<Output = Result<Option<T>, reqwest::Error>> + 'a { query: &'a [(&'a str, &'a str)]) -> impl Future<Output = Result<Option<T>, reqwest::Error>> + 'a
{
self.get(method_id, relative_url, region, query) self.get(method_id, region, relative_url, query)
}
fn get_method_rate_limit(&self, method_id: &'a str) -> Arc<RateLimit> {
Arc::clone(self.method_rate_limits.lock()
.entry(method_id)
.or_insert_with(|| Arc::new(RateLimit::new(RateLimitType::Method))))
} }
fn is_none_status_code(status: &StatusCode) -> bool { fn is_none_status_code(status: &StatusCode) -> bool {

View File

@ -5,9 +5,11 @@ use reqwest::{
Client, Client,
}; };
use super::regional_requester::RegionalRequester;
use crate::riot_api_config::RiotApiConfig; use crate::riot_api_config::RiotApiConfig;
use crate::consts::region::Region; use crate::consts::Region;
use crate::util::InsertOnlyCHashMap;
use super::RegionalRequester;
pub struct RequesterManager<'a> { pub struct RequesterManager<'a> {
/// Configuration settings. /// Configuration settings.
@ -16,5 +18,24 @@ pub struct RequesterManager<'a> {
client: &'a Client, client: &'a Client,
/// Per-region requesters. /// Per-region requesters.
regional_requesters: HashMap<&'a Region<'a>, Arc<RegionalRequester<'a>>>, regional_requesters: InsertOnlyCHashMap<&'a Region<'a>, RegionalRequester<'a>>,
}
impl<'a> RequesterManager<'a> {
pub fn new(riot_api_config: &'a RiotApiConfig<'a>, client: &'a Client) -> Self {
Self {
riot_api_config: riot_api_config,
client: client,
regional_requesters: InsertOnlyCHashMap::new(),
}
}
pub async fn get<T: serde::de::DeserializeOwned>(
&mut self, method_id: &'a str, region: &'a Region<'a>, relative_url: &'_ str,
query: &[(&'_ str, &'_ str)]) -> Result<Option<T>, reqwest::Error>
{
let regional_requester = self.regional_requesters
.get_or_insert_with(region, || RegionalRequester::new(self.riot_api_config, self.client));
regional_requester.get(method_id, region, relative_url, query).await
}
} }

View File

@ -0,0 +1,36 @@
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::Arc;
use parking_lot::Mutex;
pub struct InsertOnlyCHashMap<K: Hash + Eq, V> {
base: Mutex<HashMap<K, Arc<V>>>,
}
impl<K: Hash + Eq, V> InsertOnlyCHashMap<K, V> {
#[inline]
pub fn new() -> Self {
Self {
base: Mutex::new(HashMap::new())
}
}
#[inline]
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<Arc<V>>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.base.lock().get(key).map(|v| Arc::clone(v))
}
#[inline]
pub fn get_or_insert_with<F: FnOnce() -> V>(&self, key: K, default: F) -> Arc<V>
{
Arc::clone(self.base.lock()
.entry(key)
.or_insert_with(|| Arc::new(default())))
}
}

3
src/util/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod insert_only_chashmap;
pub use insert_only_chashmap::*;