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