Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: introduce AppBuilder trait #3941

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/cmd/src/bin/greptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::fmt;
use clap::{Parser, Subcommand};
use cmd::error::Result;
use cmd::options::{GlobalOptions, Options};
use cmd::{cli, datanode, frontend, log_versions, metasrv, standalone, start_app, App};
use cmd::{cli, datanode, frontend, log_versions, metasrv, standalone, App};
use common_version::{short_version, version};

#[derive(Parser)]
Expand Down Expand Up @@ -134,9 +134,7 @@ async fn start(cli: Command) -> Result<()> {

log_versions(version!(), short_version!());

let app = subcmd.build(opts).await?;

start_app(app).await
subcmd.build(opts).await?.run().await
}

fn setup_human_panic() {
Expand Down
51 changes: 34 additions & 17 deletions src/cmd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use async_trait::async_trait;
use common_telemetry::{error, info};

use crate::error::Result;

pub mod cli;
pub mod datanode;
pub mod error;
Expand All @@ -35,39 +37,54 @@ pub trait App: Send {
fn name(&self) -> &str;

/// A hook for implementor to make something happened before actual startup. Defaults to no-op.
async fn pre_start(&mut self) -> error::Result<()> {
async fn pre_start(&mut self) -> Result<()> {
Ok(())
}

async fn start(&mut self) -> error::Result<()>;
async fn start(&mut self) -> Result<()>;

/// Waits the quit signal by default.
fn wait_signal(&self) -> bool {
true
}

async fn stop(&self) -> error::Result<()>;
}
async fn stop(&self) -> Result<()>;

pub async fn start_app(mut app: Box<dyn App>) -> error::Result<()> {
info!("Starting app: {}", app.name());
async fn run(&mut self) -> Result<()> {
info!("Starting app: {}", self.name());

app.pre_start().await?;
self.pre_start().await?;

app.start().await?;
self.start().await?;

if app.wait_signal() {
if let Err(e) = tokio::signal::ctrl_c().await {
error!("Failed to listen for ctrl-c signal: {}", e);
// It's unusual to fail to listen for ctrl-c signal, maybe there's something unexpected in
// the underlying system. So we stop the app instead of running nonetheless to let people
// investigate the issue.
if self.wait_signal() {
if let Err(e) = tokio::signal::ctrl_c().await {
error!("Failed to listen for ctrl-c signal: {}", e);
// It's unusual to fail to listen for ctrl-c signal, maybe there's something unexpected in
// the underlying system. So we stop the app instead of running nonetheless to let people
// investigate the issue.
}
}

self.stop().await?;
info!("Goodbye!");
Ok(())
}
}

app.stop().await?;
info!("Goodbye!");
Ok(())
/// AppBuilder is a trait builds the App from the options and starts the App.
#[async_trait]
pub trait AppBuilder: Default {
/// Build the options. The final options will be merged from multiple sources(e.g. config file, cli arguments) and stored in the builder.
fn build_options(self) -> Result<Self>;

/// Build the App. After the options are built, the App can be built from the options.
async fn build_app(self) -> Result<Box<dyn App>>;

/// Build the options and app, then start the app.
async fn start(self) -> Result<()> {
self.build_options()?.build_app().await?.run().await
}
}

/// Log the versions of the application, and the arguments passed to the cli.
Expand Down