fix: mark_events_failed_v2 idempotent for already-Failed events

Review finding #2: duplicate mark_failed calls double-counted retry_count.
Now skips events already in Failed state. Added test_mark_failed_idempotent.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-11 21:31:45 +08:00
parent ce0315d9ae
commit 1a0095cd58

View File

@ -196,6 +196,9 @@ pub fn mark_events_failed_v2(event_ids: &[String]) -> u32 {
let mut count = 0; let mut count = 0;
for item in buf.iter_mut() { for item in buf.iter_mut() {
if event_ids.contains(&item.event.event_id) { if event_ids.contains(&item.event.event_id) {
if item.state == BufferedEventState::Failed {
continue; // already Failed, skip to avoid double-counting retry
}
item.state = BufferedEventState::Failed; item.state = BufferedEventState::Failed;
item.retry_count += 1; item.retry_count += 1;
count += 1; count += 1;
@ -657,6 +660,22 @@ mod tests {
teardown_session(&sid); teardown_session(&sid);
} }
#[test]
fn test_mark_failed_idempotent() {
let _guard = TEST_LOCK.lock().unwrap();
clear_all_events_v2();
let sid = setup_session("mat_idem");
let e1 = push_heartbeat_v2(&sid, "mat_idem", 15, None, 1000).unwrap();
export_pending_events_v2(100, 2000);
// Mark twice — second should be a no-op
let first = mark_events_failed_v2(&[e1.event_id.clone()]);
assert_eq!(first, 1);
let second = mark_events_failed_v2(&[e1.event_id.clone()]);
assert_eq!(second, 0, "duplicate mark_failed should return 0, not double-count");
ack_events_v2(&[e1.event_id.clone()]);
teardown_session(&sid);
}
#[test] #[test]
fn test_delta_semantics_per_event_type() { fn test_delta_semantics_per_event_type() {
let _guard = lock_test(); let _guard = lock_test();