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