Skip to content

Commit fff620c

Browse files
committed
query cache: Mem limit, turn on by default
1 parent 20e5bd3 commit fff620c

File tree

10 files changed

+187
-72
lines changed

10 files changed

+187
-72
lines changed

core/src/subgraph/instance_manager.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ use super::SubgraphInstance;
2121
lazy_static! {
2222
/// Size limit of the entity LFU cache, in bytes.
2323
// Multiplied by 1000 because the env var is in KB.
24-
pub static ref ENTITY_CACHE_SIZE: u64 = 1000
24+
pub static ref ENTITY_CACHE_SIZE: usize = 1000
2525
* std::env::var("GRAPH_ENTITY_CACHE_SIZE")
2626
.unwrap_or("10000".into())
27-
.parse::<u64>()
27+
.parse::<usize>()
2828
.expect("invalid GRAPH_ENTITY_CACHE_SIZE");
2929
}
3030

graph/src/data/store/mod.rs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::data::subgraph::SubgraphDeploymentId;
22
use crate::prelude::{format_err, EntityKey, QueryExecutionError};
3-
use crate::util::lfu_cache::CacheWeight;
43
use failure::Error;
54
use graphql_parser::query;
65
use graphql_parser::schema;
@@ -596,32 +595,6 @@ pub trait ToEntityKey {
596595
fn to_entity_key(&self, subgraph: SubgraphDeploymentId) -> EntityKey;
597596
}
598597

599-
impl CacheWeight for Value {
600-
fn weight(&self) -> u64 {
601-
use std::mem::size_of_val;
602-
size_of_val(self) as u64
603-
+ match self {
604-
Value::String(s) => s.len() as u64,
605-
Value::BigDecimal(d) => (d.digits() as f32).log2() as u64,
606-
Value::List(values) => values.iter().map(|value| value.weight()).sum(),
607-
Value::Bytes(bytes) => bytes.as_slice().len() as u64,
608-
Value::BigInt(n) => n.bits() / 8 as u64,
609-
Value::Int(_) | Value::Bool(_) | Value::Null => 0,
610-
}
611-
}
612-
}
613-
614-
impl CacheWeight for Entity {
615-
/// The weight of an entity in the cache is the approximate amount of bytes occupied by it.
616-
fn weight(&self) -> u64 {
617-
use std::mem::size_of_val;
618-
self.0
619-
.iter()
620-
.map(|(key, value)| size_of_val(key) as u64 + key.len() as u64 + value.weight())
621-
.sum()
622-
}
623-
}
624-
625598
#[test]
626599
fn value_bytes() {
627600
let graphql_value = query::Value::String("0x8f494c66afc1d3f8ac1b45df21f02a46".to_owned());

graph/src/data/store/scalar.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,8 @@ impl BigInt {
320320
BigInt(self.0.pow(&exponent))
321321
}
322322

323-
pub fn bits(&self) -> u64 {
324-
self.0.bits() as u64
323+
pub fn bits(&self) -> usize {
324+
self.0.bits()
325325
}
326326
}
327327

graph/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ pub mod prelude {
149149
ComponentLoggerConfig, ElasticComponentLoggerConfig, LoggerFactory,
150150
};
151151
pub use crate::log::split::split_logger;
152+
pub use crate::util::cache_weight::CacheWeight;
152153
pub use crate::util::error::CompatErr;
153154
pub use crate::util::futures::{retry, TimeoutError};
154155
pub use crate::util::stats::MovingStats;

graph/src/util/cache_weight.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use crate::prelude::{BigDecimal, BigInt, Entity, Value};
2+
3+
/// Estimate of how much memory a value consumes.
4+
/// Useful for measuring the size of caches.
5+
pub trait CacheWeight {
6+
/// Total weight of the value.
7+
fn weight(&self) -> usize {
8+
std::mem::size_of_val(&self) + self.indirect_weight()
9+
}
10+
11+
/// The weight of values pointed to by this value but logically owned by it, which is not
12+
/// accounted for by `size_of`.
13+
fn indirect_weight(&self) -> usize;
14+
}
15+
16+
impl<T: CacheWeight> CacheWeight for Option<T> {
17+
fn indirect_weight(&self) -> usize {
18+
match self {
19+
Some(x) => x.indirect_weight(),
20+
None => 0,
21+
}
22+
}
23+
}
24+
25+
impl<T: CacheWeight> CacheWeight for Vec<T> {
26+
fn indirect_weight(&self) -> usize {
27+
self.iter().map(CacheWeight::indirect_weight).sum()
28+
}
29+
}
30+
31+
impl<T: CacheWeight, U: CacheWeight> CacheWeight for std::collections::BTreeMap<T, U> {
32+
fn indirect_weight(&self) -> usize {
33+
self.iter()
34+
.map(|(key, value)| key.weight() + value.weight())
35+
.sum()
36+
}
37+
}
38+
39+
impl CacheWeight for &'_ [u8] {
40+
fn indirect_weight(&self) -> usize {
41+
self.len()
42+
}
43+
}
44+
45+
impl CacheWeight for String {
46+
fn indirect_weight(&self) -> usize {
47+
self.len()
48+
}
49+
}
50+
51+
impl CacheWeight for BigDecimal {
52+
fn indirect_weight(&self) -> usize {
53+
(self.digits() as f32).log2() as usize
54+
}
55+
}
56+
57+
impl CacheWeight for BigInt {
58+
fn indirect_weight(&self) -> usize {
59+
self.bits() / 8
60+
}
61+
}
62+
63+
impl CacheWeight for Value {
64+
fn indirect_weight(&self) -> usize {
65+
match self {
66+
Value::String(s) => s.indirect_weight(),
67+
Value::BigDecimal(d) => d.indirect_weight(),
68+
Value::List(values) => values.indirect_weight(),
69+
Value::Bytes(bytes) => bytes.as_slice().indirect_weight(),
70+
Value::BigInt(n) => n.indirect_weight(),
71+
Value::Int(_) | Value::Bool(_) | Value::Null => 0,
72+
}
73+
}
74+
}
75+
76+
impl CacheWeight for Entity {
77+
fn indirect_weight(&self) -> usize {
78+
self.iter()
79+
.map(|(key, value)| key.weight() + value.weight())
80+
.sum()
81+
}
82+
}
83+
84+
impl CacheWeight for graphql_parser::query::Value {
85+
fn indirect_weight(&self) -> usize {
86+
use graphql_parser::query as q;
87+
88+
match self {
89+
q::Value::Boolean(_) | q::Value::Int(_) | q::Value::Null | q::Value::Float(_) => 0,
90+
q::Value::Enum(s) | q::Value::String(s) | q::Value::Variable(s) => s.indirect_weight(),
91+
q::Value::List(l) => l.indirect_weight(),
92+
q::Value::Object(o) => o.indirect_weight(),
93+
}
94+
}
95+
}

graph/src/util/lfu_cache.rs

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::prelude::CacheWeight;
12
use priority_queue::PriorityQueue;
23
use std::cmp::Reverse;
34
use std::fmt::Debug;
@@ -6,23 +7,10 @@ use std::hash::{Hash, Hasher};
67
// The number of `evict` calls without access after which an entry is considered stale.
78
const STALE_PERIOD: u64 = 100;
89

9-
pub trait CacheWeight {
10-
fn weight(&self) -> u64;
11-
}
12-
13-
impl<T: CacheWeight> CacheWeight for Option<T> {
14-
fn weight(&self) -> u64 {
15-
match self {
16-
Some(x) => x.weight(),
17-
None => 0,
18-
}
19-
}
20-
}
21-
2210
/// `PartialEq` and `Hash` are delegated to the `key`.
2311
#[derive(Clone, Debug)]
2412
pub struct CacheEntry<K, V> {
25-
weight: u64,
13+
weight: usize,
2614
key: K,
2715
value: V,
2816
will_stale: bool,
@@ -66,7 +54,7 @@ type Priority = (bool, Reverse<u64>);
6654
#[derive(Clone, Debug)]
6755
pub struct LfuCache<K: Eq + Hash, V> {
6856
queue: PriorityQueue<CacheEntry<K, V>, Priority>,
69-
total_weight: u64,
57+
total_weight: usize,
7058
stale_counter: u64,
7159
}
7260

@@ -157,7 +145,7 @@ impl<K: Clone + Ord + Eq + Hash + Debug, V: CacheWeight + Default> LfuCache<K, V
157145
self.queue.len()
158146
}
159147

160-
pub fn evict(&mut self, max_weight: u64) {
148+
pub fn evict(&mut self, max_weight: usize) {
161149
if self.total_weight <= max_weight {
162150
return;
163151
}
@@ -204,18 +192,25 @@ impl<K: Ord + Eq + Hash, V> Extend<(CacheEntry<K, V>, Priority)> for LfuCache<K,
204192

205193
#[test]
206194
fn entity_lru_cache() {
207-
impl CacheWeight for u64 {
208-
fn weight(&self) -> u64 {
209-
*self
195+
#[derive(Default, Debug, PartialEq, Eq)]
196+
struct Weight(usize);
197+
198+
impl CacheWeight for Weight {
199+
fn weight(&self) -> usize {
200+
self.0
201+
}
202+
203+
fn indirect_weight(&self) -> usize {
204+
0
210205
}
211206
}
212207

213-
let mut cache: LfuCache<&'static str, u64> = LfuCache::new();
214-
cache.insert("panda", 2);
215-
cache.insert("cow", 1);
208+
let mut cache: LfuCache<&'static str, Weight> = LfuCache::new();
209+
cache.insert("panda", Weight(2));
210+
cache.insert("cow", Weight(1));
216211

217-
assert_eq!(cache.get(&"cow"), Some(&1));
218-
assert_eq!(cache.get(&"panda"), Some(&2));
212+
assert_eq!(cache.get(&"cow"), Some(&Weight(1)));
213+
assert_eq!(cache.get(&"panda"), Some(&Weight(2)));
219214

220215
// Total weight is 3, nothing is evicted.
221216
cache.evict(3);
@@ -226,28 +221,28 @@ fn entity_lru_cache() {
226221
cache.evict(2);
227222
assert!(cache.get(&"panda").is_none());
228223

229-
cache.insert("alligator", 2);
224+
cache.insert("alligator", Weight(2));
230225

231226
// Give "cow" and "alligator" a high frequency.
232227
for _ in 0..1000 {
233228
cache.get(&"cow");
234229
cache.get(&"alligator");
235230
}
236231

237-
cache.insert("lion", 3);
232+
cache.insert("lion", Weight(3));
238233

239234
// Make "cow" and "alligator" stale and remove them.
240235
for _ in 0..(2 * STALE_PERIOD) {
241236
cache.get(&"lion");
242237

243238
// The "whale" is something to evict so the stale counter moves.
244-
cache.insert("whale", 100);
239+
cache.insert("whale", Weight(100));
245240
cache.evict(10);
246241
}
247242

248243
// Either "cow" and "alligator" fit in the cache, or just "lion".
249244
// "lion" will be kept, it had lower frequency but was not stale.
250245
assert!(cache.get(&"cow").is_none());
251246
assert!(cache.get(&"alligator").is_none());
252-
assert_eq!(cache.get(&"lion"), Some(&3));
247+
assert_eq!(cache.get(&"lion"), Some(&Weight(3)));
253248
}

graph/src/util/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ pub mod lfu_cache;
1212
pub mod error;
1313

1414
pub mod stats;
15+
16+
pub mod cache_weight;

0 commit comments

Comments
 (0)