1
1
use futures_util:: future:: { Join , Map , Ready } ;
2
2
use futures_util:: FutureExt ;
3
3
use lazy_static:: lazy_static;
4
- use prometheus:: { register_histogram_vec, HistogramTimer , HistogramVec } ;
4
+ use prometheus:: { register_histogram_vec, Histogram , HistogramTimer , HistogramVec } ;
5
5
use std:: future:: Future ;
6
+ use std:: pin:: Pin ;
7
+ use std:: task:: { Context , Poll } ;
6
8
use tracing:: field:: Empty ;
7
9
use tracing:: instrument:: Instrumented ;
8
10
use tracing:: { info_span, Instrument , Span } ;
@@ -24,8 +26,10 @@ type ResponseFutureOutput = <ResponseFuture as Future>::Output;
24
26
25
27
type MapFn = fn ( ( ResponseFutureOutput , HistogramTimer ) ) ;
26
28
27
- type JoinedFuture = Join < ResponseFuture , Ready < HistogramTimer > > ;
28
- type MappedFuture = Map < Instrumented < JoinedFuture > , MapFn > ;
29
+ type StartTimer = fn ( Histogram ) -> HistogramTimer ;
30
+ // change type as join could maybe not start both futures
31
+ type JoinedFuture = Join < ResponseFuture , Lazy < Histogram , StartTimer > > ;
32
+ type MappedFuture = Instrumented < Map < JoinedFuture , MapFn > > ;
29
33
30
34
pub ( super ) struct TraceRequestHandler {
31
35
catalog : Catalog ,
@@ -66,18 +70,53 @@ impl RequestHandler for TraceRequestHandler {
66
70
let addr = request. src ;
67
71
let span = info_span ! ( parent: & self . span, "request" , remote. addr = %addr, name = Empty , query_type = Empty ) ;
68
72
69
- let timer =
70
- futures_util :: future :: ready ( DNS_REQ_HISTOGRAM . with_label_values ( name ) . start_timer ( ) ) ;
73
+ let timer = DNS_REQ_HISTOGRAM . with_label_values ( name ) ;
74
+ let timer = Lazy :: new ( timer , start_timer) ;
71
75
let handle_request = self . catalog . handle_request ( request, response_handle) ;
72
76
73
77
futures_util:: future:: join ( handle_request, timer)
74
- . instrument ( span)
75
78
. map ( end_timer)
79
+ . instrument ( span)
76
80
}
77
81
}
78
82
83
+ fn start_timer ( timer : Histogram ) -> HistogramTimer {
84
+ timer. start_timer ( )
85
+ }
86
+
79
87
fn end_timer ( ( res, timer) : ( ResponseFutureOutput , HistogramTimer ) ) {
80
88
timer. observe_duration ( ) ;
81
89
82
90
res
83
91
}
92
+
93
+ // todo: lazy is not needed should be possible with map and then
94
+ struct Lazy < T , F > {
95
+ data : Option < T > ,
96
+ fun : Option < F > ,
97
+ }
98
+
99
+ impl < T , F > Lazy < T , F > {
100
+ fn new ( data : T , fun : F ) -> Self {
101
+ Lazy {
102
+ data : Some ( data) ,
103
+ fun : Some ( fun) ,
104
+ }
105
+ }
106
+ }
107
+
108
+ impl < T , F , R > Future for Lazy < T , F >
109
+ where
110
+ T : Unpin ,
111
+ F : FnOnce ( T ) -> R + Unpin ,
112
+ {
113
+ type Output = R ;
114
+
115
+ fn poll ( self : Pin < & mut Self > , _cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
116
+ let this = self . get_mut ( ) ;
117
+ let fun = this. fun . take ( ) . expect ( "Polled after completion" ) ;
118
+ let data = this. data . take ( ) . expect ( "Polled after completion" ) ;
119
+
120
+ Poll :: Ready ( fun ( data) )
121
+ }
122
+ }
0 commit comments