Skip to main content

frontend/
server.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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        // Create server request memory limiter for all server protocols
73        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            // skip authentication for internal grpc port
230            None
231        };
232
233        // Determine whether it is Standalone or Distributed mode based on whether the meta client is configured.
234        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        // Use custom flight handler if provided, otherwise use the default GreptimeRequestHandler
248        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        // The API server is configured entirely under `[http]` (`enable_api_server`,
298        // `api_server_host`, `api_server_port`) and shares every other `[http]`
299        // option with the main server.
300        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            // Always init GRPC server
320            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            // Always init Internal GRPC server
333            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            // Always init the internal/full HTTP server (v1 + internal interfaces)
346            // and, when enabled, the dedicated HTTP API server (v1 + dashboard only).
347            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            // Init MySQL server
362            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            // will not watch if watch is disabled in tls option
370            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            // Init PosgresSQL Server
389            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    // In asynchronous batch mode submissions return right after enqueue and
424    // no request waits for a pending-row flush, so the timeout must not be
425    // raised either.
426    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        // With `PENDING_ROWS_BATCH_SYNC=false`, submissions return right after
501        // enqueue and no request waits for a pending-row flush, so the
502        // timeout must not be raised.
503        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        // Mirrors the conditions under which `PendingRowsBatcher::try_new`
520        // returns `None`; in these cases no request can wait for a pending-row
521        // flush, so the timeout must not be raised.
522        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        // Arrange
555        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        // Act
587        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
597        assert!(health_check.is_ok());
598    }
599}