Skip to content

Commit da7991a

Browse files
committed
chore(torture): address multiple left todos
+ more sophisticated new key generation + more sophisticated value generation + use the `max_rollback_blocks` cli argument as nomt option for `max_rollback_log_len`
1 parent d718b70 commit da7991a

File tree

5 files changed

+61
-28
lines changed

5 files changed

+61
-28
lines changed

torture/src/agent.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,12 @@ impl Agent {
234234
o.path(workdir.join("nomt_db"));
235235
o.bitbox_seed(init.bitbox_seed);
236236
o.hashtable_buckets(500_000);
237-
o.rollback(init.rollback);
237+
if let Some(n_commits) = init.rollback {
238+
o.rollback(true);
239+
o.max_rollback_log_len(n_commits);
240+
} else {
241+
o.rollback(false);
242+
}
238243
let nomt = Nomt::open(o)?;
239244
Ok(Self {
240245
workdir,

torture/src/message.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ pub struct InitPayload {
4747
/// Only used upon creation a new NOMT db.
4848
pub bitbox_seed: [u8; 16],
4949
/// Whether the agent is supposed to handle rollbacks.
50-
pub rollback: bool,
50+
/// If `Some`, the maximum amount of supported blocks in a single rollback is specified.
51+
pub rollback: Option<u32>,
5152
}
5253

5354
/// The parameters for the [`ToAgent::Commit`] message.

torture/src/supervisor/cli.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ pub struct WorkloadParams {
9292
///
9393
/// The effective number of commits used for each rollback is randomly generated in the range
9494
/// 0..max_rollback_commits.
95-
#[clap(default_value = "10")]
95+
#[clap(default_value = "100")]
9696
#[arg(long = "max-rollback-commits")]
97-
pub max_rollback_commits: usize,
97+
pub max_rollback_commits: u32,
9898

9999
/// Whether to ensure the correct application of the changest after every commit.
100100
#[clap(default_value = "false")]

torture/src/supervisor/controller.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl SpawnedAgentController {
3333
workdir: String,
3434
workload_id: u64,
3535
bitbox_seed: [u8; 16],
36-
rollback: bool,
36+
rollback: Option<u32>,
3737
) -> Result<()> {
3838
// Assign a unique ID to the agent.
3939
static AGENT_COUNT: AtomicUsize = AtomicUsize::new(0);

torture/src/supervisor/workload.rs

+50-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anyhow::Result;
22
use imbl::OrdMap;
3-
use rand::prelude::*;
3+
use rand::{distributions::WeightedIndex, prelude::*};
44
use std::time::Duration;
55
use tempfile::TempDir;
66
use tokio::time::{error::Elapsed, timeout};
@@ -28,6 +28,9 @@ struct Biases {
2828
/// When generating a key, whether it should be one that was appeared somewhere or a brand new
2929
/// key.
3030
new_key: f64,
31+
/// Distribution used when generating a new key to decide how many bytes needs to be shared
32+
/// with an already existing key.
33+
new_key_distribution: WeightedIndex<usize>,
3134
/// When executing a workload iteration ,this is the probability of executing a rollback.
3235
rollback: f64,
3336
/// When executing a commit this is the probability of causing it to crash.
@@ -45,13 +48,30 @@ impl Biases {
4548
commit_crash: u8,
4649
rollback_crash: u8,
4750
) -> Self {
51+
// When generating a new key to be inserted in the database,
52+
// this distribution will generate the key.
53+
// There is a 25% chance that the key is completely random,
54+
// half of the 25% chance that the first byte will be shared with an existing key,
55+
// one third of the 25% chance that two bytes will be shared with an existing key,
56+
// and so on.
57+
//
58+
// There are:
59+
// + 25% probability of having a key with 0 shared bytes.
60+
// + 48% probability of having a key with 1 to 9 shared bytes.
61+
// + 27% probability of having a key with more than 10 shared bytes.
62+
//
63+
// UNWRAP: provided iterator is not empty, no item is lower than zero
64+
// and the total sum is greater than one.
65+
let new_key_distribution = WeightedIndex::new((1usize..33).map(|x| (32 * 32) / x)).unwrap();
66+
4867
Self {
4968
delete: (delete as f64) / 100.0,
5069
overflow: (overflow as f64) / 100.0,
5170
new_key: (new_key as f64) / 100.0,
5271
rollback: (rollback as f64) / 100.0,
5372
commit_crash: (commit_crash as f64) / 100.0,
5473
rollback_crash: (rollback_crash as f64) / 100.0,
74+
new_key_distribution,
5575
}
5676
}
5777
}
@@ -138,10 +158,8 @@ impl WorkloadState {
138158

139159
/// Returns a KeyValueChange with a new key, a deleted or a modified one.
140160
fn gen_key_value_change(&mut self) -> KeyValueChange {
141-
// TODO: sophisticated key generation.
142-
//
143-
// - Pick a key that was already generated before, but generate a key that shares some bits.
144161
let mut key = [0; 32];
162+
// Generate a Delete KeyValueChange
145163
if !self.committed.state.is_empty() && self.rng.gen_bool(self.biases.delete) {
146164
loop {
147165
self.rng.fill_bytes(&mut key);
@@ -151,15 +169,31 @@ impl WorkloadState {
151169
}
152170
}
153171

154-
if self.committed.state.is_empty() || self.rng.gen_bool(self.biases.new_key) {
172+
// Generate a new key KeyValueChange
173+
if self.committed.state.is_empty() {
174+
self.rng.fill_bytes(&mut key);
175+
return KeyValueChange::Insert(key, self.gen_value());
176+
}
177+
178+
if self.rng.gen_bool(self.biases.new_key) {
155179
loop {
156180
self.rng.fill_bytes(&mut key);
181+
182+
let Some(next_key) = self.committed.state.get_next(&key).map(|(k, _)| *k) else {
183+
continue;
184+
};
185+
186+
let common_bytes =
187+
self.rng.sample(self.biases.new_key_distribution.clone()) as usize;
188+
key[..common_bytes].copy_from_slice(&next_key[..common_bytes]);
189+
157190
if !self.committed.state.contains_key(&key) {
158191
return KeyValueChange::Insert(key, self.gen_value());
159192
}
160193
}
161194
}
162195

196+
// Generate an update KeyValueChange
163197
loop {
164198
self.rng.fill_bytes(&mut key);
165199
if let Some((next_key, _)) = self.committed.state.get_next(&key) {
@@ -169,14 +203,12 @@ impl WorkloadState {
169203
}
170204

171205
fn gen_value(&mut self) -> Vec<u8> {
172-
// TODO: sophisticated value generation.
173-
//
174-
// - Different power of two sizes.
175-
// - Change it to be a non-even.
206+
// MAX_LEAF_VALUE_SIZE is 1332,
207+
// thus every value size bigger than this will create an overflow value.
176208
let len = if self.rng.gen_bool(self.biases.overflow) {
177-
32 * 1024
209+
self.rng.gen_range(1333..32 * 1024)
178210
} else {
179-
32
211+
self.rng.gen_range(1..1333)
180212
};
181213
let mut value = vec![0; len];
182214
self.rng.fill_bytes(&mut value);
@@ -224,7 +256,7 @@ pub struct Workload {
224256
/// Whether to randomly sample the state after every crash or rollback.
225257
sample_snapshot: bool,
226258
/// The max number of commits involved in a rollback.
227-
max_rollback_commits: usize,
259+
max_rollback_commits: u32,
228260
/// If `Some` there are rollbacks waiting to be applied.
229261
scheduled_rollbacks: ScheduledRollbacks,
230262
}
@@ -444,16 +476,8 @@ impl Workload {
444476
Ok(())
445477
}
446478

447-
fn commits_to_rollback(&mut self) -> usize {
448-
// TODO: n_commits should also depend on the max rollback supported by NOMT.
449-
std::cmp::min(
450-
self.state.rng.gen_range(1..self.max_rollback_commits) as usize,
451-
self.state.committed.sync_seqn as usize,
452-
)
453-
}
454-
455479
async fn schedule_rollback(&mut self, should_crash: bool) -> anyhow::Result<()> {
456-
let n_commits_to_rollback = self.commits_to_rollback();
480+
let n_commits_to_rollback = self.state.rng.gen_range(1..self.max_rollback_commits) as usize;
457481
if n_commits_to_rollback == 0 {
458482
trace!("No available commits to perform rollback with");
459483
return Ok(());
@@ -582,7 +606,6 @@ impl Workload {
582606
let agent_died_or_timeout = timeout(TOLERANCE, agent.died()).await;
583607
self.agent.take().unwrap().teardown().await;
584608
if let Err(Elapsed { .. }) = agent_died_or_timeout {
585-
// TODO: flag for investigation.
586609
return Err(anyhow::anyhow!("agent did not die"));
587610
}
588611

@@ -729,7 +752,11 @@ impl Workload {
729752
assert!(self.agent.is_none());
730753
controller::spawn_agent_into(&mut self.agent).await?;
731754
let workdir = self.workdir.path().display().to_string();
732-
let rollback = self.state.biases.rollback > 0.0;
755+
let rollback = if self.state.biases.rollback > 0.0 {
756+
Some(self.max_rollback_commits)
757+
} else {
758+
None
759+
};
733760
self.agent
734761
.as_mut()
735762
.unwrap()

0 commit comments

Comments
 (0)