mod common;
use chisel::{Handle, Options, Tag};
use common::{open_chisel, open_chisel_with, Backing};
fn back_to_back_handles_body(b: &Backing) {
let mut db = open_chisel(b);
db.begin().unwrap();
let mut expected = vec![
db.allocate(b"untagged-a").unwrap(),
db.allocate_tagged(b"t42-a", Tag::new(42).unwrap()).unwrap(),
db.allocate(b"untagged-b").unwrap(),
db.allocate_tagged(b"t7-a", Tag::new(7).unwrap()).unwrap(),
db.allocate_tagged(b"t42-b", Tag::new(42).unwrap()).unwrap(),
];
db.commit().unwrap();
let first = db.handles().unwrap();
let second = db.handles().unwrap();
assert_eq!(first, second, "repeated handles() must be identical");
let mut got = first.clone();
got.sort_unstable();
expected.sort_unstable();
assert_eq!(got, expected, "handles() must return exactly the live set");
db.close().unwrap();
}
dual_backing_test!(back_to_back_handles, back_to_back_handles_body);
fn back_to_back_handles_with_tag_body(b: &Backing) {
let mut db = open_chisel(b);
db.begin().unwrap();
let t42_a = db.allocate_tagged(b"t42-a", Tag::new(42).unwrap()).unwrap();
let _t7 = db.allocate_tagged(b"t7", Tag::new(7).unwrap()).unwrap();
let t42_b = db.allocate_tagged(b"t42-b", Tag::new(42).unwrap()).unwrap();
let _untagged = db.allocate(b"untagged").unwrap();
db.commit().unwrap();
let first = db.handles_with_tag(Tag::new(42).unwrap()).unwrap();
let second = db.handles_with_tag(Tag::new(42).unwrap()).unwrap();
assert_eq!(
first, second,
"repeated handles_with_tag() must be identical"
);
let mut got = first.clone();
got.sort_unstable();
let mut expected = vec![t42_a, t42_b];
expected.sort_unstable();
assert_eq!(
got, expected,
"handles_with_tag(42) must return exactly tag 42's members"
);
db.close().unwrap();
}
dual_backing_test!(
back_to_back_handles_with_tag,
back_to_back_handles_with_tag_body
);
fn reads_between_scans_body(b: &Backing) {
let mut db = open_chisel(b);
db.begin().unwrap();
let handles: Vec<Handle> = (0..16)
.map(|i| db.allocate(format!("row-{i}").as_bytes()).unwrap())
.collect();
db.commit().unwrap();
let first = db.handles().unwrap();
for &h in &handles {
let _ = db.read(h).unwrap();
}
let _ = db.read(*handles.last().unwrap()).unwrap();
let second = db.handles().unwrap();
assert_eq!(
first, second,
"reads must not perturb handles() order or contents"
);
db.close().unwrap();
}
dual_backing_test!(reads_between_scans, reads_between_scans_body);
fn update_between_scans_body(b: &Backing) {
let mut db = open_chisel(b);
db.begin().unwrap();
let h_small = db.allocate(b"small").unwrap();
let h_tagged = db
.allocate_tagged(b"tagged-small", Tag::new(42).unwrap())
.unwrap();
let h_other = db.allocate(b"other").unwrap();
db.commit().unwrap();
let first_all = db.handles().unwrap();
let first_t42 = db.handles_with_tag(Tag::new(42).unwrap()).unwrap();
db.begin().unwrap();
db.update(h_small, &[0xABu8; 9000]).unwrap();
db.update(h_tagged, &[0xCDu8; 9000]).unwrap();
db.update(h_other, b"still-small").unwrap();
db.commit().unwrap();
let second_all = db.handles().unwrap();
let second_t42 = db.handles_with_tag(Tag::new(42).unwrap()).unwrap();
assert_eq!(first_all, second_all, "update must not perturb handles()");
assert_eq!(
first_t42, second_t42,
"update must not perturb handles_with_tag()"
);
db.close().unwrap();
}
dual_backing_test!(update_between_scans, update_between_scans_body);
fn cache_eviction_between_scans_body(b: &Backing) {
const N: usize = 300;
let opts = Options::default().cache_max_bytes(32 * 8192); let mut db = open_chisel_with(b, opts);
db.begin().unwrap();
for i in 0..N {
let mut v = vec![0u8; 4000];
v[0] = i as u8;
db.allocate(&v).unwrap();
}
db.commit().unwrap();
let first = db.handles().unwrap();
assert_eq!(first.len(), N, "baseline scan should see every chunk");
for &h in &first {
let _ = db.read(h).unwrap();
}
let second = db.handles().unwrap();
assert_eq!(first, second, "cache eviction must not perturb handles()");
db.close().unwrap();
}
dual_backing_test!(
cache_eviction_between_scans,
cache_eviction_between_scans_body
);
const GROW: usize = 1100;
fn rolled_back_transaction_body(b: &Backing) {
const TAG: u32 = 99;
let mut db = open_chisel(b);
db.begin().unwrap();
let mut baseline_tag = vec![
db.allocate_tagged(b"base-1", Tag::new(TAG).unwrap())
.unwrap(),
db.allocate_tagged(b"base-2", Tag::new(TAG).unwrap())
.unwrap(),
db.allocate_tagged(b"base-3", Tag::new(TAG).unwrap())
.unwrap(),
];
let _base_untagged = db.allocate(b"base-untagged").unwrap();
db.commit().unwrap();
let baseline_all = db.handles().unwrap();
let baseline_t = db.handles_with_tag(Tag::new(TAG).unwrap()).unwrap();
db.begin().unwrap();
for i in 0..GROW {
db.allocate_tagged(format!("ephemeral-{i}").as_bytes(), Tag::new(TAG).unwrap())
.unwrap();
}
db.rollback().unwrap();
assert_eq!(
db.handles().unwrap(),
baseline_all,
"rollback must restore handles()"
);
assert_eq!(
db.handles_with_tag(Tag::new(TAG).unwrap()).unwrap(),
baseline_t,
"rollback must restore handles_with_tag()"
);
let mut got = db.handles_with_tag(Tag::new(TAG).unwrap()).unwrap();
got.sort_unstable();
baseline_tag.sort_unstable();
assert_eq!(got, baseline_tag);
db.close().unwrap();
}
dual_backing_test!(rolled_back_transaction, rolled_back_transaction_body);
fn savepoint_rollback_body(b: &Backing) {
const TAG: u32 = 123;
let mut db = open_chisel(b);
db.begin().unwrap();
let mut baseline_tag = vec![
db.allocate_tagged(b"keep-1", Tag::new(TAG).unwrap())
.unwrap(),
db.allocate_tagged(b"keep-2", Tag::new(TAG).unwrap())
.unwrap(),
];
db.commit().unwrap();
let baseline_all = db.handles().unwrap();
let baseline_t = db.handles_with_tag(Tag::new(TAG).unwrap()).unwrap();
db.begin().unwrap();
db.savepoint("grow").unwrap();
for i in 0..GROW {
db.allocate_tagged(format!("ephemeral-{i}").as_bytes(), Tag::new(TAG).unwrap())
.unwrap();
}
db.rollback_to("grow").unwrap();
db.commit().unwrap();
assert_eq!(
db.handles().unwrap(),
baseline_all,
"savepoint rollback must restore handles()"
);
assert_eq!(
db.handles_with_tag(Tag::new(TAG).unwrap()).unwrap(),
baseline_t,
"savepoint rollback must restore handles_with_tag()"
);
let mut got = db.handles_with_tag(Tag::new(TAG).unwrap()).unwrap();
got.sort_unstable();
baseline_tag.sort_unstable();
assert_eq!(got, baseline_tag);
db.close().unwrap();
}
dual_backing_test!(savepoint_rollback, savepoint_rollback_body);