112 lines
3.8 KiB
Rust
112 lines
3.8 KiB
Rust
use databuild::build_event_log::SqliteBELStorage;
|
|
use databuild::http_server::{create_router, AppState};
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
use tokio::sync::{broadcast, mpsc};
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
use databuild::orchestrator::{Orchestrator, OrchestratorConfig};
|
|
|
|
// Initialize logging
|
|
tracing_subscriber::fmt::init();
|
|
|
|
let database = ":memory:";
|
|
|
|
// Create SQLite BEL storage (shared between orchestrator and HTTP server)
|
|
let bel_storage = Arc::new(
|
|
SqliteBELStorage::create(database).expect("Failed to create BEL storage"),
|
|
);
|
|
|
|
// Create command channel for orchestrator communication
|
|
let (command_tx, command_rx) = mpsc::channel(100);
|
|
|
|
// Create shutdown broadcast channel
|
|
let (shutdown_tx, _shutdown_rx) = broadcast::channel(1);
|
|
|
|
// Spawn orchestrator in background thread
|
|
let orch_bel_storage = SqliteBELStorage::create(database).expect("Failed to create BEL storage");
|
|
let orch_shutdown_rx = shutdown_tx.subscribe();
|
|
let orch_handle = std::thread::spawn(move || {
|
|
// Create orchestrator with command channel
|
|
let config = OrchestratorConfig::default();
|
|
let mut orchestrator = Orchestrator::new_with_commands(orch_bel_storage, config, command_rx);
|
|
let mut shutdown_rx = orch_shutdown_rx;
|
|
|
|
// Run orchestrator loop
|
|
loop {
|
|
// Check for shutdown signal
|
|
if shutdown_rx.try_recv().is_ok() {
|
|
println!("Orchestrator received shutdown signal");
|
|
break;
|
|
}
|
|
|
|
if let Err(e) = orchestrator.step() {
|
|
eprintln!("Orchestrator error: {}", e);
|
|
}
|
|
// Small sleep to avoid busy-waiting
|
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
}
|
|
});
|
|
|
|
// Create app state with shared storage, command sender, and shutdown channel
|
|
let state = AppState::new(bel_storage, command_tx, shutdown_tx.clone());
|
|
|
|
// Spawn idle timeout checker task
|
|
let idle_state = state.clone();
|
|
let idle_shutdown_tx = shutdown_tx.clone();
|
|
tokio::spawn(async move {
|
|
let idle_timeout = Duration::from_secs(3 * 60 * 60); // 3 hours
|
|
|
|
loop {
|
|
tokio::time::sleep(Duration::from_secs(60)).await;
|
|
|
|
let last_request = idle_state.last_request_time.load(std::sync::atomic::Ordering::Relaxed);
|
|
let now = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_millis() as u64;
|
|
|
|
if now - last_request > idle_timeout.as_millis() as u64 {
|
|
eprintln!(
|
|
"Server idle for {} hours, shutting down",
|
|
idle_timeout.as_secs() / 3600
|
|
);
|
|
let _ = idle_shutdown_tx.send(());
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Create router
|
|
let app = create_router(state);
|
|
|
|
// Bind to port 3000
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
|
.await
|
|
.expect("Failed to bind to port 3000");
|
|
|
|
println!("DataBuild server listening on http://127.0.0.1:3000");
|
|
println!(" GET /health");
|
|
println!(" GET /api/wants");
|
|
println!(" POST /api/wants");
|
|
println!(" GET /api/wants/:id");
|
|
println!(" GET /api/partitions");
|
|
println!(" GET /api/job_runs");
|
|
|
|
// Subscribe to shutdown signal for graceful shutdown
|
|
let mut server_shutdown_rx = shutdown_tx.subscribe();
|
|
|
|
// Run the server with graceful shutdown
|
|
axum::serve(listener, app)
|
|
.with_graceful_shutdown(async move {
|
|
let _ = server_shutdown_rx.recv().await;
|
|
println!("HTTP server received shutdown signal");
|
|
})
|
|
.await
|
|
.expect("Server error");
|
|
|
|
// Wait for orchestrator to finish
|
|
let _ = orch_handle.join();
|
|
println!("Shutdown complete");
|
|
}
|