12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
+ use std:: future:: Future ;
15
16
use std:: pin:: Pin ;
16
17
use std:: str:: FromStr ;
17
18
use std:: sync:: Arc ;
19
+ use std:: task:: { ready, Context , Poll } ;
18
20
use std:: time:: { Duration , Instant } ;
19
21
20
22
use async_trait:: async_trait;
21
23
use bytes:: Bytes ;
24
+ use pin_project:: { pin_project, pinned_drop} ;
22
25
use quickwit_common:: uri:: Uri ;
23
26
use quickwit_config:: SearcherConfig ;
24
27
use quickwit_doc_mapper:: DocMapper ;
@@ -35,7 +38,7 @@ use quickwit_storage::{
35
38
MemorySizedCache , QuickwitCache , SplitCache , StorageCache , StorageResolver ,
36
39
} ;
37
40
use tantivy:: aggregation:: AggregationLimitsGuard ;
38
- use tokio:: sync:: { oneshot , Semaphore } ;
41
+ use tokio:: sync:: Semaphore ;
39
42
use tokio_stream:: wrappers:: UnboundedReceiverStream ;
40
43
41
44
use crate :: leaf:: multi_index_leaf_search;
@@ -203,17 +206,18 @@ impl SearchService for SearchServiceImpl {
203
206
. iter ( )
204
207
. map ( |req| req. split_offsets . len ( ) )
205
208
. sum :: < usize > ( ) ;
206
- let completion_tx = start_leaf_search_metric_recording ( num_splits) . await ;
207
- let leaf_search_response_result = multi_index_leaf_search (
208
- self . searcher_context . clone ( ) ,
209
- leaf_search_request,
210
- & self . storage_resolver ,
211
- )
212
- . await ;
213
-
214
- completion_tx. send ( leaf_search_response_result. is_ok ( ) ) . ok ( ) ;
215
209
216
- leaf_search_response_result
210
+ LeafSearchMetricsFuture {
211
+ tracked : multi_index_leaf_search (
212
+ self . searcher_context . clone ( ) ,
213
+ leaf_search_request,
214
+ & self . storage_resolver ,
215
+ ) ,
216
+ start : Instant :: now ( ) ,
217
+ targeted_splits : num_splits,
218
+ status : None ,
219
+ }
220
+ . await
217
221
}
218
222
219
223
async fn fetch_docs (
@@ -527,37 +531,52 @@ impl SearcherContext {
527
531
}
528
532
}
529
533
530
- /// Spawns a task that records leaf search metrics either
531
- /// - when the result is received through the returned channel (success or failure)
532
- /// - the returned channels are dropped (cancelled)
533
- #[ must_use]
534
- async fn start_leaf_search_metric_recording ( num_splits : usize ) -> oneshot:: Sender < bool > {
535
- let ( completion_tx, completion_rx) = tokio:: sync:: oneshot:: channel ( ) ;
536
- let start = Instant :: now ( ) ;
534
+ /// Wrapper around the search future to track metrics.
535
+ #[ pin_project( PinnedDrop ) ]
536
+ struct LeafSearchMetricsFuture < F >
537
+ where F : Future < Output = Result < LeafSearchResponse , SearchError > >
538
+ {
539
+ #[ pin]
540
+ tracked : F ,
541
+ start : Instant ,
542
+ targeted_splits : usize ,
543
+ status : Option < & ' static str > ,
544
+ }
537
545
538
- tokio:: spawn ( async move {
539
- let label_values = if let Ok ( is_success) = completion_rx. await {
540
- if is_success {
541
- [ "success" ]
542
- } else {
543
- [ "error" ]
544
- }
545
- } else {
546
- [ "cancelled" ]
547
- } ;
546
+ #[ pinned_drop]
547
+ impl < F > PinnedDrop for LeafSearchMetricsFuture < F >
548
+ where F : Future < Output = Result < LeafSearchResponse , SearchError > >
549
+ {
550
+ fn drop ( self : Pin < & mut Self > ) {
551
+ let label_values = [ self . status . unwrap_or ( "cancelled" ) ] ;
548
552
SEARCH_METRICS
549
553
. leaf_search_requests_total
550
554
. with_label_values ( label_values)
551
555
. inc ( ) ;
552
556
SEARCH_METRICS
553
557
. leaf_search_request_duration_seconds
554
558
. with_label_values ( label_values)
555
- . observe ( start. elapsed ( ) . as_secs_f64 ( ) ) ;
559
+ . observe ( self . start . elapsed ( ) . as_secs_f64 ( ) ) ;
556
560
SEARCH_METRICS
557
561
. leaf_search_targeted_splits
558
562
. with_label_values ( label_values)
559
- . observe ( num_splits as f64 ) ;
560
- } ) ;
563
+ . observe ( self . targeted_splits as f64 ) ;
564
+ }
565
+ }
561
566
562
- completion_tx
567
+ impl < F > Future for LeafSearchMetricsFuture < F >
568
+ where F : Future < Output = Result < LeafSearchResponse , SearchError > >
569
+ {
570
+ type Output = Result < LeafSearchResponse , SearchError > ;
571
+
572
+ fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
573
+ let this = self . project ( ) ;
574
+ let response = ready ! ( this. tracked. poll( cx) ) ;
575
+ * this. status = if response. is_ok ( ) {
576
+ Some ( "success" )
577
+ } else {
578
+ Some ( "error" )
579
+ } ;
580
+ Poll :: Ready ( Ok ( response?) )
581
+ }
563
582
}
0 commit comments