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
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            // skip authentication for internal grpc port
227            None
228        };
229
230        // Determine whether it is Standalone or Distributed mode based on whether the meta client is configured.
231        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        // Use custom flight handler if provided, otherwise use the default GreptimeRequestHandler
245        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        // The API server is configured entirely under `[http]` (`enable_api_server`,
295        // `api_server_host`, `api_server_port`) and shares every other `[http]`
296        // option with the main server.
297        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            // Always init GRPC server
317            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            // Always init Internal GRPC server
330            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            // Always init the internal/full HTTP server (v1 + internal interfaces)
343            // and, when enabled, the dedicated HTTP API server (v1 + dashboard only).
344            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            // Init MySQL server
359            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            // will not watch if watch is disabled in tls option
367            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            // Init PosgresSQL Server
386            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    // In asynchronous batch mode submissions return right after enqueue and
421    // no request waits for a pending-row flush, so the timeout must not be
422    // raised either.
423    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        // With `PENDING_ROWS_BATCH_SYNC=false`, submissions return right after
498        // enqueue and no request waits for a pending-row flush, so the
499        // timeout must not be raised.
500        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        // Mirrors the conditions under which `PendingRowsBatcher::try_new`
517        // returns `None`; in these cases no request can wait for a pending-row
518        // flush, so the timeout must not be raised.
519        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        // Arrange
552        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        // Act
584        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
594        assert!(health_check.is_ok());
595    }
596}