@@ -64,12 +64,18 @@ struct NodeConfig {
6464}
6565
6666impl NodeConfig {
67+ /// Minimum allowed shutdown timeout in seconds
68+ const MIN_SHUTDOWN_TIMEOUT_SECS : u64 = 1 ;
69+
6770 /// Default shutdown timeout in seconds (optimized for containers)
6871 const DEFAULT_SHUTDOWN_TIMEOUT_SECS : u64 = 15 ;
6972
7073 /// Maximum allowed shutdown timeout in seconds (5 minutes)
7174 const MAX_SHUTDOWN_TIMEOUT_SECS : u64 = 300 ;
7275
76+ /// Minimum allowed status check interval in seconds
77+ const MIN_STATUS_CHECK_INTERVAL_SECS : u64 = 1 ;
78+
7379 /// Default status check interval in seconds (1 hour)
7480 const DEFAULT_STATUS_CHECK_INTERVAL_SECS : u64 = 3600 ;
7581
@@ -102,14 +108,14 @@ impl NodeConfig {
102108 fn parse_shutdown_timeout ( ) -> Duration {
103109 match std:: env:: var ( "EV_RETH_SHUTDOWN_TIMEOUT" ) {
104110 Ok ( val) => match val. parse :: < u64 > ( ) {
105- Ok ( secs) if secs > 0 && secs <= Self :: MAX_SHUTDOWN_TIMEOUT_SECS => {
111+ Ok ( secs) if secs >= Self :: MIN_SHUTDOWN_TIMEOUT_SECS && secs <= Self :: MAX_SHUTDOWN_TIMEOUT_SECS => {
106112 tracing:: info!( "Using custom shutdown timeout of {}s from environment" , secs) ;
107113 Duration :: from_secs ( secs)
108114 }
109115 Ok ( secs) => {
110116 tracing:: warn!(
111- "Shutdown timeout {}s is out of bounds (1 -{}), using default {}s" ,
112- secs, Self :: MAX_SHUTDOWN_TIMEOUT_SECS , Self :: DEFAULT_SHUTDOWN_TIMEOUT_SECS
117+ "Shutdown timeout {}s is out of bounds ({} -{}), using default {}s" ,
118+ secs, Self :: MIN_SHUTDOWN_TIMEOUT_SECS , Self :: MAX_SHUTDOWN_TIMEOUT_SECS , Self :: DEFAULT_SHUTDOWN_TIMEOUT_SECS
113119 ) ;
114120 Duration :: from_secs ( Self :: DEFAULT_SHUTDOWN_TIMEOUT_SECS )
115121 }
@@ -131,14 +137,14 @@ impl NodeConfig {
131137 fn parse_status_check_interval ( ) -> u64 {
132138 match std:: env:: var ( "EV_RETH_STATUS_CHECK_INTERVAL" ) {
133139 Ok ( val) => match val. parse :: < u64 > ( ) {
134- Ok ( secs) if secs > 0 && secs <= Self :: MAX_STATUS_CHECK_INTERVAL_SECS => {
140+ Ok ( secs) if secs >= Self :: MIN_STATUS_CHECK_INTERVAL_SECS && secs <= Self :: MAX_STATUS_CHECK_INTERVAL_SECS => {
135141 tracing:: info!( "Using custom status check interval of {}s from environment" , secs) ;
136142 secs
137143 }
138144 Ok ( secs) => {
139145 tracing:: warn!(
140- "Status check interval {}s is out of bounds (1 -{}), using default {}s" ,
141- secs, Self :: MAX_STATUS_CHECK_INTERVAL_SECS , Self :: DEFAULT_STATUS_CHECK_INTERVAL_SECS
146+ "Status check interval {}s is out of bounds ({} -{}), using default {}s" ,
147+ secs, Self :: MIN_STATUS_CHECK_INTERVAL_SECS , Self :: MAX_STATUS_CHECK_INTERVAL_SECS , Self :: DEFAULT_STATUS_CHECK_INTERVAL_SECS
142148 ) ;
143149 Self :: DEFAULT_STATUS_CHECK_INTERVAL_SECS
144150 }
@@ -177,35 +183,44 @@ async fn signal_fallback_mechanism(config: &NodeConfig) {
177183 std:: future:: pending :: < ( ) > ( ) . await ;
178184}
179185
180- /// Handle shutdown signals with optimized signal handling
186+ /// Handle shutdown signals with optimized, non-redundant signal handling
181187async fn handle_shutdown_signals ( ) {
182188 #[ cfg( unix) ]
183189 {
184- // On Unix systems, use a single select to handle both SIGTERM and SIGINT efficiently
185- // This avoids redundant signal handling and potential race conditions
186- match signal:: unix:: signal ( signal:: unix:: SignalKind :: terminate ( ) ) {
187- Ok ( mut sigterm) => {
190+ // On Unix systems, handle SIGTERM and SIGINT separately to avoid redundancy
191+ // SIGTERM is sent by process managers, SIGINT is sent by Ctrl+C
192+ match (
193+ signal:: unix:: signal ( signal:: unix:: SignalKind :: terminate ( ) ) ,
194+ signal:: unix:: signal ( signal:: unix:: SignalKind :: interrupt ( ) )
195+ ) {
196+ ( Ok ( mut sigterm) , Ok ( mut sigint) ) => {
188197 tokio:: select! {
189198 _ = sigterm. recv( ) => {
190199 tracing:: info!( "Received SIGTERM, initiating graceful shutdown" ) ;
191200 }
192- _ = signal :: ctrl_c ( ) => {
193- tracing:: info!( "Received SIGINT/Ctrl+C , initiating graceful shutdown" ) ;
201+ _ = sigint . recv ( ) => {
202+ tracing:: info!( "Received SIGINT, initiating graceful shutdown" ) ;
194203 }
195204 }
196205 }
197- Err ( err) => {
198- tracing:: warn!( "Failed to install SIGTERM handler: {}, using SIGINT only" , err) ;
199- // Fallback to SIGINT only - this is the most common scenario
200- if let Err ( ctrl_c_err) = signal:: ctrl_c ( ) . await {
201- tracing:: error!( "Failed to wait for SIGINT: {}" , ctrl_c_err) ;
202- tracing:: warn!( "No signal handling available, shutdown will only occur on natural node exit" ) ;
203- let config = NodeConfig :: from_env ( ) ;
204- signal_fallback_mechanism ( & config) . await ;
205- } else {
206- tracing:: info!( "Received SIGINT/Ctrl+C, initiating graceful shutdown" ) ;
206+ ( Ok ( mut sigterm) , Err ( sigint_err) ) => {
207+ tracing:: warn!( "Failed to install SIGINT handler: {}, using SIGTERM only" , sigint_err) ;
208+ if sigterm. recv ( ) . await . is_some ( ) {
209+ tracing:: info!( "Received SIGTERM, initiating graceful shutdown" ) ;
207210 }
208211 }
212+ ( Err ( sigterm_err) , Ok ( mut sigint) ) => {
213+ tracing:: warn!( "Failed to install SIGTERM handler: {}, using SIGINT only" , sigterm_err) ;
214+ if sigint. recv ( ) . await . is_some ( ) {
215+ tracing:: info!( "Received SIGINT, initiating graceful shutdown" ) ;
216+ }
217+ }
218+ ( Err ( sigterm_err) , Err ( sigint_err) ) => {
219+ tracing:: error!( "Failed to install both SIGTERM and SIGINT handlers: SIGTERM={}, SIGINT={}" , sigterm_err, sigint_err) ;
220+ tracing:: warn!( "No signal handling available, shutdown will only occur on natural node exit" ) ;
221+ let config = NodeConfig :: from_env ( ) ;
222+ signal_fallback_mechanism ( & config) . await ;
223+ }
209224 }
210225 }
211226
@@ -371,14 +386,17 @@ fn main() {
371386 _ = handle_shutdown_signals( ) => {
372387 tracing:: info!( "Shutdown signal received, initiating graceful shutdown" ) ;
373388
374- // Structured shutdown phases for better observability
375- tracing:: info!( "Phase 1 - Stopping new connections" ) ;
376- tracing:: info!( "Phase 2 - Draining active requests" ) ;
389+ // Structured shutdown phases for better observability (informational only)
390+ // Note: These phases are logged for monitoring purposes but don't implement
391+ // specific connection stopping or request draining - the underlying reth node
392+ // handles the actual shutdown logic when the handle is dropped
393+ tracing:: info!( "Phase 1 - Initiating shutdown sequence" ) ;
394+ tracing:: info!( "Phase 2 - Waiting for graceful node termination" ) ;
377395
378396 // Wait for the node to actually exit with a timeout
379397 let shutdown_result = timeout( config. shutdown_timeout, & mut handle. node_exit_future) . await ;
380398
381- tracing:: info!( "Phase 3 - Shutdown completed" ) ;
399+ tracing:: info!( "Phase 3 - Shutdown sequence completed" ) ;
382400
383401 match shutdown_result {
384402 Ok ( result) => {
0 commit comments