1use std::net::SocketAddr;
16use std::sync::Arc;
17use std::time::Duration;
18
19use auth::UserProviderRef;
20use axum::extract::{Request, State};
21use axum::middleware::Next;
22use axum::response::IntoResponse;
23use common_base::Plugins;
24use common_config::Configurable;
25use common_telemetry::{info, warn};
26use meta_client::MetaClientOptions;
27use servers::batcher::{BatchingProtocol, pending_rows_batch_sync_enabled};
28use servers::error::Error as ServerError;
29use servers::grpc::builder::GrpcServerBuilder;
30use servers::grpc::flight::FlightCraftRef;
31use servers::grpc::frontend_grpc_handler::FrontendGrpcHandler;
32use servers::grpc::greptime_handler::GreptimeRequestHandler;
33use servers::grpc::{GrpcOptions, GrpcServer};
34use servers::http::event::LogValidatorRef;
35use servers::http::result::error_result::ErrorResponse;
36use servers::http::utils::router::RouterConfigurator;
37use servers::http::{HttpOptions, HttpServer, HttpServerBuilder};
38use servers::interceptor::LogIngestInterceptorRef;
39use servers::metrics_handler::MetricsHandler;
40use servers::mysql::server::{MysqlServer, MysqlSpawnConfig, MysqlSpawnRef};
41use servers::otel_arrow::OtelArrowServiceHandler;
42use servers::postgres::PostgresServer;
43use servers::request_memory_limiter::ServerMemoryLimiter;
44use servers::server::{Server, ServerHandlers};
45use servers::tls::{ReloadableTlsServerConfig, maybe_watch_server_tls_config};
46use session::context::Channel;
47use snafu::ResultExt;
48use tonic::Status;
49
50use crate::error::{self, Result, StartServerSnafu, TomlFormatSnafu};
51use crate::frontend::FrontendOptions;
52use crate::instance::Instance;
53use crate::service_config::PromStoreOptions;
54
55pub struct Services<T>
56where
57 T: Into<FrontendOptions> + Configurable + Clone,
58{
59 opts: T,
60 instance: Arc<Instance>,
61 grpc_server_builder: Option<GrpcServerBuilder>,
62 http_server_builder: Option<HttpServerBuilder>,
63 plugins: Plugins,
64 flight_handler: Option<FlightCraftRef>,
65 internal_flight_handler: Option<FlightCraftRef>,
66 pub server_memory_limiter: ServerMemoryLimiter,
67}
68
69impl<T> Services<T>
70where
71 T: Into<FrontendOptions> + Configurable + Clone,
72{
73 pub fn new(opts: T, instance: Arc<Instance>, plugins: Plugins) -> Self {
74 let feopts = opts.clone().into();
75 instance.init_logical_batcher(&feopts);
76 let server_memory_limiter = ServerMemoryLimiter::new(
78 feopts.max_in_flight_write_bytes.as_bytes(),
79 feopts.write_bytes_exhausted_policy,
80 );
81
82 Self {
83 opts,
84 instance,
85 grpc_server_builder: None,
86 http_server_builder: None,
87 plugins,
88 flight_handler: None,
89 internal_flight_handler: None,
90 server_memory_limiter,
91 }
92 }
93
94 pub fn grpc_server_builder(
95 &self,
96 opts: &GrpcOptions,
97 request_memory_limiter: ServerMemoryLimiter,
98 ) -> Result<GrpcServerBuilder> {
99 let builder = GrpcServerBuilder::new(opts.as_config(), common_runtime::global_runtime())
100 .with_memory_limiter(request_memory_limiter)
101 .with_tls_config(opts.tls.clone())
102 .context(error::InvalidTlsConfigSnafu)?;
103 Ok(builder)
104 }
105
106 pub fn http_server_builder(
107 &self,
108 opts: &FrontendOptions,
109 request_memory_limiter: ServerMemoryLimiter,
110 ) -> HttpServerBuilder {
111 let mut builder = HttpServerBuilder::new(effective_http_options(opts))
112 .with_batching_protocols(opts.table_batcher_options().protocols.clone())
113 .with_logical_batching_protocols(opts.logical_batcher_options().protocols)
114 .with_memory_limiter(request_memory_limiter)
115 .with_sql_handler(self.instance.clone());
116
117 let validator = self.plugins.get::<LogValidatorRef>();
118 let ingest_interceptor = self.plugins.get::<LogIngestInterceptorRef<ServerError>>();
119 builder =
120 builder.with_log_ingest_handler(self.instance.clone(), validator, ingest_interceptor);
121 builder = builder.with_logs_handler(self.instance.clone());
122
123 if let Some(user_provider) = self.plugins.get::<UserProviderRef>() {
124 builder = builder.with_user_provider(user_provider);
125 }
126
127 if opts.opentsdb.enable {
128 builder = builder.with_opentsdb_handler(self.instance.clone());
129 }
130
131 if opts.influxdb.enable {
132 builder = builder.with_influxdb_handler(self.instance.clone());
133 }
134
135 let prom_store = effective_prom_store_options(opts);
136 if prom_store.enable {
137 let pending_rows_batcher = opts
138 .logical_batcher_options()
139 .protocols
140 .contains(&BatchingProtocol::Prom)
141 .then(|| self.instance.logical_batcher().cloned())
142 .flatten();
143 builder = builder
144 .with_prom_handler(
145 self.instance.clone(),
146 Some(self.instance.clone()),
147 opts.prom_store.with_metric_engine,
148 opts.prom_store.prom_validation_mode,
149 opts.prom_store
150 .experimental_enable_prometheus_native_histogram,
151 pending_rows_batcher,
152 )
153 .with_prometheus_handler(self.instance.clone());
154 }
155
156 if opts.otlp.enable {
157 builder = builder.with_otlp_handler(
158 self.instance.clone(),
159 opts.prom_store.with_metric_engine,
160 opts.otlp.experimental_enable_exponential_histogram,
161 );
162 }
163
164 if opts.jaeger.enable {
165 builder = builder.with_jaeger_handler(self.instance.clone());
166 }
167
168 builder = builder.with_dashboard_handler(self.instance.clone());
169
170 if let Some(configurator) = self.plugins.get::<RouterConfigurator>() {
171 info!("Adding extra router from plugins");
172 builder = builder.with_extra_router(configurator.router());
173 }
174
175 builder.add_layer(axum::middleware::from_fn_with_state(
176 self.instance.clone(),
177 async move |State(state): State<Arc<Instance>>, request: Request, next: Next| {
178 if state.is_suspended() {
179 return ErrorResponse::from_error(servers::error::SuspendedSnafu.build())
180 .into_response();
181 }
182 next.run(request).await
183 },
184 ))
185 }
186
187 pub fn with_grpc_server_builder(self, builder: GrpcServerBuilder) -> Self {
188 Self {
189 grpc_server_builder: Some(builder),
190 ..self
191 }
192 }
193
194 pub fn with_http_server_builder(self, builder: HttpServerBuilder) -> Self {
195 Self {
196 http_server_builder: Some(builder),
197 ..self
198 }
199 }
200
201 pub fn with_flight_handler(self, flight_handler: FlightCraftRef) -> Self {
202 Self {
203 flight_handler: Some(flight_handler),
204 ..self
205 }
206 }
207
208 pub fn with_internal_flight_handler(self, flight_handler: FlightCraftRef) -> Self {
209 Self {
210 internal_flight_handler: Some(flight_handler),
211 ..self
212 }
213 }
214
215 fn build_grpc_server(
216 &mut self,
217 grpc: &GrpcOptions,
218 meta_client: &Option<MetaClientOptions>,
219 name: Option<String>,
220 external: bool,
221 request_memory_limiter: ServerMemoryLimiter,
222 ) -> Result<GrpcServer> {
223 let builder = if let Some(builder) = self.grpc_server_builder.take() {
224 builder
225 } else {
226 self.grpc_server_builder(grpc, request_memory_limiter)?
227 };
228
229 let user_provider = if external {
230 self.plugins.get::<UserProviderRef>()
231 } else {
232 None
234 };
235
236 let runtime = if meta_client.is_none() {
238 Some(builder.runtime().clone())
239 } else {
240 None
241 };
242
243 let greptime_request_handler = GreptimeRequestHandler::new(
244 self.instance.clone(),
245 user_provider.clone(),
246 runtime,
247 grpc.flight_compression,
248 );
249
250 let default_flight_handler = Arc::new(greptime_request_handler.clone()) as FlightCraftRef;
251 let flight_handler = if external {
252 self.flight_handler
253 .clone()
254 .unwrap_or(default_flight_handler)
255 } else {
256 self.internal_flight_handler
257 .clone()
258 .unwrap_or(default_flight_handler)
259 };
260
261 let grpc_server = builder
262 .name(name)
263 .database_handler(greptime_request_handler.clone())
264 .prometheus_handler(self.instance.clone(), user_provider.clone())
265 .otel_arrow_handler(OtelArrowServiceHandler::new(
266 self.instance.clone(),
267 user_provider.clone(),
268 ))
269 .flight_handler(flight_handler)
270 .add_layer(axum::middleware::from_fn_with_state(
271 self.instance.clone(),
272 move |State(state): State<Arc<Instance>>, mut request: Request, next: Next| async move {
273 if state.is_suspended() {
274 let status = Status::from(servers::error::SuspendedSnafu.build());
275 return status.into_http();
276 }
277 if !external {
279 request.extensions_mut().insert(Channel::Internal);
280 }
281 next.run(request).await
282 },
283 ));
284
285 let grpc_server = if !external {
286 let frontend_grpc_handler =
287 FrontendGrpcHandler::new(self.instance.process_manager().clone());
288 grpc_server.frontend_grpc_handler(frontend_grpc_handler)
289 } else {
290 grpc_server
291 }
292 .build();
293
294 Ok(grpc_server)
295 }
296
297 fn build_http_server(
298 &mut self,
299 opts: &FrontendOptions,
300 toml: String,
301 request_memory_limiter: ServerMemoryLimiter,
302 ) -> Result<(HttpServer, Option<HttpServer>)> {
303 let builder = if let Some(builder) = self.http_server_builder.take() {
304 builder
305 } else {
306 self.http_server_builder(opts, request_memory_limiter)
307 };
308
309 let (internal, api) = builder
313 .with_metrics_handler(MetricsHandler)
314 .with_greptime_config_options(toml)
315 .build_servers();
316 Ok((internal, api))
317 }
318
319 pub fn build(mut self) -> Result<ServerHandlers> {
320 let opts = self.opts.clone();
321 let instance = self.instance.clone();
322
323 let toml = opts.to_toml().context(TomlFormatSnafu)?;
324 let opts: FrontendOptions = opts.into();
325
326 let handlers = ServerHandlers::default();
327
328 let user_provider = self.plugins.get::<UserProviderRef>();
329
330 {
331 let grpc_addr = parse_addr(&opts.grpc.bind_addr)?;
333 let grpc_server = self.build_grpc_server(
334 &opts.grpc,
335 &opts.meta_client,
336 None,
337 true,
338 self.server_memory_limiter.clone(),
339 )?;
340 handlers.insert((Box::new(grpc_server), grpc_addr));
341 }
342
343 if let Some(internal_grpc) = &opts.internal_grpc {
344 let grpc_addr = parse_addr(&internal_grpc.bind_addr)?;
346 let grpc_server = self.build_grpc_server(
347 internal_grpc,
348 &opts.meta_client,
349 Some("INTERNAL_GRPC_SERVER".to_string()),
350 false,
351 self.server_memory_limiter.clone(),
352 )?;
353 handlers.insert((Box::new(grpc_server), grpc_addr));
354 }
355
356 {
357 let http_options = &opts.http;
360 let http_addr = parse_addr(&http_options.addr)?;
361 let (http_server, http_api_server) =
362 self.build_http_server(&opts, toml, self.server_memory_limiter.clone())?;
363 handlers.insert((Box::new(http_server), http_addr));
364
365 if let Some(http_api_server) = http_api_server {
366 let http_api_addr = parse_addr(&http_options.api_server_addr)?;
367 info!("HTTP API server is enabled at {}", http_api_addr);
368 handlers.insert((Box::new(http_api_server), http_api_addr));
369 }
370 }
371
372 let table_batcher = opts.table_batcher_options();
373 let batching_enabled = table_batcher.pending_rows_batching_enabled();
374 let mysql_batching =
375 batching_enabled && table_batcher.protocols.contains(&BatchingProtocol::Mysql);
376 let postgres_batching = batching_enabled
377 && table_batcher
378 .protocols
379 .contains(&BatchingProtocol::Postgres);
380
381 if opts.mysql.enable {
382 let opts = &opts.mysql;
384 let mysql_addr = parse_addr(&opts.addr)?;
385
386 let tls_server_config = Arc::new(
387 ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
388 );
389
390 maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
392
393 let mysql_server = MysqlServer::create_server(
394 common_runtime::global_runtime(),
395 Arc::new(MysqlSpawnRef::new(instance.clone(), user_provider.clone())),
396 Arc::new(
397 MysqlSpawnConfig::new(
398 opts.tls.should_force_tls(),
399 tls_server_config,
400 opts.keep_alive.as_secs(),
401 opts.reject_no_database.unwrap_or(false),
402 opts.prepared_stmt_cache_size,
403 )
404 .with_batching_enabled(mysql_batching),
405 ),
406 Some(instance.process_manager().clone()),
407 );
408 handlers.insert((mysql_server, mysql_addr));
409 }
410
411 if opts.postgres.enable {
412 let opts = &opts.postgres;
414 let pg_addr = parse_addr(&opts.addr)?;
415
416 let tls_server_config = Arc::new(
417 ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
418 );
419
420 maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
421
422 let pg_server = Box::new(
423 PostgresServer::new(
424 instance.clone(),
425 opts.tls.should_force_tls(),
426 tls_server_config,
427 opts.keep_alive.as_secs(),
428 common_runtime::global_runtime(),
429 user_provider.clone(),
430 Some(self.instance.process_manager().clone()),
431 )
432 .with_batching_enabled(postgres_batching),
433 ) as Box<dyn Server>;
434
435 handlers.insert((pg_server, pg_addr));
436 }
437
438 Ok(handlers)
439 }
440}
441
442fn effective_prom_store_options(opts: &FrontendOptions) -> PromStoreOptions {
444 let mut prom_store = opts.prom_store.clone();
445 let shared = opts.logical_batcher_options();
446 if shared.protocols.contains(&BatchingProtocol::Prom) {
447 prom_store.pending_rows_flush_interval = shared.pending_rows_flush_interval;
448 prom_store.max_batch_rows = shared.max_batch_rows;
449 prom_store.max_concurrent_flushes = shared.max_concurrent_flushes;
450 prom_store.worker_channel_capacity = shared.worker_channel_capacity;
451 prom_store.max_inflight_requests = shared.max_inflight_requests;
452 prom_store.flow_notification_queue_capacity = shared.flow_notification_queue_capacity;
453 } else {
454 prom_store.pending_rows_flush_interval = Duration::ZERO;
455 }
456 prom_store
457}
458
459fn effective_http_options(opts: &FrontendOptions) -> HttpOptions {
460 effective_http_options_with_sync(opts, pending_rows_batch_sync_enabled())
461}
462
463fn effective_http_options_with_sync(opts: &FrontendOptions, batch_sync: bool) -> HttpOptions {
464 let mut http = opts.http.clone();
465 let prom_store = effective_prom_store_options(opts);
466 let shared = opts.table_batcher_options();
467 let common_enabled = batch_sync
468 && shared.pending_rows_batching_enabled()
469 && shared.protocols.iter().any(|protocol| {
470 !matches!(
471 protocol,
472 BatchingProtocol::Mysql | BatchingProtocol::Postgres
473 ) && (*protocol != BatchingProtocol::Prom
474 || (prom_store.enable && !prom_store.with_metric_engine))
475 });
476 let common_interval = common_enabled.then_some(shared.pending_rows_flush_interval);
477 let prom_interval = (prom_store.pending_rows_batching_enabled() && batch_sync)
478 .then_some(prom_store.pending_rows_flush_interval);
479 let logical = opts.logical_batcher_options();
480 let otlp_interval = (batch_sync
481 && opts.otlp.enable
482 && opts.prom_store.with_metric_engine
483 && logical.protocols.contains(&BatchingProtocol::Otlp)
484 && logical.pending_rows_batching_enabled())
485 .then_some(logical.pending_rows_flush_interval);
486 let Some(flush_interval) = common_interval
487 .into_iter()
488 .chain(prom_interval)
489 .chain(otlp_interval)
490 .max()
491 else {
492 return http;
493 };
494 let fallback_timeout = flush_interval.saturating_add(Duration::from_secs(1));
495 if http.timeout.is_zero() || http.timeout > fallback_timeout {
496 return http;
497 }
498
499 let configured_timeout = http.timeout;
500 http.timeout = fallback_timeout;
501 warn!(
502 ?configured_timeout,
503 ?flush_interval,
504 ?fallback_timeout,
505 "HTTP request timeout is not longer than the pending-row timeout fallback; using the fallback"
506 );
507 http
508}
509
510fn parse_addr(addr: &str) -> Result<SocketAddr> {
511 addr.parse().context(error::ParseAddrSnafu { addr })
512}
513
514#[cfg(test)]
515mod tests {
516 use std::num::NonZeroUsize;
517 use std::sync::Arc;
518 use std::sync::atomic::{AtomicUsize, Ordering};
519 use std::time::Duration;
520
521 use api::v1::HealthCheckRequest;
522 use api::v1::health_check_client::HealthCheckClient;
523 use api::v1::meta::Role;
524 use arrow_flight::{FlightData, PutResult, Ticket};
525 use async_trait::async_trait;
526 use auth::{UserProviderRef, static_user_provider_from_option};
527 use client::{Client, Database};
528 use meta_client::client::MetaClientBuilder;
529 use servers::grpc::GRPC_SERVER;
530 use servers::grpc::flight::{FlightCraft, FlightCraftRef, TonicStream};
531 use tonic::{Code, Request, Response, Status, Streaming};
532
533 use crate::instance::builder::FrontendBuilder;
534 use crate::server::*;
535
536 #[tokio::test]
537 async fn test_logical_batcher_shared_without_prom_endpoint() {
538 use crate::service_config::BatcherOptions;
539 let mut options = FrontendOptions::default();
540 options.prom_store.enable = false;
541 options.pending_rows_batcher.logical_table = Some(BatcherOptions {
542 protocols: vec![BatchingProtocol::Otlp],
543 pending_rows_flush_interval: Duration::from_millis(5),
544 ..Default::default()
545 });
546 let meta_client = Arc::new(
547 MetaClientBuilder::new(0, Role::Frontend)
548 .enable_procedure()
549 .build(),
550 );
551 let instance = Arc::new(
552 FrontendBuilder::new_test(&options, meta_client)
553 .try_build()
554 .await
555 .unwrap(),
556 );
557 let services = Services::new(options.clone(), instance.clone(), Plugins::default());
558 let batcher = instance.logical_batcher().unwrap().clone();
559 instance.init_logical_batcher(&options);
560 assert!(Arc::ptr_eq(&batcher, instance.logical_batcher().unwrap()));
561 let weak = Arc::downgrade(&instance);
562 drop(services);
563 drop(instance);
564 assert!(
565 weak.upgrade().is_none(),
566 "batcher must not retain its schema owner"
567 );
568 }
569
570 #[test]
571 fn test_logical_batcher_http_timeout_and_prom_disable() {
572 use crate::service_config::pending_rows_batcher::BatcherOptions;
573 let mut opts = FrontendOptions::default();
574 opts.http.timeout = Duration::from_secs(1);
575 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
576 opts.pending_rows_batcher.logical_table = Some(BatcherOptions {
577 protocols: vec![BatchingProtocol::Otlp],
578 pending_rows_flush_interval: Duration::from_secs(5),
579 ..Default::default()
580 });
581 assert!(!effective_prom_store_options(&opts).pending_rows_batching_enabled());
582 opts.prom_store.enable = false;
585 assert_eq!(
586 effective_http_options_with_sync(&opts, true).timeout,
587 Duration::from_secs(6)
588 );
589 assert_eq!(
590 effective_http_options_with_sync(&opts, false).timeout,
591 opts.http.timeout
592 );
593 opts.prom_store.with_metric_engine = false;
594 assert_eq!(
595 effective_http_options_with_sync(&opts, false).timeout,
596 opts.http.timeout
597 );
598 opts.prom_store.with_metric_engine = true;
599 opts.pending_rows_batcher
600 .logical_table
601 .as_mut()
602 .unwrap()
603 .protocols
604 .clear();
605 assert_eq!(
606 effective_http_options_with_sync(&opts, true).timeout,
607 opts.http.timeout
608 );
609 }
610
611 #[test]
612 fn test_effective_prom_batching_controls() {
613 for (selected, shared_enabled, metric_engine, prom_enabled) in [
615 (true, true, true, true),
616 (false, true, true, true),
617 (true, false, true, true),
618 (true, true, false, true),
619 (true, true, true, false),
620 ] {
621 let mut opts = FrontendOptions::default();
622 opts.http.timeout = Duration::from_millis(1);
623 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
624 opts.prom_store.with_metric_engine = metric_engine;
625 opts.prom_store.enable = prom_enabled;
626 opts.prom_store
627 .experimental_enable_prometheus_native_histogram = true;
628 let shared = &mut opts.pending_rows_batcher.table;
629 shared.protocols = vec![if selected {
630 BatchingProtocol::Prom
631 } else {
632 BatchingProtocol::Influxdb
633 }];
634 shared.pending_rows_flush_interval = if shared_enabled {
635 Duration::from_secs(5)
636 } else {
637 Duration::ZERO
638 };
639 shared.max_batch_rows = 7;
640 shared.max_concurrent_flushes = 3;
641 shared.worker_channel_capacity = 11;
642 shared.max_inflight_requests = 13;
643 shared.flow_notification_queue_capacity = NonZeroUsize::new(17).unwrap();
644
645 let mut expected = opts.prom_store.clone();
646 if selected && shared_enabled {
647 expected.pending_rows_flush_interval = shared.pending_rows_flush_interval;
648 expected.max_batch_rows = shared.max_batch_rows;
649 expected.max_concurrent_flushes = shared.max_concurrent_flushes;
650 expected.worker_channel_capacity = shared.worker_channel_capacity;
651 expected.max_inflight_requests = shared.max_inflight_requests;
652 expected.flow_notification_queue_capacity = shared.flow_notification_queue_capacity;
653 }
654 let actual = effective_prom_store_options(&opts);
655 assert_eq!(actual, expected);
656 assert_eq!(
657 actual.pending_rows_batching_enabled(),
658 metric_engine && prom_enabled
659 );
660 }
661 }
662
663 #[test]
664 fn test_http_timeout_covers_synchronous_batchers() {
665 for (
667 protocols,
668 metric_engine,
669 batch_sync,
670 shared_secs,
671 legacy_secs,
672 timeout_secs,
673 expected_secs,
674 ) in [
675 (vec![BatchingProtocol::Prom], false, false, 5, 2, 1, 1),
676 (vec![BatchingProtocol::Mysql], false, false, 5, 0, 1, 1),
677 (vec![BatchingProtocol::Postgres], false, false, 5, 0, 1, 1),
678 (vec![BatchingProtocol::Mysql], false, true, 5, 0, 1, 1),
679 (vec![BatchingProtocol::Postgres], false, true, 5, 0, 1, 1),
680 (vec![BatchingProtocol::Prom], true, false, 5, 2, 1, 1),
681 (vec![BatchingProtocol::Prom], true, true, 5, 2, 1, 6),
682 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 1, 1),
683 (vec![BatchingProtocol::Influxdb], true, true, 5, 8, 1, 9),
684 (vec![BatchingProtocol::Influxdb], true, true, 8, 5, 1, 9),
685 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 0, 0),
686 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 10, 10),
687 (vec![BatchingProtocol::Prom], false, false, 0, 2, 1, 1),
688 (vec![], true, false, 5, 2, 1, 1),
689 (vec![], true, true, 5, 2, 1, 3),
690 ] {
691 let mut opts = FrontendOptions::default();
692 opts.http.timeout = Duration::from_secs(timeout_secs);
693 opts.prom_store.with_metric_engine = metric_engine;
694 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(legacy_secs);
695 opts.pending_rows_batcher.table.protocols = protocols;
696 opts.pending_rows_batcher.table.pending_rows_flush_interval =
697 Duration::from_secs(shared_secs);
698 assert_eq!(
699 effective_http_options_with_sync(&opts, batch_sync).timeout,
700 Duration::from_secs(expected_secs)
701 );
702 }
703 }
704
705 struct CountingFlightCraft {
706 inner: FlightCraftRef,
707 do_get_calls: AtomicUsize,
708 do_put_calls: AtomicUsize,
709 }
710
711 #[async_trait]
712 impl FlightCraft for CountingFlightCraft {
713 async fn do_get(
714 &self,
715 request: Request<Ticket>,
716 ) -> std::result::Result<Response<TonicStream<FlightData>>, Status> {
717 assert_eq!(
718 request.extensions().get::<Channel>(),
719 Some(&Channel::Internal)
720 );
721 self.do_get_calls.fetch_add(1, Ordering::SeqCst);
722 self.inner.do_get(request).await
723 }
724
725 async fn do_put(
726 &self,
727 request: Request<Streaming<FlightData>>,
728 ) -> std::result::Result<Response<TonicStream<PutResult>>, Status> {
729 assert_eq!(
730 request.extensions().get::<Channel>(),
731 Some(&Channel::Internal)
732 );
733 self.do_put_calls.fetch_add(1, Ordering::SeqCst);
734 self.inner.do_put(request).await
735 }
736 }
737
738 #[test]
739 fn test_effective_http_timeout_for_pending_rows() {
740 let cases = [
741 ("disabled timeout", 0, 5000, true, true, 0),
742 ("disabled prom store", 1000, 5000, false, true, 1000),
743 ("disabled metric engine", 1000, 5000, true, false, 1000),
744 ("disabled batching", 1000, 0, true, true, 1000),
745 ("timeout below flush interval", 4000, 5000, true, true, 6000),
746 (
747 "timeout equals flush interval",
748 5000,
749 5000,
750 true,
751 true,
752 6000,
753 ),
754 ("timeout below fallback", 5500, 5000, true, true, 6000),
755 ("timeout equals fallback", 6000, 5000, true, true, 6000),
756 ("timeout above fallback", 7000, 5000, true, true, 7000),
757 ];
758
759 for (name, timeout, flush_interval, enable, with_metric_engine, expected) in cases {
760 let mut opts = FrontendOptions::default();
761 opts.http.timeout = Duration::from_millis(timeout);
762 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(flush_interval);
763 opts.prom_store.enable = enable;
764 opts.prom_store.with_metric_engine = with_metric_engine;
765
766 assert_eq!(
767 Duration::from_millis(expected),
768 effective_http_options_with_sync(&opts, true).timeout,
769 "{name}"
770 );
771 }
772 }
773
774 #[test]
775 fn test_effective_http_timeout_skips_fallback_in_async_batch_mode() {
776 let mut opts = FrontendOptions::default();
780 opts.http.timeout = Duration::from_millis(1000);
781 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
782
783 assert_eq!(
784 Duration::from_millis(1000),
785 effective_http_options_with_sync(&opts, false).timeout,
786 );
787 assert_eq!(
788 Duration::from_millis(6000),
789 effective_http_options_with_sync(&opts, true).timeout,
790 );
791 }
792
793 #[test]
794 fn test_invalid_shared_batching_preserves_prom_store_options() {
795 type KnobMutator = fn(&mut FrontendOptions);
796 let cases: [KnobMutator; 5] = [
797 |opts| opts.pending_rows_batcher.table.max_concurrent_flushes = usize::MAX,
798 |opts| opts.pending_rows_batcher.table.worker_channel_capacity = usize::MAX,
799 |opts| opts.pending_rows_batcher.table.max_inflight_requests = usize::MAX,
800 |opts| {
801 opts.pending_rows_batcher
802 .table
803 .flow_notification_queue_capacity = NonZeroUsize::new(usize::MAX).unwrap()
804 },
805 |opts| opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::MAX,
806 ];
807 for invalidate in cases {
808 let mut opts = FrontendOptions::default();
809 opts.http.timeout = Duration::from_secs(1);
810 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(5);
811 opts.pending_rows_batcher.table.protocols =
812 vec![BatchingProtocol::Prom, BatchingProtocol::Influxdb];
813 opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::from_secs(10);
814 invalidate(&mut opts);
815 assert!(
816 !opts
817 .pending_rows_batcher
818 .table
819 .pending_rows_batching_enabled()
820 );
821 assert_eq!(opts.prom_store, effective_prom_store_options(&opts));
822 assert_eq!(
823 Duration::from_secs(6),
824 effective_http_options_with_sync(&opts, true).timeout
825 );
826 opts.prom_store.pending_rows_flush_interval = Duration::ZERO;
827 assert_eq!(
828 Duration::from_secs(1),
829 effective_http_options_with_sync(&opts, true).timeout
830 );
831 }
832 }
833
834 #[test]
835 fn test_effective_http_timeout_skips_fallback_when_batcher_disabled() {
836 type KnobMutator = fn(&mut FrontendOptions);
840 let cases: [(&str, KnobMutator); 9] = [
841 ("oversized flush concurrency", |opts| {
842 opts.prom_store.max_concurrent_flushes = usize::MAX
843 }),
844 ("oversized worker channel", |opts| {
845 opts.prom_store.worker_channel_capacity = usize::MAX
846 }),
847 ("oversized inflight limit", |opts| {
848 opts.prom_store.max_inflight_requests = usize::MAX
849 }),
850 ("oversized flow queue", |opts| {
851 opts.prom_store.flow_notification_queue_capacity =
852 std::num::NonZeroUsize::new(usize::MAX).unwrap()
853 }),
854 ("unrepresentable deadline", |opts| {
855 opts.prom_store.pending_rows_flush_interval = Duration::MAX
856 }),
857 ("zero max_batch_rows", |opts| {
858 opts.prom_store.max_batch_rows = 0
859 }),
860 ("zero max_concurrent_flushes", |opts| {
861 opts.prom_store.max_concurrent_flushes = 0
862 }),
863 ("zero worker_channel_capacity", |opts| {
864 opts.prom_store.worker_channel_capacity = 0
865 }),
866 ("zero max_inflight_requests", |opts| {
867 opts.prom_store.max_inflight_requests = 0
868 }),
869 ];
870
871 for (name, disable_batcher) in cases {
872 let mut opts = FrontendOptions::default();
873 opts.http.timeout = Duration::from_millis(1000);
874 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
875 disable_batcher(&mut opts);
876
877 assert_eq!(
878 Duration::from_millis(1000),
879 effective_http_options_with_sync(&opts, true).timeout,
880 "{name}"
881 );
882 }
883 }
884
885 #[tokio::test]
886 async fn test_database_sql_authentication_differs_between_public_and_internal_grpc() {
887 let options = FrontendOptions {
888 http: HttpOptions {
889 addr: "127.0.0.1:0".to_string(),
890 ..Default::default()
891 },
892 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
893 internal_grpc: Some(GrpcOptions::default().with_bind_addr("127.0.0.1:0")),
894 mysql: crate::service_config::MysqlOptions {
895 enable: false,
896 ..Default::default()
897 },
898 postgres: crate::service_config::PostgresOptions {
899 enable: false,
900 ..Default::default()
901 },
902 ..Default::default()
903 };
904 let meta_client = Arc::new(
905 MetaClientBuilder::new(0, Role::Frontend)
906 .enable_procedure()
907 .build(),
908 );
909 let instance = Arc::new(
910 FrontendBuilder::new_test(&options, meta_client)
911 .try_build()
912 .await
913 .unwrap(),
914 );
915 let plugins = Plugins::new();
916 let provider =
917 static_user_provider_from_option("static_user_provider:cmd:greptime=greptime").unwrap();
918 plugins.insert::<UserProviderRef>(Arc::new(provider));
919 let public_flight_handler = Arc::new(GreptimeRequestHandler::new(
920 instance.clone(),
921 plugins.get::<UserProviderRef>(),
922 None,
923 options.grpc.flight_compression,
924 )) as FlightCraftRef;
925 let internal_flight_handler = Arc::new(CountingFlightCraft {
926 inner: Arc::new(GreptimeRequestHandler::new(
927 instance.clone(),
928 None,
929 None,
930 options.grpc.flight_compression,
931 )),
932 do_get_calls: AtomicUsize::new(0),
933 do_put_calls: AtomicUsize::new(0),
934 });
935 let internal_flight_handler_ref = internal_flight_handler.clone() as FlightCraftRef;
936 let mut services = Services::new(options, instance, plugins)
937 .with_flight_handler(public_flight_handler)
938 .with_internal_flight_handler(internal_flight_handler_ref)
939 .build()
940 .unwrap();
941
942 services.start_all().await.unwrap();
943 let public_addr = services.addr(GRPC_SERVER).unwrap();
944 let internal_addr = services.addr("INTERNAL_GRPC_SERVER").unwrap();
945 let public_database = Database::new(
946 "greptime",
947 "public",
948 Client::with_urls([public_addr.to_string()]),
949 );
950 let internal_database = Database::new(
951 "greptime",
952 "public",
953 Client::with_urls([internal_addr.to_string()]),
954 );
955
956 let internal_result = internal_database.sql("SELECT 1").await;
957 let put_result = internal_database
958 .do_put(Box::pin(futures::stream::empty()))
959 .await;
960 let public_result = public_database.sql("SELECT 1").await;
961
962 services.shutdown_all().await.unwrap();
963
964 assert!(internal_result.is_ok());
965 assert!(put_result.is_ok());
966 assert_eq!(
967 1,
968 internal_flight_handler.do_get_calls.load(Ordering::SeqCst)
969 );
970 assert_eq!(
971 1,
972 internal_flight_handler.do_put_calls.load(Ordering::SeqCst)
973 );
974 assert_eq!(
975 Some(Code::Unauthenticated),
976 public_result
977 .as_ref()
978 .err()
979 .and_then(|err| err.tonic_code())
980 );
981 }
982
983 #[tokio::test]
984 async fn test_services_builder_health_check_is_reachable() {
985 let options = FrontendOptions {
987 http: HttpOptions {
988 addr: "127.0.0.1:0".to_string(),
989 ..Default::default()
990 },
991 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
992 mysql: crate::service_config::MysqlOptions {
993 enable: false,
994 ..Default::default()
995 },
996 postgres: crate::service_config::PostgresOptions {
997 enable: false,
998 ..Default::default()
999 },
1000 ..Default::default()
1001 };
1002 let meta_client = Arc::new(
1003 MetaClientBuilder::new(0, Role::Frontend)
1004 .enable_procedure()
1005 .build(),
1006 );
1007 let instance = Arc::new(
1008 FrontendBuilder::new_test(&options, meta_client)
1009 .try_build()
1010 .await
1011 .unwrap(),
1012 );
1013 let mut services = Services::new(options, instance, Default::default())
1014 .build()
1015 .unwrap();
1016
1017 services.start_all().await.unwrap();
1019 let addr = services.addr(GRPC_SERVER).unwrap();
1020 let health_check = HealthCheckClient::connect(format!("http://{addr}"))
1021 .await
1022 .unwrap()
1023 .health_check(HealthCheckRequest {})
1024 .await;
1025 services.shutdown_all().await.unwrap();
1026
1027 assert!(health_check.is_ok());
1029 }
1030}