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::error::Error as ServerError;
28use servers::grpc::builder::GrpcServerBuilder;
29use servers::grpc::flight::FlightCraftRef;
30use servers::grpc::frontend_grpc_handler::FrontendGrpcHandler;
31use servers::grpc::greptime_handler::GreptimeRequestHandler;
32use servers::grpc::{GrpcOptions, GrpcServer};
33use servers::http::event::LogValidatorRef;
34use servers::http::result::error_result::ErrorResponse;
35use servers::http::utils::router::RouterConfigurator;
36use servers::http::{HttpOptions, HttpServer, HttpServerBuilder};
37use servers::interceptor::LogIngestInterceptorRef;
38use servers::metrics_handler::MetricsHandler;
39use servers::mysql::server::{MysqlServer, MysqlSpawnConfig, MysqlSpawnRef};
40use servers::otel_arrow::OtelArrowServiceHandler;
41use servers::pending_rows_batcher::{PendingRowsBatcher, pending_rows_batch_sync_enabled};
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 snafu::ResultExt;
47use tonic::Status;
48
49use crate::error::{self, Result, StartServerSnafu, TomlFormatSnafu};
50use crate::frontend::FrontendOptions;
51use crate::instance::Instance;
52
53pub struct Services<T>
54where
55 T: Into<FrontendOptions> + Configurable + Clone,
56{
57 opts: T,
58 instance: Arc<Instance>,
59 grpc_server_builder: Option<GrpcServerBuilder>,
60 http_server_builder: Option<HttpServerBuilder>,
61 plugins: Plugins,
62 flight_handler: Option<FlightCraftRef>,
63 pub server_memory_limiter: ServerMemoryLimiter,
64}
65
66impl<T> Services<T>
67where
68 T: Into<FrontendOptions> + Configurable + Clone,
69{
70 pub fn new(opts: T, instance: Arc<Instance>, plugins: Plugins) -> Self {
71 let feopts = opts.clone().into();
72 let server_memory_limiter = ServerMemoryLimiter::new(
74 feopts.max_in_flight_write_bytes.as_bytes(),
75 feopts.write_bytes_exhausted_policy,
76 );
77
78 Self {
79 opts,
80 instance,
81 grpc_server_builder: None,
82 http_server_builder: None,
83 plugins,
84 flight_handler: None,
85 server_memory_limiter,
86 }
87 }
88
89 pub fn grpc_server_builder(
90 &self,
91 opts: &GrpcOptions,
92 request_memory_limiter: ServerMemoryLimiter,
93 ) -> Result<GrpcServerBuilder> {
94 let builder = GrpcServerBuilder::new(opts.as_config(), common_runtime::global_runtime())
95 .with_memory_limiter(request_memory_limiter)
96 .with_tls_config(opts.tls.clone())
97 .context(error::InvalidTlsConfigSnafu)?;
98 Ok(builder)
99 }
100
101 pub fn http_server_builder(
102 &self,
103 opts: &FrontendOptions,
104 request_memory_limiter: ServerMemoryLimiter,
105 ) -> HttpServerBuilder {
106 let mut builder = HttpServerBuilder::new(effective_http_options(opts))
107 .with_memory_limiter(request_memory_limiter)
108 .with_sql_handler(self.instance.clone());
109
110 let validator = self.plugins.get::<LogValidatorRef>();
111 let ingest_interceptor = self.plugins.get::<LogIngestInterceptorRef<ServerError>>();
112 builder =
113 builder.with_log_ingest_handler(self.instance.clone(), validator, ingest_interceptor);
114 builder = builder.with_logs_handler(self.instance.clone());
115
116 if let Some(user_provider) = self.plugins.get::<UserProviderRef>() {
117 builder = builder.with_user_provider(user_provider);
118 }
119
120 if opts.opentsdb.enable {
121 builder = builder.with_opentsdb_handler(self.instance.clone());
122 }
123
124 if opts.influxdb.enable {
125 builder = builder.with_influxdb_handler(self.instance.clone());
126 }
127
128 if opts.prom_store.enable {
129 let pending_rows_batcher = if opts.prom_store.with_metric_engine {
130 PendingRowsBatcher::try_new(
131 self.instance.partition_manager().clone(),
132 self.instance.node_manager().clone(),
133 self.instance.catalog_manager().clone(),
134 self.instance.table_flownode_set_cache().clone(),
135 opts.prom_store.with_metric_engine,
136 self.instance.clone(),
137 opts.prom_store.pending_rows_flush_interval,
138 opts.prom_store.max_batch_rows,
139 opts.prom_store.max_concurrent_flushes,
140 opts.prom_store.worker_channel_capacity,
141 opts.prom_store.max_inflight_requests,
142 opts.prom_store.flow_notification_queue_capacity,
143 )
144 } else {
145 None
146 };
147 builder = builder
148 .with_prom_handler(
149 self.instance.clone(),
150 Some(self.instance.clone()),
151 opts.prom_store.with_metric_engine,
152 opts.prom_store.prom_validation_mode,
153 opts.prom_store
154 .experimental_enable_prometheus_native_histogram,
155 pending_rows_batcher,
156 )
157 .with_prometheus_handler(self.instance.clone());
158 }
159
160 if opts.otlp.enable {
161 builder = builder.with_otlp_handler(
162 self.instance.clone(),
163 opts.prom_store.with_metric_engine,
164 opts.otlp.experimental_enable_exponential_histogram,
165 );
166 }
167
168 if opts.jaeger.enable {
169 builder = builder.with_jaeger_handler(self.instance.clone());
170 }
171
172 builder = builder.with_dashboard_handler(self.instance.clone());
173
174 if let Some(configurator) = self.plugins.get::<RouterConfigurator>() {
175 info!("Adding extra router from plugins");
176 builder = builder.with_extra_router(configurator.router());
177 }
178
179 builder.add_layer(axum::middleware::from_fn_with_state(
180 self.instance.clone(),
181 async move |State(state): State<Arc<Instance>>, request: Request, next: Next| {
182 if state.is_suspended() {
183 return ErrorResponse::from_error(servers::error::SuspendedSnafu.build())
184 .into_response();
185 }
186 next.run(request).await
187 },
188 ))
189 }
190
191 pub fn with_grpc_server_builder(self, builder: GrpcServerBuilder) -> Self {
192 Self {
193 grpc_server_builder: Some(builder),
194 ..self
195 }
196 }
197
198 pub fn with_http_server_builder(self, builder: HttpServerBuilder) -> Self {
199 Self {
200 http_server_builder: Some(builder),
201 ..self
202 }
203 }
204
205 pub fn with_flight_handler(self, flight_handler: FlightCraftRef) -> Self {
206 Self {
207 flight_handler: Some(flight_handler),
208 ..self
209 }
210 }
211
212 fn build_grpc_server(
213 &mut self,
214 grpc: &GrpcOptions,
215 meta_client: &Option<MetaClientOptions>,
216 name: Option<String>,
217 external: bool,
218 request_memory_limiter: ServerMemoryLimiter,
219 ) -> Result<GrpcServer> {
220 let builder = if let Some(builder) = self.grpc_server_builder.take() {
221 builder
222 } else {
223 self.grpc_server_builder(grpc, request_memory_limiter)?
224 };
225
226 let user_provider = if external {
227 self.plugins.get::<UserProviderRef>()
228 } else {
229 None
231 };
232
233 let runtime = if meta_client.is_none() {
235 Some(builder.runtime().clone())
236 } else {
237 None
238 };
239
240 let greptime_request_handler = GreptimeRequestHandler::new(
241 self.instance.clone(),
242 user_provider.clone(),
243 runtime,
244 grpc.flight_compression,
245 );
246
247 let flight_handler = self
249 .flight_handler
250 .clone()
251 .unwrap_or_else(|| Arc::new(greptime_request_handler.clone()) as FlightCraftRef);
252
253 let grpc_server = builder
254 .name(name)
255 .database_handler(greptime_request_handler.clone())
256 .prometheus_handler(self.instance.clone(), user_provider.clone())
257 .otel_arrow_handler(OtelArrowServiceHandler::new(
258 self.instance.clone(),
259 user_provider.clone(),
260 ))
261 .flight_handler(flight_handler)
262 .add_layer(axum::middleware::from_fn_with_state(
263 self.instance.clone(),
264 async move |State(state): State<Arc<Instance>>, request: Request, next: Next| {
265 if state.is_suspended() {
266 let status = Status::from(servers::error::SuspendedSnafu.build());
267 return status.into_http();
268 }
269 next.run(request).await
270 },
271 ));
272
273 let grpc_server = if !external {
274 let frontend_grpc_handler =
275 FrontendGrpcHandler::new(self.instance.process_manager().clone());
276 grpc_server.frontend_grpc_handler(frontend_grpc_handler)
277 } else {
278 grpc_server
279 }
280 .build();
281
282 Ok(grpc_server)
283 }
284
285 fn build_http_server(
286 &mut self,
287 opts: &FrontendOptions,
288 toml: String,
289 request_memory_limiter: ServerMemoryLimiter,
290 ) -> Result<(HttpServer, Option<HttpServer>)> {
291 let builder = if let Some(builder) = self.http_server_builder.take() {
292 builder
293 } else {
294 self.http_server_builder(opts, request_memory_limiter)
295 };
296
297 let (internal, api) = builder
301 .with_metrics_handler(MetricsHandler)
302 .with_greptime_config_options(toml)
303 .build_servers();
304 Ok((internal, api))
305 }
306
307 pub fn build(mut self) -> Result<ServerHandlers> {
308 let opts = self.opts.clone();
309 let instance = self.instance.clone();
310
311 let toml = opts.to_toml().context(TomlFormatSnafu)?;
312 let opts: FrontendOptions = opts.into();
313
314 let handlers = ServerHandlers::default();
315
316 let user_provider = self.plugins.get::<UserProviderRef>();
317
318 {
319 let grpc_addr = parse_addr(&opts.grpc.bind_addr)?;
321 let grpc_server = self.build_grpc_server(
322 &opts.grpc,
323 &opts.meta_client,
324 None,
325 true,
326 self.server_memory_limiter.clone(),
327 )?;
328 handlers.insert((Box::new(grpc_server), grpc_addr));
329 }
330
331 if let Some(internal_grpc) = &opts.internal_grpc {
332 let grpc_addr = parse_addr(&internal_grpc.bind_addr)?;
334 let grpc_server = self.build_grpc_server(
335 internal_grpc,
336 &opts.meta_client,
337 Some("INTERNAL_GRPC_SERVER".to_string()),
338 false,
339 self.server_memory_limiter.clone(),
340 )?;
341 handlers.insert((Box::new(grpc_server), grpc_addr));
342 }
343
344 {
345 let http_options = &opts.http;
348 let http_addr = parse_addr(&http_options.addr)?;
349 let (http_server, http_api_server) =
350 self.build_http_server(&opts, toml, self.server_memory_limiter.clone())?;
351 handlers.insert((Box::new(http_server), http_addr));
352
353 if let Some(http_api_server) = http_api_server {
354 let http_api_addr = parse_addr(&http_options.api_server_addr)?;
355 info!("HTTP API server is enabled at {}", http_api_addr);
356 handlers.insert((Box::new(http_api_server), http_api_addr));
357 }
358 }
359
360 if opts.mysql.enable {
361 let opts = &opts.mysql;
363 let mysql_addr = parse_addr(&opts.addr)?;
364
365 let tls_server_config = Arc::new(
366 ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
367 );
368
369 maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
371
372 let mysql_server = MysqlServer::create_server(
373 common_runtime::global_runtime(),
374 Arc::new(MysqlSpawnRef::new(instance.clone(), user_provider.clone())),
375 Arc::new(MysqlSpawnConfig::new(
376 opts.tls.should_force_tls(),
377 tls_server_config,
378 opts.keep_alive.as_secs(),
379 opts.reject_no_database.unwrap_or(false),
380 opts.prepared_stmt_cache_size,
381 )),
382 Some(instance.process_manager().clone()),
383 );
384 handlers.insert((mysql_server, mysql_addr));
385 }
386
387 if opts.postgres.enable {
388 let opts = &opts.postgres;
390 let pg_addr = parse_addr(&opts.addr)?;
391
392 let tls_server_config = Arc::new(
393 ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
394 );
395
396 maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
397
398 let pg_server = Box::new(PostgresServer::new(
399 instance.clone(),
400 opts.tls.should_force_tls(),
401 tls_server_config,
402 opts.keep_alive.as_secs(),
403 common_runtime::global_runtime(),
404 user_provider.clone(),
405 Some(self.instance.process_manager().clone()),
406 )) as Box<dyn Server>;
407
408 handlers.insert((pg_server, pg_addr));
409 }
410
411 Ok(handlers)
412 }
413}
414
415fn effective_http_options(opts: &FrontendOptions) -> HttpOptions {
416 effective_http_options_with_sync(opts, pending_rows_batch_sync_enabled())
417}
418
419fn effective_http_options_with_sync(opts: &FrontendOptions, batch_sync: bool) -> HttpOptions {
420 let mut http = opts.http.clone();
421 let flush_interval = opts.prom_store.pending_rows_flush_interval;
422 let fallback_timeout = flush_interval.saturating_add(Duration::from_secs(1));
423 if !opts.prom_store.pending_rows_batching_enabled()
427 || !batch_sync
428 || http.timeout.is_zero()
429 || http.timeout > fallback_timeout
430 {
431 return http;
432 }
433
434 let configured_timeout = http.timeout;
435 http.timeout = fallback_timeout;
436 warn!(
437 ?configured_timeout,
438 ?flush_interval,
439 ?fallback_timeout,
440 "HTTP request timeout is not longer than the pending-row timeout fallback; using the fallback"
441 );
442 http
443}
444
445fn parse_addr(addr: &str) -> Result<SocketAddr> {
446 addr.parse().context(error::ParseAddrSnafu { addr })
447}
448
449#[cfg(test)]
450mod tests {
451 use std::time::Duration;
452
453 use api::v1::HealthCheckRequest;
454 use api::v1::health_check_client::HealthCheckClient;
455 use api::v1::meta::Role;
456 use meta_client::client::MetaClientBuilder;
457 use servers::grpc::GRPC_SERVER;
458
459 use super::*;
460 use crate::instance::builder::FrontendBuilder;
461
462 #[test]
463 fn test_effective_http_timeout_for_pending_rows() {
464 let cases = [
465 ("disabled timeout", 0, 5000, true, true, 0),
466 ("disabled prom store", 1000, 5000, false, true, 1000),
467 ("disabled metric engine", 1000, 5000, true, false, 1000),
468 ("disabled batching", 1000, 0, true, true, 1000),
469 ("timeout below flush interval", 4000, 5000, true, true, 6000),
470 (
471 "timeout equals flush interval",
472 5000,
473 5000,
474 true,
475 true,
476 6000,
477 ),
478 ("timeout below fallback", 5500, 5000, true, true, 6000),
479 ("timeout equals fallback", 6000, 5000, true, true, 6000),
480 ("timeout above fallback", 7000, 5000, true, true, 7000),
481 ];
482
483 for (name, timeout, flush_interval, enable, with_metric_engine, expected) in cases {
484 let mut opts = FrontendOptions::default();
485 opts.http.timeout = Duration::from_millis(timeout);
486 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(flush_interval);
487 opts.prom_store.enable = enable;
488 opts.prom_store.with_metric_engine = with_metric_engine;
489
490 assert_eq!(
491 Duration::from_millis(expected),
492 effective_http_options_with_sync(&opts, true).timeout,
493 "{name}"
494 );
495 }
496 }
497
498 #[test]
499 fn test_effective_http_timeout_skips_fallback_in_async_batch_mode() {
500 let mut opts = FrontendOptions::default();
504 opts.http.timeout = Duration::from_millis(1000);
505 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
506
507 assert_eq!(
508 Duration::from_millis(1000),
509 effective_http_options_with_sync(&opts, false).timeout,
510 );
511 assert_eq!(
512 Duration::from_millis(6000),
513 effective_http_options_with_sync(&opts, true).timeout,
514 );
515 }
516
517 #[test]
518 fn test_effective_http_timeout_skips_fallback_when_batcher_disabled() {
519 type KnobMutator = fn(&mut FrontendOptions);
523 let cases: [(&str, KnobMutator); 4] = [
524 ("zero max_batch_rows", |opts| {
525 opts.prom_store.max_batch_rows = 0
526 }),
527 ("zero max_concurrent_flushes", |opts| {
528 opts.prom_store.max_concurrent_flushes = 0
529 }),
530 ("zero worker_channel_capacity", |opts| {
531 opts.prom_store.worker_channel_capacity = 0
532 }),
533 ("zero max_inflight_requests", |opts| {
534 opts.prom_store.max_inflight_requests = 0
535 }),
536 ];
537
538 for (name, disable_batcher) in cases {
539 let mut opts = FrontendOptions::default();
540 opts.http.timeout = Duration::from_millis(1000);
541 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
542 disable_batcher(&mut opts);
543
544 assert_eq!(
545 Duration::from_millis(1000),
546 effective_http_options_with_sync(&opts, true).timeout,
547 "{name}"
548 );
549 }
550 }
551
552 #[tokio::test]
553 async fn test_services_builder_health_check_is_reachable() {
554 let options = FrontendOptions {
556 http: HttpOptions {
557 addr: "127.0.0.1:0".to_string(),
558 ..Default::default()
559 },
560 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
561 mysql: crate::service_config::MysqlOptions {
562 enable: false,
563 ..Default::default()
564 },
565 postgres: crate::service_config::PostgresOptions {
566 enable: false,
567 ..Default::default()
568 },
569 ..Default::default()
570 };
571 let meta_client = Arc::new(
572 MetaClientBuilder::new(0, Role::Frontend)
573 .enable_procedure()
574 .build(),
575 );
576 let instance = Arc::new(
577 FrontendBuilder::new_test(&options, meta_client)
578 .try_build()
579 .await
580 .unwrap(),
581 );
582 let mut services = Services::new(options, instance, Default::default())
583 .build()
584 .unwrap();
585
586 services.start_all().await.unwrap();
588 let addr = services.addr(GRPC_SERVER).unwrap();
589 let health_check = HealthCheckClient::connect(format!("http://{addr}"))
590 .await
591 .unwrap()
592 .health_check(HealthCheckRequest {})
593 .await;
594 services.shutdown_all().await.unwrap();
595
596 assert!(health_check.is_ok());
598 }
599}