1use std::collections::HashMap;
18use std::future::Future;
19use std::str::FromStr;
20use std::sync::{Arc, RwLock};
21use std::time::Instant;
22
23use api::helper::request_type;
24use api::v1::greptime_request::Request as QueryRequest;
25use api::v1::query_request::Query;
26use api::v1::{GreptimeRequest, QueryRequest as ApiQueryRequest, RequestHeader};
27use auth::UserProviderRef;
28use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
29use common_catalog::parse_catalog_and_schema_from_db_string;
30use common_error::ext::ErrorExt;
31use common_error::status_code::StatusCode;
32use common_grpc::flight::do_put::DoPutResponse;
33use common_query::Output;
34use common_runtime::Runtime;
35use common_runtime::runtime::RuntimeTrait;
36use common_session::ReadPreference;
37use common_telemetry::tracing_context::{FutureExt, TracingContext};
38use common_telemetry::{debug, error, tracing, warn};
39use common_time::timezone::parse_timezone;
40use futures_util::StreamExt;
41use session::context::{Channel, QueryContextBuilder, QueryContextRef};
42use session::hints::{INSERT_SKIP_WAL_HINT, READ_PREFERENCE_HINT, is_reserved_extension_key};
43use snafu::{OptionExt, ResultExt};
44use tokio::sync::mpsc;
45use tokio::sync::mpsc::error::TrySendError;
46use tokio_util::task::AbortOnDropHandle;
47use tonic::Status;
48
49use crate::error::{InvalidQuerySnafu, JoinTaskSnafu, Result, UnknownHintSnafu};
50use crate::grpc::flight::PutRecordBatchRequestStream;
51use crate::grpc::{FlightCompression, TonicResult, context_auth};
52use crate::metrics::{self, METRIC_SERVER_GRPC_DB_REQUEST_TIMER};
53use crate::query_handler::grpc::ServerGrpcQueryHandlerRef;
54
55#[derive(Clone)]
56pub struct GreptimeRequestHandler {
57 handler: ServerGrpcQueryHandlerRef,
58 pub(crate) user_provider: Option<UserProviderRef>,
59 runtime: Option<Runtime>,
60 pub(crate) flight_compression: FlightCompression,
61}
62
63impl GreptimeRequestHandler {
64 pub fn new(
65 handler: ServerGrpcQueryHandlerRef,
66 user_provider: Option<UserProviderRef>,
67 runtime: Option<Runtime>,
68 flight_compression: FlightCompression,
69 ) -> Self {
70 Self {
71 handler,
72 user_provider,
73 runtime,
74 flight_compression,
75 }
76 }
77
78 #[tracing::instrument(skip_all, fields(protocol = "grpc", request_type = get_request_type(&request)))]
79 pub(crate) async fn handle_request(
80 &self,
81 request: GreptimeRequest,
82 hints: Vec<(String, String)>,
83 channel: Channel,
84 ) -> Result<Output> {
85 let header = request.header.as_ref();
86 let hints = crate::hint_headers::validate_public_hints(hints)?;
87 let query_ctx = create_query_context(channel, header, hints, HashMap::new())?;
88 let query = request.request.context(InvalidQuerySnafu {
89 reason: "Expecting non-empty GreptimeRequest.",
90 })?;
91 self.authenticate_request_with_query_ctx(request.header.as_ref(), &query_ctx)
92 .await?;
93 self.handle_request_with_query_ctx(query, query_ctx).await
94 }
95
96 pub(crate) async fn authenticate_request_with_query_ctx(
97 &self,
98 header: Option<&RequestHeader>,
99 query_ctx: &QueryContextRef,
100 ) -> Result<()> {
101 let user_info = context_auth::auth(self.user_provider.clone(), header, query_ctx).await?;
102 query_ctx.set_current_user(user_info);
103 Ok(())
104 }
105
106 pub(crate) fn handle_request_with_query_ctx(
107 &self,
108 query: QueryRequest,
109 query_ctx: QueryContextRef,
110 ) -> impl Future<Output = Result<Output>> + Send + 'static {
111 let handler = self.handler.clone();
112 let runtime = self.runtime.clone();
113 let cancel_on_drop = matches!(
115 &query,
116 QueryRequest::Query(ApiQueryRequest {
117 query: Some(Query::LogicalPlan(_) | Query::PromRangeQuery(_)),
118 })
119 );
120 let request_type = request_type(&query).to_string();
121 let db = query_ctx.get_db_string();
122 let timer = RequestTimer::new(db.clone(), request_type);
123 let tracing_context = TracingContext::from_current_span();
124
125 async move {
126 let result_future = async move {
127 handler
128 .do_query(query, query_ctx)
129 .trace(tracing_context.attach(tracing::info_span!(
130 "GreptimeRequestHandler::handle_request_runtime"
131 )))
132 .await
133 .map_err(|e| {
134 if e.status_code().should_log_error() {
135 let root_error = e.root_cause().unwrap_or(&e);
136 error!(e; "Failed to handle request, error: {}", root_error.to_string());
137 } else {
138 debug!("Failed to handle request, err: {:?}", e);
140 }
141 e
142 })
143 };
144
145 match runtime {
146 Some(runtime) => {
147 let task = runtime.spawn(result_future);
148 let result = if cancel_on_drop {
149 AbortOnDropHandle::new(task).await
150 } else {
151 task.await
154 };
155 result.context(JoinTaskSnafu).inspect_err(|e| {
156 timer.record(e.status_code());
157 })?
158 }
159 None => result_future.await,
160 }
161 }
162 }
163
164 pub(crate) async fn put_record_batches(
165 &self,
166 stream: PutRecordBatchRequestStream,
167 result_sender: mpsc::Sender<TonicResult<DoPutResponse>>,
168 query_ctx: QueryContextRef,
169 ) {
170 let handler = self.handler.clone();
171 let runtime = self
172 .runtime
173 .clone()
174 .unwrap_or_else(common_runtime::global_runtime);
175 runtime.spawn(async move {
176 let mut result_stream = handler.handle_put_record_batch_stream(stream, query_ctx);
177
178 while let Some(result) = result_stream.next().await {
179 match &result {
180 Ok(response) => {
181 metrics::GRPC_BULK_INSERT_ELAPSED.observe(response.elapsed_secs());
183 }
184 Err(e) => {
185 error!(e; "Failed to handle flight record batches");
186 }
187 }
188
189 if let Err(e) = result_sender.try_send(result.map_err(Status::from))
190 && let TrySendError::Closed(_) = e
191 {
192 warn!(r#""DoPut" client maybe unreachable, abort handling its message"#);
193 break;
194 }
195 }
196 });
197 }
198}
199
200pub fn get_request_type(request: &GreptimeRequest) -> &'static str {
201 request
202 .request
203 .as_ref()
204 .map(request_type)
205 .unwrap_or_default()
206}
207
208pub(crate) fn create_query_context(
211 channel: Channel,
212 header: Option<&RequestHeader>,
213 extensions: Vec<(String, String)>,
214 snapshot_seqs: HashMap<u64, u64>,
215) -> Result<QueryContextRef> {
216 let (catalog, schema) = header
217 .map(|header| {
218 if !header.dbname.is_empty() {
221 parse_catalog_and_schema_from_db_string(&header.dbname)
222 } else {
223 (
224 if !header.catalog.is_empty() {
225 header.catalog.to_lowercase()
226 } else {
227 DEFAULT_CATALOG_NAME.to_string()
228 },
229 if !header.schema.is_empty() {
230 header.schema.to_lowercase()
231 } else {
232 DEFAULT_SCHEMA_NAME.to_string()
233 },
234 )
235 }
236 })
237 .unwrap_or_else(|| {
238 (
239 DEFAULT_CATALOG_NAME.to_string(),
240 DEFAULT_SCHEMA_NAME.to_string(),
241 )
242 });
243 let timezone = parse_timezone(header.map(|h| h.timezone.as_str()));
244 let mut ctx_builder = QueryContextBuilder::default()
245 .current_catalog(catalog)
246 .current_schema(schema)
247 .timezone(timezone)
248 .channel(channel)
249 .snapshot_seqs(Arc::new(RwLock::new(snapshot_seqs)));
250
251 for (key, value) in extensions {
252 match key.as_str() {
253 READ_PREFERENCE_HINT => {
254 let Ok(read_preference) = ReadPreference::from_str(&value) else {
255 return UnknownHintSnafu {
256 hint: format!("{key}={value}"),
257 }
258 .fail();
259 };
260 ctx_builder = ctx_builder.read_preference(read_preference);
261 }
262 INSERT_SKIP_WAL_HINT => {
263 let skip_wal = value.parse::<bool>().map_err(|_| {
264 UnknownHintSnafu {
265 hint: format!("{key}={value}"),
266 }
267 .build()
268 })?;
269 ctx_builder = ctx_builder.skip_wal(skip_wal);
270 }
271 _ if is_reserved_extension_key(&key) => {
272 debug!(
273 key = key.as_str(),
274 "Ignoring reserved external query context extension key"
275 );
276 }
277 _ => {
278 ctx_builder = ctx_builder.set_extension(key, value);
279 }
280 }
281 }
282 Ok(ctx_builder.build().into())
283}
284
285pub(crate) struct RequestTimer {
289 start: Instant,
290 db: String,
291 request_type: String,
292 status_code: StatusCode,
293}
294
295impl RequestTimer {
296 pub fn new(db: String, request_type: String) -> RequestTimer {
298 RequestTimer {
299 start: Instant::now(),
300 db,
301 request_type,
302 status_code: StatusCode::Success,
303 }
304 }
305
306 pub fn record(mut self, status_code: StatusCode) {
308 self.status_code = status_code;
309 }
310}
311
312impl Drop for RequestTimer {
313 fn drop(&mut self) {
314 METRIC_SERVER_GRPC_DB_REQUEST_TIMER
315 .with_label_values(&[
316 self.db.as_str(),
317 self.request_type.as_str(),
318 self.status_code.as_ref(),
319 ])
320 .observe(self.start.elapsed().as_secs_f64());
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use std::error::Error as StdError;
327 use std::result::Result as StdResult;
328 use std::time::Duration;
329
330 use async_trait::async_trait;
331 use chrono::FixedOffset;
332 use common_error::ext::BoxedError;
333 use common_error::{GREPTIME_DB_HEADER_ERROR_CODE, GREPTIME_DB_HEADER_ERROR_RETRY_HINT};
334 use common_time::Timezone;
335 use futures::stream::{self, BoxStream};
336 use query::options::FLOW_SCHEDULED_TIME_MILLIS;
337 use session::context::QueryContext;
338 use session::hints::{
339 INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY, READ_PREFERENCE_EXTENSION_KEY,
340 REMOTE_QUERY_ID_EXTENSION_KEY,
341 };
342 use snafu::ResultExt;
343 use tokio::sync::Notify;
344 use tokio::time::timeout;
345 use tonic::Code;
346
347 use super::*;
348 use crate::error::{ExecuteGrpcRequestSnafu, InvalidParameterSnafu};
349 use crate::query_handler::grpc::GrpcQueryHandler;
350
351 struct BlockingQueryHandler {
352 started: Arc<Notify>,
353 finished: Arc<Notify>,
354 resume: Arc<Notify>,
355 completed: Arc<Notify>,
356 }
357
358 struct QueryFinished(Arc<Notify>);
359
360 impl Drop for QueryFinished {
361 fn drop(&mut self) {
362 self.0.notify_one();
363 }
364 }
365
366 #[async_trait]
367 impl GrpcQueryHandler for BlockingQueryHandler {
368 async fn do_query(&self, _: QueryRequest, _: QueryContextRef) -> Result<Output> {
369 let _finished = QueryFinished(self.finished.clone());
370 self.started.notify_one();
371 self.resume.notified().await;
372 self.completed.notify_one();
373 Ok(Output::new_with_affected_rows(1))
374 }
375
376 fn handle_put_record_batch_stream(
377 &self,
378 _: PutRecordBatchRequestStream,
379 _: QueryContextRef,
380 ) -> BoxStream<'static, Result<DoPutResponse>> {
381 Box::pin(stream::empty())
382 }
383 }
384
385 async fn check_request_cancellation(
386 request: QueryRequest,
387 runtime: Option<Runtime>,
388 cancels: bool,
389 ) -> StdResult<(), Box<dyn StdError + Send + Sync>> {
390 let started = Arc::new(Notify::new());
391 let finished = Arc::new(Notify::new());
392 let resume = Arc::new(Notify::new());
393 let completed = Arc::new(Notify::new());
394 let handler = GreptimeRequestHandler::new(
395 Arc::new(BlockingQueryHandler {
396 started: started.clone(),
397 finished: finished.clone(),
398 resume: resume.clone(),
399 completed: completed.clone(),
400 }),
401 None,
402 runtime,
403 FlightCompression::default(),
404 );
405 let task = AbortOnDropHandle::new(tokio::spawn(
406 handler.handle_request_with_query_ctx(request, QueryContext::arc()),
407 ));
408 timeout(Duration::from_secs(5), started.notified()).await?;
409 task.abort();
410 assert!(task.await.is_err_and(|error| error.is_cancelled()));
411 if !cancels {
412 resume.notify_one();
414 timeout(Duration::from_secs(5), completed.notified()).await?;
415 }
416 timeout(Duration::from_secs(5), finished.notified()).await?;
417 Ok(())
418 }
419
420 #[tokio::test]
421 async fn test_drop_read_request_cancels_execution()
422 -> StdResult<(), Box<dyn StdError + Send + Sync>> {
423 for query in [
425 Query::LogicalPlan(Vec::new()),
426 Query::PromRangeQuery(Default::default()),
427 ] {
428 let request = QueryRequest::Query(ApiQueryRequest { query: Some(query) });
429 for runtime in [None, Some(common_runtime::query_runtime())] {
430 check_request_cancellation(request.clone(), runtime, true).await?;
431 }
432 }
433 Ok(())
434 }
435
436 #[tokio::test]
437 async fn test_drop_other_request_preserves_spawned_execution()
438 -> StdResult<(), Box<dyn StdError + Send + Sync>> {
439 let requests = [
440 QueryRequest::Inserts(Default::default()),
441 QueryRequest::RowInserts(Default::default()),
442 QueryRequest::Deletes(Default::default()),
443 QueryRequest::RowDeletes(Default::default()),
444 QueryRequest::Ddl(Default::default()),
445 QueryRequest::Query(ApiQueryRequest {
446 query: Some(Query::InsertIntoPlan(Default::default())),
447 }),
448 QueryRequest::Query(ApiQueryRequest { query: None }),
449 QueryRequest::Query(ApiQueryRequest {
450 query: Some(Query::Sql("SELECT * FROM numbers".into())),
451 }),
452 QueryRequest::Query(ApiQueryRequest {
453 query: Some(Query::Sql("INSERT INTO t VALUES (1)".into())),
454 }),
455 QueryRequest::Query(ApiQueryRequest {
456 query: Some(Query::Sql(
457 "CREATE TABLE t (ts TIMESTAMP TIME INDEX)".into(),
458 )),
459 }),
460 ];
461 for request in requests {
462 for runtime in [None, Some(common_runtime::query_runtime())] {
463 let cancels = runtime.is_none();
465 check_request_cancellation(request.clone(), runtime, cancels).await?;
466 }
467 }
468 Ok(())
469 }
470
471 #[test]
472 fn test_create_query_context_typed_skip_wal() {
473 let ctx = create_query_context(Channel::Grpc, None, vec![], HashMap::new()).unwrap();
474 assert!(!ctx.skip_wal());
475 let legacy = create_query_context(
476 Channel::Grpc,
477 None,
478 vec![("skip_wal".to_string(), "true".to_string())],
479 HashMap::new(),
480 )
481 .unwrap();
482 assert!(!legacy.skip_wal());
483 assert_eq!(legacy.extension("skip_wal"), Some("true"));
484 for (value, expected) in [("true", true), ("false", false)] {
485 let ctx = create_query_context(
486 Channel::Grpc,
487 None,
488 vec![(INSERT_SKIP_WAL_HINT.to_string(), value.to_string())],
489 HashMap::new(),
490 )
491 .unwrap();
492 assert_eq!(ctx.skip_wal(), expected);
493 assert_eq!(ctx.extension(INSERT_SKIP_WAL_HINT), None);
494 }
495 for value in ["", "TRUE", "1", "invalid"] {
496 assert!(
497 create_query_context(
498 Channel::Grpc,
499 None,
500 vec![(INSERT_SKIP_WAL_HINT.to_string(), value.to_string())],
501 HashMap::new()
502 )
503 .is_err()
504 );
505 }
506 let ctx = create_query_context(
507 Channel::Grpc,
508 None,
509 vec![
510 (INSERT_SKIP_WAL_HINT.to_string(), "true".to_string()),
511 (INSERT_SKIP_WAL_HINT.to_string(), "false".to_string()),
512 ],
513 HashMap::new(),
514 )
515 .unwrap();
516 assert!(!ctx.skip_wal());
517 assert_eq!(ctx.extension(INSERT_SKIP_WAL_HINT), None);
518 }
519
520 #[test]
521 fn test_create_query_context_read_preference_duplicates() {
522 for (values, valid) in [
523 (["leader", "LEADER"], true),
524 (["invalid", "leader"], false),
525 (["leader", "invalid"], false),
526 ] {
527 let result = create_query_context(
528 Channel::Grpc,
529 None,
530 values
531 .into_iter()
532 .map(|value| (READ_PREFERENCE_HINT.to_string(), value.to_string()))
533 .collect(),
534 HashMap::new(),
535 );
536 if valid {
537 let ctx = result.unwrap();
538 assert!(matches!(ctx.read_preference(), ReadPreference::Leader));
539 assert_eq!(ctx.extension(READ_PREFERENCE_HINT), None);
540 } else {
541 assert!(result.is_err());
542 }
543 }
544 }
545
546 #[test]
547 fn test_create_query_context() {
548 let header = RequestHeader {
549 catalog: "cat-a-log".to_string(),
550 timezone: "+01:00".to_string(),
551 ..Default::default()
552 };
553 let query_context = create_query_context(
554 Channel::Unknown,
555 Some(&header),
556 vec![
557 ("auto_create_table".to_string(), "true".to_string()),
558 ("read_preference".to_string(), "leader".to_string()),
559 (
560 READ_PREFERENCE_EXTENSION_KEY.to_string(),
561 "follower".to_string(),
562 ),
563 (
564 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
565 "spoofed".to_string(),
566 ),
567 (
568 INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY.to_string(),
569 "spoofed-regs".to_string(),
570 ),
571 (
572 FLOW_SCHEDULED_TIME_MILLIS.to_string(),
573 "1700000000000".to_string(),
574 ),
575 ],
576 HashMap::from([(7, 88)]),
577 )
578 .unwrap();
579 assert_eq!(query_context.get_snapshot(7), Some(88));
580 assert_eq!(query_context.current_catalog(), "cat-a-log");
581 assert_eq!(query_context.current_schema(), DEFAULT_SCHEMA_NAME);
582 assert_eq!(
583 query_context.timezone(),
584 Timezone::Offset(FixedOffset::east_opt(3600).unwrap())
585 );
586 assert!(matches!(
587 query_context.read_preference(),
588 ReadPreference::Leader
589 ));
590 assert_eq!(query_context.extension("auto_create_table"), Some("true"));
591 assert_eq!(query_context.extension(READ_PREFERENCE_HINT), None);
592 assert_eq!(query_context.extension(READ_PREFERENCE_EXTENSION_KEY), None);
593 assert_ne!(query_context.remote_query_id(), Some("spoofed"));
594 assert!(
595 query_context
596 .extension(INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY)
597 .is_none()
598 );
599 assert_eq!(
600 query_context.extension(FLOW_SCHEDULED_TIME_MILLIS),
601 Some("1700000000000")
602 );
603 }
604
605 #[test]
606 fn test_create_query_context_ignores_remote_query_id_extension() {
607 let query_context = create_query_context(
608 Channel::Grpc,
609 None,
610 vec![(
611 REMOTE_QUERY_ID_EXTENSION_KEY.to_string(),
612 "spoofed-query-id".to_string(),
613 )],
614 HashMap::new(),
615 )
616 .unwrap();
617
618 assert_ne!(query_context.remote_query_id(), Some("spoofed-query-id"));
619 assert_eq!(
620 query_context.extension(REMOTE_QUERY_ID_EXTENSION_KEY),
621 query_context.remote_query_id()
622 );
623 }
624
625 #[test]
626 fn test_record_batch_error_to_status_preserves_error_details() {
627 let inner = InvalidParameterSnafu {
628 reason: "Column not found, column: new_col",
629 }
630 .build();
631 let err = Err::<(), _>(BoxedError::new(inner))
632 .context(ExecuteGrpcRequestSnafu)
633 .unwrap_err();
634
635 let status = Status::from(err);
636
637 assert_eq!(status.code(), Code::InvalidArgument);
638 assert!(
639 status
640 .message()
641 .contains("Column not found, column: new_col")
642 );
643 assert!(
644 status
645 .message()
646 .contains("Invalid request parameter: Column not found")
647 );
648 assert!(
649 status
650 .metadata()
651 .contains_key(GREPTIME_DB_HEADER_ERROR_CODE)
652 );
653 assert!(
654 status
655 .metadata()
656 .contains_key(GREPTIME_DB_HEADER_ERROR_RETRY_HINT)
657 );
658 }
659}