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