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 with_metric_engine = effective_prom_store_options(&opts).with_metric_engine;
413 let opts = &opts.postgres;
414 let pg_addr = parse_addr(&opts.addr)?;
415
416 let tls_server_config = Arc::new(
417 ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
418 );
419
420 maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
421
422 let pg_server = Box::new(
423 PostgresServer::new(
424 instance.clone(),
425 instance.clone(),
426 opts.tls.should_force_tls(),
427 tls_server_config,
428 opts.keep_alive.as_secs(),
429 common_runtime::global_runtime(),
430 user_provider.clone(),
431 Some(self.instance.process_manager().clone()),
432 )
433 .with_influxdb_handler(instance.clone())
434 .with_otlp_handler(instance.clone(), with_metric_engine)
435 .with_memory_limiter(self.server_memory_limiter.clone())
436 .with_batching_enabled(postgres_batching),
437 ) as Box<dyn Server>;
438
439 handlers.insert((pg_server, pg_addr));
440 }
441
442 Ok(handlers)
443 }
444}
445
446fn effective_prom_store_options(opts: &FrontendOptions) -> PromStoreOptions {
448 let mut prom_store = opts.prom_store.clone();
449 let shared = opts.logical_batcher_options();
450 if shared.protocols.contains(&BatchingProtocol::Prom) {
451 prom_store.pending_rows_flush_interval = shared.pending_rows_flush_interval;
452 prom_store.max_batch_rows = shared.max_batch_rows;
453 prom_store.max_concurrent_flushes = shared.max_concurrent_flushes;
454 prom_store.worker_channel_capacity = shared.worker_channel_capacity;
455 prom_store.max_inflight_requests = shared.max_inflight_requests;
456 prom_store.flow_notification_queue_capacity = shared.flow_notification_queue_capacity;
457 } else {
458 prom_store.pending_rows_flush_interval = Duration::ZERO;
459 }
460 prom_store
461}
462
463pub fn effective_http_options(opts: &FrontendOptions) -> HttpOptions {
465 effective_http_options_with_sync(opts, pending_rows_batch_sync_enabled())
466}
467
468fn effective_http_options_with_sync(opts: &FrontendOptions, batch_sync: bool) -> HttpOptions {
469 let mut http = opts.http.clone();
470 let prom_store = effective_prom_store_options(opts);
471 let shared = opts.table_batcher_options();
472 let common_enabled = batch_sync
473 && shared.pending_rows_batching_enabled()
474 && shared.protocols.iter().any(|protocol| {
475 !matches!(
476 protocol,
477 BatchingProtocol::Mysql | BatchingProtocol::Postgres
478 ) && (*protocol != BatchingProtocol::Prom
479 || (prom_store.enable && !prom_store.with_metric_engine))
480 });
481 let common_interval = common_enabled.then_some(shared.pending_rows_flush_interval);
482 let prom_interval = (prom_store.pending_rows_batching_enabled() && batch_sync)
483 .then_some(prom_store.pending_rows_flush_interval);
484 let logical = opts.logical_batcher_options();
485 let otlp_interval = (batch_sync
486 && opts.otlp.enable
487 && opts.prom_store.with_metric_engine
488 && logical.protocols.contains(&BatchingProtocol::Otlp)
489 && logical.pending_rows_batching_enabled())
490 .then_some(logical.pending_rows_flush_interval);
491 let Some(flush_interval) = common_interval
492 .into_iter()
493 .chain(prom_interval)
494 .chain(otlp_interval)
495 .max()
496 else {
497 return http;
498 };
499 let fallback_timeout = flush_interval.saturating_add(Duration::from_secs(1));
500 if http.timeout.is_zero() || http.timeout > fallback_timeout {
501 return http;
502 }
503
504 let configured_timeout = http.timeout;
505 http.timeout = fallback_timeout;
506 warn!(
507 ?configured_timeout,
508 ?flush_interval,
509 ?fallback_timeout,
510 "HTTP request timeout is not longer than the pending-row timeout fallback; using the fallback"
511 );
512 http
513}
514
515fn parse_addr(addr: &str) -> Result<SocketAddr> {
516 addr.parse().context(error::ParseAddrSnafu { addr })
517}
518
519#[cfg(test)]
520mod tests {
521 use std::num::NonZeroUsize;
522 use std::sync::Arc;
523 use std::sync::atomic::{AtomicUsize, Ordering};
524 use std::time::Duration;
525
526 use api::v1::HealthCheckRequest;
527 use api::v1::health_check_client::HealthCheckClient;
528 use api::v1::meta::Role;
529 use arrow_flight::{FlightData, PutResult, Ticket};
530 use async_trait::async_trait;
531 use auth::{UserProviderRef, static_user_provider_from_option};
532 use client::{Client, Database};
533 use common_grpc::channel_manager::ChannelManager;
534 use meta_client::client::MetaClientBuilder;
535 use reqwest::header::{ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_REQUEST_METHOD, ORIGIN};
536 use servers::grpc::GRPC_SERVER;
537 use servers::grpc::flight::{FlightCraft, FlightCraftRef, TonicStream};
538 use tonic::{Code, Request, Response, Status, Streaming};
539
540 use crate::instance::builder::FrontendBuilder;
541 use crate::server::*;
542
543 #[tokio::test]
544 async fn test_logical_batcher_shared_without_prom_endpoint() {
545 use crate::service_config::BatcherOptions;
546 let mut options = FrontendOptions::default();
547 options.prom_store.enable = false;
548 options.pending_rows_batcher.logical_table = Some(BatcherOptions {
549 protocols: vec![BatchingProtocol::Otlp],
550 pending_rows_flush_interval: Duration::from_millis(5),
551 ..Default::default()
552 });
553 let meta_client = Arc::new(
554 MetaClientBuilder::new(0, Role::Frontend)
555 .enable_procedure()
556 .build(),
557 );
558 let instance = Arc::new(
559 FrontendBuilder::new_test(&options, meta_client)
560 .try_build()
561 .await
562 .unwrap(),
563 );
564 let mut disabled_options = options.clone();
565 disabled_options.otlp.enable = false;
566 instance.init_logical_batcher(&disabled_options);
567 assert!(instance.logical_batcher().is_none());
568
569 let services = Services::new(options.clone(), instance.clone(), Plugins::default());
570 let batcher = instance.logical_batcher().unwrap().clone();
571 instance.init_logical_batcher(&options);
572 assert!(Arc::ptr_eq(&batcher, instance.logical_batcher().unwrap()));
573 let weak = Arc::downgrade(&instance);
574 drop(services);
575 drop(instance);
576 assert!(
577 weak.upgrade().is_none(),
578 "batcher must not retain its schema owner"
579 );
580 }
581
582 #[test]
583 fn test_logical_batcher_http_timeout_and_prom_disable() {
584 use crate::service_config::pending_rows_batcher::BatcherOptions;
585 let mut opts = FrontendOptions::default();
586 opts.http.timeout = Duration::from_secs(1);
587 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
588 opts.pending_rows_batcher.logical_table = Some(BatcherOptions {
589 protocols: vec![BatchingProtocol::Otlp],
590 pending_rows_flush_interval: Duration::from_secs(5),
591 ..Default::default()
592 });
593 assert!(!effective_prom_store_options(&opts).pending_rows_batching_enabled());
594 opts.prom_store.enable = false;
597 assert_eq!(
598 effective_http_options_with_sync(&opts, true).timeout,
599 Duration::from_secs(6)
600 );
601 assert_eq!(
602 effective_http_options_with_sync(&opts, false).timeout,
603 opts.http.timeout
604 );
605 opts.prom_store.with_metric_engine = false;
606 assert_eq!(
607 effective_http_options_with_sync(&opts, false).timeout,
608 opts.http.timeout
609 );
610 opts.prom_store.with_metric_engine = true;
611 opts.pending_rows_batcher
612 .logical_table
613 .as_mut()
614 .unwrap()
615 .protocols
616 .clear();
617 assert_eq!(
618 effective_http_options_with_sync(&opts, true).timeout,
619 opts.http.timeout
620 );
621 }
622
623 #[test]
624 fn test_effective_prom_batching_controls() {
625 for (selected, shared_enabled, metric_engine, prom_enabled) in [
627 (true, true, true, true),
628 (false, true, true, true),
629 (true, false, true, true),
630 (true, true, false, true),
631 (true, true, true, false),
632 ] {
633 let mut opts = FrontendOptions::default();
634 opts.http.timeout = Duration::from_millis(1);
635 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
636 opts.prom_store.with_metric_engine = metric_engine;
637 opts.prom_store.enable = prom_enabled;
638 let shared = &mut opts.pending_rows_batcher.table;
639 shared.protocols = vec![if selected {
640 BatchingProtocol::Prom
641 } else {
642 BatchingProtocol::Influxdb
643 }];
644 shared.pending_rows_flush_interval = if shared_enabled {
645 Duration::from_secs(5)
646 } else {
647 Duration::ZERO
648 };
649 shared.max_batch_rows = 7;
650 shared.max_concurrent_flushes = 3;
651 shared.worker_channel_capacity = 11;
652 shared.max_inflight_requests = 13;
653 shared.flow_notification_queue_capacity = NonZeroUsize::new(17).unwrap();
654
655 let mut expected = opts.prom_store.clone();
656 if selected && shared_enabled {
657 expected.pending_rows_flush_interval = shared.pending_rows_flush_interval;
658 expected.max_batch_rows = shared.max_batch_rows;
659 expected.max_concurrent_flushes = shared.max_concurrent_flushes;
660 expected.worker_channel_capacity = shared.worker_channel_capacity;
661 expected.max_inflight_requests = shared.max_inflight_requests;
662 expected.flow_notification_queue_capacity = shared.flow_notification_queue_capacity;
663 }
664 let actual = effective_prom_store_options(&opts);
665 assert_eq!(actual, expected);
666 assert_eq!(
667 actual.pending_rows_batching_enabled(),
668 metric_engine && prom_enabled
669 );
670 }
671 }
672
673 #[test]
674 fn test_http_timeout_covers_synchronous_batchers() {
675 for (
677 protocols,
678 metric_engine,
679 batch_sync,
680 shared_secs,
681 legacy_secs,
682 timeout_secs,
683 expected_secs,
684 ) in [
685 (vec![BatchingProtocol::Prom], false, false, 5, 2, 1, 1),
686 (vec![BatchingProtocol::Mysql], false, false, 5, 0, 1, 1),
687 (vec![BatchingProtocol::Postgres], false, false, 5, 0, 1, 1),
688 (vec![BatchingProtocol::Mysql], false, true, 5, 0, 1, 1),
689 (vec![BatchingProtocol::Postgres], false, true, 5, 0, 1, 1),
690 (vec![BatchingProtocol::Prom], true, false, 5, 2, 1, 1),
691 (vec![BatchingProtocol::Prom], true, true, 5, 2, 1, 6),
692 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 1, 1),
693 (vec![BatchingProtocol::Influxdb], true, true, 5, 8, 1, 9),
694 (vec![BatchingProtocol::Influxdb], true, true, 8, 5, 1, 9),
695 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 0, 0),
696 (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 10, 10),
697 (vec![BatchingProtocol::Prom], false, false, 0, 2, 1, 1),
698 (vec![], true, false, 5, 2, 1, 1),
699 (vec![], true, true, 5, 2, 1, 3),
700 ] {
701 let mut opts = FrontendOptions::default();
702 opts.http.timeout = Duration::from_secs(timeout_secs);
703 opts.prom_store.with_metric_engine = metric_engine;
704 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(legacy_secs);
705 opts.pending_rows_batcher.table.protocols = protocols;
706 opts.pending_rows_batcher.table.pending_rows_flush_interval =
707 Duration::from_secs(shared_secs);
708 assert_eq!(
709 effective_http_options_with_sync(&opts, batch_sync).timeout,
710 Duration::from_secs(expected_secs)
711 );
712 }
713 }
714
715 struct CountingFlightCraft {
716 inner: FlightCraftRef,
717 do_get_calls: AtomicUsize,
718 do_put_calls: AtomicUsize,
719 }
720
721 #[async_trait]
722 impl FlightCraft for CountingFlightCraft {
723 async fn do_get(
724 &self,
725 request: Request<Ticket>,
726 ) -> std::result::Result<Response<TonicStream<FlightData>>, Status> {
727 assert_eq!(
728 request.extensions().get::<Channel>(),
729 Some(&Channel::Internal)
730 );
731 self.do_get_calls.fetch_add(1, Ordering::SeqCst);
732 self.inner.do_get(request).await
733 }
734
735 async fn do_put(
736 &self,
737 request: Request<Streaming<FlightData>>,
738 ) -> std::result::Result<Response<TonicStream<PutResult>>, Status> {
739 assert_eq!(
740 request.extensions().get::<Channel>(),
741 Some(&Channel::Internal)
742 );
743 self.do_put_calls.fetch_add(1, Ordering::SeqCst);
744 self.inner.do_put(request).await
745 }
746 }
747
748 #[test]
749 fn test_effective_http_timeout_for_pending_rows() {
750 let cases = [
751 ("disabled timeout", 0, 5000, true, true, 0),
752 ("disabled prom store", 1000, 5000, false, true, 1000),
753 ("disabled metric engine", 1000, 5000, true, false, 1000),
754 ("disabled batching", 1000, 0, true, true, 1000),
755 ("timeout below flush interval", 4000, 5000, true, true, 6000),
756 (
757 "timeout equals flush interval",
758 5000,
759 5000,
760 true,
761 true,
762 6000,
763 ),
764 ("timeout below fallback", 5500, 5000, true, true, 6000),
765 ("timeout equals fallback", 6000, 5000, true, true, 6000),
766 ("timeout above fallback", 7000, 5000, true, true, 7000),
767 ];
768
769 for (name, timeout, flush_interval, enable, with_metric_engine, expected) in cases {
770 let mut opts = FrontendOptions::default();
771 opts.http.timeout = Duration::from_millis(timeout);
772 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(flush_interval);
773 opts.prom_store.enable = enable;
774 opts.prom_store.with_metric_engine = with_metric_engine;
775
776 assert_eq!(
777 Duration::from_millis(expected),
778 effective_http_options_with_sync(&opts, true).timeout,
779 "{name}"
780 );
781 }
782 }
783
784 #[test]
785 fn test_effective_http_timeout_skips_fallback_in_async_batch_mode() {
786 let mut opts = FrontendOptions::default();
790 opts.http.timeout = Duration::from_millis(1000);
791 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
792
793 assert_eq!(
794 Duration::from_millis(1000),
795 effective_http_options_with_sync(&opts, false).timeout,
796 );
797 assert_eq!(
798 Duration::from_millis(6000),
799 effective_http_options_with_sync(&opts, true).timeout,
800 );
801 }
802
803 #[test]
804 fn test_invalid_shared_batching_preserves_prom_store_options() {
805 type KnobMutator = fn(&mut FrontendOptions);
806 let cases: [KnobMutator; 5] = [
807 |opts| opts.pending_rows_batcher.table.max_concurrent_flushes = usize::MAX,
808 |opts| opts.pending_rows_batcher.table.worker_channel_capacity = usize::MAX,
809 |opts| opts.pending_rows_batcher.table.max_inflight_requests = usize::MAX,
810 |opts| {
811 opts.pending_rows_batcher
812 .table
813 .flow_notification_queue_capacity = NonZeroUsize::new(usize::MAX).unwrap()
814 },
815 |opts| opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::MAX,
816 ];
817 for invalidate in cases {
818 let mut opts = FrontendOptions::default();
819 opts.http.timeout = Duration::from_secs(1);
820 opts.prom_store.pending_rows_flush_interval = Duration::from_secs(5);
821 opts.pending_rows_batcher.table.protocols =
822 vec![BatchingProtocol::Prom, BatchingProtocol::Influxdb];
823 opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::from_secs(10);
824 invalidate(&mut opts);
825 assert!(
826 !opts
827 .pending_rows_batcher
828 .table
829 .pending_rows_batching_enabled()
830 );
831 assert_eq!(opts.prom_store, effective_prom_store_options(&opts));
832 assert_eq!(
833 Duration::from_secs(6),
834 effective_http_options_with_sync(&opts, true).timeout
835 );
836 opts.prom_store.pending_rows_flush_interval = Duration::ZERO;
837 assert_eq!(
838 Duration::from_secs(1),
839 effective_http_options_with_sync(&opts, true).timeout
840 );
841 }
842 }
843
844 #[test]
845 fn test_effective_http_timeout_skips_fallback_when_batcher_disabled() {
846 type KnobMutator = fn(&mut FrontendOptions);
850 let cases: [(&str, KnobMutator); 9] = [
851 ("oversized flush concurrency", |opts| {
852 opts.prom_store.max_concurrent_flushes = usize::MAX
853 }),
854 ("oversized worker channel", |opts| {
855 opts.prom_store.worker_channel_capacity = usize::MAX
856 }),
857 ("oversized inflight limit", |opts| {
858 opts.prom_store.max_inflight_requests = usize::MAX
859 }),
860 ("oversized flow queue", |opts| {
861 opts.prom_store.flow_notification_queue_capacity =
862 std::num::NonZeroUsize::new(usize::MAX).unwrap()
863 }),
864 ("unrepresentable deadline", |opts| {
865 opts.prom_store.pending_rows_flush_interval = Duration::MAX
866 }),
867 ("zero max_batch_rows", |opts| {
868 opts.prom_store.max_batch_rows = 0
869 }),
870 ("zero max_concurrent_flushes", |opts| {
871 opts.prom_store.max_concurrent_flushes = 0
872 }),
873 ("zero worker_channel_capacity", |opts| {
874 opts.prom_store.worker_channel_capacity = 0
875 }),
876 ("zero max_inflight_requests", |opts| {
877 opts.prom_store.max_inflight_requests = 0
878 }),
879 ];
880
881 for (name, disable_batcher) in cases {
882 let mut opts = FrontendOptions::default();
883 opts.http.timeout = Duration::from_millis(1000);
884 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
885 disable_batcher(&mut opts);
886
887 assert_eq!(
888 Duration::from_millis(1000),
889 effective_http_options_with_sync(&opts, true).timeout,
890 "{name}"
891 );
892 }
893 }
894
895 #[tokio::test]
896 async fn test_database_sql_authentication_differs_between_public_and_internal_grpc() {
897 let options = FrontendOptions {
898 http: HttpOptions {
899 addr: "127.0.0.1:0".to_string(),
900 ..Default::default()
901 },
902 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
903 internal_grpc: Some(GrpcOptions::default().with_bind_addr("127.0.0.1:0")),
904 mysql: crate::service_config::MysqlOptions {
905 enable: false,
906 ..Default::default()
907 },
908 postgres: crate::service_config::PostgresOptions {
909 enable: false,
910 ..Default::default()
911 },
912 ..Default::default()
913 };
914 let meta_client = Arc::new(
915 MetaClientBuilder::new(0, Role::Frontend)
916 .enable_procedure()
917 .build(),
918 );
919 let instance = Arc::new(
920 FrontendBuilder::new_test(&options, meta_client)
921 .try_build()
922 .await
923 .unwrap(),
924 );
925 let plugins = Plugins::new();
926 let provider =
927 static_user_provider_from_option("static_user_provider:cmd:greptime=greptime").unwrap();
928 plugins.insert::<UserProviderRef>(Arc::new(provider));
929 let public_flight_handler = Arc::new(GreptimeRequestHandler::new(
930 instance.clone(),
931 plugins.get::<UserProviderRef>(),
932 None,
933 options.grpc.flight_compression,
934 )) as FlightCraftRef;
935 let internal_flight_handler = Arc::new(CountingFlightCraft {
936 inner: Arc::new(GreptimeRequestHandler::new(
937 instance.clone(),
938 None,
939 None,
940 options.grpc.flight_compression,
941 )),
942 do_get_calls: AtomicUsize::new(0),
943 do_put_calls: AtomicUsize::new(0),
944 });
945 let internal_flight_handler_ref = internal_flight_handler.clone() as FlightCraftRef;
946 let mut services = Services::new(options, instance, plugins)
947 .with_flight_handler(public_flight_handler)
948 .with_internal_flight_handler(internal_flight_handler_ref)
949 .build()
950 .unwrap();
951
952 services.start_all().await.unwrap();
953 let public_addr = services.addr(GRPC_SERVER).unwrap();
954 let internal_addr = services.addr("INTERNAL_GRPC_SERVER").unwrap();
955 let public_database = Database::new(
956 "greptime",
957 "public",
958 Client::with_query_and_control_managers(
959 ChannelManager::new(),
960 ChannelManager::new(),
961 [public_addr.to_string()],
962 ),
963 );
964 let internal_database = Database::new(
965 "greptime",
966 "public",
967 Client::with_query_and_control_managers(
968 ChannelManager::new(),
969 ChannelManager::new(),
970 [internal_addr.to_string()],
971 ),
972 );
973
974 let internal_result = internal_database.sql("SELECT 1").await;
975 let put_result = internal_database
976 .do_put(Box::pin(futures::stream::empty()))
977 .await;
978 let public_result = public_database.sql("SELECT 1").await;
979
980 services.shutdown_all().await.unwrap();
981
982 assert!(internal_result.is_ok());
983 assert!(put_result.is_ok());
984 assert_eq!(
985 1,
986 internal_flight_handler.do_get_calls.load(Ordering::SeqCst)
987 );
988 assert_eq!(
989 1,
990 internal_flight_handler.do_put_calls.load(Ordering::SeqCst)
991 );
992 assert_eq!(
993 Some(Code::Unauthenticated),
994 public_result
995 .as_ref()
996 .err()
997 .and_then(|err| err.tonic_code())
998 );
999 }
1000
1001 #[tokio::test]
1002 async fn test_services_builder_health_check_is_reachable() {
1003 let options = FrontendOptions {
1005 http: HttpOptions {
1006 addr: "127.0.0.1:0".to_string(),
1007 ..Default::default()
1008 },
1009 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
1010 mysql: crate::service_config::MysqlOptions {
1011 enable: false,
1012 ..Default::default()
1013 },
1014 postgres: crate::service_config::PostgresOptions {
1015 enable: false,
1016 ..Default::default()
1017 },
1018 ..Default::default()
1019 };
1020 let meta_client = Arc::new(
1021 MetaClientBuilder::new(0, Role::Frontend)
1022 .enable_procedure()
1023 .build(),
1024 );
1025 let instance = Arc::new(
1026 FrontendBuilder::new_test(&options, meta_client)
1027 .try_build()
1028 .await
1029 .unwrap(),
1030 );
1031 let mut services = Services::new(options, instance, Default::default())
1032 .build()
1033 .unwrap();
1034
1035 services.start_all().await.unwrap();
1037 let addr = services.addr(GRPC_SERVER).unwrap();
1038 let health_check = HealthCheckClient::connect(format!("http://{addr}"))
1039 .await
1040 .unwrap()
1041 .health_check(HealthCheckRequest {})
1042 .await;
1043 services.shutdown_all().await.unwrap();
1044
1045 assert!(health_check.is_ok());
1047 }
1048
1049 #[tokio::test]
1050 async fn test_internal_grpc_server_never_serves_cors() {
1051 let options = FrontendOptions {
1052 http: HttpOptions {
1053 addr: "127.0.0.1:0".to_string(),
1054 ..Default::default()
1055 },
1056 grpc: GrpcOptions {
1057 enable_cors: true,
1058 ..GrpcOptions::default().with_bind_addr("127.0.0.1:0")
1059 },
1060 internal_grpc: Some(GrpcOptions {
1061 enable_cors: true,
1062 ..GrpcOptions::default().with_bind_addr("127.0.0.1:0")
1063 }),
1064 mysql: crate::service_config::MysqlOptions {
1065 enable: false,
1066 ..Default::default()
1067 },
1068 postgres: crate::service_config::PostgresOptions {
1069 enable: false,
1070 ..Default::default()
1071 },
1072 ..Default::default()
1073 };
1074 let meta_client = Arc::new(
1075 MetaClientBuilder::new(0, Role::Frontend)
1076 .enable_procedure()
1077 .build(),
1078 );
1079 let instance = Arc::new(
1080 FrontendBuilder::new_test(&options, meta_client)
1081 .try_build()
1082 .await
1083 .unwrap(),
1084 );
1085 let mut services = Services::new(options, instance, Default::default())
1086 .build()
1087 .unwrap();
1088
1089 services.start_all().await.unwrap();
1090 let public_addr = services.addr(GRPC_SERVER).unwrap();
1091 let internal_addr = services.addr("INTERNAL_GRPC_SERVER").unwrap();
1092 let public = send_cors_preflight(public_addr).await;
1093 let internal = send_cors_preflight(internal_addr).await;
1094 services.shutdown_all().await.unwrap();
1095
1096 assert!(public.headers().contains_key(ACCESS_CONTROL_ALLOW_ORIGIN));
1097 assert!(!internal.headers().contains_key(ACCESS_CONTROL_ALLOW_ORIGIN));
1098 }
1099
1100 async fn send_cors_preflight(addr: std::net::SocketAddr) -> reqwest::Response {
1101 reqwest::Client::new()
1102 .request(
1103 reqwest::Method::OPTIONS,
1104 format!("http://{addr}/greptime.v1.HealthCheck/Check"),
1105 )
1106 .header(ORIGIN, "https://example.com")
1107 .header(ACCESS_CONTROL_REQUEST_METHOD, "POST")
1108 .send()
1109 .await
1110 .unwrap()
1111 }
1112}