diff --git a/quickwit/quickwit-common/src/tracing_utils.rs b/quickwit/quickwit-common/src/tracing_utils.rs index c39686442db..f449793ae9b 100644 --- a/quickwit/quickwit-common/src/tracing_utils.rs +++ b/quickwit/quickwit-common/src/tracing_utils.rs @@ -80,14 +80,13 @@ pub fn extract_context(metadata: &MetadataMap) -> Context { global::get_text_map_propagator(|propagator| propagator.extract(&extractor)) } -/// Extracts a W3C trace context from incoming gRPC request metadata and -/// installs it as the parent of the currently-active tracing span. Use this -/// at the entry of a gRPC handler that is itself wrapped in a -/// `#[tracing::instrument]` so the handler's span is stitched into the -/// caller's trace. -pub fn set_current_span_parent_from_metadata(metadata: &MetadataMap) { - let parent_context = extract_context(metadata); - let _ = Span::current().set_parent(parent_context); +/// Records an attribute on the currently-active span's OpenTelemetry span. +/// +/// Unlike a `tracing` field (`#[instrument(fields(...))]` or `Span::record`), this does +/// not propagate to log events, so it can carry verbose values (e.g. a query AST) +/// without bloating logs. No-op when no OpenTelemetry layer is installed. +pub fn record_current_span_attribute(key: &'static str, value: impl Into) { + Span::current().set_attribute(key, value); } /// Tonic interceptor that injects the active span's W3C trace context into diff --git a/quickwit/quickwit-search/src/fetch_docs.rs b/quickwit/quickwit-search/src/fetch_docs.rs index 21e848fe30f..29be8eb2be2 100644 --- a/quickwit/quickwit-search/src/fetch_docs.rs +++ b/quickwit/quickwit-search/src/fetch_docs.rs @@ -28,7 +28,7 @@ use tantivy::schema::document::CompactDocValue; use tantivy::schema::{Document as DocumentTrait, Field, TantivyDocument, Value}; use tantivy::snippet::SnippetGenerator; use tantivy::{ReloadPolicy, Score, Searcher, Term}; -use tracing::{Instrument, error}; +use tracing::{Instrument, error, instrument}; use crate::leaf::open_index_with_caches; use crate::service::SearcherContext; @@ -153,6 +153,7 @@ struct Document { } /// Fetching docs from a specific split. +#[instrument(skip_all, fields(split_id = split.split_id, num_docs = global_doc_addrs.len()))] async fn fetch_docs_in_split( searcher_context: Arc, mut global_doc_addrs: Vec, diff --git a/quickwit/quickwit-search/src/leaf.rs b/quickwit/quickwit-search/src/leaf.rs index 34f661f3530..d8db6417fbc 100644 --- a/quickwit/quickwit-search/src/leaf.rs +++ b/quickwit/quickwit-search/src/leaf.rs @@ -67,7 +67,7 @@ use crate::metrics::{ }; use crate::root::is_metadata_count_request_with_ast; use crate::search_permit_provider::{ - SearchPermit, SearchPermitFuture, compute_initial_memory_allocation, + BlockReasonHandle, SearchPermit, SearchPermitFuture, compute_initial_memory_allocation, }; use crate::service::{SearcherContext, deserialize_doc_mapper}; use crate::{QuickwitAggregations, SearchError}; @@ -300,7 +300,6 @@ async fn run_cancellable( /// /// Returns whether the query is provably empty in this split (i.e. `on_absent` fired and /// warmup was short-circuited). -#[instrument(skip_all)] pub(crate) async fn warmup( searcher: &Searcher, warmup_info: &WarmupInfo, @@ -779,7 +778,27 @@ async fn leaf_search_single_split( HitSet::empty(), ); }; - warmup(&searcher, &warmup_info, &record_absence).await? + // `warmup_mb` is the total warmed into the byte-range cache; `downloaded_mb` is + // the subset actually fetched from object storage (cache hits excluded). Both are + // recorded after warmup, before the search adds to either. + const BYTES_PER_MB: f64 = 1_000_000.0; + let warmup_span = info_span!( + "warmup", + warmup_mb = tracing::field::Empty, + downloaded_mb = tracing::field::Empty + ); + let provably_empty = warmup(&searcher, &warmup_info, &record_absence) + .instrument(warmup_span.clone()) + .await?; + warmup_span.record( + "warmup_mb", + byte_range_cache.get_num_bytes() as f64 / BYTES_PER_MB, + ); + warmup_span.record( + "downloaded_mb", + download_counters.snapshot().0 as f64 / BYTES_PER_MB, + ); + provably_empty }; let warmup_end = Instant::now(); let warmup_duration: Duration = warmup_end.duration_since(warmup_start); @@ -841,7 +860,10 @@ async fn leaf_search_single_split( } let split_num_docs = split.num_docs; - let span = info_span!("tantivy_search"); + // Spans the CPU-pool queue wait: created here (right after warmup) and closed when the + // closure starts executing on a pool thread. `tantivy_search` is created inside the + // closure so it covers only the CPU execution, not this wait. + let cpu_wait_span = info_span!("waiting_for_cpu_pool"); let split_clone = split.clone(); @@ -852,9 +874,12 @@ async fn leaf_search_single_split( let search_request_and_result: Option<(SearchRequest, LeafSearchResponse)> = crate::search_thread_pool() .run_cpu_intensive(move || { + // The CPU-pool queue wait ends as this closure starts executing. + drop(cpu_wait_span); leaf_search_state_guard.set_state(SplitSearchState::Cpu); let cpu_start = Instant::now(); let cpu_thread_pool_wait_microsecs = cpu_start.duration_since(warmup_end); + let span = info_span!("tantivy_search"); let _span_guard = span.enter(); // Our search execution has been scheduled, let's check if we can improve the // request based on the results of the preceding searches @@ -1545,7 +1570,7 @@ pub async fn multi_index_leaf_search( let searcher_context = searcher_context.clone(); let search_request = search_request.clone(); - leaf_request_futures.spawn({ + leaf_request_futures.spawn( async move { let storage = storage_resolver.resolve(&index_uri).await?; single_doc_mapping_leaf_search( @@ -1555,10 +1580,10 @@ pub async fn multi_index_leaf_search( leaf_search_request_ref.split_offsets, doc_mapper, ) - .in_current_span() .await } - }); + .instrument(Span::current()), + ); } // Creates a collector which merges responses into one @@ -1723,12 +1748,15 @@ async fn run_offloaded_search_tasks( }], }; let invoker = lambda_invoker.clone(); - lambda_tasks_joinset.spawn(async move { - ( - batch_split_ids, - invoker.invoke_leaf_search(leaf_request).await, - ) - }); + lambda_tasks_joinset.spawn( + async move { + ( + batch_split_ids, + invoker.invoke_leaf_search(leaf_request).await, + ) + } + .in_current_span(), + ); } while let Some(join_res) = lambda_tasks_joinset.join_next().await { @@ -2006,6 +2034,42 @@ pub async fn single_doc_mapping_leaf_search( Ok(leaf_search_response) } +/// Records the permit block reason onto the `waiting_for_leaf_search_split_semaphore` +/// span when the wait ends. +/// +/// Implemented as a drop guard (rather than reading after `.await`) so the reason is +/// recorded even when the wait future is cancelled before the permit is granted — the +/// long waits that hit a search deadline are exactly the ones that never get granted. +struct WaitBlockReasonRecorder { + wait_span: Span, + block_reason: BlockReasonHandle, + started_at: Instant, +} + +impl WaitBlockReasonRecorder { + fn new(wait_span: Span, block_reason: BlockReasonHandle) -> Self { + Self { + wait_span, + block_reason, + started_at: Instant::now(), + } + } +} + +impl Drop for WaitBlockReasonRecorder { + fn drop(&mut self) { + // Skip near-instant grants: a sub-millisecond wait isn't worth attributing and + // keeps the span uncluttered. + const MIN_WAIT_FOR_BLOCK_ATTRIBUTION: Duration = Duration::from_millis(1); + if self.started_at.elapsed() < MIN_WAIT_FOR_BLOCK_ATTRIBUTION { + return; + } + if let Some(block_reason) = self.block_reason.get() { + self.wait_span.record("blocked_on", block_reason.as_str()); + } + } +} + async fn run_local_search_tasks( local_search_tasks: Vec, index_storage: Arc, @@ -2021,9 +2085,26 @@ async fn run_local_search_tasks( search_permit_future, } in local_search_tasks { - let leaf_split_search_permit = search_permit_future - .instrument(info_span!("waiting_for_leaf_search_split_semaphore")) - .await; + // Per-split span covering both the permit wait and the search, so each split is a + // single subtree (wait + warmup + tantivy) rather than flat siblings. + let split_span = info_span!( + "leaf_search_single_split_wrapper", + split_id = split.split_id, + num_docs = split.num_docs + ); + let wait_span = info_span!( + parent: &split_span, + "waiting_for_leaf_search_split_semaphore", + blocked_on = tracing::field::Empty + ); + // Records the block reason on `wait_span` when the wait ends — whether the permit + // is granted or the wait is cancelled on a search deadline (the important, long + // waits are exactly the ones that get cancelled before being granted). + let _block_reason_recorder = WaitBlockReasonRecorder::new( + wait_span.clone(), + search_permit_future.block_reason_handle(), + ); + let leaf_split_search_permit = search_permit_future.instrument(wait_span).await; // We run simplify search request again: as we push split into the merge collector, // we may have discovered that we won't find any better candidates for top hits in this @@ -2045,7 +2126,7 @@ async fn run_local_search_tasks( split.clone(), leaf_split_search_permit, ) - .in_current_span(), + .instrument(split_span), ); task_id_to_split_id_map.insert(handle.id(), split_id); } @@ -2174,7 +2255,6 @@ struct LeafSearchContext { split_filter: Arc>, } -#[instrument(skip_all, fields(split_id = split.split_id, num_docs = split.num_docs))] async fn leaf_search_single_split_wrapper( request: SearchRequest, ctx: Arc, diff --git a/quickwit/quickwit-search/src/lib.rs b/quickwit/quickwit-search/src/lib.rs index cde7597b186..52b45ad7526 100644 --- a/quickwit/quickwit-search/src/lib.rs +++ b/quickwit/quickwit-search/src/lib.rs @@ -188,6 +188,7 @@ pub async fn list_all_splits( } /// Extract the list of relevant splits for a given request. +#[tracing::instrument(skip_all, fields(num_indexes = index_uids.len()))] pub async fn list_relevant_splits( index_uids: Vec, start_timestamp: Option, diff --git a/quickwit/quickwit-search/src/root.rs b/quickwit/quickwit-search/src/root.rs index aaf29b7c813..4d2bf9ec001 100644 --- a/quickwit/quickwit-search/src/root.rs +++ b/quickwit/quickwit-search/src/root.rs @@ -789,7 +789,7 @@ fn compute_root_resource_stats( /// If this method fails for some splits, a partial search response is returned, with the list of /// faulty splits in the failed_splits field. -#[instrument(level = "debug", skip_all)] +#[instrument(skip_all)] pub(crate) async fn search_partial_hits_phase( searcher_context: &SearcherContext, indexes_metas_for_leaf_search: &IndexesMetasForLeafSearch, @@ -1249,6 +1249,7 @@ async fn refine_and_list_matches( } /// Fetches the list of splits and their metadata from the metastore +#[instrument(skip_all)] async fn plan_splits_for_root_search( search_request: &mut SearchRequest, metastore: &mut MetastoreServiceClient, @@ -1311,8 +1312,9 @@ pub async fn root_search( let num_docs: usize = split_metadatas.iter().map(|split| split.num_docs).sum(); let num_splits = split_metadatas.len(); - // It would have been nice to add those in the context of the trace span, - // but with our current logging setting, it makes logs too verbose. + // These would bloat the logs if added as tracing span fields (they propagate to + // every log event in the span), so they go on a one-shot `info!` for logs and, via + // `record_current_span_attribute`, on the OpenTelemetry span only for the trace. info!( query_ast = search_request.query_ast.as_str(), agg = search_request.aggregation_request(), @@ -1322,6 +1324,24 @@ pub async fn root_search( num_splits = num_splits, "root_search" ); + quickwit_common::tracing_utils::record_current_span_attribute( + "query_ast", + search_request.query_ast.clone(), + ); + quickwit_common::tracing_utils::record_current_span_attribute("num_docs", num_docs as i64); + quickwit_common::tracing_utils::record_current_span_attribute("num_splits", num_splits as i64); + if let Some(start_timestamp) = search_request.start_timestamp { + quickwit_common::tracing_utils::record_current_span_attribute( + "start_timestamp", + start_timestamp, + ); + } + if let Some(end_timestamp) = search_request.end_timestamp { + quickwit_common::tracing_utils::record_current_span_attribute( + "end_timestamp", + end_timestamp, + ); + } if let Some(max_total_split_searches) = searcher_context.searcher_config.max_splits_per_search && max_total_split_searches < num_splits diff --git a/quickwit/quickwit-search/src/search_permit_provider.rs b/quickwit/quickwit-search/src/search_permit_provider.rs index ff9b0368d68..6485089ccf1 100644 --- a/quickwit/quickwit-search/src/search_permit_provider.rs +++ b/quickwit/quickwit-search/src/search_permit_provider.rs @@ -17,7 +17,7 @@ use std::collections::binary_heap::PeekMut; use std::future::Future; use std::pin::Pin; use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering}; use std::task::{Context, Poll}; use std::time::{Duration, Instant}; @@ -58,6 +58,66 @@ pub(crate) struct SplitSearchTaskMetadata { pub job_cost: usize, } +/// Why the head of the permit queue could not be granted. +/// +/// The actor writes it into the shared [`BlockReasonHandle`] each time it fails to serve +/// the head, so any waiter can read it whether its permit is eventually granted or the +/// wait is cancelled (e.g. on a search deadline). This is what lets us attribute the +/// acquisition latency to the binding resource. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PermitBlockReason { + /// All concurrent warmup/download slots were in use. + WarmupSlots, + /// The memory budget could not fit the request's estimated allocation. + MemoryBudget, +} + +impl PermitBlockReason { + pub fn as_str(&self) -> &'static str { + match self { + PermitBlockReason::WarmupSlots => "warmup_slots", + PermitBlockReason::MemoryBudget => "memory_budget", + } + } + + fn to_code(self) -> u8 { + match self { + PermitBlockReason::WarmupSlots => 1, + PermitBlockReason::MemoryBudget => 2, + } + } + + fn from_code(code: u8) -> Option { + match code { + 1 => Some(PermitBlockReason::WarmupSlots), + 2 => Some(PermitBlockReason::MemoryBudget), + _ => None, + } + } +} + +/// Shared cell recording the resource currently blocking the head of the permit queue. +/// +/// A single instance is shared by the actor (which writes it) and every +/// [`SearchPermitFuture`] (which reads it). Because permits are served in order, whatever +/// blocks the head effectively blocks every waiter, so a queue-level reason is correct for +/// a waiter at any position — including one cancelled deep in the queue before it ever +/// reaches the head. Uses an atomic so it can be read after the wait future is dropped on +/// cancellation. +#[derive(Clone, Default)] +pub struct BlockReasonHandle(Arc); + +impl BlockReasonHandle { + fn set(&self, reason: PermitBlockReason) { + self.0.store(reason.to_code(), Ordering::Relaxed); + } + + /// The resource currently blocking the queue, or `None` if it isn't blocked. + pub fn get(&self) -> Option { + PermitBlockReason::from_code(self.0.load(Ordering::Relaxed)) + } +} + pub enum SearchPermitMessage { RequestWithOffload { task_metadata: Vec, @@ -116,6 +176,7 @@ impl SearchPermitProvider { permits_requests: BinaryHeap::new(), total_memory_allocated: 0u64, total_job_cost: total_job_cost.clone(), + block_reason: BlockReasonHandle::default(), }; let actor_join_handle = Arc::new(tokio::spawn(actor.run())); Self { @@ -209,6 +270,9 @@ struct SearchPermitActor { /// Only the actor mutates it, so [`Ordering::Relaxed`] is sufficient. total_job_cost: Arc, permits_requests: BinaryHeap, + /// Shared with every [`SearchPermitFuture`]; set to the resource blocking the head of + /// the queue each time it can't be served. + block_reason: BlockReasonHandle, } struct SingleSplitPermitRequest { @@ -253,6 +317,7 @@ impl LeafPermitRequest { // `task_metadata` must not be empty. fn from_task_metadata( task_metadata: Vec, + block_reason: BlockReasonHandle, ) -> (Self, Vec) { assert!(!task_metadata.is_empty(), "task_metadata must not be empty"); // Stamped on every `SingleSplitPermitRequest` we're about to enqueue. @@ -272,7 +337,10 @@ impl LeafPermitRequest { job_cost: meta.job_cost, requested_at, }); - permits.push(SearchPermitFuture(rx)); + permits.push(SearchPermitFuture { + receiver: rx, + block_reason: block_reason.clone(), + }); } ( LeafPermitRequest { @@ -338,7 +406,7 @@ impl SearchPermitActor { SEARCHER_NODE_LOAD.set(new_load as f64); let (leaf_permit_request, permit_futures) = - LeafPermitRequest::from_task_metadata(task_metadata); + LeafPermitRequest::from_task_metadata(task_metadata, self.block_reason.clone()); self.permits_requests.push(leaf_permit_request); self.assign_available_permits(); let _ = permit_sender.send(permit_futures); @@ -374,12 +442,25 @@ impl SearchPermitActor { } fn pop_next_request_if_serviceable(&mut self) -> Option { + // Nothing is waiting, so there is no block to attribute (avoids leaving a stale + // reason set once the queue drains and slots/memory are exhausted). + if self.permits_requests.is_empty() { + return None; + } if self.num_warmup_slots_available == 0 { + self.block_reason.set(PermitBlockReason::WarmupSlots); return None; } - let available_memory = self + let available_memory = match self .total_memory_budget - .checked_sub(self.total_memory_allocated)?; + .checked_sub(self.total_memory_allocated) + { + Some(available_memory) => available_memory, + None => { + self.block_reason.set(PermitBlockReason::MemoryBudget); + return None; + } + }; let mut peeked = self.permits_requests.peek_mut()?; assert!( @@ -392,6 +473,8 @@ impl SearchPermitActor { } return Some(permit_request); } + // The head request needs more memory than is currently available. + self.block_reason.set(PermitBlockReason::MemoryBudget); None } @@ -510,13 +593,25 @@ impl Drop for SearchPermit { } } -pub struct SearchPermitFuture(oneshot::Receiver); +pub struct SearchPermitFuture { + receiver: oneshot::Receiver, + block_reason: BlockReasonHandle, +} + +impl SearchPermitFuture { + /// Handle to the reason this permit is blocked on. Readable at any time, including + /// after this future is dropped on cancellation, so the waiter can attribute the + /// wait even when the permit is never granted. + pub fn block_reason_handle(&self) -> BlockReasonHandle { + self.block_reason.clone() + } +} impl Future for SearchPermitFuture { type Output = SearchPermit; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let receiver = Pin::new(&mut self.get_mut().0); + let receiver = Pin::new(&mut self.get_mut().receiver); match receiver.poll(cx) { Poll::Ready(Ok(search_permit)) => Poll::Ready(search_permit), Poll::Ready(Err(_)) => panic!( @@ -837,6 +932,52 @@ mod tests { permits.push(try_get(next_blocked_permit_fut).await.unwrap()); } + #[tokio::test] + async fn test_permit_block_reason_warmup_slots() { + // A single warmup slot with ample memory: once a second request is queued, the + // queue can only be blocked on the warmup slot. The reason is queue-level and + // shared by every waiter's handle, readable without being granted a permit. + let permit_provider = SearchPermitProvider::new(1, ByteSize::mb(1000)); + let first_fut = permit_provider + .get_permits(make_splits(10, 1)) + .await + .into_iter() + .next() + .unwrap(); + // First was granted immediately; nothing is blocked yet. + assert_eq!(first_fut.block_reason_handle().get(), None); + let second_fut = permit_provider + .get_permits(make_splits(10, 1)) + .await + .into_iter() + .next() + .unwrap(); + // Second can't get the single slot: the queue is now blocked on warmup slots. + assert_eq!( + second_fut.block_reason_handle().get(), + Some(PermitBlockReason::WarmupSlots) + ); + // Draining still works: freeing the slot lets the second request through. + let first_permit = first_fut.await; + drop(first_permit); + let _second_permit = second_fut.await; + } + + #[tokio::test] + async fn test_permit_block_reason_memory_budget() { + // Ample slots but tight memory (100MB / 10MB = 10 permits): the 11th request can + // only be blocked on the memory budget. + let permit_provider = SearchPermitProvider::new(100, ByteSize::mb(100)); + let permit_futs = permit_provider.get_permits(make_splits(10, 11)).await; + assert_eq!(permit_futs.len(), 11); + // The 11th can't fit, so the queue is blocked on memory. The reason is queue-level + // and shared, so any waiter's handle reports it — even one deep in the queue. + assert_eq!( + permit_futs[10].block_reason_handle().get(), + Some(PermitBlockReason::MemoryBudget) + ); + } + #[tokio::test] async fn test_get_load_counts_queued_and_active() { let permit_provider = SearchPermitProvider::new(1, ByteSize::mb(100)); diff --git a/quickwit/quickwit-serve/src/search_api/grpc_adapter.rs b/quickwit/quickwit-serve/src/search_api/grpc_adapter.rs index 7fed7539e9b..8492aac01ba 100644 --- a/quickwit/quickwit-serve/src/search_api/grpc_adapter.rs +++ b/quickwit/quickwit-serve/src/search_api/grpc_adapter.rs @@ -15,7 +15,6 @@ use std::sync::Arc; use async_trait::async_trait; -use quickwit_common::tracing_utils::set_current_span_parent_from_metadata; use quickwit_proto::error::convert_to_grpc_result; use quickwit_proto::search::{ GetKvRequest, GetKvResponse, GetLoadRequest, GetLoadResponse, LeafListFieldsRequest, @@ -24,7 +23,6 @@ use quickwit_proto::search::{ }; use quickwit_proto::tonic; use quickwit_search::SearchService; -use tracing::instrument; #[derive(Clone)] pub struct GrpcSearchAdapter(Arc); @@ -35,58 +33,52 @@ impl From> for GrpcSearchAdapter { } } +// The `grpc-request` span installed by the tonic server's `trace_fn` already +// extracts the caller's W3C trace context from the request headers and is the +// active span for each handler, so these methods deliberately add no span of +// their own to avoid collinear duplicates. #[async_trait] impl grpc::SearchService for GrpcSearchAdapter { - #[instrument(skip(self, request))] async fn root_search( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let search_request = request.into_inner(); let search_result = self.0.root_search(search_request).await; convert_to_grpc_result(search_result) } - #[instrument(skip(self, request))] async fn leaf_search( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let leaf_search_request = request.into_inner(); let leaf_search_result = self.0.leaf_search(leaf_search_request).await; convert_to_grpc_result(leaf_search_result) } - #[instrument(skip(self, request))] async fn fetch_docs( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let fetch_docs_request = request.into_inner(); let fetch_docs_result = self.0.fetch_docs(fetch_docs_request).await; convert_to_grpc_result(fetch_docs_result) } - #[instrument(skip(self, request))] async fn root_list_terms( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let search_request = request.into_inner(); let search_result = self.0.root_list_terms(search_request).await; convert_to_grpc_result(search_result) } - #[instrument(skip(self, request))] async fn leaf_list_terms( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let leaf_search_request = request.into_inner(); let leaf_search_result = self.0.leaf_list_terms(leaf_search_request).await; convert_to_grpc_result(leaf_search_result) @@ -101,12 +93,10 @@ impl grpc::SearchService for GrpcSearchAdapter { convert_to_grpc_result(scroll_result) } - #[instrument(skip(self, request))] async fn put_kv( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let put_request = request.into_inner(); self.0.put_kv(put_request).await; Ok(tonic::Response::new( @@ -114,54 +104,45 @@ impl grpc::SearchService for GrpcSearchAdapter { )) } - #[instrument(skip(self, request))] async fn get_kv( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let get_search_after_context_request = request.into_inner(); let payload = self.0.get_kv(get_search_after_context_request).await; let get_response = GetKvResponse { payload }; Ok(tonic::Response::new(get_response)) } - #[instrument(skip(self, request))] async fn report_splits( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let get_search_after_context_request = request.into_inner(); self.0.report_splits(get_search_after_context_request).await; Ok(tonic::Response::new(ReportSplitsResponse {})) } - #[instrument(skip(self, request))] async fn list_fields( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let resp = self.0.root_list_fields(request.into_inner()).await; convert_to_grpc_result(resp) } - #[instrument(skip(self, request))] + async fn leaf_list_fields( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let resp = self.0.leaf_list_fields(request.into_inner()).await; convert_to_grpc_result(resp) } - #[instrument(skip(self, request))] async fn search_plan( &self, request: tonic::Request, ) -> Result, tonic::Status> { - set_current_span_parent_from_metadata(request.metadata()); let search_request = request.into_inner(); let search_result = self.0.search_plan(search_request).await; convert_to_grpc_result(search_result)