use clap::Parser;
use oxidelake_core::BackendKind;
use oxidelake_runtime::cluster;
use tracing_subscriber::EnvFilter;
#[derive(Parser)]
#[command(
name = "oxide-worker",
version,
about = "OxideLake worker (Apache DataFusion Ballista executor)"
)]
struct Cli {
#[arg(long, default_value = "localhost")]
scheduler_host: String,
#[arg(long, default_value_t = 50050)]
scheduler_port: u16,
#[arg(long, default_value_t = 50051)]
port: u16,
#[arg(long, default_value_t = 50052)]
grpc_port: u16,
#[arg(long)]
concurrent_tasks: Option<usize>,
#[arg(long)]
work_dir: Option<String>,
#[arg(long, value_name = "BACKEND")]
backend: Option<BackendKind>,
#[arg(long, value_name = "PORT")]
metrics_port: Option<u16>,
#[arg(long, default_value = "127.0.0.1", value_name = "HOST")]
metrics_host: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr()))
.init();
let cli = Cli::parse();
let backend = match cli.backend {
Some(kind) => oxidelake_compute::init_local_backend(Some(kind.as_str()))?,
None => oxidelake_compute::local_backend()?,
};
tracing::info!(backend = %backend.kind(), "worker backend");
serve_metrics(cli.metrics_port, &cli.metrics_host).await?;
cluster::run_executor(cluster::executor_config(
&cli.scheduler_host,
cli.scheduler_port,
cli.port,
cli.grpc_port,
cli.concurrent_tasks,
cli.work_dir,
))
.await?;
Ok(())
}
#[cfg(feature = "metrics")]
async fn serve_metrics(port: Option<u16>, host: &str) -> anyhow::Result<()> {
use std::net::ToSocketAddrs;
let Some(port) = port else { return Ok(()) };
let addr = (host, port)
.to_socket_addrs()?
.next()
.ok_or_else(|| anyhow::anyhow!("--metrics-host '{host}' resolved to no address"))?;
let hub = std::sync::Arc::clone(oxidelake_core::telemetry::TelemetryHub::global());
let (bound, server) = oxidelake_runtime::metrics::serve(addr, hub).await?;
tracing::info!(%bound, path = oxidelake_runtime::metrics::METRICS_PATH, "serving metrics");
tokio::spawn(server);
Ok(())
}
#[cfg(not(feature = "metrics"))]
async fn serve_metrics(port: Option<u16>, _host: &str) -> anyhow::Result<()> {
match port {
None => Ok(()),
Some(_) => anyhow::bail!(
"--metrics-port needs the `metrics` feature; rebuild with \
`cargo build -p oxidelake-runtime --features metrics`"
),
}
}