Skip to content

Commit b59c1f2

Browse files
committed
test: tighten model runner snapshot invalidation semantics
1 parent 708173b commit b59c1f2

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

src/db/tests/core/compaction_correctness.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ struct ModelRunner {
643643
harness: ScenarioHarness,
644644
oracle: MvccOracle,
645645
l0_ssts: Vec<u64>,
646+
// When set, reads run against this exact snapshot until a mutating op invalidates it.
646647
active_snapshot_ts: Option<u64>,
647648
active_snapshot: Option<TxSnapshot>,
648649
allow_reopen: bool,
@@ -684,6 +685,8 @@ impl ModelRunner {
684685
}
685686

686687
fn clear_active_snapshot(&mut self) {
688+
// Model snapshots are treated as read-only checkpoints. Any write/flush operation
689+
// invalidates the pinned snapshot so follow-up reads observe a fresh view.
687690
self.active_snapshot_ts = None;
688691
self.active_snapshot = None;
689692
}
@@ -973,6 +976,68 @@ fn mvcc_oracle_visible_version_prefers_tombstone() {
973976
assert_eq!(scan, vec![("c".to_string(), 31)]);
974977
}
975978

979+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
980+
async fn model_runner_mutation_ops_clear_active_snapshot() -> Result<(), Box<dyn std::error::Error>>
981+
{
982+
let mut runner = ModelRunner::new(7, "compaction-correctness-model-snapshot-clear").await?;
983+
984+
runner.apply_op(OpKind::Snapshot).await?;
985+
assert!(runner.active_snapshot_ts.is_some());
986+
assert!(runner.active_snapshot.is_some());
987+
988+
runner.apply_op(OpKind::Put).await?;
989+
assert!(runner.active_snapshot_ts.is_none());
990+
assert!(runner.active_snapshot.is_none());
991+
992+
runner.apply_op(OpKind::Snapshot).await?;
993+
assert!(runner.active_snapshot_ts.is_some());
994+
assert!(runner.active_snapshot.is_some());
995+
996+
runner.apply_op(OpKind::Delete).await?;
997+
assert!(runner.active_snapshot_ts.is_none());
998+
assert!(runner.active_snapshot.is_none());
999+
1000+
runner.apply_op(OpKind::Snapshot).await?;
1001+
assert!(runner.active_snapshot_ts.is_some());
1002+
assert!(runner.active_snapshot.is_some());
1003+
1004+
runner.apply_op(OpKind::Flush).await?;
1005+
assert!(runner.active_snapshot_ts.is_none());
1006+
assert!(runner.active_snapshot.is_none());
1007+
1008+
Ok(())
1009+
}
1010+
1011+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1012+
async fn model_runner_reads_use_pinned_snapshot_until_mutation()
1013+
-> Result<(), Box<dyn std::error::Error>> {
1014+
let mut runner = ModelRunner::new(11, "compaction-correctness-model-pinned-read").await?;
1015+
1016+
runner.apply_op(OpKind::Snapshot).await?;
1017+
let pinned_ts = runner
1018+
.active_snapshot_ts
1019+
.ok_or("snapshot should set active_snapshot_ts")?;
1020+
1021+
runner.apply_op(OpKind::Get).await?;
1022+
let last = runner
1023+
.trace
1024+
.entries
1025+
.back()
1026+
.ok_or("expected get op in trace")?;
1027+
match &last.op {
1028+
Op::Get { snapshot_ts, .. } => assert_eq!(*snapshot_ts, pinned_ts),
1029+
other => return Err(format!("expected Get op, got {other:?}").into()),
1030+
}
1031+
assert_eq!(runner.active_snapshot_ts, Some(pinned_ts));
1032+
assert!(runner.active_snapshot.is_some());
1033+
1034+
runner.apply_op(OpKind::Put).await?;
1035+
assert!(runner.active_snapshot_ts.is_none());
1036+
assert!(runner.active_snapshot.is_none());
1037+
1038+
Ok(())
1039+
}
1040+
9761041
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
9771042
async fn compaction_correctness_overwrite_chain() -> Result<(), Box<dyn std::error::Error>> {
9781043
let scenario = "overwrite_chain";

0 commit comments

Comments
 (0)