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