cmd/
lib.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
15#![feature(assert_matches, let_chains)]
16
17use async_trait::async_trait;
18use common_telemetry::{error, info};
19use stat::{get_cpu_limit, get_memory_limit};
20
21use crate::error::Result;
22
23pub mod cli;
24pub mod datanode;
25pub mod error;
26pub mod flownode;
27pub mod frontend;
28pub mod metasrv;
29pub mod options;
30pub mod standalone;
31
32lazy_static::lazy_static! {
33    static ref APP_VERSION: prometheus::IntGaugeVec =
34        prometheus::register_int_gauge_vec!("greptime_app_version", "app version", &["version", "short_version", "app"]).unwrap();
35
36    static ref CPU_LIMIT: prometheus::IntGaugeVec =
37        prometheus::register_int_gauge_vec!("greptime_cpu_limit_in_millicores", "cpu limit in millicores", &["app"]).unwrap();
38
39    static ref MEMORY_LIMIT: prometheus::IntGaugeVec =
40        prometheus::register_int_gauge_vec!("greptime_memory_limit_in_bytes", "memory limit in bytes", &["app"]).unwrap();
41}
42
43/// wait for the close signal, for unix platform it's SIGINT or SIGTERM
44#[cfg(unix)]
45async fn start_wait_for_close_signal() -> std::io::Result<()> {
46    use tokio::signal::unix::{signal, SignalKind};
47    let mut sigint = signal(SignalKind::interrupt())?;
48    let mut sigterm = signal(SignalKind::terminate())?;
49
50    tokio::select! {
51        _ = sigint.recv() => {
52            info!("Received SIGINT, shutting down");
53        }
54        _ = sigterm.recv() => {
55            info!("Received SIGTERM, shutting down");
56        }
57    }
58
59    Ok(())
60}
61
62/// wait for the close signal, for non-unix platform it's ctrl-c
63#[cfg(not(unix))]
64async fn start_wait_for_close_signal() -> std::io::Result<()> {
65    tokio::signal::ctrl_c().await
66}
67
68#[async_trait]
69pub trait App: Send {
70    fn name(&self) -> &str;
71
72    /// A hook for implementor to make something happened before actual startup. Defaults to no-op.
73    async fn pre_start(&mut self) -> Result<()> {
74        Ok(())
75    }
76
77    async fn start(&mut self) -> Result<()>;
78
79    /// Waits the quit signal by default.
80    fn wait_signal(&self) -> bool {
81        true
82    }
83
84    async fn stop(&mut self) -> Result<()>;
85
86    async fn run(&mut self) -> Result<()> {
87        info!("Starting app: {}", self.name());
88
89        self.pre_start().await?;
90
91        self.start().await?;
92
93        if self.wait_signal() {
94            if let Err(e) = start_wait_for_close_signal().await {
95                error!(e; "Failed to listen for close signal");
96                // It's unusual to fail to listen for close signal, maybe there's something unexpected in
97                // the underlying system. So we stop the app instead of running nonetheless to let people
98                // investigate the issue.
99            }
100        }
101
102        self.stop().await?;
103        info!("Goodbye!");
104        Ok(())
105    }
106}
107
108/// Log the versions of the application, and the arguments passed to the cli.
109///
110/// `version` should be the same as the output of cli "--version";
111/// and the `short_version` is the short version of the codes, often consist of git branch and commit.
112pub fn log_versions(version: &str, short_version: &str, app: &str) {
113    // Report app version as gauge.
114    APP_VERSION
115        .with_label_values(&[env!("CARGO_PKG_VERSION"), short_version, app])
116        .inc();
117
118    // Log version and argument flags.
119    info!("GreptimeDB version: {}", version);
120
121    log_env_flags();
122}
123
124pub fn create_resource_limit_metrics(app: &str) {
125    if let Some(cpu_limit) = get_cpu_limit() {
126        info!(
127            "GreptimeDB start with cpu limit in millicores: {}",
128            cpu_limit
129        );
130        CPU_LIMIT.with_label_values(&[app]).set(cpu_limit);
131    }
132
133    if let Some(memory_limit) = get_memory_limit() {
134        info!(
135            "GreptimeDB start with memory limit in bytes: {}",
136            memory_limit
137        );
138        MEMORY_LIMIT.with_label_values(&[app]).set(memory_limit);
139    }
140}
141
142fn log_env_flags() {
143    info!("command line arguments");
144    for argument in std::env::args() {
145        info!("argument: {}", argument);
146    }
147}