From 2e373836a63435975ed8d0b1db1dc6156bdcdb74 Mon Sep 17 00:00:00 2001 From: Evgeny Formanenko Date: Wed, 22 Jul 2026 11:59:02 +0300 Subject: [PATCH] fix(hotblocks-retain): act on SIGTERM instead of waiting for the kill The process runs as PID 1 in its container, and Linux gives PID 1 no default signal dispositions -- a signal with no handler installed is ignored outright rather than being fatal. So SIGTERM did nothing here, and the loop sleeps up to an hour between passes, which left SIGKILL at the termination grace as the only way this ever stopped. Measured on testnet-dev by polling a pod through its termination: this container exits 137 while its neighbours exit 0 and 1. It cost 5s per rotation when the grace was 5s and nobody noticed; now that hotblocks needs a real grace it holds every pod for the full 60s, long after the drain is done at ~25s. Exiting straight away is safe: retention is applied over HTTP and nothing is held locally, so a pass cut mid-flight is simply recomputed from the scheduler status on the next start. SIGINT is left on the default handler, matching the hotblocks binary -- a dev Ctrl-C is not PID 1 and already works. --- crates/hotblocks-retain/src/main.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/crates/hotblocks-retain/src/main.rs b/crates/hotblocks-retain/src/main.rs index 7eceeba4..bfcaedb5 100644 --- a/crates/hotblocks-retain/src/main.rs +++ b/crates/hotblocks-retain/src/main.rs @@ -12,7 +12,10 @@ use std::{ use anyhow::Context; use clap::Parser; use cli::Cli; -use tokio::time::Instant; +use tokio::{ + signal::unix::{SignalKind, signal}, + time::Instant +}; use types::{DatasetId, DatasetsConfig}; use url::Url; @@ -31,17 +34,29 @@ fn main() -> anyhow::Result<()> { tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? - .block_on( - HotblocksRetain::new( + .block_on(async { + let mut retain = HotblocksRetain::new( args.hotblocks_url, args.status_url, args.datasets_url, datasets, Duration::from_secs(args.datasets_update_interval_secs), Duration::from_secs(args.retain_delay_secs) - ) - .run() - )?; + ); + + // We run as PID 1, which Linux gives no default signal dispositions: without a + // handler installed SIGTERM is ignored outright, not fatal. SIGINT is left alone + // so a dev Ctrl-C keeps working. + let mut sigterm = signal(SignalKind::terminate()).context("failed to install the SIGTERM handler")?; + + tokio::select! { + res = retain.run() => res, + _ = sigterm.recv() => { + tracing::info!("SIGTERM received, exiting"); + Ok(()) + } + } + })?; Ok(()) }