Batuta Integration

Batuta provides GPU pricing and queue management services. The ecosystem module integrates with Batuta for cost estimation and ETA adjustments.

BatutaClient

The client for interacting with Batuta pricing and queue services:

#![allow(unused)]
fn main() {
use entrenar::ecosystem::BatutaClient;

// Create client with fallback pricing
let client = BatutaClient::new();

// Or connect to a Batuta instance
let client = BatutaClient::with_url("http://batuta.local:8080")
    .with_timeout(Duration::from_secs(10));
}

GPU Pricing

Get hourly rates for GPU types:

#![allow(unused)]
fn main() {
let pricing = client.get_hourly_rate("a100-80gb")?;

println!("GPU: {}", pricing.gpu_type);
println!("Rate: ${}/hr", pricing.hourly_rate);
println!("Memory: {} GB", pricing.memory_gb);
println!("Spot: {}", pricing.is_spot);
println!("Provider: {}", pricing.provider);
println!("Region: {}", pricing.region);
}

Available GPUs

GPU TypeHourly RateMemory
a100-80gb$3.0080 GB
a100-40gb$2.5040 GB
h100-80gb$4.5080 GB
v100$2.0016 GB
t4$0.5016 GB
l4$0.7524 GB
a10g$1.0024 GB

Cost Estimation

#![allow(unused)]
fn main() {
// Estimate training cost
let hours = 10.0;
let cost = client.estimate_cost("a100-80gb", hours)?;
println!("Estimated cost: ${:.2}", cost);

// Find cheapest GPU meeting requirements
if let Some(gpu) = client.cheapest_gpu(24) { // 24GB minimum
    println!("Recommended: {} @ ${}/hr", gpu.gpu_type, gpu.hourly_rate);
}
}

Queue Management

Monitor queue state for GPU availability:

#![allow(unused)]
fn main() {
let queue = client.get_queue_depth("a100-80gb")?;

println!("Queue depth: {}", queue.queue_depth);
println!("Available GPUs: {}/{}", queue.available_gpus, queue.total_gpus);
println!("Avg wait: {}s", queue.avg_wait_seconds);
println!("Utilization: {:.1}%", queue.utilization() * 100.0);

if queue.is_available() {
    println!("GPUs available now!");
}
}

ETA Adjustment

Adjust estimated completion time based on queue state:

#![allow(unused)]
fn main() {
use entrenar::ecosystem::adjust_eta;

let base_eta_seconds = 3600; // 1 hour training time
let queue = client.get_queue_depth("a100-80gb")?;

let adjusted = adjust_eta(base_eta_seconds, &queue);
println!("Adjusted ETA: {:?}", adjusted);
}

Adjustment Factors

The ETA is adjusted based on:

  1. Queue wait time - If GPUs not immediately available
  2. Average wait time - Historical wait times per queued job
  3. Utilization - High utilization (>80%) increases estimates
  4. Queue ETA - Uses queue-provided ETA if higher
#![allow(unused)]
fn main() {
// Example adjustments:
// - No queue: ETA unchanged
// - 3 jobs queued, 5min avg wait: +15 minutes
// - 90% utilization: +20% to ETA
}

Fallback Pricing

When Batuta is unavailable, the client uses fallback pricing:

#![allow(unused)]
fn main() {
use entrenar::ecosystem::FallbackPricing;

let mut fallback = FallbackPricing::new();

// Get fallback rate
if let Some(pricing) = fallback.get_rate("v100") {
    println!("Fallback rate: ${}/hr", pricing.hourly_rate);
}

// Add custom GPU pricing
fallback.set_rate(GpuPricing::new("rtx-4090", 0.80, 24));
}

Custom Fallback

#![allow(unused)]
fn main() {
let client = BatutaClient::new()
    .with_fallback(FallbackPricing::new());
}

Combined Status

Get pricing and queue state together:

#![allow(unused)]
fn main() {
let (pricing, queue) = client.get_status("a100-80gb")?;

println!("GPU: {} @ ${}/hr", pricing.gpu_type, pricing.hourly_rate);
println!("Available: {}", queue.is_available());
}

Error Handling

#![allow(unused)]
fn main() {
use entrenar::ecosystem::BatutaError;

match client.get_hourly_rate("unknown-gpu") {
    Ok(pricing) => println!("Rate: ${}", pricing.hourly_rate),
    Err(BatutaError::UnknownGpuType(gpu)) => {
        println!("Unknown GPU type: {}", gpu);
    }
    Err(BatutaError::ServiceUnavailable(msg)) => {
        println!("Batuta unavailable: {}", msg);
    }
    Err(e) => println!("Error: {}", e),
}
}

See Also