Add the `BuildHasher` generic parameter to `dataloader::HashMapCache` to allow custom hashing algorithms. #455
This commit is contained in:
parent
018d56d2ca
commit
8a67781180
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
nd this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.7.4] 2021-04-02
|
||||
|
||||
- Add the `BuildHasher` generic parameter to `dataloader::HashMapCache` to allow custom hashing algorithms. [#455](https://github.com/async-graphql/async-graphql/issues/455)
|
||||
|
||||
## [2.7.3] 2021-04-02
|
||||
|
||||
## Added
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::hash_map::RandomState;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Factory for creating cache storage.
|
||||
|
@ -80,24 +81,40 @@ where
|
|||
}
|
||||
|
||||
/// [std::collections::HashMap] cache.
|
||||
pub struct HashMapCache;
|
||||
pub struct HashMapCache<S = RandomState> {
|
||||
_mark: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl CacheFactory for HashMapCache {
|
||||
impl<S: Send + Sync + BuildHasher + Default + 'static> HashMapCache<S> {
|
||||
/// Use specified `S: BuildHasher` to create a `HashMap` cache.
|
||||
pub fn new() -> Self {
|
||||
Self { _mark: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HashMapCache<RandomState> {
|
||||
fn default() -> Self {
|
||||
Self { _mark: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Send + Sync + BuildHasher + Default + 'static> CacheFactory for HashMapCache<S> {
|
||||
fn create<K, V>(&self) -> Box<dyn CacheStorage<Key = K, Value = V>>
|
||||
where
|
||||
K: Send + Sync + Clone + Eq + Hash + 'static,
|
||||
V: Send + Sync + Clone + 'static,
|
||||
{
|
||||
Box::new(HashMapCacheImpl(Default::default()))
|
||||
Box::new(HashMapCacheImpl::<K, V, S>(HashMap::<K, V, S>::default()))
|
||||
}
|
||||
}
|
||||
|
||||
struct HashMapCacheImpl<K, V>(HashMap<K, V>);
|
||||
struct HashMapCacheImpl<K, V, S>(HashMap<K, V, S>);
|
||||
|
||||
impl<K, V> CacheStorage for HashMapCacheImpl<K, V>
|
||||
impl<K, V, S> CacheStorage for HashMapCacheImpl<K, V, S>
|
||||
where
|
||||
K: Send + Sync + Clone + Eq + Hash + 'static,
|
||||
V: Send + Sync + Clone + 'static,
|
||||
S: Send + Sync + BuildHasher + 'static,
|
||||
{
|
||||
type Key = K;
|
||||
type Value = V;
|
||||
|
|
|
@ -66,11 +66,10 @@ use std::hash::Hash;
|
|||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use futures_channel::oneshot;
|
||||
use futures_timer::Delay;
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
pub use cache::{CacheFactory, CacheStorage, HashMapCache, LruCache, NoCache};
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
|
@ -391,6 +390,7 @@ impl<T, C: CacheFactory> DataLoader<T, C> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use fnv::FnvBuildHasher;
|
||||
use std::sync::Arc;
|
||||
|
||||
struct MyLoader;
|
||||
|
@ -470,13 +470,44 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_dataloader_load_empty() {
|
||||
let loader = DataLoader::with_cache(MyLoader, HashMapCache);
|
||||
let loader = DataLoader::new(MyLoader);
|
||||
assert!(loader.load_many::<i32, _>(vec![]).await.unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dataloader_with_cache() {
|
||||
let loader = DataLoader::with_cache(MyLoader, HashMapCache);
|
||||
let loader = DataLoader::with_cache(MyLoader, HashMapCache::default());
|
||||
loader.feed_many(vec![(1, 10), (2, 20), (3, 30)]).await;
|
||||
|
||||
// All from the cache
|
||||
assert_eq!(
|
||||
loader.load_many(vec![1, 2, 3]).await.unwrap(),
|
||||
vec![(1, 10), (2, 20), (3, 30)].into_iter().collect()
|
||||
);
|
||||
|
||||
// Part from the cache
|
||||
assert_eq!(
|
||||
loader.load_many(vec![1, 5, 6]).await.unwrap(),
|
||||
vec![(1, 10), (5, 5), (6, 6)].into_iter().collect()
|
||||
);
|
||||
|
||||
// All from the loader
|
||||
assert_eq!(
|
||||
loader.load_many(vec![8, 9, 10]).await.unwrap(),
|
||||
vec![(8, 8), (9, 9), (10, 10)].into_iter().collect()
|
||||
);
|
||||
|
||||
// Clear cache
|
||||
loader.clear::<i32>();
|
||||
assert_eq!(
|
||||
loader.load_many(vec![1, 2, 3]).await.unwrap(),
|
||||
vec![(1, 1), (2, 2), (3, 3)].into_iter().collect()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dataloader_with_cache_hashmap_fnv() {
|
||||
let loader = DataLoader::with_cache(MyLoader, HashMapCache::<FnvBuildHasher>::new());
|
||||
loader.feed_many(vec![(1, 10), (2, 20), (3, 30)]).await;
|
||||
|
||||
// All from the cache
|
||||
|
|
Loading…
Reference in New Issue