xecute-cli 0.1.3

NestJS-style project and resource generator for Xecute
//! Server startup, socket peer information, and bounded graceful request draining.

use super::{logging, shutdown};
use crate::{app_module, configs::Config};
use std::{future::IntoFuture, sync::Arc};
use tokio::sync::Notify;

/// Start the configured listener and stop accepting work on a termination signal.
/// Active requests drain until the configured grace period expires.
pub async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    crate::configs::load_env()?;
    logging::init()?;
    let config = Config::from_env()?;
    let app = super::router(app_module::AppModule::router()?, &config);
    let listener = tokio::net::TcpListener::bind(config.bind_address).await?;
    tracing::info!(address = %listener.local_addr()?, "server listening");
    let draining = Arc::new(Notify::new());
    let shutdown_started = draining.clone();
    let server = axum::serve(
        listener,
        app.into_make_service_with_connect_info::<std::net::SocketAddr>(),
    )
    .with_graceful_shutdown(async move {
        shutdown::signal().await;
        tracing::info!("shutdown requested; draining requests");
        shutdown_started.notify_one();
    })
    .into_future();
    tokio::pin!(server);
    tokio::select! {
        result = &mut server => result?,
        _ = draining.notified() => {
            match tokio::time::timeout(config.shutdown_grace, &mut server).await {
                Ok(result) => result?,
                Err(error) => {
                    tracing::error!("shutdown drain deadline exceeded");
                    return Err(error.into());
                }
            }
        }
    }
    tracing::info!("server stopped");
    Ok(())
}