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
162 .with_otlp_handler(self.instance.clone(), opts.prom_store.with_metric_engine);
163 }
164
165 if opts.jaeger.enable {
166 builder = builder.with_jaeger_handler(self.instance.clone());
167 }
168
169 builder = builder.with_dashboard_handler(self.instance.clone());
170
171 if let Some(configurator) = self.plugins.get::<RouterConfigurator>() {
172 info!("Adding extra router from plugins");
173 builder = builder.with_extra_router(configurator.router());
174 }
175
176 builder.add_layer(axum::middleware::from_fn_with_state(
177 self.instance.clone(),
178 async move |State(state): State<Arc<Instance>>, request: Request, next: Next| {
179 if state.is_suspended() {
180 return ErrorResponse::from_error(servers::error::SuspendedSnafu.build())
181 .into_response();
182 }
183 next.run(request).await
184 },
185 ))
186 }
187
188 pub fn with_grpc_server_builder(self, builder: GrpcServerBuilder) -> Self {
189 Self {
190 grpc_server_builder: Some(builder),
191 ..self
192 }
193 }
194
195 pub fn with_http_server_builder(self, builder: HttpServerBuilder) -> Self {
196 Self {
197 http_server_builder: Some(builder),
198 ..self
199 }
200 }
201
202 pub fn with_flight_handler(self, flight_handler: FlightCraftRef) -> Self {
203 Self {
204 flight_handler: Some(flight_handler),
205 ..self
206 }
207 }
208
209 fn build_grpc_server(
210 &mut self,
211 grpc: &GrpcOptions,
212 meta_client: &Option<MetaClientOptions>,
213 name: Option<String>,
214 external: bool,
215 request_memory_limiter: ServerMemoryLimiter,
216 ) -> Result<GrpcServer> {
217 let builder = if let Some(builder) = self.grpc_server_builder.take() {
218 builder
219 } else {
220 self.grpc_server_builder(grpc, request_memory_limiter)?
221 };
222
223 let user_provider = if external {
224 self.plugins.get::<UserProviderRef>()
225 } else {
226 None
228 };
229
230 let runtime = if meta_client.is_none() {
232 Some(builder.runtime().clone())
233 } else {
234 None
235 };
236
237 let greptime_request_handler = GreptimeRequestHandler::new(
238 self.instance.clone(),
239 user_provider.clone(),
240 runtime,
241 grpc.flight_compression,
242 );
243
244 let flight_handler = self
246 .flight_handler
247 .clone()
248 .unwrap_or_else(|| Arc::new(greptime_request_handler.clone()) as FlightCraftRef);
249
250 let grpc_server = builder
251 .name(name)
252 .database_handler(greptime_request_handler.clone())
253 .prometheus_handler(self.instance.clone(), user_provider.clone())
254 .otel_arrow_handler(OtelArrowServiceHandler::new(
255 self.instance.clone(),
256 user_provider.clone(),
257 ))
258 .flight_handler(flight_handler)
259 .add_layer(axum::middleware::from_fn_with_state(
260 self.instance.clone(),
261 async move |State(state): State<Arc<Instance>>, request: Request, next: Next| {
262 if state.is_suspended() {
263 let status = Status::from(servers::error::SuspendedSnafu.build());
264 return status.into_http();
265 }
266 next.run(request).await
267 },
268 ));
269
270 let grpc_server = if !external {
271 let frontend_grpc_handler =
272 FrontendGrpcHandler::new(self.instance.process_manager().clone());
273 grpc_server.frontend_grpc_handler(frontend_grpc_handler)
274 } else {
275 grpc_server
276 }
277 .build();
278
279 Ok(grpc_server)
280 }
281
282 fn build_http_server(
283 &mut self,
284 opts: &FrontendOptions,
285 toml: String,
286 request_memory_limiter: ServerMemoryLimiter,
287 ) -> Result<(HttpServer, Option<HttpServer>)> {
288 let builder = if let Some(builder) = self.http_server_builder.take() {
289 builder
290 } else {
291 self.http_server_builder(opts, request_memory_limiter)
292 };
293
294 let (internal, api) = builder
298 .with_metrics_handler(MetricsHandler)
299 .with_greptime_config_options(toml)
300 .build_servers();
301 Ok((internal, api))
302 }
303
304 pub fn build(mut self) -> Result<ServerHandlers> {
305 let opts = self.opts.clone();
306 let instance = self.instance.clone();
307
308 let toml = opts.to_toml().context(TomlFormatSnafu)?;
309 let opts: FrontendOptions = opts.into();
310
311 let handlers = ServerHandlers::default();
312
313 let user_provider = self.plugins.get::<UserProviderRef>();
314
315 {
316 let grpc_addr = parse_addr(&opts.grpc.bind_addr)?;
318 let grpc_server = self.build_grpc_server(
319 &opts.grpc,
320 &opts.meta_client,
321 None,
322 true,
323 self.server_memory_limiter.clone(),
324 )?;
325 handlers.insert((Box::new(grpc_server), grpc_addr));
326 }
327
328 if let Some(internal_grpc) = &opts.internal_grpc {
329 let grpc_addr = parse_addr(&internal_grpc.bind_addr)?;
331 let grpc_server = self.build_grpc_server(
332 internal_grpc,
333 &opts.meta_client,
334 Some("INTERNAL_GRPC_SERVER".to_string()),
335 false,
336 self.server_memory_limiter.clone(),
337 )?;
338 handlers.insert((Box::new(grpc_server), grpc_addr));
339 }
340
341 {
342 let http_options = &opts.http;
345 let http_addr = parse_addr(&http_options.addr)?;
346 let (http_server, http_api_server) =
347 self.build_http_server(&opts, toml, self.server_memory_limiter.clone())?;
348 handlers.insert((Box::new(http_server), http_addr));
349
350 if let Some(http_api_server) = http_api_server {
351 let http_api_addr = parse_addr(&http_options.api_server_addr)?;
352 info!("HTTP API server is enabled at {}", http_api_addr);
353 handlers.insert((Box::new(http_api_server), http_api_addr));
354 }
355 }
356
357 if opts.mysql.enable {
358 let opts = &opts.mysql;
360 let mysql_addr = parse_addr(&opts.addr)?;
361
362 let tls_server_config = Arc::new(
363 ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
364 );
365
366 maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
368
369 let mysql_server = MysqlServer::create_server(
370 common_runtime::global_runtime(),
371 Arc::new(MysqlSpawnRef::new(instance.clone(), user_provider.clone())),
372 Arc::new(MysqlSpawnConfig::new(
373 opts.tls.should_force_tls(),
374 tls_server_config,
375 opts.keep_alive.as_secs(),
376 opts.reject_no_database.unwrap_or(false),
377 opts.prepared_stmt_cache_size,
378 )),
379 Some(instance.process_manager().clone()),
380 );
381 handlers.insert((mysql_server, mysql_addr));
382 }
383
384 if opts.postgres.enable {
385 let opts = &opts.postgres;
387 let pg_addr = parse_addr(&opts.addr)?;
388
389 let tls_server_config = Arc::new(
390 ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
391 );
392
393 maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
394
395 let pg_server = Box::new(PostgresServer::new(
396 instance.clone(),
397 opts.tls.should_force_tls(),
398 tls_server_config,
399 opts.keep_alive.as_secs(),
400 common_runtime::global_runtime(),
401 user_provider.clone(),
402 Some(self.instance.process_manager().clone()),
403 )) as Box<dyn Server>;
404
405 handlers.insert((pg_server, pg_addr));
406 }
407
408 Ok(handlers)
409 }
410}
411
412fn effective_http_options(opts: &FrontendOptions) -> HttpOptions {
413 effective_http_options_with_sync(opts, pending_rows_batch_sync_enabled())
414}
415
416fn effective_http_options_with_sync(opts: &FrontendOptions, batch_sync: bool) -> HttpOptions {
417 let mut http = opts.http.clone();
418 let flush_interval = opts.prom_store.pending_rows_flush_interval;
419 let fallback_timeout = flush_interval.saturating_add(Duration::from_secs(1));
420 if !opts.prom_store.pending_rows_batching_enabled()
424 || !batch_sync
425 || http.timeout.is_zero()
426 || http.timeout > fallback_timeout
427 {
428 return http;
429 }
430
431 let configured_timeout = http.timeout;
432 http.timeout = fallback_timeout;
433 warn!(
434 ?configured_timeout,
435 ?flush_interval,
436 ?fallback_timeout,
437 "HTTP request timeout is not longer than the pending-row timeout fallback; using the fallback"
438 );
439 http
440}
441
442fn parse_addr(addr: &str) -> Result<SocketAddr> {
443 addr.parse().context(error::ParseAddrSnafu { addr })
444}
445
446#[cfg(test)]
447mod tests {
448 use std::time::Duration;
449
450 use api::v1::HealthCheckRequest;
451 use api::v1::health_check_client::HealthCheckClient;
452 use api::v1::meta::Role;
453 use meta_client::client::MetaClientBuilder;
454 use servers::grpc::GRPC_SERVER;
455
456 use super::*;
457 use crate::instance::builder::FrontendBuilder;
458
459 #[test]
460 fn test_effective_http_timeout_for_pending_rows() {
461 let cases = [
462 ("disabled timeout", 0, 5000, true, true, 0),
463 ("disabled prom store", 1000, 5000, false, true, 1000),
464 ("disabled metric engine", 1000, 5000, true, false, 1000),
465 ("disabled batching", 1000, 0, true, true, 1000),
466 ("timeout below flush interval", 4000, 5000, true, true, 6000),
467 (
468 "timeout equals flush interval",
469 5000,
470 5000,
471 true,
472 true,
473 6000,
474 ),
475 ("timeout below fallback", 5500, 5000, true, true, 6000),
476 ("timeout equals fallback", 6000, 5000, true, true, 6000),
477 ("timeout above fallback", 7000, 5000, true, true, 7000),
478 ];
479
480 for (name, timeout, flush_interval, enable, with_metric_engine, expected) in cases {
481 let mut opts = FrontendOptions::default();
482 opts.http.timeout = Duration::from_millis(timeout);
483 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(flush_interval);
484 opts.prom_store.enable = enable;
485 opts.prom_store.with_metric_engine = with_metric_engine;
486
487 assert_eq!(
488 Duration::from_millis(expected),
489 effective_http_options_with_sync(&opts, true).timeout,
490 "{name}"
491 );
492 }
493 }
494
495 #[test]
496 fn test_effective_http_timeout_skips_fallback_in_async_batch_mode() {
497 let mut opts = FrontendOptions::default();
501 opts.http.timeout = Duration::from_millis(1000);
502 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
503
504 assert_eq!(
505 Duration::from_millis(1000),
506 effective_http_options_with_sync(&opts, false).timeout,
507 );
508 assert_eq!(
509 Duration::from_millis(6000),
510 effective_http_options_with_sync(&opts, true).timeout,
511 );
512 }
513
514 #[test]
515 fn test_effective_http_timeout_skips_fallback_when_batcher_disabled() {
516 type KnobMutator = fn(&mut FrontendOptions);
520 let cases: [(&str, KnobMutator); 4] = [
521 ("zero max_batch_rows", |opts| {
522 opts.prom_store.max_batch_rows = 0
523 }),
524 ("zero max_concurrent_flushes", |opts| {
525 opts.prom_store.max_concurrent_flushes = 0
526 }),
527 ("zero worker_channel_capacity", |opts| {
528 opts.prom_store.worker_channel_capacity = 0
529 }),
530 ("zero max_inflight_requests", |opts| {
531 opts.prom_store.max_inflight_requests = 0
532 }),
533 ];
534
535 for (name, disable_batcher) in cases {
536 let mut opts = FrontendOptions::default();
537 opts.http.timeout = Duration::from_millis(1000);
538 opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
539 disable_batcher(&mut opts);
540
541 assert_eq!(
542 Duration::from_millis(1000),
543 effective_http_options_with_sync(&opts, true).timeout,
544 "{name}"
545 );
546 }
547 }
548
549 #[tokio::test]
550 async fn test_services_builder_health_check_is_reachable() {
551 let options = FrontendOptions {
553 http: HttpOptions {
554 addr: "127.0.0.1:0".to_string(),
555 ..Default::default()
556 },
557 grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
558 mysql: crate::service_config::MysqlOptions {
559 enable: false,
560 ..Default::default()
561 },
562 postgres: crate::service_config::PostgresOptions {
563 enable: false,
564 ..Default::default()
565 },
566 ..Default::default()
567 };
568 let meta_client = Arc::new(
569 MetaClientBuilder::new(0, Role::Frontend)
570 .enable_procedure()
571 .build(),
572 );
573 let instance = Arc::new(
574 FrontendBuilder::new_test(&options, meta_client)
575 .try_build()
576 .await
577 .unwrap(),
578 );
579 let mut services = Services::new(options, instance, Default::default())
580 .build()
581 .unwrap();
582
583 services.start_all().await.unwrap();
585 let addr = services.addr(GRPC_SERVER).unwrap();
586 let health_check = HealthCheckClient::connect(format!("http://{addr}"))
587 .await
588 .unwrap()
589 .health_check(HealthCheckRequest {})
590 .await;
591 services.shutdown_all().await.unwrap();
592
593 assert!(health_check.is_ok());
595 }
596}