huskies: merge 889

This commit is contained in:
dave
2026-05-01 14:56:13 +00:00
parent 61cf7684de
commit f8a295eaec
7 changed files with 215 additions and 3 deletions
+81
View File
@@ -733,3 +733,84 @@ async fn bug_511_rowid_replay_preserves_field_update_after_list_insert() {
);
}
}
// ── Story 889 regression tests ───────────────────────────────────────────────
/// Regression for story 889: a tombstoned story must not be resurrected by
/// concurrent write_item calls racing the delete. Spawns a tokio task that
/// hammers write_item every 10ms, tombstones the item mid-race, then verifies
/// the projection stays empty for ~500ms and remains empty after the writer
/// stops.
///
/// The tokio current_thread runtime keeps all tasks on the same OS thread, so
/// the thread-local test CRDT is visible to the spawned task.
#[tokio::test]
async fn tombstone_survives_concurrent_writes() {
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use super::super::read::{evict_item, read_item};
init_for_test();
let story_id = "889_story_tombstone_concurrent";
write_item(
story_id,
"2_current",
Some("Tombstone Concurrent Test"),
None,
None,
None,
None,
None,
None,
None,
);
assert!(
read_item(story_id).is_some(),
"item must exist before eviction"
);
let stop = Arc::new(AtomicBool::new(false));
let stop_clone = stop.clone();
let writer = tokio::task::spawn(async move {
while !stop_clone.load(Ordering::Relaxed) {
write_item(
story_id,
"2_current",
Some("Tombstone Concurrent Test"),
None,
None,
None,
None,
None,
None,
None,
);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
});
tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
evict_item(story_id).expect("evict_item must succeed");
let deadline = tokio::time::Instant::now() + tokio::time::Duration::from_millis(500);
while tokio::time::Instant::now() < deadline {
assert!(
read_item(story_id).is_none(),
"tombstoned story must not reappear while concurrent writes are in flight"
);
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
stop.store(true, Ordering::Relaxed);
writer.await.unwrap();
assert!(
read_item(story_id).is_none(),
"tombstoned story must stay gone after concurrent writer stops"
);
}