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 meta_client::client::MetaClientBuilder;
524 use servers::grpc::GRPC_SERVER;
525 use servers::grpc::flight::{FlightCraft, FlightCraftRef, TonicStream};
526 use tonic::{Code, Request, Response, Status, Streaming};
527
528 use crate::instance::builder::FrontendBuilder;
529 use crate::server::*;
530
531 #[tokio::test]
532 async fn test_logical_batcher_shared_without_prom_endpoint() {
533 use crate::service_config::BatcherOptions;
534 let mut options = FrontendOptions::default();
535 options.prom_store.enable = false;
536 options.pending_rows_batcher.logical_table = Some(BatcherOptions {
537 protocols: vec![BatchingProtocol::Otlp],
538 pending_rows_flush_interval: Duration::from_millis(5),
539 ..Default::default()
540 });
541 let meta_client = Arc::new(
542 MetaClientBuilder::new(0, Role::Frontend)
543 .enable_procedure()
544 .build(),
545 );
546 let instance = Arc::new(
547 FrontendBuilder::new_test(&options, meta_client)
548 .try_build()
549 .await
550 .unwrap(),
551 );
552 let services = Services::new(options.clone(), instance.clone(), Plugins::default());
553 let batcher = instance.logical_batcher().unwrap().clone();
554 instance.init_logical_batcher(&options);
555 assert!(Arc::ptr_eq(&batcher, instance.logical_batcher().unwrap()));
556 let weak = Arc::downgrade(&instance);
557 drop(services);
558 drop(instance);
559 assert!(
560 weak.upgrade().is_none(),
561 "batcher must not retain its schema owner"
562 );
563 }
564
565 #[test]
566 fn test_logical_batcher_http_timeout_and_prom_disable() {
567 use crate::service_config::pending_rows_batcher::BatcherOptions;
568 let mut opts = FrontendOptions::default();
569 opts.http.timeout = Duration::from_secs(1);
570 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
571 opts.pending_rows_batcher.logical_table = Some(BatcherOptions {
572 protocols: vec![BatchingProtocol::Otlp],
573 pending_rows_flush_interval: Duration::from_secs(5),
574 ..Default::default()
575 });
576 assert!(!effective_prom_store_options(&opts).pending_rows_batching_enabled());
577 opts.prom_store.enable = false;
580 assert_eq!(
581 effective_http_options_with_sync(&opts, true).timeout,
582 Duration::from_secs(6)
583 );
584 assert_eq!(
585 effective_http_options_with_sync(&opts, false).timeout,
586 opts.http.timeout
587 );
588 opts.prom_store.with_metric_engine = false;
589 assert_eq!(
590 effective_http_options_with_sync(&opts, false).timeout,
591 opts.http.timeout
592 );
593 opts.prom_store.with_metric_engine = true;
594 opts.pending_rows_batcher
595 .logical_table
596 .as_mut()
597 .unwrap()
598 .protocols
599 .clear();
600 assert_eq!(
601 effective_http_options_with_sync(&opts, true).timeout,
602 opts.http.timeout
603 );
604 }
605
606 #[test]
607 fn test_effective_prom_batching_controls() {
608 for (selected, shared_enabled, metric_engine, prom_enabled) in [
610 (true, true, true, true),
611 (false, true, true, true),
612 (true, false, true, true),
613 (true, true, false, true),
614 (true, true, true, false),
615 ] {
616 let mut opts = FrontendOptions::default();
617 opts.http.timeout = Duration::from_millis(1);
618 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
619 opts.prom_store.with_metric_engine = metric_engine;
620 opts.prom_store.enable = prom_enabled;
621 let shared = &mut opts.pending_rows_batcher.table;
622 shared.protocols = vec![if selected {
623 BatchingProtocol::Prom
624 } else {
625 BatchingProtocol::Influxdb
626 }];
627 shared.pending_rows_flush_interval = if shared_enabled {
628 Duration::from_secs(5)
629 } else {
630 Duration::ZERO
631 };
632 shared.max_batch_rows = 7;
633 shared.max_concurrent_flushes = 3;
634 shared.worker_channel_capacity = 11;
635 shared.max_inflight_requests = 13;
636 shared.flow_notification_queue_capacity = NonZeroUsize::new(17).unwrap();
637
638 let mut expected = opts.prom_store.clone();
639 if selected && shared_enabled {
640 expected.pending_rows_flush_interval = shared.pending_rows_flush_interval;
641 expected.max_batch_rows = shared.max_batch_rows;
642 expected.max_concurrent_flushes = shared.max_concurrent_flushes;
643 expected.worker_channel_capacity = shared.worker_channel_capacity;
644 expected.max_inflight_requests = shared.max_inflight_requests;
645 expected.flow_notification_queue_capacity = shared.flow_notification_queue_capacity;
646 }
647 let actual = effective_prom_store_options(&opts);
648 assert_eq!(actual, expected);
649 assert_eq!(
650 actual.pending_rows_batching_enabled(),
651 metric_engine && prom_enabled
652 );
653 }
654 }
655
656 #[test]
657 fn test_http_timeout_covers_synchronous_batchers() {
658 for (
660 protocols,
661 metric_engine,
662 batch_sync,
663 shared_secs,
664 legacy_secs,
665 timeout_secs,
666 expected_secs,
667 ) in [
668 (vec![BatchingProtocol::Prom], false, false, 5, 2, 1, 1),
669 (vec![BatchingProtocol::Mysql], false, false, 5, 0, 1, 1),
670 (vec![BatchingProtocol::Postgres], false, false, 5, 0, 1, 1),
671 (vec![BatchingProtocol::Mysql], false, true, 5, 0, 1, 1),
672 (vec![BatchingProtocol::Postgres], false, true, 5, 0, 1, 1),
673 (vec![BatchingProtocol::Prom], true, false, 5, 2, 1, 1),
674 (vec![BatchingProtocol::Prom], true, true, 5, 2, 1, 6),
675 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 1, 1),
676 (vec![BatchingProtocol::Influxdb], true, true, 5, 8, 1, 9),
677 (vec![BatchingProtocol::Influxdb], true, true, 8, 5, 1, 9),
678 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 0, 0),
679 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 10, 10),
680 (vec![BatchingProtocol::Prom], false, false, 0, 2, 1, 1),
681 (vec![], true, false, 5, 2, 1, 1),
682 (vec![], true, true, 5, 2, 1, 3),
683 ] {
684 let mut opts = FrontendOptions::default();
685 opts.http.timeout = Duration::from_secs(timeout_secs);
686 opts.prom_store.with_metric_engine = metric_engine;
687 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(legacy_secs);
688 opts.pending_rows_batcher.table.protocols = protocols;
689 opts.pending_rows_batcher.table.pending_rows_flush_interval =
690 Duration::from_secs(shared_secs);
691 assert_eq!(
692 effective_http_options_with_sync(&opts, batch_sync).timeout,
693 Duration::from_secs(expected_secs)
694 );
695 }
696 }
697
698 struct CountingFlightCraft {
699 inner: FlightCraftRef,
700 do_get_calls: AtomicUsize,
701 do_put_calls: AtomicUsize,
702 }
703
704 #[async_trait]
705 impl FlightCraft for CountingFlightCraft {
706 async fn do_get(
707 &self,
708 request: Request<Ticket>,
709 ) -> std::result::Result<Response<TonicStream<FlightData>>, Status> {
710 assert_eq!(
711 request.extensions().get::<Channel>(),
712 Some(&Channel::Internal)
713 );
714 self.do_get_calls.fetch_add(1, Ordering::SeqCst);
715 self.inner.do_get(request).await
716 }
717
718 async fn do_put(
719 &self,
720 request: Request<Streaming<FlightData>>,
721 ) -> std::result::Result<Response<TonicStream<PutResult>>, Status> {
722 assert_eq!(
723 request.extensions().get::<Channel>(),
724 Some(&Channel::Internal)
725 );
726 self.do_put_calls.fetch_add(1, Ordering::SeqCst);
727 self.inner.do_put(request).await
728 }
729 }
730
731 #[test]
732 fn test_effective_http_timeout_for_pending_rows() {
733 let cases = [
734 ("disabled timeout", 0, 5000, true, true, 0),
735 ("disabled prom store", 1000, 5000, false, true, 1000),
736 ("disabled metric engine", 1000, 5000, true, false, 1000),
737 ("disabled batching", 1000, 0, true, true, 1000),
738 ("timeout below flush interval", 4000, 5000, true, true, 6000),
739 (
740 "timeout equals flush interval",
741 5000,
742 5000,
743 true,
744 true,
745 6000,
746 ),
747 ("timeout below fallback", 5500, 5000, true, true, 6000),
748 ("timeout equals fallback", 6000, 5000, true, true, 6000),
749 ("timeout above fallback", 7000, 5000, true, true, 7000),
750 ];
751
752 for (name, timeout, flush_interval, enable, with_metric_engine, expected) in cases {
753 let mut opts = FrontendOptions::default();
754 opts.http.timeout = Duration::from_millis(timeout);
755 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(flush_interval);
756 opts.prom_store.enable = enable;
757 opts.prom_store.with_metric_engine = with_metric_engine;
758
759 assert_eq!(
760 Duration::from_millis(expected),
761 effective_http_options_with_sync(&opts, true).timeout,
762 "{name}"
763 );
764 }
765 }
766
767 #[test]
768 fn test_effective_http_timeout_skips_fallback_in_async_batch_mode() {
769 let mut opts = FrontendOptions::default();
773 opts.http.timeout = Duration::from_millis(1000);
774 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
775
776 assert_eq!(
777 Duration::from_millis(1000),
778 effective_http_options_with_sync(&opts, false).timeout,
779 );
780 assert_eq!(
781 Duration::from_millis(6000),
782 effective_http_options_with_sync(&opts, true).timeout,
783 );
784 }
785
786 #[test]
787 fn test_invalid_shared_batching_preserves_prom_store_options() {
788 type KnobMutator = fn(&mut FrontendOptions);
789 let cases: [KnobMutator; 5] = [
790 |opts| opts.pending_rows_batcher.table.max_concurrent_flushes = usize::MAX,
791 |opts| opts.pending_rows_batcher.table.worker_channel_capacity = usize::MAX,
792 |opts| opts.pending_rows_batcher.table.max_inflight_requests = usize::MAX,
793 |opts| {
794 opts.pending_rows_batcher
795 .table
796 .flow_notification_queue_capacity = NonZeroUsize::new(usize::MAX).unwrap()
797 },
798 |opts| opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::MAX,
799 ];
800 for invalidate in cases {
801 let mut opts = FrontendOptions::default();
802 opts.http.timeout = Duration::from_secs(1);
803 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(5);
804 opts.pending_rows_batcher.table.protocols =
805 vec![BatchingProtocol::Prom, BatchingProtocol::Influxdb];
806 opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::from_secs(10);
807 invalidate(&mut opts);
808 assert!(
809 !opts
810 .pending_rows_batcher
811 .table
812 .pending_rows_batching_enabled()
813 );
814 assert_eq!(opts.prom_store, effective_prom_store_options(&opts));
815 assert_eq!(
816 Duration::from_secs(6),
817 effective_http_options_with_sync(&opts, true).timeout
818 );
819 opts.prom_store.pending_rows_flush_interval = Duration::ZERO;
820 assert_eq!(
821 Duration::from_secs(1),
822 effective_http_options_with_sync(&opts, true).timeout
823 );
824 }
825 }
826
827 #[test]
828 fn test_effective_http_timeout_skips_fallback_when_batcher_disabled() {
829 type KnobMutator = fn(&mut FrontendOptions);
833 let cases: [(&str, KnobMutator); 9] = [
834 ("oversized flush concurrency", |opts| {
835 opts.prom_store.max_concurrent_flushes = usize::MAX
836 }),
837 ("oversized worker channel", |opts| {
838 opts.prom_store.worker_channel_capacity = usize::MAX
839 }),
840 ("oversized inflight limit", |opts| {
841 opts.prom_store.max_inflight_requests = usize::MAX
842 }),
843 ("oversized flow queue", |opts| {
844 opts.prom_store.flow_notification_queue_capacity =
845 std::num::NonZeroUsize::new(usize::MAX).unwrap()
846 }),
847 ("unrepresentable deadline", |opts| {
848 opts.prom_store.pending_rows_flush_interval = Duration::MAX
849 }),
850 ("zero max_batch_rows", |opts| {
851 opts.prom_store.max_batch_rows = 0
852 }),
853 ("zero max_concurrent_flushes", |opts| {
854 opts.prom_store.max_concurrent_flushes = 0
855 }),
856 ("zero worker_channel_capacity", |opts| {
857 opts.prom_store.worker_channel_capacity = 0
858 }),
859 ("zero max_inflight_requests", |opts| {
860 opts.prom_store.max_inflight_requests = 0
861 }),
862 ];
863
864 for (name, disable_batcher) in cases {
865 let mut opts = FrontendOptions::default();
866 opts.http.timeout = Duration::from_millis(1000);
867 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
868 disable_batcher(&mut opts);
869
870 assert_eq!(
871 Duration::from_millis(1000),
872 effective_http_options_with_sync(&opts, true).timeout,
873 "{name}"
874 );
875 }
876 }
877
878 #[tokio::test]
879 async fn test_database_sql_authentication_differs_between_public_and_internal_grpc() {
880 let options = FrontendOptions {
881 http: HttpOptions {
882 addr: "127.0.0.1:0".to_string(),
883 ..Default::default()
884 },
885 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
886 internal_grpc: Some(GrpcOptions::default().with_bind_addr("127.0.0.1:0")),
887 mysql: crate::service_config::MysqlOptions {
888 enable: false,
889 ..Default::default()
890 },
891 postgres: crate::service_config::PostgresOptions {
892 enable: false,
893 ..Default::default()
894 },
895 ..Default::default()
896 };
897 let meta_client = Arc::new(
898 MetaClientBuilder::new(0, Role::Frontend)
899 .enable_procedure()
900 .build(),
901 );
902 let instance = Arc::new(
903 FrontendBuilder::new_test(&options, meta_client)
904 .try_build()
905 .await
906 .unwrap(),
907 );
908 let plugins = Plugins::new();
909 let provider =
910 static_user_provider_from_option("static_user_provider:cmd:greptime=greptime").unwrap();
911 plugins.insert::<UserProviderRef>(Arc::new(provider));
912 let public_flight_handler = Arc::new(GreptimeRequestHandler::new(
913 instance.clone(),
914 plugins.get::<UserProviderRef>(),
915 None,
916 options.grpc.flight_compression,
917 )) as FlightCraftRef;
918 let internal_flight_handler = Arc::new(CountingFlightCraft {
919 inner: Arc::new(GreptimeRequestHandler::new(
920 instance.clone(),
921 None,
922 None,
923 options.grpc.flight_compression,
924 )),
925 do_get_calls: AtomicUsize::new(0),
926 do_put_calls: AtomicUsize::new(0),
927 });
928 let internal_flight_handler_ref = internal_flight_handler.clone() as FlightCraftRef;
929 let mut services = Services::new(options, instance, plugins)
930 .with_flight_handler(public_flight_handler)
931 .with_internal_flight_handler(internal_flight_handler_ref)
932 .build()
933 .unwrap();
934
935 services.start_all().await.unwrap();
936 let public_addr = services.addr(GRPC_SERVER).unwrap();
937 let internal_addr = services.addr("INTERNAL_GRPC_SERVER").unwrap();
938 let public_database = Database::new(
939 "greptime",
940 "public",
941 Client::with_urls([public_addr.to_string()]),
942 );
943 let internal_database = Database::new(
944 "greptime",
945 "public",
946 Client::with_urls([internal_addr.to_string()]),
947 );
948
949 let internal_result = internal_database.sql("SELECT 1").await;
950 let put_result = internal_database
951 .do_put(Box::pin(futures::stream::empty()))
952 .await;
953 let public_result = public_database.sql("SELECT 1").await;
954
955 services.shutdown_all().await.unwrap();
956
957 assert!(internal_result.is_ok());
958 assert!(put_result.is_ok());
959 assert_eq!(
960 1,
961 internal_flight_handler.do_get_calls.load(Ordering::SeqCst)
962 );
963 assert_eq!(
964 1,
965 internal_flight_handler.do_put_calls.load(Ordering::SeqCst)
966 );
967 assert_eq!(
968 Some(Code::Unauthenticated),
969 public_result
970 .as_ref()
971 .err()
972 .and_then(|err| err.tonic_code())
973 );
974 }
975
976 #[tokio::test]
977 async fn test_services_builder_health_check_is_reachable() {
978 let options = FrontendOptions {
980 http: HttpOptions {
981 addr: "127.0.0.1:0".to_string(),
982 ..Default::default()
983 },
984 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
985 mysql: crate::service_config::MysqlOptions {
986 enable: false,
987 ..Default::default()
988 },
989 postgres: crate::service_config::PostgresOptions {
990 enable: false,
991 ..Default::default()
992 },
993 ..Default::default()
994 };
995 let meta_client = Arc::new(
996 MetaClientBuilder::new(0, Role::Frontend)
997 .enable_procedure()
998 .build(),
999 );
1000 let instance = Arc::new(
1001 FrontendBuilder::new_test(&options, meta_client)
1002 .try_build()
1003 .await
1004 .unwrap(),
1005 );
1006 let mut services = Services::new(options, instance, Default::default())
1007 .build()
1008 .unwrap();
1009
1010 services.start_all().await.unwrap();
1012 let addr = services.addr(GRPC_SERVER).unwrap();
1013 let health_check = HealthCheckClient::connect(format!("http://{addr}"))
1014 .await
1015 .unwrap()
1016 .health_check(HealthCheckRequest {})
1017 .await;
1018 services.shutdown_all().await.unwrap();
1019
1020 assert!(health_check.is_ok());
1022 }
1023}