From 246fc989f0b46884afe8b6b41604d6c2db4dd335 Mon Sep 17 00:00:00 2001 From: YoursFunny Date: Thu, 13 Aug 2026 22:26:16 +0800 Subject: [PATCH] feat(main): bound graceful shutdown with a 30s timeout The stop sequence awaited the queue workers, which can be mid-download (30s client timeout) or mid-ugoira encode (minutes). A stuck worker would hold shutdown forever; now the process logs and exits after 30s. --- crates/xmedia-bot/src/main.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/xmedia-bot/src/main.rs b/crates/xmedia-bot/src/main.rs index f5ef781..cf2596a 100644 --- a/crates/xmedia-bot/src/main.rs +++ b/crates/xmedia-bot/src/main.rs @@ -178,13 +178,22 @@ async fn main() { .await; } - // Graceful stop (Ctrl+C / SIGTERM): stop the sweep, notify the admin, drain the queue. + // Graceful stop (Ctrl+C / SIGTERM): stop the sweep, notify the admin, + // drain the queue. Bounded: a worker mid-download (30 s timeout) or a + // long ugoira encode must not hold the shutdown hostage forever. log::info!("Stopping bot"); - let _ = stop_tx.send(true); - handlers::stop_url_workers(); - if let Some(admin) = CONFIG.admin_ids.first() { - let _ = bot.send_message(ChatId(*admin), "Shutting down...").await; + const SHUTDOWN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30); + let shutdown = async { + let _ = stop_tx.send(true); + handlers::stop_url_workers(); + if let Some(admin) = CONFIG.admin_ids.first() { + let _ = bot.send_message(ChatId(*admin), "Shutting down...").await; + } + TASK_QUEUE.stop().await; + }; + if tokio::time::timeout(SHUTDOWN_TIMEOUT, shutdown).await.is_err() { + log::warn!("graceful shutdown timed out after {SHUTDOWN_TIMEOUT:?}; exiting"); + } else { + log::info!("Bot stopped"); } - TASK_QUEUE.stop().await; - log::info!("Bot stopped"); }