main.rs (2652B)
1 use std::time::Duration; 2 3 use clap::{Parser, Subcommand}; 4 5 #[derive(Parser)] 6 #[command(name = "mkv", about = "Distributed key-value store")] 7 struct Cli { 8 #[arg(short, long, env = "MKV_DB", default_value = "/tmp/mkv/index.db")] 9 db: String, 10 11 #[arg( 12 short, 13 long, 14 env = "MKV_VOLUMES", 15 required = true, 16 value_delimiter = ',' 17 )] 18 volumes: Vec<String>, 19 20 #[arg(short, long, env = "MKV_REPLICAS", default_value_t = 2)] 21 replicas: usize, 22 23 /// Timeout for volume health checks (in milliseconds) 24 #[arg(long, env = "MKV_VOLTIMEOUT", default_value_t = 1000)] 25 voltimeout: u64, 26 27 #[command(subcommand)] 28 command: Commands, 29 } 30 31 #[derive(Subcommand)] 32 enum Commands { 33 /// Start the index server 34 Serve { 35 #[arg(short, long, env = "MKV_PORT", default_value_t = 3000)] 36 port: u16, 37 }, 38 /// Rebuild SQLite index from volume servers 39 Rebuild, 40 /// Rebalance data after adding/removing volumes 41 Rebalance { 42 #[arg(long)] 43 dry_run: bool, 44 }, 45 } 46 47 async fn shutdown_signal() { 48 let ctrl_c = tokio::signal::ctrl_c(); 49 #[cfg(unix)] 50 { 51 let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) 52 .expect("failed to install SIGTERM handler"); 53 tokio::select! { 54 _ = ctrl_c => tracing::info!("Received SIGINT, shutting down..."), 55 _ = sigterm.recv() => tracing::info!("Received SIGTERM, shutting down..."), 56 } 57 } 58 #[cfg(not(unix))] 59 { 60 ctrl_c.await.expect("failed to listen for ctrl-c"); 61 tracing::info!("Received ctrl-c, shutting down..."); 62 } 63 } 64 65 #[tokio::main] 66 async fn main() { 67 tracing_subscriber::fmt::init(); 68 let cli = Cli::parse(); 69 70 let args = mkv::Args { 71 db_path: cli.db, 72 volumes: cli.volumes, 73 replicas: cli.replicas, 74 voltimeout: Duration::from_millis(cli.voltimeout), 75 }; 76 77 match cli.command { 78 Commands::Serve { port } => { 79 let app = mkv::build_app(&args); 80 let addr = format!("0.0.0.0:{port}"); 81 tracing::info!("Listening on {addr}"); 82 let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); 83 axum::serve(listener, app) 84 .with_graceful_shutdown(shutdown_signal()) 85 .await 86 .unwrap(); 87 tracing::info!("Shutdown complete"); 88 } 89 Commands::Rebuild => { 90 mkv::rebuild::run(&args).await; 91 } 92 Commands::Rebalance { dry_run } => { 93 mkv::rebalance::run(&args, dry_run).await; 94 } 95 } 96 }