1+ use std:: collections:: HashMap ;
12use std:: env;
23use std:: str:: FromStr ;
34use std:: sync:: Arc ;
@@ -6,6 +7,8 @@ use std::time::{Duration, Instant};
67use crate :: prelude:: { QueryExecutionOptions , StoreResolver , SubscriptionExecutionOptions } ;
78use crate :: query:: execute_query;
89use crate :: subscription:: execute_prepared_subscription;
10+ use graph:: prelude:: MetricsRegistry ;
11+ use graph:: prometheus:: { Gauge , Histogram } ;
912use graph:: {
1013 components:: store:: SubscriptionManager ,
1114 prelude:: {
@@ -21,12 +24,59 @@ use graph::{
2124
2225use lazy_static:: lazy_static;
2326
27+ pub struct ResultSizeMetrics {
28+ histogram : Box < Histogram > ,
29+ max_gauge : Box < Gauge > ,
30+ }
31+
32+ impl ResultSizeMetrics {
33+ fn new ( registry : Arc < impl MetricsRegistry > ) -> Self {
34+ // Divide the Histogram into exponentially sized buckets between 1k and 32G
35+ let bins = ( 10 ..26 ) . map ( |n| 2u64 . pow ( n) as f64 ) . collect :: < Vec < _ > > ( ) ;
36+ let histogram = registry
37+ . new_histogram (
38+ "query_result_size" ,
39+ "the size of the result of successful GraphQL queries (in CacheWeight)" ,
40+ bins,
41+ )
42+ . unwrap ( ) ;
43+
44+ let max_gauge = registry
45+ . new_gauge (
46+ "query_result_max" ,
47+ "the maximum size of a query result (in CacheWeight)" ,
48+ HashMap :: new ( ) ,
49+ )
50+ . unwrap ( ) ;
51+
52+ Self {
53+ histogram,
54+ max_gauge,
55+ }
56+ }
57+
58+ // Tests need to construct one of these, but normal code doesn't
59+ #[ cfg( debug_assertions) ]
60+ pub fn make ( registry : Arc < impl MetricsRegistry > ) -> Self {
61+ Self :: new ( registry)
62+ }
63+
64+ pub fn observe ( & self , size : usize ) {
65+ let size = size as f64 ;
66+ self . histogram . observe ( size) ;
67+ if self . max_gauge . get ( ) < size {
68+ self . max_gauge . set ( size) ;
69+ }
70+ }
71+ }
72+
2473/// GraphQL runner implementation for The Graph.
2574pub struct GraphQlRunner < S , SM > {
2675 logger : Logger ,
2776 store : Arc < S > ,
2877 subscription_manager : Arc < SM > ,
2978 load_manager : Arc < LoadManager > ,
79+ result_size : Arc < ResultSizeMetrics > ,
3080}
3181
3282lazy_static ! {
@@ -81,13 +131,16 @@ where
81131 store : Arc < S > ,
82132 subscription_manager : Arc < SM > ,
83133 load_manager : Arc < LoadManager > ,
134+ registry : Arc < impl MetricsRegistry > ,
84135 ) -> Self {
85136 let logger = logger. new ( o ! ( "component" => "GraphQlRunner" ) ) ;
137+ let result_size = Arc :: new ( ResultSizeMetrics :: new ( registry) ) ;
86138 GraphQlRunner {
87139 logger,
88140 store,
89141 subscription_manager,
90142 load_manager,
143+ result_size,
91144 }
92145 }
93146
@@ -129,6 +182,7 @@ where
129182 max_depth : Option < u8 > ,
130183 max_first : Option < u32 > ,
131184 max_skip : Option < u32 > ,
185+ result_size : Arc < ResultSizeMetrics > ,
132186 ) -> Result < QueryResults , QueryResults > {
133187 // We need to use the same `QueryStore` for the entire query to ensure
134188 // we have a consistent view if the world, even when replicas, which
@@ -181,6 +235,7 @@ where
181235 bc,
182236 error_policy,
183237 query. schema . id ( ) . clone ( ) ,
238+ result_size. cheap_clone ( ) ,
184239 )
185240 . await ?;
186241 max_block = max_block. max ( resolver. block_number ( ) ) ;
@@ -242,6 +297,7 @@ where
242297 max_depth,
243298 max_first,
244299 max_skip,
300+ self . result_size . cheap_clone ( ) ,
245301 )
246302 . await
247303 . unwrap_or_else ( |e| e)
@@ -288,6 +344,7 @@ where
288344 max_depth : * GRAPHQL_MAX_DEPTH ,
289345 max_first : * GRAPHQL_MAX_FIRST ,
290346 max_skip : * GRAPHQL_MAX_SKIP ,
347+ result_size : self . result_size . clone ( ) ,
291348 } ,
292349 )
293350 . await
0 commit comments