Skip to content

Commit 6c7f0b7

Browse files
committed
fix(node): graceful shutdown, genesis sync, macroblock self-assembly
- SIGTERM handler in qnet-node.rs: flush RocksDB before exit (prevents data loss on docker stop) - stop_grace_period: 60s in docker-compose for all node services - node_001: sync genesis from network before creating new one (prevents chain fork on restart) - calculate_qualified_candidates: self-assemble macroblock from local microblocks if NOT_FOUND - PFP non-leader: self-assemble macroblock on leader timeout instead of waiting forever - rpc.rs handle_graceful_shutdown: flush storage before process exit Made-with: Cursor
1 parent fad603c commit 6c7f0b7

4 files changed

Lines changed: 244 additions & 62 deletions

File tree

development/qnet-integration/src/bin/qnet-node.rs

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)