use anyhow::Result;
use async_trait::async_trait;
use clap::Parser;
use rlt::{BenchSuite, IterInfo, IterReport, Status, bench_cli, bench_cli_run};
use tokio::time::Instant;
use tokio_postgres::{Client, NoTls};
bench_cli!(DBBench, {
#[clap(long, default_value = "localhost")]
pub host: String,
#[clap(long, default_value_t = 5432)]
pub port: u16,
#[clap(long, default_value = "postgres")]
pub user: String,
#[clap(long)]
pub password: Option<String>,
#[clap(long, short = 'b')]
pub batch_size: u32,
#[clap(long, default_value = "t")]
pub table: String,
});
#[async_trait]
impl BenchSuite for DBBench {
type WorkerState = Client;
async fn state(&self, _: u32) -> Result<Self::WorkerState> {
let (client, conn) = tokio_postgres::connect(
&format!(
"host={} port={} user={} password='{}'",
self.host,
self.port,
self.user,
self.password.as_deref().unwrap_or_default()
),
NoTls,
)
.await?;
tokio::spawn(async move {
if let Err(e) = conn.await {
eprintln!("connection error: {}", e);
}
});
Ok(client)
}
async fn setup(&mut self, client: &mut Self::WorkerState, _: u32) -> Result<()> {
client.execute("BEGIN", &[]).await?;
client
.execute("CREATE TABLE t(id SERIAL PRIMARY KEY, name TEXT)", &[])
.await?;
Ok(())
}
async fn bench(&mut self, client: &mut Self::WorkerState, _: &IterInfo) -> Result<IterReport> {
let t = Instant::now();
client
.query(
"INSERT INTO t(name) SELECT MD5(i::TEXT) FROM generate_series(1, $1) i",
&[&(self.batch_size as i32)],
)
.await?;
let duration = t.elapsed();
Ok(IterReport {
duration,
status: Status::success(0),
bytes: 0,
items: self.batch_size as u64,
})
}
async fn teardown(self, client: Self::WorkerState, _: IterInfo) -> Result<()> {
client.execute("ROLLBACK", &[]).await?;
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<()> {
bench_cli_run!(DBBench).await
}