use std::net::SocketAddr;
use std::sync::Arc;
use sqlx::PgPool;
use tokio::net::TcpListener;
use tokio::sync::mpsc;
use uuid::Uuid;
use crate::api::{create_router, ConnectionManager};
use crate::db::DataPlanesRepository;
const STALE_SWEEP_INTERVAL_SECS: u64 = 60;
const STALE_THRESHOLD_MINUTES: i64 = 2;
pub struct ServerConfig {
pub listen_addr: SocketAddr,
pub pool: PgPool,
}
pub async fn run(config: ServerConfig) -> anyhow::Result<()> {
let repo = DataPlanesRepository::new(config.pool.clone());
let marked = repo.mark_all_offline().await?;
if marked > 0 {
tracing::info!(
count = marked,
"Marked stale data planes as offline on startup"
);
}
let (tx, rx) = mpsc::channel::<Uuid>(100);
let worker_pool = config.pool.clone();
tokio::spawn(async move {
crate::compiler::run_worker(worker_pool, rx).await;
});
let sweep_pool = config.pool.clone();
tokio::spawn(async move {
run_stale_sweep(sweep_pool).await;
});
let connection_manager = Arc::new(ConnectionManager::new());
let app = create_router(config.pool, Some(tx), connection_manager);
let listener = TcpListener::bind(config.listen_addr).await?;
tracing::info!("Control plane listening on {}", config.listen_addr);
axum::serve(listener, app).await?;
Ok(())
}
async fn run_stale_sweep(pool: PgPool) {
let repo = DataPlanesRepository::new(pool);
let mut interval =
tokio::time::interval(std::time::Duration::from_secs(STALE_SWEEP_INTERVAL_SECS));
interval.tick().await;
loop {
interval.tick().await;
match repo.mark_stale_offline(STALE_THRESHOLD_MINUTES).await {
Ok(0) => {}
Ok(count) => {
tracing::info!(count, "Marked stale data planes as offline");
}
Err(e) => {
tracing::error!(error = %e, "Failed to sweep stale data planes");
}
}
}
}