pub mod http;
pub mod manager;
pub mod messaging;
pub mod websocket;
pub use http::HttpClient;
pub use manager::ClientManager;
pub use messaging::BroadcastTracker;
pub use websocket::WebSocketClient;
use crate::common::ClientId;
use crate::constants::*;
use crate::errors::Result;
use async_trait::async_trait;
use std::time::Duration;
use tokio::time::sleep;
use tracing::{error, info};
#[async_trait]
pub trait StressTestClient {
async fn run(&mut self) -> Result<()>;
fn client_id(&self) -> ClientId;
}
#[async_trait]
impl StressTestClient for HttpClient {
async fn run(&mut self) -> Result<()> {
info!("Client {} starting", self.core.client_id);
self.core.metrics.init_client(self.core.client_id).await;
match self.run_http_test().await {
Ok(_) => {
info!("Client {} completed successfully", self.core.client_id);
Ok(())
}
Err(e) => {
error!("Client {} HTTP test failed: {}", self.core.client_id, e);
Err(e)
}
}
}
fn client_id(&self) -> ClientId {
self.core.client_id
}
}
#[async_trait]
impl StressTestClient for WebSocketClient {
async fn run(&mut self) -> Result<()> {
info!("Client {} starting", self.core.client_id);
self.core
.metrics
.init_client_with_count(self.core.client_id, self.total_clients)
.await;
let mut reconnect_attempts = 0;
let initial_backoff = Duration::from_millis(INITIAL_BACKOFF_MS);
loop {
match self.connect_and_run().await {
Ok(_) => {
info!("Client {} completed successfully", self.core.client_id);
break;
}
Err(e) => {
error!(
"Client {} encountered error: {} (attempt {}/{})",
self.core.client_id,
e,
reconnect_attempts + 1,
MAX_RECONNECT_ATTEMPTS
);
self.core
.metrics
.record_connection_error(self.core.client_id)
.await;
reconnect_attempts += 1;
if reconnect_attempts >= MAX_RECONNECT_ATTEMPTS {
error!(
"Client {} exceeded maximum reconnection attempts",
self.core.client_id
);
return Err(e);
}
let backoff = initial_backoff * 2_u32.pow(reconnect_attempts.saturating_sub(1));
tracing::debug!(
"Client {} backing off for {:?}",
self.core.client_id,
backoff
);
sleep(backoff).await;
}
}
}
Ok(())
}
fn client_id(&self) -> ClientId {
self.core.client_id
}
}