Skip to main content

servers/
grpc.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
15pub mod builder;
16mod cancellation;
17pub mod context_auth;
18mod database;
19pub mod flight;
20pub mod frontend_grpc_handler;
21pub mod greptime_handler;
22pub mod memory_limit;
23pub mod prom_query_gateway;
24pub mod region_server;
25
26use std::any::Any;
27use std::net::{IpAddr, SocketAddr};
28use std::time::Duration;
29
30use api::v1::health_check_server::{HealthCheck, HealthCheckServer};
31use api::v1::{HealthCheckRequest, HealthCheckResponse};
32use async_trait::async_trait;
33use common_base::readable_size::ReadableSize;
34use common_grpc::channel_manager::{
35    DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE, DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
36};
37use common_telemetry::{error, info, warn};
38use futures::FutureExt;
39use otel_arrow_rust::proto::opentelemetry::arrow::v1::arrow_metrics_service_server::ArrowMetricsServiceServer;
40use serde::{Deserialize, Serialize};
41use snafu::{OptionExt, ResultExt, ensure};
42use tokio::net::TcpListener;
43use tokio::sync::Mutex;
44use tokio::sync::oneshot::{self, Receiver, Sender};
45use tonic::service::Routes;
46use tonic::service::interceptor::InterceptedService;
47use tonic::transport::ServerTlsConfig;
48use tonic::transport::server::TcpIncoming;
49use tonic::{Request, Response, Status};
50use tonic_reflection::server::v1::{ServerReflection, ServerReflectionServer};
51
52use crate::error::{AlreadyStartedSnafu, InternalSnafu, Result, StartGrpcSnafu, TcpBindSnafu};
53use crate::grpc::memory_limit::MemoryLimiterExtensionService;
54use crate::install_default_crypto_provider;
55use crate::metrics::MetricsMiddlewareLayer;
56use crate::otel_arrow::{HeaderInterceptor, OtelArrowServiceHandler};
57use crate::query_handler::OpenTelemetryProtocolHandlerRef;
58use crate::server::Server;
59use crate::tls::TlsOption;
60
61type TonicResult<T> = std::result::Result<T, Status>;
62
63#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
64#[serde(default)]
65pub struct GrpcOptions {
66    /// The address to bind the gRPC server.
67    pub bind_addr: String,
68    /// The address to advertise to clients.
69    pub server_addr: String,
70    /// Max gRPC receiving(decoding) message size
71    pub max_recv_message_size: ReadableSize,
72    /// Max gRPC sending(encoding) message size
73    pub max_send_message_size: ReadableSize,
74    /// Compression mode in Arrow Flight service.
75    pub flight_compression: FlightCompression,
76    pub runtime_size: usize,
77    #[serde(default = "Default::default")]
78    pub tls: TlsOption,
79    /// Maximum time that a channel may exist.
80    /// Useful when the server wants to control the reconnection of its clients.
81    /// Default to `None`, means infinite.
82    #[serde(with = "humantime_serde")]
83    pub max_connection_age: Option<Duration>,
84    /// The HTTP/2 keep-alive interval.
85    #[serde(with = "humantime_serde")]
86    pub http2_keep_alive_interval: Duration,
87    /// The HTTP/2 keep-alive timeout.
88    #[serde(with = "humantime_serde")]
89    pub http2_keep_alive_timeout: Duration,
90}
91
92impl GrpcOptions {
93    /// Detect the server address.
94    #[cfg(not(target_os = "android"))]
95    pub fn detect_server_addr(&mut self) {
96        if self.server_addr.is_empty() {
97            match local_ip_address::local_ip() {
98                Ok(ip) => {
99                    let port = port_from_bind_addr(&self.bind_addr);
100                    let detected_addr = format_server_addr(ip, port);
101                    info!("Using detected: {} as server address", detected_addr);
102                    self.server_addr = detected_addr;
103                }
104                Err(e) => {
105                    error!("Failed to detect local ip address: {}", e);
106                }
107            }
108        }
109    }
110
111    #[cfg(target_os = "android")]
112    pub fn detect_server_addr(&mut self) {
113        if self.server_addr.is_empty() {
114            common_telemetry::debug!("detect local IP is not supported on Android");
115        }
116    }
117
118    /// Create a [GrpcServerConfig] from self's options.
119    pub fn as_config(&self) -> GrpcServerConfig {
120        GrpcServerConfig {
121            max_recv_message_size: self.max_recv_message_size.as_bytes() as usize,
122            max_send_message_size: self.max_send_message_size.as_bytes() as usize,
123            tls: self.tls.clone(),
124            max_connection_age: self.max_connection_age,
125        }
126    }
127}
128
129const DEFAULT_GRPC_ADDR_PORT: u16 = 4001;
130
131fn port_from_bind_addr(bind_addr: &str) -> u16 {
132    bind_addr
133        .rsplit_once(':')
134        .and_then(|(_, port)| port.parse().ok())
135        .unwrap_or(DEFAULT_GRPC_ADDR_PORT)
136}
137
138fn format_server_addr(ip: IpAddr, port: u16) -> String {
139    SocketAddr::new(ip, port).to_string()
140}
141
142const DEFAULT_INTERNAL_GRPC_ADDR_PORT: &str = "4010";
143
144impl Default for GrpcOptions {
145    fn default() -> Self {
146        Self {
147            bind_addr: format!("127.0.0.1:{}", DEFAULT_GRPC_ADDR_PORT),
148            // If hostname is not set, the server will use the local ip address as the hostname.
149            server_addr: String::new(),
150            max_recv_message_size: DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE,
151            max_send_message_size: DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
152            flight_compression: FlightCompression::ArrowIpc,
153            runtime_size: 8,
154            tls: TlsOption::default(),
155            max_connection_age: None,
156            http2_keep_alive_interval: Duration::from_secs(10),
157            http2_keep_alive_timeout: Duration::from_secs(3),
158        }
159    }
160}
161
162impl GrpcOptions {
163    /// Default options for internal gRPC server.
164    /// The internal gRPC server is used for communication between different nodes in cluster.
165    /// It is not exposed to the outside world.
166    pub fn internal_default() -> Self {
167        Self {
168            bind_addr: format!("127.0.0.1:{}", DEFAULT_INTERNAL_GRPC_ADDR_PORT),
169            // If hostname is not set, the server will use the local ip address as the hostname.
170            server_addr: format!("127.0.0.1:{}", DEFAULT_INTERNAL_GRPC_ADDR_PORT),
171            max_recv_message_size: DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE,
172            max_send_message_size: DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE,
173            flight_compression: FlightCompression::ArrowIpc,
174            runtime_size: 8,
175            tls: TlsOption::default(),
176            max_connection_age: None,
177            http2_keep_alive_interval: Duration::from_secs(10),
178            http2_keep_alive_timeout: Duration::from_secs(3),
179        }
180    }
181
182    pub fn with_bind_addr(mut self, bind_addr: &str) -> Self {
183        self.bind_addr = bind_addr.to_string();
184        self
185    }
186
187    pub fn with_server_addr(mut self, server_addr: &str) -> Self {
188        self.server_addr = server_addr.to_string();
189        self
190    }
191}
192
193#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
194#[serde(rename_all = "snake_case")]
195pub enum FlightCompression {
196    /// Disable all compression in Arrow Flight service.
197    #[default]
198    None,
199    /// Enable only transport layer compression (zstd).
200    Transport,
201    /// Enable only payload compression (lz4)
202    ArrowIpc,
203    /// Enable all compression.
204    All,
205}
206
207impl FlightCompression {
208    pub fn transport_compression(&self) -> bool {
209        self == &FlightCompression::Transport || self == &FlightCompression::All
210    }
211
212    pub fn arrow_compression(&self) -> bool {
213        self == &FlightCompression::ArrowIpc || self == &FlightCompression::All
214    }
215}
216
217/// The wrapped OTLP Arrow service type used by [`GrpcServer`].
218type OtelArrowService = MemoryLimiterExtensionService<
219    InterceptedService<
220        ArrowMetricsServiceServer<OtelArrowServiceHandler<OpenTelemetryProtocolHandlerRef>>,
221        HeaderInterceptor,
222    >,
223>;
224
225pub struct GrpcServer {
226    // states
227    shutdown_tx: Mutex<Option<Sender<()>>>,
228    /// gRPC serving state receiver. Only present if the gRPC server is started.
229    /// Used to wait for the server to stop, performing the old blocking fashion.
230    serve_state: Mutex<Option<Receiver<Result<()>>>>,
231    // handlers
232    routes: Mutex<Option<Routes>>,
233    // tls config
234    tls_config: Option<ServerTlsConfig>,
235    // Otel arrow service
236    otel_arrow_service: Mutex<Option<OtelArrowService>>,
237    bind_addr: Option<SocketAddr>,
238    name: Option<String>,
239    config: GrpcServerConfig,
240}
241
242/// Grpc Server configuration
243#[derive(Debug, Clone)]
244pub struct GrpcServerConfig {
245    // Max gRPC receiving(decoding) message size
246    pub max_recv_message_size: usize,
247    // Max gRPC sending(encoding) message size
248    pub max_send_message_size: usize,
249    pub tls: TlsOption,
250    /// Maximum time that a channel may exist.
251    /// Useful when the server wants to control the reconnection of its clients.
252    /// Default to `None`, means infinite.
253    pub max_connection_age: Option<Duration>,
254}
255
256impl Default for GrpcServerConfig {
257    fn default() -> Self {
258        Self {
259            max_recv_message_size: DEFAULT_MAX_GRPC_RECV_MESSAGE_SIZE.as_bytes() as usize,
260            max_send_message_size: DEFAULT_MAX_GRPC_SEND_MESSAGE_SIZE.as_bytes() as usize,
261            tls: TlsOption::default(),
262            max_connection_age: None,
263        }
264    }
265}
266
267impl GrpcServer {
268    pub fn create_healthcheck_service(&self) -> HealthCheckServer<impl HealthCheck> {
269        HealthCheckServer::new(HealthCheckHandler)
270    }
271
272    pub fn create_reflection_service(&self) -> ServerReflectionServer<impl ServerReflection> {
273        tonic_reflection::server::Builder::configure()
274            .register_encoded_file_descriptor_set(api::v1::GREPTIME_GRPC_DESC)
275            .with_service_name("greptime.v1.GreptimeDatabase")
276            .with_service_name("greptime.v1.HealthCheck")
277            .with_service_name("greptime.v1.RegionServer")
278            .build_v1()
279            .inspect_err(|e| {
280                common_telemetry::error!(e; "Failed to build gRPC reflection server");
281            })
282            .unwrap()
283    }
284
285    pub async fn wait_for_serve(&self) -> Result<()> {
286        let mut serve_state = self.serve_state.lock().await;
287        let rx = serve_state.take().context(InternalSnafu {
288            err_msg: "gRPC serving state is unknown, maybe the server is not started, \
289                      or we have already waited for the serve result before.",
290        })?;
291        let Ok(result) = rx.await else {
292            warn!("Background gRPC serving task is quited before we can receive the serve result.");
293            return Ok(());
294        };
295        if let Err(e) = result {
296            error!(e; "GRPC serve error");
297        }
298        Ok(())
299    }
300}
301
302pub struct HealthCheckHandler;
303
304#[async_trait]
305impl HealthCheck for HealthCheckHandler {
306    async fn health_check(
307        &self,
308        _req: Request<HealthCheckRequest>,
309    ) -> TonicResult<Response<HealthCheckResponse>> {
310        Ok(Response::new(HealthCheckResponse {}))
311    }
312}
313
314pub const GRPC_SERVER: &str = "GRPC_SERVER";
315
316#[async_trait]
317impl Server for GrpcServer {
318    async fn shutdown(&self) -> Result<()> {
319        let mut shutdown_tx = self.shutdown_tx.lock().await;
320        if let Some(tx) = shutdown_tx.take()
321            && tx.send(()).is_err()
322        {
323            info!("Receiver dropped, the grpc server has already exited");
324        }
325        info!("Shutdown grpc server");
326
327        Ok(())
328    }
329
330    async fn start(&mut self, addr: SocketAddr) -> Result<()> {
331        let routes = {
332            let mut routes = self.routes.lock().await;
333            let Some(routes) = routes.take() else {
334                return AlreadyStartedSnafu {
335                    server: self.name(),
336                }
337                .fail();
338            };
339            routes
340        };
341
342        let (tx, rx) = oneshot::channel();
343        let (incoming, addr) = {
344            let mut shutdown_tx = self.shutdown_tx.lock().await;
345            ensure!(
346                shutdown_tx.is_none(),
347                AlreadyStartedSnafu { server: "gRPC" }
348            );
349
350            let listener = TcpListener::bind(addr)
351                .await
352                .context(TcpBindSnafu { addr })?;
353            let addr = listener.local_addr().context(TcpBindSnafu { addr })?;
354            let incoming = TcpIncoming::from(listener).with_nodelay(Some(true));
355            info!("gRPC server(name={}) is bound to {}", self.name(), addr);
356
357            *shutdown_tx = Some(tx);
358
359            (incoming, addr)
360        };
361
362        let metrics_layer = tower::ServiceBuilder::new()
363            .layer(MetricsMiddlewareLayer)
364            .into_inner();
365
366        let mut builder = tonic::transport::Server::builder()
367            .accept_http1(true)
368            .layer(metrics_layer)
369            .layer(tonic_web::GrpcWebLayer::new());
370
371        if let Some(tls_config) = self.tls_config.clone() {
372            // tonic builds the underlying rustls server config here, which requires a
373            // process-level crypto provider to be installed first.
374            if let Err(err) = install_default_crypto_provider() {
375                warn!("Failed to install default rustls crypto provider: {err}");
376            }
377            builder = builder.tls_config(tls_config).context(StartGrpcSnafu)?;
378        }
379
380        if let Some(max_connection_age) = self.config.max_connection_age {
381            builder = builder.max_connection_age(max_connection_age);
382        }
383
384        let mut builder = builder
385            .add_routes(routes)
386            .add_service(self.create_healthcheck_service())
387            .add_service(self.create_reflection_service());
388
389        if let Some(otel_arrow_service) = self.otel_arrow_service.lock().await.take() {
390            builder = builder.add_service(otel_arrow_service);
391        }
392
393        let (serve_state_tx, serve_state_rx) = oneshot::channel();
394        let mut serve_state = self.serve_state.lock().await;
395        *serve_state = Some(serve_state_rx);
396
397        let _handle = common_runtime::spawn_global(async move {
398            let result = builder
399                .serve_with_incoming_shutdown(incoming, rx.map(drop))
400                .await
401                .context(StartGrpcSnafu);
402            serve_state_tx.send(result)
403        });
404
405        self.bind_addr = Some(addr);
406        Ok(())
407    }
408
409    fn name(&self) -> &str {
410        if let Some(name) = &self.name {
411            name
412        } else {
413            GRPC_SERVER
414        }
415    }
416
417    fn bind_addr(&self) -> Option<SocketAddr> {
418        self.bind_addr
419    }
420
421    fn as_any(&self) -> &dyn Any {
422        self
423    }
424}
425
426#[cfg(test)]
427mod tests {
428    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
429
430    use super::{DEFAULT_GRPC_ADDR_PORT, format_server_addr, port_from_bind_addr};
431
432    #[test]
433    fn test_port_from_bind_addr() {
434        assert_eq!(3002, port_from_bind_addr("127.0.0.1:3002"));
435        assert_eq!(3002, port_from_bind_addr("[::]:3002"));
436        assert_eq!(
437            3002,
438            port_from_bind_addr("greptimedb-metasrv.default.svc.cluster.local:3002")
439        );
440        assert_eq!(
441            DEFAULT_GRPC_ADDR_PORT,
442            port_from_bind_addr("invalid-bind-addr")
443        );
444    }
445
446    #[test]
447    fn test_format_server_addr() {
448        assert_eq!(
449            "127.0.0.1:3002",
450            format_server_addr(IpAddr::V4(Ipv4Addr::LOCALHOST), 3002)
451        );
452        assert_eq!(
453            "[::1]:3002",
454            format_server_addr(IpAddr::V6(Ipv6Addr::LOCALHOST), 3002)
455        );
456    }
457}