1
1
use {
2
2
crate :: {
3
3
api:: { ApiBlockChainState , NetworkId , RestError , StateTag } ,
4
- history:: RequestStatus ,
4
+ config:: LATENCY_BUCKETS ,
5
+ history:: { RequestQueryBuilder , RequestStatus , SearchField } ,
5
6
} ,
6
7
axum:: {
7
8
extract:: { Query , State } ,
8
9
Json ,
9
10
} ,
10
11
chrono:: { DateTime , Utc } ,
12
+ prometheus_client:: {
13
+ encoding:: { EncodeLabelSet , EncodeLabelValue } ,
14
+ metrics:: { family:: Family , histogram:: Histogram } ,
15
+ registry:: Registry ,
16
+ } ,
17
+ std:: sync:: Arc ,
18
+ tokio:: { sync:: RwLock , time:: Instant } ,
11
19
utoipa:: IntoParams ,
12
20
} ;
13
21
14
- #[ derive( Debug , serde:: Serialize , serde:: Deserialize , IntoParams ) ]
22
+ #[ derive( Debug ) ]
23
+ pub struct ExplorerMetrics {
24
+ results_latency : Family < QueryTags , Histogram > ,
25
+ count_latency : Family < QueryTags , Histogram > ,
26
+ }
27
+
28
+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , EncodeLabelSet ) ]
29
+ pub struct QueryTags {
30
+ search_type : Option < SearchType > ,
31
+ has_network_id_filter : bool ,
32
+ has_state_filter : bool ,
33
+ }
34
+
35
+ impl < ' a > From < RequestQueryBuilder < ' a > > for QueryTags {
36
+ fn from ( builder : RequestQueryBuilder < ' a > ) -> Self {
37
+ QueryTags {
38
+ search_type : builder. search . map ( |val| match val {
39
+ SearchField :: TxHash ( _) => SearchType :: TxHash ,
40
+ SearchField :: Sender ( _) => SearchType :: Sender ,
41
+ SearchField :: SequenceNumber ( _) => SearchType :: SequenceNumber ,
42
+ } ) ,
43
+ has_network_id_filter : builder. network_id . is_some ( ) ,
44
+ has_state_filter : builder. state . is_some ( ) ,
45
+ }
46
+ }
47
+ }
48
+
49
+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , EncodeLabelValue ) ]
50
+ enum SearchType {
51
+ TxHash ,
52
+ Sender ,
53
+ SequenceNumber ,
54
+ }
55
+
56
+ impl ExplorerMetrics {
57
+ pub async fn new ( metrics_registry : Arc < RwLock < Registry > > ) -> Self {
58
+ let mut guard = metrics_registry. write ( ) . await ;
59
+ let sub_registry = guard. sub_registry_with_prefix ( "explorer" ) ;
60
+
61
+ let results_latency = Family :: < QueryTags , Histogram > :: new_with_constructor ( || {
62
+ Histogram :: new ( LATENCY_BUCKETS . into_iter ( ) )
63
+ } ) ;
64
+ sub_registry. register (
65
+ "results_latency" ,
66
+ "The latency of requests to the database to collect the limited results." ,
67
+ results_latency. clone ( ) ,
68
+ ) ;
69
+
70
+ let count_latency = Family :: < QueryTags , Histogram > :: new_with_constructor ( || {
71
+ Histogram :: new ( LATENCY_BUCKETS . into_iter ( ) )
72
+ } ) ;
73
+ sub_registry. register (
74
+ "count_latency" ,
75
+ "The latency of requests to the database to collect the total matching result count." ,
76
+ count_latency. clone ( ) ,
77
+ ) ;
78
+
79
+ Self {
80
+ results_latency,
81
+ count_latency,
82
+ }
83
+ }
84
+ }
85
+
86
+ #[ derive( Debug , Clone , serde:: Serialize , serde:: Deserialize , IntoParams ) ]
15
87
#[ into_params( parameter_in=Query ) ]
16
88
pub struct ExplorerQueryParams {
17
89
/// Only return logs that are newer or equal to this timestamp. Timestamp is in ISO 8601 format with UTC timezone.
@@ -96,7 +168,13 @@ pub async fn explorer(
96
168
query = query. max_timestamp ( max_timestamp) ;
97
169
}
98
170
99
- let ( requests, total_results) = tokio:: join!( query. execute( ) , query. count_results( ) ) ;
171
+ let results_latency = & state. explorer_metrics . results_latency ;
172
+ let count_latency = & state. explorer_metrics . count_latency ;
173
+ let query_tags = & query. clone ( ) . into ( ) ;
174
+ let ( requests, total_results) = tokio:: join!(
175
+ measure_latency( results_latency, query_tags, query. execute( ) ) ,
176
+ measure_latency( count_latency, query_tags, query. count_results( ) )
177
+ ) ;
100
178
let requests = requests. map_err ( |_| RestError :: TemporarilyUnavailable ) ?;
101
179
let total_results = total_results. map_err ( |_| RestError :: TemporarilyUnavailable ) ?;
102
180
@@ -105,3 +183,19 @@ pub async fn explorer(
105
183
total_results,
106
184
} ) )
107
185
}
186
+
187
+ async fn measure_latency < T , F > (
188
+ metric : & Family < QueryTags , Histogram > ,
189
+ query_tags : & QueryTags ,
190
+ function : F ,
191
+ ) -> T
192
+ where
193
+ F : std:: future:: Future < Output = T > ,
194
+ {
195
+ let start = Instant :: now ( ) ;
196
+ let return_value = function. await ;
197
+ metric
198
+ . get_or_create ( query_tags)
199
+ . observe ( start. elapsed ( ) . as_secs_f64 ( ) ) ;
200
+ return_value
201
+ }
0 commit comments