use anyhow::Result; use tokio::sync::mpsc; use super::authenticated_client::*; use crate::metrics::RequestMetric; pub struct MixedWorkloadClient { client: AuthenticatedClient, client_id: usize, metrics_tx: mpsc::UnboundedSender, } impl MixedWorkloadClient { pub fn new( client: AuthenticatedClient, client_id: usize, metrics_tx: mpsc::UnboundedSender, ) -> Self { Self { client, client_id, metrics_tx, } } /// Run mixed workload with realistic distribution: /// - 60% order submissions /// /// - 30% position queries /// - 8% backtesting requests /// /// - 2% ML training requests pub async fn run_mixed_workload(&mut self, duration: std::time::Duration) -> Result<()> { use rand::Rng; let start = std::time::Instant::now(); while start.elapsed() < duration { // Generate random number each iteration to avoid holding RNG across await let workload_type = { let mut rng = rand::thread_rng(); rng.gen_range(0..100) }; let metric = match workload_type { 0..=59 => { // 60% - Submit order self.client .submit_order(self.client_id, TestOrder::random()) .await? }, 60..=89 => { // 30% - Query positions self.client.get_positions(self.client_id).await? }, 90..=97 => { // 8% - Run backtest self.client .run_backtest(self.client_id, BacktestConfig::default()) .await? }, 98..=99 => { // 2% - Train model self.client .train_model(self.client_id, TrainingConfig::default()) .await? }, _ => unreachable!(), }; self.metrics_tx.send(metric)?; // Small random think time (1-50ms) to simulate realistic user behavior let think_time_ms = { let mut rng = rand::thread_rng(); rng.gen_range(1..=50) }; tokio::time::sleep(tokio::time::Duration::from_millis(think_time_ms)).await; } Ok(()) } }