- netboot-base.nix with SSH key auth - Launch scripts for node01/02/03 - Node configuration.nix and disko.nix - Nix modules for first-boot automation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
196 lines
5.6 KiB
Rust
196 lines
5.6 KiB
Rust
use chainfire_client::ChainFireClient;
|
|
use chainfire_server::config::{ClusterConfig, NetworkConfig, NodeConfig, RaftConfig, ServerConfig, StorageConfig};
|
|
use chainfire_server::node::Node;
|
|
use chainfire_types::RaftRole;
|
|
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
|
|
use std::time::Duration;
|
|
use tempfile::TempDir;
|
|
use tokio::runtime::Runtime;
|
|
|
|
const VALUE_SIZE: usize = 1024; // 1KB
|
|
const NUM_KEYS_THROUGHPUT: usize = 10_000; // 10K for throughput tests
|
|
const NUM_KEYS_LATENCY: usize = 100; // 100 for latency tests
|
|
|
|
fn create_test_node(temp_dir: &TempDir) -> (Node, Runtime) {
|
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
.worker_threads(4)
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
|
|
let config = ServerConfig {
|
|
node: NodeConfig {
|
|
id: 1,
|
|
name: "benchmark-node".to_string(),
|
|
role: "control_plane".to_string(),
|
|
},
|
|
cluster: ClusterConfig {
|
|
id: 1,
|
|
bootstrap: true,
|
|
initial_members: vec![],
|
|
},
|
|
network: NetworkConfig {
|
|
api_addr: "127.0.0.1:2379".parse().unwrap(),
|
|
raft_addr: "127.0.0.1:2380".parse().unwrap(),
|
|
gossip_addr: "127.0.0.1:2381".parse().unwrap(),
|
|
tls: None,
|
|
},
|
|
storage: StorageConfig {
|
|
data_dir: temp_dir.path().to_path_buf(),
|
|
},
|
|
raft: RaftConfig {
|
|
role: RaftRole::Voter,
|
|
tick_interval_ms: 100,
|
|
election_timeout_ticks: 10,
|
|
heartbeat_interval_ticks: 3,
|
|
snapshot_interval_secs: 3600,
|
|
max_applied_log_to_keep: 1000,
|
|
},
|
|
};
|
|
|
|
let node = rt.block_on(async { Node::new(config).await.unwrap() });
|
|
|
|
(node, rt)
|
|
}
|
|
|
|
fn bench_put_throughput(c: &mut Criterion) {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let (node, rt) = create_test_node(&temp_dir);
|
|
|
|
// Start server
|
|
let server_handle = rt.spawn(async move {
|
|
node.run().await.unwrap();
|
|
});
|
|
|
|
// Give server time to start
|
|
std::thread::sleep(Duration::from_millis(500));
|
|
|
|
// Create client
|
|
let mut client = rt.block_on(async {
|
|
ChainFireClient::connect("http://127.0.0.1:2379")
|
|
.await
|
|
.unwrap()
|
|
});
|
|
|
|
let value = vec![b'x'; VALUE_SIZE];
|
|
|
|
let mut group = c.benchmark_group("put_throughput");
|
|
group.throughput(Throughput::Elements(NUM_KEYS_THROUGHPUT as u64));
|
|
group.sample_size(10);
|
|
group.measurement_time(Duration::from_secs(30));
|
|
|
|
group.bench_function(BenchmarkId::from_parameter(NUM_KEYS_THROUGHPUT), |b| {
|
|
b.iter(|| {
|
|
rt.block_on(async {
|
|
for i in 0..NUM_KEYS_THROUGHPUT {
|
|
let key = format!("bench_key_{}", i);
|
|
client.put(black_box(&key), black_box(&value)).await.unwrap();
|
|
}
|
|
})
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
|
|
// Cleanup
|
|
server_handle.abort();
|
|
drop(rt);
|
|
}
|
|
|
|
fn bench_get_throughput(c: &mut Criterion) {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let (node, rt) = create_test_node(&temp_dir);
|
|
|
|
// Start server
|
|
let server_handle = rt.spawn(async move {
|
|
node.run().await.unwrap();
|
|
});
|
|
|
|
// Give server time to start
|
|
std::thread::sleep(Duration::from_millis(500));
|
|
|
|
// Create client and populate data
|
|
let mut client = rt.block_on(async {
|
|
ChainFireClient::connect("http://127.0.0.1:2379")
|
|
.await
|
|
.unwrap()
|
|
});
|
|
|
|
let value = vec![b'x'; VALUE_SIZE];
|
|
|
|
// Pre-populate keys
|
|
rt.block_on(async {
|
|
for i in 0..NUM_KEYS_THROUGHPUT {
|
|
let key = format!("bench_key_{}", i);
|
|
client.put(&key, &value).await.unwrap();
|
|
}
|
|
});
|
|
|
|
let mut group = c.benchmark_group("get_throughput");
|
|
group.throughput(Throughput::Elements(NUM_KEYS_THROUGHPUT as u64));
|
|
group.sample_size(10);
|
|
group.measurement_time(Duration::from_secs(30));
|
|
|
|
group.bench_function(BenchmarkId::from_parameter(NUM_KEYS_THROUGHPUT), |b| {
|
|
b.iter(|| {
|
|
rt.block_on(async {
|
|
for i in 0..NUM_KEYS_THROUGHPUT {
|
|
let key = format!("bench_key_{}", i);
|
|
let _ = client.get(black_box(&key)).await.unwrap();
|
|
}
|
|
})
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
|
|
// Cleanup
|
|
server_handle.abort();
|
|
drop(rt);
|
|
}
|
|
|
|
fn bench_put_latency(c: &mut Criterion) {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let (node, rt) = create_test_node(&temp_dir);
|
|
|
|
// Start server
|
|
let server_handle = rt.spawn(async move {
|
|
node.run().await.unwrap();
|
|
});
|
|
|
|
// Give server time to start
|
|
std::thread::sleep(Duration::from_millis(500));
|
|
|
|
// Create client
|
|
let mut client = rt.block_on(async {
|
|
ChainFireClient::connect("http://127.0.0.1:2379")
|
|
.await
|
|
.unwrap()
|
|
});
|
|
|
|
let value = vec![b'x'; VALUE_SIZE];
|
|
|
|
let mut group = c.benchmark_group("put_latency");
|
|
group.sample_size(1000); // Larger sample for better p99/p999 estimates
|
|
group.measurement_time(Duration::from_secs(60));
|
|
|
|
group.bench_function("single_put", |b| {
|
|
let mut key_counter = 0;
|
|
b.iter(|| {
|
|
let key = format!("latency_key_{}", key_counter);
|
|
key_counter += 1;
|
|
rt.block_on(async {
|
|
client.put(black_box(&key), black_box(&value)).await.unwrap();
|
|
})
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
|
|
// Cleanup
|
|
server_handle.abort();
|
|
drop(rt);
|
|
}
|
|
|
|
criterion_group!(benches, bench_put_throughput, bench_get_throughput, bench_put_latency);
|
|
criterion_main!(benches);
|