Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cassandra cluster bench #969

Merged
merged 4 commits into from Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/build_and_test.yaml
Expand Up @@ -71,7 +71,10 @@ jobs:
- name: Ensure that tests pass
run: cargo test ${{ matrix.cargo_flags }} --all-features -- --include-ignored --show-output --nocapture ${{ matrix.test_flags }}
- name: Ensure that custom benches run
run: cargo run --release --example cassandra_bench -- --config-dir example-configs/cassandra-passthrough --rate 1000
run: |
cargo run --release --example cassandra_bench -- --config-dir example-configs/cassandra-passthrough --rate 1000
cargo run --release --example cassandra_cluster_bench
cargo run --release --example cassandra_cluster_flamegraph
if: ${{ matrix.name == 'Ubuntu 20.04 - Release' }}
- name: Ensure that tests did not create or modify any files that arent .gitignore'd
run: |
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -23,4 +23,7 @@ output.txt
/docs/mdbook_bin
shotover-proxy/read-localhost:9042.json
shotover-proxy/read-localhost:9043.json
shotover-proxy/read-localhost:9043.json
shotover-proxy/flamegraph.svg
shotover-proxy/read-172.16.1.2:9044.json
/shotover-proxy/build/packages
117 changes: 7 additions & 110 deletions shotover-proxy/examples/cassandra_bench.rs
@@ -1,5 +1,6 @@
use clap::Parser;
use test_helpers::docker_compose::DockerCompose;
use test_helpers::latte::Latte;
use test_helpers::shotover_process::shotover_from_topology_file;

/// e.g.
Expand All @@ -18,131 +19,27 @@ pub struct Args {

#[tokio::main]
async fn main() {
if env!("PROFILE") != "release" {
println!("Need to run with --release flag");
return;
}

test_helpers::bench::init();
let args = Args::parse();

// The benches and tests automatically set the working directory to CARGO_MANIFEST_DIR.
// We need to do the same as the DockerCompose + ShotoverManager types rely on this.
std::env::set_current_dir(env!("CARGO_MANIFEST_DIR")).unwrap();

let latte = Latte::new(args.rate);
let bench = "read";
{
let _compose = DockerCompose::new(&format!("{}/docker-compose.yaml", args.config_dir));

// Uses ShotoverProcess instead of ShotoverManager for a more accurate benchmark
let shotover =
shotover_from_topology_file(&format!("{}/topology.yaml", args.config_dir)).await;

println!("Benching Shotover ...");
bench_read(&latte, "localhost:9043", "localhost:9042");
latte.init(bench, "localhost:9043");
latte.bench(bench, "localhost:9042");
println!("Benching Direct Cassandra ...");
bench_read(&latte, "localhost:9043", "localhost:9043");
latte.init(bench, "localhost:9043");
latte.bench(bench, "localhost:9043");

shotover.shutdown_and_then_consume_events(&[]).await;
}

println!("Direct Cassandra (A) vs Shotover (B)");
latte.compare("read-localhost:9043.json", "read-localhost:9042.json");
}

fn bench_read(latte: &Latte, address_load: &str, address_bench: &str) {
latte.bench("read", address_load, address_bench)
}

// TODO: Shelling out directly like this is just for experimenting.
// Either:
// * get access to latte as a crate
// * write our own benchmark logic
struct Latte {
rate: u64,
}

impl Latte {
fn new(rate: u64) -> Latte {
test_helpers::docker_compose::run_command(
"cargo",
&[
"install",
"--git",
"https://github.com/pkolaczk/latte",
"--rev",
"3294afdb56ddea77f9f56bc795f325cb734b352c",
],
)
.unwrap();
Latte { rate }
}

fn bench(&self, name: &str, address_load: &str, address_bench: &str) {
test_helpers::docker_compose::run_command(
"latte",
&[
"schema",
"--user",
"cassandra",
"--password",
"cassandra",
&format!("examples/{name}.rn"),
"--",
address_load,
],
)
.unwrap();
test_helpers::docker_compose::run_command(
"latte",
&[
"load",
"--user",
"cassandra",
"--password",
"cassandra",
&format!("examples/{name}.rn"),
"--",
address_load,
],
)
.unwrap();
test_helpers::docker_compose::run_command(
"latte",
&[
"run",
"--user",
"cassandra",
"--password",
"cassandra",
"--rate",
&self.rate.to_string(),
"--duration",
"15s", // default is 60s but 15 seems fine
"--connections",
"128", // Shotover performs extremely poorly with 1 connection and this is not currently an intended usecase
"--output",
&format!("{name}-{address_bench}.json"),
&format!("examples/{name}.rn"),
"--",
address_bench,
],
)
.unwrap();
}

fn compare(&self, file_a: &str, file_b: &str) {
run_command_to_stdout("latte", &["show", file_b, "-b", file_a]);
}
}

/// unlike test_helpers::docker_compose::run_command stdout of the command is sent to the stdout of the application
fn run_command_to_stdout(command: &str, args: &[&str]) {
assert!(
std::process::Command::new(command)
.args(args)
.status()
.unwrap()
.success(),
"Failed to run: {command} {args:?}"
);
}
29 changes: 29 additions & 0 deletions shotover-proxy/examples/cassandra_cluster_bench.rs
@@ -0,0 +1,29 @@
use test_helpers::docker_compose::DockerCompose;
use test_helpers::latte::Latte;
use test_helpers::shotover_process::shotover_from_topology_file;

#[tokio::main]
async fn main() {
test_helpers::bench::init();

let latte = Latte::new(10000000);
let config_dir = "example-configs/cassandra-cluster-v4";
let bench = "read";
{
let _compose = DockerCompose::new(&format!("{}/docker-compose.yaml", config_dir));
let shotover = shotover_from_topology_file(&format!("{}/topology.yaml", config_dir)).await;

println!("Benching Shotover ...");
latte.init(bench, "172.16.1.2:9044");
latte.bench(bench, "localhost:9042");

println!("Benching Direct Cassandra ...");
latte.init(bench, "172.16.1.2:9044");
latte.bench(bench, "172.16.1.2:9044");

shotover.shutdown_and_then_consume_events(&[]).await;
}

println!("Direct Cassandra (A) vs Shotover (B)");
latte.compare("read-172.16.1.2:9044.json", "read-localhost:9042.json");
}
23 changes: 23 additions & 0 deletions shotover-proxy/examples/cassandra_cluster_flamegraph.rs
@@ -0,0 +1,23 @@
use test_helpers::docker_compose::DockerCompose;
use test_helpers::latte::Latte;
use test_helpers::shotover_process::shotover_from_topology_file;

#[tokio::main]
async fn main() {
test_helpers::bench::init();

let latte = Latte::new(10000000);
let config_dir = "example-configs/cassandra-cluster-v4";
let bench = "read";
{
let _compose = DockerCompose::new(&format!("{}/docker-compose.yaml", config_dir));
latte.init(bench, "172.16.1.2:9044");

let shotover = shotover_from_topology_file(&format!("{}/topology.yaml", config_dir)).await;

println!("Benching Shotover ...");
latte.bench(bench, "localhost:9042");

shotover.shutdown_and_then_consume_events(&[]).await;
}
}
17 changes: 17 additions & 0 deletions test-helpers/src/bench.rs
@@ -0,0 +1,17 @@
use std::path::Path;

pub fn init() {
if env!("PROFILE") != "release" {
panic!("Need to run with --release flag");
}

// The benches and tests automatically set the working directory to CARGO_MANIFEST_DIR.
// We need to do the same as the DockerCompose + ShotoverManager types rely on this.
std::env::set_current_dir(
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("shotover-proxy"),
)
.unwrap();
}
96 changes: 96 additions & 0 deletions test-helpers/src/latte.rs
@@ -0,0 +1,96 @@
// TODO: Shelling out directly like this is just for experimenting.
// Either:
// * get access to latte as a crate
// * write our own benchmark logic
pub struct Latte {
rate: u64,
}

impl Latte {
pub fn new(rate: u64) -> Latte {
crate::docker_compose::run_command(
"cargo",
&[
"install",
"--git",
"https://github.com/pkolaczk/latte",
"--rev",
"571e9ed2456e85668890cb4599686c8ccd43adad",
],
)
.unwrap();
Latte { rate }
}

pub fn init(&self, name: &str, address_load: &str) {
crate::docker_compose::run_command(
"latte",
&[
"schema",
"--user",
"cassandra",
"--password",
"cassandra",
&format!("examples/{name}.rn"),
"--",
address_load,
],
)
.unwrap();
crate::docker_compose::run_command(
"latte",
&[
"load",
"--user",
"cassandra",
"--password",
"cassandra",
&format!("examples/{name}.rn"),
"--",
address_load,
],
)
.unwrap();
}

pub fn bench(&self, name: &str, address_bench: &str) {
crate::docker_compose::run_command(
"latte",
&[
"run",
"--user",
"cassandra",
"--password",
"cassandra",
"--rate",
&self.rate.to_string(),
"--duration",
"15s", // default is 60s but 15 seems fine
"--connections",
"128", // Shotover performs extremely poorly with 1 connection and this is not currently an intended usecase
"--output",
&format!("{name}-{address_bench}.json"),
&format!("examples/{name}.rn"),
"--",
address_bench,
],
)
.unwrap();
}

pub fn compare(&self, file_a: &str, file_b: &str) {
run_command_to_stdout("latte", &["show", file_b, "-b", file_a]);
}
}

/// unlike crate::docker_compose::run_command stdout of the command is sent to the stdout of the application
fn run_command_to_stdout(command: &str, args: &[&str]) {
assert!(
std::process::Command::new(command)
.args(args)
.status()
.unwrap()
.success(),
"Failed to run: {command} {args:?}"
);
}
2 changes: 2 additions & 0 deletions test-helpers/src/lib.rs
@@ -1,8 +1,10 @@
#![allow(clippy::derive_partial_eq_without_eq)]

pub mod bench;
pub mod cert;
pub mod connection;
pub mod docker_compose;
pub mod latte;
pub mod lazy;
pub mod shotover_process;

Expand Down