@@ -2954,41 +2954,66 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
29542954 // v3.50: certificate_history no longer persisted (Dilithium-only verification)
29552955 // ═══════════════════════════════════════════════════════════════════════════
29562956
2957- tokio:: select! {
2958- // Normal monitoring loop
2959- _ = node_handle => {
2960- println!( "[SHUTDOWN] Node monitoring ended unexpectedly" ) ;
2957+ // v5.0: Handle both SIGINT (Ctrl+C) and SIGTERM (docker stop)
2958+ // Docker sends SIGTERM on `docker stop`. Without this, the process ignores
2959+ // SIGTERM and gets SIGKILL after 10s, losing unflushed macroblock data.
2960+ #[ cfg( unix) ]
2961+ let mut sigterm = tokio:: signal:: unix:: signal (
2962+ tokio:: signal:: unix:: SignalKind :: terminate ( )
2963+ ) . expect ( "failed to register SIGTERM handler" ) ;
2964+
2965+ let shutdown_handler = async {
2966+ let signal_name;
2967+ #[ cfg( unix) ]
2968+ {
2969+ tokio:: select! {
2970+ _ = tokio:: signal:: ctrl_c( ) => { signal_name = "SIGINT" ; }
2971+ _ = sigterm. recv( ) => { signal_name = "SIGTERM" ; }
2972+ }
29612973 }
2962-
2963- // Graceful shutdown on Ctrl+C or SIGTERM
2964- _ = tokio:: signal:: ctrl_c( ) => {
2965- println!( "\n [SHUTDOWN] 🛑 Received shutdown signal..." ) ;
2966- println!( "[SHUTDOWN] 💾 Saving certificate history..." ) ;
2967-
2968- // Persist certificate history before exit
2969- if node_type != NodeType :: Light {
2970- if let Some ( p2p) = node. get_unified_p2p( ) {
2971- // Use QNET_STORAGE_PATH (set during init) with fallback to "data"
2972- let storage_path = std:: env:: var( "QNET_STORAGE_PATH" ) . unwrap_or_else( |_| "data" . to_string( ) ) ;
2973- let data_dir = std:: path:: Path :: new( & storage_path) ;
2974- if let Err ( e) = std:: fs:: create_dir_all( & data_dir) {
2975- println!( "[SHUTDOWN] ⚠️ Failed to create data dir {}: {}" , storage_path, e) ;
2976- } else if let Ok ( mut cert_manager) = p2p. certificate_manager. write( ) {
2977- // v3.18: Full node type removed - only Light and Super remain
2978- let unified_node_type = match node_type {
2979- NodeType :: Light => qnet_integration:: unified_p2p:: NodeType :: Light ,
2980- NodeType :: Super => qnet_integration:: unified_p2p:: NodeType :: Super ,
2981- } ;
2982- match cert_manager. persist_to_disk( & data_dir, unified_node_type) {
2983- Ok ( _) => println!( "[SHUTDOWN] ✅ Certificate history saved to {}" , storage_path) ,
2984- Err ( e) => println!( "[SHUTDOWN] ⚠️ Failed to save certificates: {}" , e) ,
2985- }
2974+ #[ cfg( not( unix) ) ]
2975+ {
2976+ tokio:: signal:: ctrl_c ( ) . await . ok ( ) ;
2977+ signal_name = "SIGINT" ;
2978+ }
2979+
2980+ println ! ( "\n [SHUTDOWN] Received {} — starting graceful shutdown..." , signal_name) ;
2981+
2982+ // 1. Flush RocksDB (WAL → SST), prevents macroblock/block data loss
2983+ let storage = node. get_storage ( ) ;
2984+ match storage. flush_all ( ) {
2985+ Ok ( ( ) ) => println ! ( "[SHUTDOWN] storage.flush_all() complete" ) ,
2986+ Err ( e) => println ! ( "[ERR][SHUTDOWN] storage.flush_all() failed: {}" , e) ,
2987+ }
2988+
2989+ // 2. Persist certificate history
2990+ if node_type != NodeType :: Light {
2991+ if let Some ( p2p) = node. get_unified_p2p ( ) {
2992+ let storage_path = std:: env:: var ( "QNET_STORAGE_PATH" ) . unwrap_or_else ( |_| "data" . to_string ( ) ) ;
2993+ let data_dir = std:: path:: Path :: new ( & storage_path) ;
2994+ if let Err ( e) = std:: fs:: create_dir_all ( & data_dir) {
2995+ println ! ( "[WARN][SHUTDOWN] create_dir fail: {}" , e) ;
2996+ } else if let Ok ( mut cert_manager) = p2p. certificate_manager . write ( ) {
2997+ let unified_node_type = match node_type {
2998+ NodeType :: Light => qnet_integration:: unified_p2p:: NodeType :: Light ,
2999+ NodeType :: Super => qnet_integration:: unified_p2p:: NodeType :: Super ,
3000+ } ;
3001+ match cert_manager. persist_to_disk ( & data_dir, unified_node_type) {
3002+ Ok ( _) => println ! ( "[SHUTDOWN] certificates saved to {}" , storage_path) ,
3003+ Err ( e) => println ! ( "[WARN][SHUTDOWN] cert save failed: {}" , e) ,
29863004 }
29873005 }
29883006 }
2989-
2990- println!( "[SHUTDOWN] ✅ Graceful shutdown complete" ) ;
29913007 }
3008+
3009+ println ! ( "[SHUTDOWN] graceful shutdown complete" ) ;
3010+ } ;
3011+
3012+ tokio:: select! {
3013+ _ = node_handle => {
3014+ println!( "[SHUTDOWN] Node monitoring ended unexpectedly" ) ;
3015+ }
3016+ _ = shutdown_handler => { }
29923017 }
29933018
29943019 Ok ( ( ) )
0 commit comments