Skip to main content

greptime/
greptime.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#![recursion_limit = "256"]
16#![doc = include_str!("../../../../README.md")]
17
18use clap::{Parser, Subcommand};
19use cmd::datanode::builder::InstanceBuilder;
20#[cfg(unix)]
21use cmd::error::DaemonizeSnafu;
22use cmd::error::{InitTlsProviderSnafu, Result};
23use cmd::options::GlobalOptions;
24use cmd::{App, cli, datanode, flownode, frontend, metasrv, standalone, user};
25use common_base::Plugins;
26use common_version::{product_name, verbose_version, version};
27use servers::install_default_crypto_provider;
28
29#[derive(Parser)]
30#[command(name = product_name(), author, version, long_version = verbose_version(), about)]
31#[command(propagate_version = true)]
32pub(crate) struct Command {
33    #[clap(subcommand)]
34    pub(crate) subcmd: SubCommand,
35
36    #[clap(flatten)]
37    pub(crate) global_options: GlobalOptions,
38}
39
40#[derive(Subcommand)]
41enum SubCommand {
42    /// Start datanode service.
43    #[clap(name = "datanode")]
44    Datanode(datanode::Command),
45
46    /// Start flownode service.
47    #[clap(name = "flownode")]
48    Flownode(flownode::Command),
49
50    /// Start frontend service.
51    #[clap(name = "frontend")]
52    Frontend(frontend::Command),
53
54    /// Start metasrv service.
55    #[clap(name = "metasrv")]
56    Metasrv(metasrv::Command),
57
58    /// Start service in standalone mode.
59    #[clap(name = "standalone")]
60    Standalone(standalone::Command),
61
62    /// Execute the cli tools.
63    #[clap(name = "cli")]
64    Cli(cli::Command),
65
66    /// Manage user credentials.
67    #[clap(name = "user")]
68    User(user::Command),
69}
70
71#[cfg(not(windows))]
72#[global_allocator]
73static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
74
75#[cfg(debug_assertions)]
76fn main() -> Result<()> {
77    use snafu::ResultExt;
78
79    let cli = Command::parse();
80    maybe_daemonize(&cli)?;
81
82    // Set the stack size to 8MB for the thread so it wouldn't overflow on large stack usage in debug mode
83    // see https://github.com/GreptimeTeam/greptimedb/pull/4317
84    // and https://github.com/rust-lang/rust/issues/34283
85    std::thread::Builder::new()
86        .name("main_spawn".to_string())
87        .stack_size(8 * 1024 * 1024)
88        .spawn(move || {
89            {
90                tokio::runtime::Builder::new_multi_thread()
91                    .thread_stack_size(8 * 1024 * 1024)
92                    .enable_all()
93                    .build()
94                    .expect("Failed building the Runtime")
95                    .block_on(main_body(cli))
96            }
97        })
98        .context(cmd::error::SpawnThreadSnafu)?
99        .join()
100        .expect("Couldn't join on the associated thread")
101}
102
103#[cfg(not(debug_assertions))]
104fn main() -> Result<()> {
105    let cli = Command::parse();
106    maybe_daemonize(&cli)?;
107
108    tokio::runtime::Builder::new_multi_thread()
109        .enable_all()
110        .build()
111        .expect("Failed building the Runtime")
112        .block_on(main_body(cli))
113}
114
115async fn main_body(cli: Command) -> Result<()> {
116    setup_human_panic();
117    install_default_crypto_provider().map_err(|msg| InitTlsProviderSnafu { msg }.build())?;
118    start(cli).await
119}
120
121/// If `--daemon` was passed for `standalone start`, fork into the background
122/// before any Tokio runtime is built. On non-Unix platforms the `--daemon`
123/// flag does not exist, so this is a no-op.
124fn maybe_daemonize(cli: &Command) -> Result<()> {
125    let daemon = matches!(&cli.subcmd, SubCommand::Standalone(cmd) if cmd.is_daemon());
126    if !daemon {
127        return Ok(());
128    }
129
130    #[cfg(unix)]
131    {
132        let cwd = std::env::current_dir().map_err(|e| {
133            DaemonizeSnafu {
134                msg: format!("failed to read current working directory: {e}"),
135            }
136            .build()
137        })?;
138
139        daemonize::Daemonize::new()
140            .working_directory(cwd)
141            .start()
142            .map_err(|e| {
143                DaemonizeSnafu {
144                    msg: format!("failed to daemonize: {e}"),
145                }
146                .build()
147            })?;
148    }
149
150    Ok(())
151}
152
153async fn start(cli: Command) -> Result<()> {
154    match cli.subcmd {
155        SubCommand::Datanode(cmd) => match cmd.subcmd {
156            datanode::SubCommand::Start(ref start) => {
157                let opts = start.load_options(&cli.global_options)?;
158                let plugins = Plugins::new();
159                let builder = InstanceBuilder::try_new_with_init(opts, plugins).await?;
160                cmd.build_with(builder).await?.run().await
161            }
162            datanode::SubCommand::Objbench(ref bench) => bench.run().await,
163            datanode::SubCommand::Scanbench(ref bench) => bench.run().await,
164            datanode::SubCommand::Parquetbench(ref bench) => bench.run().await,
165            #[cfg(feature = "dev-tools")]
166            datanode::SubCommand::ParquetMeta(ref cmd) => cmd.run().await,
167            #[cfg(feature = "dev-tools")]
168            datanode::SubCommand::ParquetRewrite(ref cmd) => cmd.run().await,
169            #[cfg(feature = "dev-tools")]
170            datanode::SubCommand::SstReplace(ref cmd) => cmd.run().await,
171        },
172        SubCommand::Flownode(cmd) => {
173            cmd.build(cmd.load_options(&cli.global_options)?)
174                .await?
175                .run()
176                .await
177        }
178        SubCommand::Frontend(cmd) => {
179            cmd.build(cmd.load_options(&cli.global_options)?)
180                .await?
181                .run()
182                .await
183        }
184        SubCommand::Metasrv(cmd) => {
185            cmd.build(cmd.load_options(&cli.global_options)?)
186                .await?
187                .run()
188                .await
189        }
190        SubCommand::Standalone(cmd) => {
191            cmd.build(cmd.load_options(&cli.global_options)?)
192                .await?
193                .run()
194                .await
195        }
196        SubCommand::Cli(cmd) => {
197            cmd.build(cmd.load_options(&cli.global_options)?)
198                .await?
199                .run()
200                .await
201        }
202        SubCommand::User(cmd) => cmd.run(),
203    }
204}
205
206fn setup_human_panic() {
207    human_panic::setup_panic!(
208        human_panic::Metadata::new(product_name(), version())
209            .homepage("https://github.com/GreptimeTeam/greptimedb/discussions")
210    );
211
212    common_telemetry::set_panic_hook();
213}