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