Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion durable-storage/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ reset-regressions:

long-test:
@cargo run --release --features rocksdb,unstable-test-utils \
--bin database_long_test -- test --max-minutes 10
--bin database_long_test -- test --max-minutes 10 --keep-epochs 1

.PHONY: all check test gen-regression-inputs long-test
8 changes: 8 additions & 0 deletions durable-storage/src/bin/database_long_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//!
//! [`Database`]: octez_riscv_durable_storage::database::Database

use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::time::Duration;

Expand Down Expand Up @@ -51,6 +52,11 @@ enum Commands {
/// Maximum number of epochs to run (default: run until the time budget).
#[arg(long)]
epochs: Option<u64>,

/// Number of most recent epoch snapshots to keep; older snapshots and
/// commits are cleared after each successful epoch (default: keep everything).
#[arg(long)]
keep_epochs: Option<NonZeroUsize>,
},
/// Replay the failing epoch described by `<DIR>/meta.json`.
Replay {
Expand All @@ -67,6 +73,7 @@ fn main() -> Result<()> {
seed,
max_minutes,
epochs,
keep_epochs,
} => {
let seed = match seed {
Some(seed) => {
Expand All @@ -86,6 +93,7 @@ fn main() -> Result<()> {
seed,
time_budget: max_minutes.map(|m| Duration::from_secs(m * 60)),
epochs,
keep_epochs,
};

run_long_test(config)
Expand Down
38 changes: 37 additions & 1 deletion durable-storage/src/long_test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ pub mod model;
pub mod run_case;
pub mod strategy;

use std::collections::VecDeque;
use std::fs;
use std::num::NonZeroUsize;
use std::path::Path;
use std::time::Duration;
use std::time::Instant;
Expand Down Expand Up @@ -68,6 +70,8 @@ pub struct LongTestConfig {
pub seed: Option<Hash>,
/// Time budget. The loop stops cleanly once exceeded.
pub time_budget: Option<Duration>,
/// Number of most recent epoch snapshots to keep. `None` keeps everything.
pub keep_epochs: Option<NonZeroUsize>,
}

/// Metadata persisted alongside a failure which enables replaying it.
Expand Down Expand Up @@ -97,6 +101,7 @@ pub fn run_long_test(config: LongTestConfig) -> Result<()> {
let max_epochs = config.epochs;
let ops_per_epoch = config.ops_per_epoch;
let cases_per_epoch = config.cases_per_epoch;
let keep_epochs = config.keep_epochs;
let out_dir = tempfile::Builder::new()
.prefix("database_long_test-")
.tempdir()?
Expand All @@ -109,6 +114,9 @@ pub fn run_long_test(config: LongTestConfig) -> Result<()> {
if let Some(epochs) = max_epochs {
rerun.push_str(&format!(" --epochs {epochs}"));
}
if let Some(keep_epochs) = keep_epochs {
rerun.push_str(&format!(" --keep-epochs {keep_epochs}"));
}
if let Some(budget) = config.time_budget {
rerun.push_str(&format!(" --max-minutes {}", budget.as_secs() / 60));
}
Expand All @@ -134,6 +142,10 @@ pub fn run_long_test(config: LongTestConfig) -> Result<()> {
let mut base = initial_base(handle, &in_memory_repo, &persistent_repo);
let mut epoch = 0u64;

// Base commits of the most recent epochs, oldest first; the newest entry is
// the current base.
let mut recent_commits = VecDeque::from([base.commit]);

let start = Instant::now();
loop {
if let Some(max) = max_epochs {
Expand Down Expand Up @@ -163,6 +175,7 @@ pub fn run_long_test(config: LongTestConfig) -> Result<()> {
&base,
&advance_ops,
);
recent_commits.push_back(base.commit);

// Run the property test on this base.
let strategy = long_test_ops_strategy(&base.model.pools(), ops_per_epoch);
Expand All @@ -180,7 +193,7 @@ pub fn run_long_test(config: LongTestConfig) -> Result<()> {
let snapshot_size = dir_size(&snapshot_dir)
.context("measuring the size of the latest snapshot")?;
eprintln!(
"epoch {epoch} ok ({} keys, latest snapshot: {:.2} MiB)",
"epoch {epoch} ok (db contains {} entries, latest snapshot: {:.2} MiB)",
base.model.data.len(),
snapshot_size as f64 / (1024.0 * 1024.0),
);
Expand All @@ -190,6 +203,28 @@ pub fn run_long_test(config: LongTestConfig) -> Result<()> {
"epoch {epoch} ok (db contains {} entries)",
base.model.data.len()
);

// Clear snapshots older than the retention window. Only the
// current base is needed for the next epoch and for failure replay.
if let Some(keep_epochs) = keep_epochs {
while recent_commits.len() > keep_epochs.get() {
let old = recent_commits.pop_front().expect("non-empty");
// Content-addressed commits can repeat; keep the
// snapshot if a retained epoch still references it.
if recent_commits.contains(&old) {
continue;
}
let old_dir = persistent_repo.database_commit_dir(&old);
if old_dir.exists() {
fs::remove_dir_all(&old_dir).with_context(|| {
format!("removing disk snapshot {}", old_dir.display())
})?;
}
in_memory_repo
.remove_commit(&old)
.context("removing in-memory snapshot {old}")?;
}
}
}
Err(TestError::Fail(reason, ops)) => {
let meta = FailureMeta {
Expand Down Expand Up @@ -438,6 +473,7 @@ mod tests {
cases_per_epoch: 32,
seed: None,
time_budget: None,
keep_epochs: Some(NonZeroUsize::new(2).expect("non-zero")),
})
.expect("the short long test run should succeed");
}
Expand Down
12 changes: 12 additions & 0 deletions durable-storage/src/storage/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ pub struct InMemoryRepo {
registry_commits: std::sync::Arc<RwLock<HashMap<crate::commit::CommitId, Vec<u8>>>>,
}

#[cfg(test_utils)]
impl InMemoryRepo {
/// Remove the snapshot stored for `id` if it exists
pub fn remove_commit(&self, id: &crate::commit::CommitId) -> Result<(), OperationalError> {
self.commits
.write()
.map_err(|_| OperationalError::LockPoisoned)?
.remove(id);
Ok(())
}
}

/// In-memory key-value store
#[derive(Debug, Default)]
pub struct InMemoryKeyValueStore {
Expand Down
Loading