use std::sync::Arc;
use std::time::Instant;
use super::in_flight::IN_FLIGHT_CHECKPOINT_EVERY;
use super::{ClaimEvidence, DedupCache, DedupDecision};
use crate::durability::bridge::block_on;
use crate::durability::store::EngineReadAccountingGuard;
use crate::durability::tests_memory_store::MemoryStore;
use crate::durability::{DurableStore, ProcessingReceipt, open_ephemeral};
type TestResult = Result<(), Box<dyn std::error::Error>>;
const NAMESPACE: &str = "liminal:in-flight-test";
fn evidence() -> ClaimEvidence {
ClaimEvidence::new("orders", vec![0])
}
fn settled_claims() -> Result<usize, Box<dyn std::error::Error>> {
Ok(usize::try_from(IN_FLIGHT_CHECKPOINT_EVERY)? * 5)
}
fn land(
test: &str,
seeded: &MemoryStore,
seed_started: Instant,
) -> Result<Arc<dyn DurableStore>, Box<dyn std::error::Error>> {
let seed_ms = seed_started.elapsed().as_secs_f64() * 1000.0;
let store: Arc<dyn DurableStore> = Arc::new(open_ephemeral(1)?);
let landing = Instant::now();
block_on(seeded.land_on(store.as_ref()))??;
println!(
"{test}: seed through the cache in memory {seed_ms:.1} ms, land on haematite {:.1} ms",
landing.elapsed().as_secs_f64() * 1000.0
);
Ok(store)
}
#[test]
fn ceiling_a_start_reads_the_index_window_not_every_claim_ever_made() -> TestResult {
let seed_started = Instant::now();
let seeded = Arc::new(MemoryStore::default());
let running = DedupCache::new(Arc::clone(&seeded) as Arc<dyn DurableStore>, NAMESPACE);
block_on(running.claim_with_evidence("held", evidence(), 1))??;
let settled = settled_claims()?;
for index in 0..settled {
let key = format!("settled-{index}");
assert_eq!(
block_on(running.claim_with_evidence(&key, evidence(), 1))??,
DedupDecision::Claimed
);
if index % 2 == 0 {
block_on(running.complete_receipt(&key, ProcessingReceipt::for_position(1)))??;
} else {
block_on(running.release_claim(&key))??;
}
}
drop(running);
let store = land("a_start_reads_the_index_window", &seeded, seed_started)?;
let restarted = DedupCache::new(Arc::clone(&store), NAMESPACE);
let accounting = EngineReadAccountingGuard::start();
let claims = block_on(restarted.in_flight_claims())??;
let start = accounting.snapshot();
drop(accounting);
assert_eq!(claims, [("held".to_owned(), Some(evidence()))]);
assert!(!start.counter_overflow_observed, "{start:?}");
assert_eq!(
start.streams_enumerated, 0,
"a start never walks the namespace"
);
let bound = usize::try_from(IN_FLIGHT_CHECKPOINT_EVERY * 2)? * 3 + 8;
assert!(
start.engine_entries <= bound,
"a start after {settled} settled claims read {} entries (bound {bound})",
start.engine_entries
);
Ok(())
}
#[test]
fn ceiling_a_claim_made_after_its_key_was_released_stays_in_the_index() -> TestResult {
let seed_started = Instant::now();
let seeded = Arc::new(MemoryStore::default());
let running = DedupCache::new(Arc::clone(&seeded) as Arc<dyn DurableStore>, NAMESPACE);
block_on(running.claim_with_evidence("k", evidence(), 1))??;
block_on(running.release_claim("k"))??;
block_on(running.claim_with_evidence("k", evidence(), 2))??;
for index in 0..settled_claims()? {
let key = format!("settled-{index}");
block_on(running.claim_with_evidence(&key, evidence(), 1))??;
block_on(running.complete_receipt(&key, ProcessingReceipt::for_position(1)))??;
}
drop(running);
let store = land(
"a_claim_made_after_its_key_was_released",
&seeded,
seed_started,
)?;
let restarted = DedupCache::new(store, NAMESPACE);
assert_eq!(
block_on(restarted.in_flight_claims())??,
[("k".to_owned(), Some(evidence()))]
);
Ok(())
}