Add the `BuildHasher` generic parameter to `dataloader::HashMapCache` to allow custom hashing algorithms. #455

This commit is contained in:
Sunli 2021-04-02 15:55:39 +08:00
parent 018d56d2ca
commit 8a67781180
3 changed files with 62 additions and 10 deletions

View File

@ -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

View File

@ -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;

View File

@ -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