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                    opts.prom_store
150                        .experimental_enable_prometheus_native_histogram,
151                    pending_rows_batcher,
152                )
153                .with_prometheus_handler(self.instance.clone());
154        }
155
156        if opts.otlp.enable {
157            builder = builder.with_otlp_handler(
158                self.instance.clone(),
159                opts.prom_store.with_metric_engine,
160                opts.otlp.experimental_enable_exponential_histogram,
161            );
162        }
163
164        if opts.jaeger.enable {
165            builder = builder.with_jaeger_handler(self.instance.clone());
166        }
167
168        builder = builder.with_dashboard_handler(self.instance.clone());
169
170        if let Some(configurator) = self.plugins.get::<RouterConfigurator>() {
171            info!("Adding extra router from plugins");
172            builder = builder.with_extra_router(configurator.router());
173        }
174
175        builder.add_layer(axum::middleware::from_fn_with_state(
176            self.instance.clone(),
177            async move |State(state): State<Arc<Instance>>, request: Request, next: Next| {
178                if state.is_suspended() {
179                    return ErrorResponse::from_error(servers::error::SuspendedSnafu.build())
180                        .into_response();
181                }
182                next.run(request).await
183            },
184        ))
185    }
186
187    pub fn with_grpc_server_builder(self, builder: GrpcServerBuilder) -> Self {
188        Self {
189            grpc_server_builder: Some(builder),
190            ..self
191        }
192    }
193
194    pub fn with_http_server_builder(self, builder: HttpServerBuilder) -> Self {
195        Self {
196            http_server_builder: Some(builder),
197            ..self
198        }
199    }
200
201    pub fn with_flight_handler(self, flight_handler: FlightCraftRef) -> Self {
202        Self {
203            flight_handler: Some(flight_handler),
204            ..self
205        }
206    }
207
208    pub fn with_internal_flight_handler(self, flight_handler: FlightCraftRef) -> Self {
209        Self {
210            internal_flight_handler: Some(flight_handler),
211            ..self
212        }
213    }
214
215    fn build_grpc_server(
216        &mut self,
217        grpc: &GrpcOptions,
218        meta_client: &Option<MetaClientOptions>,
219        name: Option<String>,
220        external: bool,
221        request_memory_limiter: ServerMemoryLimiter,
222    ) -> Result<GrpcServer> {
223        let builder = if let Some(builder) = self.grpc_server_builder.take() {
224            builder
225        } else {
226            self.grpc_server_builder(grpc, request_memory_limiter)?
227        };
228
229        let user_provider = if external {
230            self.plugins.get::<UserProviderRef>()
231        } else {
232            // skip authentication for internal grpc port
233            None
234        };
235
236        // Determine whether it is Standalone or Distributed mode based on whether the meta client is configured.
237        let runtime = if meta_client.is_none() {
238            Some(builder.runtime().clone())
239        } else {
240            None
241        };
242
243        let greptime_request_handler = GreptimeRequestHandler::new(
244            self.instance.clone(),
245            user_provider.clone(),
246            runtime,
247            grpc.flight_compression,
248        );
249
250        let default_flight_handler = Arc::new(greptime_request_handler.clone()) as FlightCraftRef;
251        let flight_handler = if external {
252            self.flight_handler
253                .clone()
254                .unwrap_or(default_flight_handler)
255        } else {
256            self.internal_flight_handler
257                .clone()
258                .unwrap_or(default_flight_handler)
259        };
260
261        let grpc_server = builder
262            .name(name)
263            .database_handler(greptime_request_handler.clone())
264            .prometheus_handler(self.instance.clone(), user_provider.clone())
265            .otel_arrow_handler(OtelArrowServiceHandler::new(
266                self.instance.clone(),
267                user_provider.clone(),
268            ))
269            .flight_handler(flight_handler)
270            .add_layer(axum::middleware::from_fn_with_state(
271                self.instance.clone(),
272                move |State(state): State<Arc<Instance>>, mut request: Request, next: Next| async move {
273                    if state.is_suspended() {
274                        let status = Status::from(servers::error::SuspendedSnafu.build());
275                        return status.into_http();
276                    }
277                    // The listener owns this marker; clients cannot set request extensions.
278                    if !external {
279                        request.extensions_mut().insert(Channel::Internal);
280                    }
281                    next.run(request).await
282                },
283            ));
284
285        let grpc_server = if !external {
286            let frontend_grpc_handler =
287                FrontendGrpcHandler::new(self.instance.process_manager().clone());
288            grpc_server.frontend_grpc_handler(frontend_grpc_handler)
289        } else {
290            grpc_server
291        }
292        .build();
293
294        Ok(grpc_server)
295    }
296
297    fn build_http_server(
298        &mut self,
299        opts: &FrontendOptions,
300        toml: String,
301        request_memory_limiter: ServerMemoryLimiter,
302    ) -> Result<(HttpServer, Option<HttpServer>)> {
303        let builder = if let Some(builder) = self.http_server_builder.take() {
304            builder
305        } else {
306            self.http_server_builder(opts, request_memory_limiter)
307        };
308
309        // The API server is configured entirely under `[http]` (`enable_api_server`,
310        // `api_server_host`, `api_server_port`) and shares every other `[http]`
311        // option with the main server.
312        let (internal, api) = builder
313            .with_metrics_handler(MetricsHandler)
314            .with_greptime_config_options(toml)
315            .build_servers();
316        Ok((internal, api))
317    }
318
319    pub fn build(mut self) -> Result<ServerHandlers> {
320        let opts = self.opts.clone();
321        let instance = self.instance.clone();
322
323        let toml = opts.to_toml().context(TomlFormatSnafu)?;
324        let opts: FrontendOptions = opts.into();
325
326        let handlers = ServerHandlers::default();
327
328        let user_provider = self.plugins.get::<UserProviderRef>();
329
330        {
331            // Always init GRPC server
332            let grpc_addr = parse_addr(&opts.grpc.bind_addr)?;
333            let grpc_server = self.build_grpc_server(
334                &opts.grpc,
335                &opts.meta_client,
336                None,
337                true,
338                self.server_memory_limiter.clone(),
339            )?;
340            handlers.insert((Box::new(grpc_server), grpc_addr));
341        }
342
343        if let Some(internal_grpc) = &opts.internal_grpc {
344            // Always init Internal GRPC server
345            let grpc_addr = parse_addr(&internal_grpc.bind_addr)?;
346            let grpc_server = self.build_grpc_server(
347                internal_grpc,
348                &opts.meta_client,
349                Some("INTERNAL_GRPC_SERVER".to_string()),
350                false,
351                self.server_memory_limiter.clone(),
352            )?;
353            handlers.insert((Box::new(grpc_server), grpc_addr));
354        }
355
356        {
357            // Always init the internal/full HTTP server (v1 + internal interfaces)
358            // and, when enabled, the dedicated HTTP API server (v1 + dashboard only).
359            let http_options = &opts.http;
360            let http_addr = parse_addr(&http_options.addr)?;
361            let (http_server, http_api_server) =
362                self.build_http_server(&opts, toml, self.server_memory_limiter.clone())?;
363            handlers.insert((Box::new(http_server), http_addr));
364
365            if let Some(http_api_server) = http_api_server {
366                let http_api_addr = parse_addr(&http_options.api_server_addr)?;
367                info!("HTTP API server is enabled at {}", http_api_addr);
368                handlers.insert((Box::new(http_api_server), http_api_addr));
369            }
370        }
371
372        let table_batcher = opts.table_batcher_options();
373        let batching_enabled = table_batcher.pending_rows_batching_enabled();
374        let mysql_batching =
375            batching_enabled && table_batcher.protocols.contains(&BatchingProtocol::Mysql);
376        let postgres_batching = batching_enabled
377            && table_batcher
378                .protocols
379                .contains(&BatchingProtocol::Postgres);
380
381        if opts.mysql.enable {
382            // Init MySQL server
383            let opts = &opts.mysql;
384            let mysql_addr = parse_addr(&opts.addr)?;
385
386            let tls_server_config = Arc::new(
387                ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
388            );
389
390            // will not watch if watch is disabled in tls option
391            maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
392
393            let mysql_server = MysqlServer::create_server(
394                common_runtime::global_runtime(),
395                Arc::new(MysqlSpawnRef::new(instance.clone(), user_provider.clone())),
396                Arc::new(
397                    MysqlSpawnConfig::new(
398                        opts.tls.should_force_tls(),
399                        tls_server_config,
400                        opts.keep_alive.as_secs(),
401                        opts.reject_no_database.unwrap_or(false),
402                        opts.prepared_stmt_cache_size,
403                    )
404                    .with_batching_enabled(mysql_batching),
405                ),
406                Some(instance.process_manager().clone()),
407            );
408            handlers.insert((mysql_server, mysql_addr));
409        }
410
411        if opts.postgres.enable {
412            // Init PosgresSQL Server
413            let opts = &opts.postgres;
414            let pg_addr = parse_addr(&opts.addr)?;
415
416            let tls_server_config = Arc::new(
417                ReloadableTlsServerConfig::try_new(opts.tls.clone()).context(StartServerSnafu)?,
418            );
419
420            maybe_watch_server_tls_config(tls_server_config.clone()).context(StartServerSnafu)?;
421
422            let pg_server = Box::new(
423                PostgresServer::new(
424                    instance.clone(),
425                    opts.tls.should_force_tls(),
426                    tls_server_config,
427                    opts.keep_alive.as_secs(),
428                    common_runtime::global_runtime(),
429                    user_provider.clone(),
430                    Some(self.instance.process_manager().clone()),
431                )
432                .with_batching_enabled(postgres_batching),
433            ) as Box<dyn Server>;
434
435            handlers.insert((pg_server, pg_addr));
436        }
437
438        Ok(handlers)
439    }
440}
441
442/// Selected shared controls override legacy Prom batching knobs, not protocol behavior.
443fn effective_prom_store_options(opts: &FrontendOptions) -> PromStoreOptions {
444    let mut prom_store = opts.prom_store.clone();
445    let shared = opts.logical_batcher_options();
446    if shared.protocols.contains(&BatchingProtocol::Prom) {
447        prom_store.pending_rows_flush_interval = shared.pending_rows_flush_interval;
448        prom_store.max_batch_rows = shared.max_batch_rows;
449        prom_store.max_concurrent_flushes = shared.max_concurrent_flushes;
450        prom_store.worker_channel_capacity = shared.worker_channel_capacity;
451        prom_store.max_inflight_requests = shared.max_inflight_requests;
452        prom_store.flow_notification_queue_capacity = shared.flow_notification_queue_capacity;
453    } else {
454        prom_store.pending_rows_flush_interval = Duration::ZERO;
455    }
456    prom_store
457}
458
459fn effective_http_options(opts: &FrontendOptions) -> HttpOptions {
460    effective_http_options_with_sync(opts, pending_rows_batch_sync_enabled())
461}
462
463fn effective_http_options_with_sync(opts: &FrontendOptions, batch_sync: bool) -> HttpOptions {
464    let mut http = opts.http.clone();
465    let prom_store = effective_prom_store_options(opts);
466    let shared = opts.table_batcher_options();
467    let common_enabled = batch_sync
468        && shared.pending_rows_batching_enabled()
469        && shared.protocols.iter().any(|protocol| {
470            !matches!(
471                protocol,
472                BatchingProtocol::Mysql | BatchingProtocol::Postgres
473            ) && (*protocol != BatchingProtocol::Prom
474                || (prom_store.enable && !prom_store.with_metric_engine))
475        });
476    let common_interval = common_enabled.then_some(shared.pending_rows_flush_interval);
477    let prom_interval = (prom_store.pending_rows_batching_enabled() && batch_sync)
478        .then_some(prom_store.pending_rows_flush_interval);
479    let logical = opts.logical_batcher_options();
480    let otlp_interval = (batch_sync
481        && opts.otlp.enable
482        && opts.prom_store.with_metric_engine
483        && logical.protocols.contains(&BatchingProtocol::Otlp)
484        && logical.pending_rows_batching_enabled())
485    .then_some(logical.pending_rows_flush_interval);
486    let Some(flush_interval) = common_interval
487        .into_iter()
488        .chain(prom_interval)
489        .chain(otlp_interval)
490        .max()
491    else {
492        return http;
493    };
494    let fallback_timeout = flush_interval.saturating_add(Duration::from_secs(1));
495    if http.timeout.is_zero() || http.timeout > fallback_timeout {
496        return http;
497    }
498
499    let configured_timeout = http.timeout;
500    http.timeout = fallback_timeout;
501    warn!(
502        ?configured_timeout,
503        ?flush_interval,
504        ?fallback_timeout,
505        "HTTP request timeout is not longer than the pending-row timeout fallback; using the fallback"
506    );
507    http
508}
509
510fn parse_addr(addr: &str) -> Result<SocketAddr> {
511    addr.parse().context(error::ParseAddrSnafu { addr })
512}
513
514#[cfg(test)]
515mod tests {
516    use std::num::NonZeroUsize;
517    use std::sync::Arc;
518    use std::sync::atomic::{AtomicUsize, Ordering};
519    use std::time::Duration;
520
521    use api::v1::HealthCheckRequest;
522    use api::v1::health_check_client::HealthCheckClient;
523    use api::v1::meta::Role;
524    use arrow_flight::{FlightData, PutResult, Ticket};
525    use async_trait::async_trait;
526    use auth::{UserProviderRef, static_user_provider_from_option};
527    use client::{Client, Database};
528    use meta_client::client::MetaClientBuilder;
529    use servers::grpc::GRPC_SERVER;
530    use servers::grpc::flight::{FlightCraft, FlightCraftRef, TonicStream};
531    use tonic::{Code, Request, Response, Status, Streaming};
532
533    use crate::instance::builder::FrontendBuilder;
534    use crate::server::*;
535
536    #[tokio::test]
537    async fn test_logical_batcher_shared_without_prom_endpoint() {
538        use crate::service_config::BatcherOptions;
539        let mut options = FrontendOptions::default();
540        options.prom_store.enable = false;
541        options.pending_rows_batcher.logical_table = Some(BatcherOptions {
542            protocols: vec![BatchingProtocol::Otlp],
543            pending_rows_flush_interval: Duration::from_millis(5),
544            ..Default::default()
545        });
546        let meta_client = Arc::new(
547            MetaClientBuilder::new(0, Role::Frontend)
548                .enable_procedure()
549                .build(),
550        );
551        let instance = Arc::new(
552            FrontendBuilder::new_test(&options, meta_client)
553                .try_build()
554                .await
555                .unwrap(),
556        );
557        let services = Services::new(options.clone(), instance.clone(), Plugins::default());
558        let batcher = instance.logical_batcher().unwrap().clone();
559        instance.init_logical_batcher(&options);
560        assert!(Arc::ptr_eq(&batcher, instance.logical_batcher().unwrap()));
561        let weak = Arc::downgrade(&instance);
562        drop(services);
563        drop(instance);
564        assert!(
565            weak.upgrade().is_none(),
566            "batcher must not retain its schema owner"
567        );
568    }
569
570    #[test]
571    fn test_logical_batcher_http_timeout_and_prom_disable() {
572        use crate::service_config::pending_rows_batcher::BatcherOptions;
573        let mut opts = FrontendOptions::default();
574        opts.http.timeout = Duration::from_secs(1);
575        opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
576        opts.pending_rows_batcher.logical_table = Some(BatcherOptions {
577            protocols: vec![BatchingProtocol::Otlp],
578            pending_rows_flush_interval: Duration::from_secs(5),
579            ..Default::default()
580        });
581        assert!(!effective_prom_store_options(&opts).pending_rows_batching_enabled());
582        // Both logical protocols follow the global acknowledgement policy,
583        // independently of enabling the Prom HTTP endpoint.
584        opts.prom_store.enable = false;
585        assert_eq!(
586            effective_http_options_with_sync(&opts, true).timeout,
587            Duration::from_secs(6)
588        );
589        assert_eq!(
590            effective_http_options_with_sync(&opts, false).timeout,
591            opts.http.timeout
592        );
593        opts.prom_store.with_metric_engine = false;
594        assert_eq!(
595            effective_http_options_with_sync(&opts, false).timeout,
596            opts.http.timeout
597        );
598        opts.prom_store.with_metric_engine = true;
599        opts.pending_rows_batcher
600            .logical_table
601            .as_mut()
602            .unwrap()
603            .protocols
604            .clear();
605        assert_eq!(
606            effective_http_options_with_sync(&opts, true).timeout,
607            opts.http.timeout
608        );
609    }
610
611    #[test]
612    fn test_effective_prom_batching_controls() {
613        // Only an enabled shared Prom selection replaces the legacy controls.
614        for (selected, shared_enabled, metric_engine, prom_enabled) in [
615            (true, true, true, true),
616            (false, true, true, true),
617            (true, false, true, true),
618            (true, true, false, true),
619            (true, true, true, false),
620        ] {
621            let mut opts = FrontendOptions::default();
622            opts.http.timeout = Duration::from_millis(1);
623            opts.prom_store.pending_rows_flush_interval = Duration::from_secs(2);
624            opts.prom_store.with_metric_engine = metric_engine;
625            opts.prom_store.enable = prom_enabled;
626            opts.prom_store
627                .experimental_enable_prometheus_native_histogram = true;
628            let shared = &mut opts.pending_rows_batcher.table;
629            shared.protocols = vec![if selected {
630                BatchingProtocol::Prom
631            } else {
632                BatchingProtocol::Influxdb
633            }];
634            shared.pending_rows_flush_interval = if shared_enabled {
635                Duration::from_secs(5)
636            } else {
637                Duration::ZERO
638            };
639            shared.max_batch_rows = 7;
640            shared.max_concurrent_flushes = 3;
641            shared.worker_channel_capacity = 11;
642            shared.max_inflight_requests = 13;
643            shared.flow_notification_queue_capacity = NonZeroUsize::new(17).unwrap();
644
645            let mut expected = opts.prom_store.clone();
646            if selected && shared_enabled {
647                expected.pending_rows_flush_interval = shared.pending_rows_flush_interval;
648                expected.max_batch_rows = shared.max_batch_rows;
649                expected.max_concurrent_flushes = shared.max_concurrent_flushes;
650                expected.worker_channel_capacity = shared.worker_channel_capacity;
651                expected.max_inflight_requests = shared.max_inflight_requests;
652                expected.flow_notification_queue_capacity = shared.flow_notification_queue_capacity;
653            }
654            let actual = effective_prom_store_options(&opts);
655            assert_eq!(actual, expected);
656            assert_eq!(
657                actual.pending_rows_batching_enabled(),
658                metric_engine && prom_enabled
659            );
660        }
661    }
662
663    #[test]
664    fn test_http_timeout_covers_synchronous_batchers() {
665        // Only synchronous batchers extend the HTTP timeout.
666        for (
667            protocols,
668            metric_engine,
669            batch_sync,
670            shared_secs,
671            legacy_secs,
672            timeout_secs,
673            expected_secs,
674        ) in [
675            (vec![BatchingProtocol::Prom], false, false, 5, 2, 1, 1),
676            (vec![BatchingProtocol::Mysql], false, false, 5, 0, 1, 1),
677            (vec![BatchingProtocol::Postgres], false, false, 5, 0, 1, 1),
678            (vec![BatchingProtocol::Mysql], false, true, 5, 0, 1, 1),
679            (vec![BatchingProtocol::Postgres], false, true, 5, 0, 1, 1),
680            (vec![BatchingProtocol::Prom], true, false, 5, 2, 1, 1),
681            (vec![BatchingProtocol::Prom], true, true, 5, 2, 1, 6),
682            (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 1, 1),
683            (vec![BatchingProtocol::Influxdb], true, true, 5, 8, 1, 9),
684            (vec![BatchingProtocol::Influxdb], true, true, 8, 5, 1, 9),
685            (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 0, 0),
686            (vec![BatchingProtocol::Influxdb], true, false, 5, 2, 10, 10),
687            (vec![BatchingProtocol::Prom], false, false, 0, 2, 1, 1),
688            (vec![], true, false, 5, 2, 1, 1),
689            (vec![], true, true, 5, 2, 1, 3),
690        ] {
691            let mut opts = FrontendOptions::default();
692            opts.http.timeout = Duration::from_secs(timeout_secs);
693            opts.prom_store.with_metric_engine = metric_engine;
694            opts.prom_store.pending_rows_flush_interval = Duration::from_secs(legacy_secs);
695            opts.pending_rows_batcher.table.protocols = protocols;
696            opts.pending_rows_batcher.table.pending_rows_flush_interval =
697                Duration::from_secs(shared_secs);
698            assert_eq!(
699                effective_http_options_with_sync(&opts, batch_sync).timeout,
700                Duration::from_secs(expected_secs)
701            );
702        }
703    }
704
705    struct CountingFlightCraft {
706        inner: FlightCraftRef,
707        do_get_calls: AtomicUsize,
708        do_put_calls: AtomicUsize,
709    }
710
711    #[async_trait]
712    impl FlightCraft for CountingFlightCraft {
713        async fn do_get(
714            &self,
715            request: Request<Ticket>,
716        ) -> std::result::Result<Response<TonicStream<FlightData>>, Status> {
717            assert_eq!(
718                request.extensions().get::<Channel>(),
719                Some(&Channel::Internal)
720            );
721            self.do_get_calls.fetch_add(1, Ordering::SeqCst);
722            self.inner.do_get(request).await
723        }
724
725        async fn do_put(
726            &self,
727            request: Request<Streaming<FlightData>>,
728        ) -> std::result::Result<Response<TonicStream<PutResult>>, Status> {
729            assert_eq!(
730                request.extensions().get::<Channel>(),
731                Some(&Channel::Internal)
732            );
733            self.do_put_calls.fetch_add(1, Ordering::SeqCst);
734            self.inner.do_put(request).await
735        }
736    }
737
738    #[test]
739    fn test_effective_http_timeout_for_pending_rows() {
740        let cases = [
741            ("disabled timeout", 0, 5000, true, true, 0),
742            ("disabled prom store", 1000, 5000, false, true, 1000),
743            ("disabled metric engine", 1000, 5000, true, false, 1000),
744            ("disabled batching", 1000, 0, true, true, 1000),
745            ("timeout below flush interval", 4000, 5000, true, true, 6000),
746            (
747                "timeout equals flush interval",
748                5000,
749                5000,
750                true,
751                true,
752                6000,
753            ),
754            ("timeout below fallback", 5500, 5000, true, true, 6000),
755            ("timeout equals fallback", 6000, 5000, true, true, 6000),
756            ("timeout above fallback", 7000, 5000, true, true, 7000),
757        ];
758
759        for (name, timeout, flush_interval, enable, with_metric_engine, expected) in cases {
760            let mut opts = FrontendOptions::default();
761            opts.http.timeout = Duration::from_millis(timeout);
762            opts.prom_store.pending_rows_flush_interval = Duration::from_millis(flush_interval);
763            opts.prom_store.enable = enable;
764            opts.prom_store.with_metric_engine = with_metric_engine;
765
766            assert_eq!(
767                Duration::from_millis(expected),
768                effective_http_options_with_sync(&opts, true).timeout,
769                "{name}"
770            );
771        }
772    }
773
774    #[test]
775    fn test_effective_http_timeout_skips_fallback_in_async_batch_mode() {
776        // With `PENDING_ROWS_BATCH_SYNC=false`, submissions return right after
777        // enqueue and no request waits for a pending-row flush, so the
778        // timeout must not be raised.
779        let mut opts = FrontendOptions::default();
780        opts.http.timeout = Duration::from_millis(1000);
781        opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
782
783        assert_eq!(
784            Duration::from_millis(1000),
785            effective_http_options_with_sync(&opts, false).timeout,
786        );
787        assert_eq!(
788            Duration::from_millis(6000),
789            effective_http_options_with_sync(&opts, true).timeout,
790        );
791    }
792
793    #[test]
794    fn test_invalid_shared_batching_preserves_prom_store_options() {
795        type KnobMutator = fn(&mut FrontendOptions);
796        let cases: [KnobMutator; 5] = [
797            |opts| opts.pending_rows_batcher.table.max_concurrent_flushes = usize::MAX,
798            |opts| opts.pending_rows_batcher.table.worker_channel_capacity = usize::MAX,
799            |opts| opts.pending_rows_batcher.table.max_inflight_requests = usize::MAX,
800            |opts| {
801                opts.pending_rows_batcher
802                    .table
803                    .flow_notification_queue_capacity = NonZeroUsize::new(usize::MAX).unwrap()
804            },
805            |opts| opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::MAX,
806        ];
807        for invalidate in cases {
808            let mut opts = FrontendOptions::default();
809            opts.http.timeout = Duration::from_secs(1);
810            opts.prom_store.pending_rows_flush_interval = Duration::from_secs(5);
811            opts.pending_rows_batcher.table.protocols =
812                vec![BatchingProtocol::Prom, BatchingProtocol::Influxdb];
813            opts.pending_rows_batcher.table.pending_rows_flush_interval = Duration::from_secs(10);
814            invalidate(&mut opts);
815            assert!(
816                !opts
817                    .pending_rows_batcher
818                    .table
819                    .pending_rows_batching_enabled()
820            );
821            assert_eq!(opts.prom_store, effective_prom_store_options(&opts));
822            assert_eq!(
823                Duration::from_secs(6),
824                effective_http_options_with_sync(&opts, true).timeout
825            );
826            opts.prom_store.pending_rows_flush_interval = Duration::ZERO;
827            assert_eq!(
828                Duration::from_secs(1),
829                effective_http_options_with_sync(&opts, true).timeout
830            );
831        }
832    }
833
834    #[test]
835    fn test_effective_http_timeout_skips_fallback_when_batcher_disabled() {
836        // Mirrors the conditions under which `LogicalTablePendingRowsBatcher::try_new`
837        // returns `None`; in these cases no request can wait for a pending-row
838        // flush, so the timeout must not be raised.
839        type KnobMutator = fn(&mut FrontendOptions);
840        let cases: [(&str, KnobMutator); 9] = [
841            ("oversized flush concurrency", |opts| {
842                opts.prom_store.max_concurrent_flushes = usize::MAX
843            }),
844            ("oversized worker channel", |opts| {
845                opts.prom_store.worker_channel_capacity = usize::MAX
846            }),
847            ("oversized inflight limit", |opts| {
848                opts.prom_store.max_inflight_requests = usize::MAX
849            }),
850            ("oversized flow queue", |opts| {
851                opts.prom_store.flow_notification_queue_capacity =
852                    std::num::NonZeroUsize::new(usize::MAX).unwrap()
853            }),
854            ("unrepresentable deadline", |opts| {
855                opts.prom_store.pending_rows_flush_interval = Duration::MAX
856            }),
857            ("zero max_batch_rows", |opts| {
858                opts.prom_store.max_batch_rows = 0
859            }),
860            ("zero max_concurrent_flushes", |opts| {
861                opts.prom_store.max_concurrent_flushes = 0
862            }),
863            ("zero worker_channel_capacity", |opts| {
864                opts.prom_store.worker_channel_capacity = 0
865            }),
866            ("zero max_inflight_requests", |opts| {
867                opts.prom_store.max_inflight_requests = 0
868            }),
869        ];
870
871        for (name, disable_batcher) in cases {
872            let mut opts = FrontendOptions::default();
873            opts.http.timeout = Duration::from_millis(1000);
874            opts.prom_store.pending_rows_flush_interval = Duration::from_millis(5000);
875            disable_batcher(&mut opts);
876
877            assert_eq!(
878                Duration::from_millis(1000),
879                effective_http_options_with_sync(&opts, true).timeout,
880                "{name}"
881            );
882        }
883    }
884
885    #[tokio::test]
886    async fn test_database_sql_authentication_differs_between_public_and_internal_grpc() {
887        let options = FrontendOptions {
888            http: HttpOptions {
889                addr: "127.0.0.1:0".to_string(),
890                ..Default::default()
891            },
892            grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
893            internal_grpc: Some(GrpcOptions::default().with_bind_addr("127.0.0.1:0")),
894            mysql: crate::service_config::MysqlOptions {
895                enable: false,
896                ..Default::default()
897            },
898            postgres: crate::service_config::PostgresOptions {
899                enable: false,
900                ..Default::default()
901            },
902            ..Default::default()
903        };
904        let meta_client = Arc::new(
905            MetaClientBuilder::new(0, Role::Frontend)
906                .enable_procedure()
907                .build(),
908        );
909        let instance = Arc::new(
910            FrontendBuilder::new_test(&options, meta_client)
911                .try_build()
912                .await
913                .unwrap(),
914        );
915        let plugins = Plugins::new();
916        let provider =
917            static_user_provider_from_option("static_user_provider:cmd:greptime=greptime").unwrap();
918        plugins.insert::<UserProviderRef>(Arc::new(provider));
919        let public_flight_handler = Arc::new(GreptimeRequestHandler::new(
920            instance.clone(),
921            plugins.get::<UserProviderRef>(),
922            None,
923            options.grpc.flight_compression,
924        )) as FlightCraftRef;
925        let internal_flight_handler = Arc::new(CountingFlightCraft {
926            inner: Arc::new(GreptimeRequestHandler::new(
927                instance.clone(),
928                None,
929                None,
930                options.grpc.flight_compression,
931            )),
932            do_get_calls: AtomicUsize::new(0),
933            do_put_calls: AtomicUsize::new(0),
934        });
935        let internal_flight_handler_ref = internal_flight_handler.clone() as FlightCraftRef;
936        let mut services = Services::new(options, instance, plugins)
937            .with_flight_handler(public_flight_handler)
938            .with_internal_flight_handler(internal_flight_handler_ref)
939            .build()
940            .unwrap();
941
942        services.start_all().await.unwrap();
943        let public_addr = services.addr(GRPC_SERVER).unwrap();
944        let internal_addr = services.addr("INTERNAL_GRPC_SERVER").unwrap();
945        let public_database = Database::new(
946            "greptime",
947            "public",
948            Client::with_urls([public_addr.to_string()]),
949        );
950        let internal_database = Database::new(
951            "greptime",
952            "public",
953            Client::with_urls([internal_addr.to_string()]),
954        );
955
956        let internal_result = internal_database.sql("SELECT 1").await;
957        let put_result = internal_database
958            .do_put(Box::pin(futures::stream::empty()))
959            .await;
960        let public_result = public_database.sql("SELECT 1").await;
961
962        services.shutdown_all().await.unwrap();
963
964        assert!(internal_result.is_ok());
965        assert!(put_result.is_ok());
966        assert_eq!(
967            1,
968            internal_flight_handler.do_get_calls.load(Ordering::SeqCst)
969        );
970        assert_eq!(
971            1,
972            internal_flight_handler.do_put_calls.load(Ordering::SeqCst)
973        );
974        assert_eq!(
975            Some(Code::Unauthenticated),
976            public_result
977                .as_ref()
978                .err()
979                .and_then(|err| err.tonic_code())
980        );
981    }
982
983    #[tokio::test]
984    async fn test_services_builder_health_check_is_reachable() {
985        // Arrange
986        let options = FrontendOptions {
987            http: HttpOptions {
988                addr: "127.0.0.1:0".to_string(),
989                ..Default::default()
990            },
991            grpc: GrpcOptions::default().with_bind_addr("127.0.0.1:0"),
992            mysql: crate::service_config::MysqlOptions {
993                enable: false,
994                ..Default::default()
995            },
996            postgres: crate::service_config::PostgresOptions {
997                enable: false,
998                ..Default::default()
999            },
1000            ..Default::default()
1001        };
1002        let meta_client = Arc::new(
1003            MetaClientBuilder::new(0, Role::Frontend)
1004                .enable_procedure()
1005                .build(),
1006        );
1007        let instance = Arc::new(
1008            FrontendBuilder::new_test(&options, meta_client)
1009                .try_build()
1010                .await
1011                .unwrap(),
1012        );
1013        let mut services = Services::new(options, instance, Default::default())
1014            .build()
1015            .unwrap();
1016
1017        // Act
1018        services.start_all().await.unwrap();
1019        let addr = services.addr(GRPC_SERVER).unwrap();
1020        let health_check = HealthCheckClient::connect(format!("http://{addr}"))
1021            .await
1022            .unwrap()
1023            .health_check(HealthCheckRequest {})
1024            .await;
1025        services.shutdown_all().await.unwrap();
1026
1027        // Assert
1028        assert!(health_check.is_ok());
1029    }
1030}