fix(zset): mark a rebuild chunk's push as a step so a panicking push is counted (improvise-bw4p.5)

Also renames the MemTrace clone-panic test to what it asserts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Claude (zset-rollback)
2026-10-10 18:28:18 -07:00
committed by Edward Langley
parent b2445fed97
commit 8b0b715213
7 changed files with 66 additions and 4 deletions
+24
View File
@@ -167,3 +167,27 @@ fn f4_a_failed_rebuild_step_in_a_txn_then_rollback_then_resume() {
"[f4] header: a rebuild stepped in a rolled-back txn reports ZS_ERR_INTERRUPTED; got rc={rc} done={done}"
);
}
#[test]
fn f5_a_chunk_whose_push_overflows_in_a_txn_then_rollback_is_interrupted() {
let (c, i, _s) = rig();
let pending = zset(&[(b"a", i64::MAX)]);
assert_eq!(unsafe { zs_circuit_push(c, i, pending) }, OK);
let snap = zset(&[(b"a", 1), (b"b", 1)]);
let mut job = null_mut();
assert_eq!(unsafe { zs_rebuild_new(c, i, snap, 1, &mut job) }, OK);
assert_eq!(unsafe { zs_circuit_begin(c) }, OK);
let mut done = 0;
assert_eq!(
unsafe { zs_rebuild_step(job, c, None, null_mut(), &mut done) },
Code::Overflow as i32,
"the chunk's push overflows the pending delta"
);
assert_eq!(unsafe { zs_circuit_rollback(c) }, OK);
let rc = unsafe { zs_rebuild_step(job, c, None, null_mut(), &mut done) };
unsafe { zs_rebuild_free(job) };
unsafe { zs_zset_free(snap) };
unsafe { zs_zset_free(pending) };
unsafe { zs_circuit_free(c) };
assert_eq!(rc, Code::Interrupted as i32, "[f5] got rc={rc} done={done}");
}
+1
View File
@@ -789,6 +789,7 @@ impl<K: Ord> Rebuild<K> {
if chunk.is_zero() && self.began_at.is_some() {
return Progress::Done;
}
circuit.stepped |= circuit.open.is_some();
self.input.push(chunk);
if self.began_at.is_some() {
circuit.step_continue();
+1 -1
View File
@@ -172,7 +172,7 @@ fn c1_a_clone_panic_anywhere_in_a_step_is_rolled_back() {
}
#[test]
fn c2_memtrace_value_clone_panic_leaves_an_empty_group() {
fn a_panicking_value_clone_leaves_a_memtrace_unchanged() {
quiet();
let mut t: MemTrace<u8, Boom> = MemTrace::new();
CLONE_TRIP.with(|c| c.set(Some(0)));
+23
View File
@@ -142,3 +142,26 @@ fn b1_a_closure_that_reads_a_stream_during_eval_does_not_panic() {
c.rollback();
c.step();
}
#[test]
fn p1_a_chunk_whose_push_panics_in_a_txn_is_counted() {
let mut c = Circuit::new();
let (input, s) = c.input::<u8>();
let i = c.integrate(&s);
input.push(z(&[(1, i64::MAX)]));
let mut job = Rebuild::new(input.clone(), z(&[(1, 1), (2, 1)]), 1);
c.begin();
let r = catch_unwind(AssertUnwindSafe(|| job.step(&mut c, || false)));
assert!(r.is_err(), "the push overflows");
c.rollback();
let interrupted = job.interrupted(&c);
let mut got = None;
if !interrupted {
while job.step(&mut c, || false) == Progress::Pending {}
got = Some(i.current());
}
assert!(
interrupted,
"[p1] the chunk left the job unstepped; finishing it gives {got:?}"
);
}