fix: per-handler claim lease in ledger, nats max_deliver, pin handler names
This commit is contained in:
@@ -477,3 +477,83 @@ async fn duplicate_handler_names_are_rejected() {
|
||||
let err = result.err().expect("duplicate names must be rejected");
|
||||
assert!(err.to_string().contains("same"), "{err}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_handler_already_running_elsewhere_is_not_run_concurrently() {
|
||||
use domain::ports::{Begin, ProcessedEventLedger};
|
||||
let (handler, calls) = CountingHandler::new("slow", 0);
|
||||
let store = InMemoryStore::new();
|
||||
let id = domain::events::EventId::new();
|
||||
// Another delivery holds a live claim on this (event, handler).
|
||||
assert_eq!(store.begin(&id, "slow").await.unwrap(), Begin::Run);
|
||||
let nacks = Arc::new(Mutex::new(0));
|
||||
let consumer = RedeliveringConsumer {
|
||||
id: Some(id),
|
||||
event: movie_discovered(),
|
||||
times: 1,
|
||||
nacks: Arc::clone(&nacks),
|
||||
};
|
||||
|
||||
WorkerService::new(Arc::new(consumer), vec![handler], store)
|
||||
.unwrap()
|
||||
.run(tokio::sync::watch::channel(false).1)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
*calls.lock().unwrap(),
|
||||
0,
|
||||
"must not run beside the claim holder"
|
||||
);
|
||||
assert_eq!(*nacks.lock().unwrap(), 1, "delivery is retried later");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_stale_in_progress_claim_is_taken_over() {
|
||||
use domain::ports::HANDLER_LEASE_SECONDS;
|
||||
let (handler, calls) = CountingHandler::new("crashed", 0);
|
||||
let store = InMemoryStore::new();
|
||||
let id = domain::events::EventId::new();
|
||||
store.seed_processed_event(
|
||||
&id,
|
||||
"crashed",
|
||||
false,
|
||||
chrono::Utc::now().naive_utc() - chrono::Duration::seconds(HANDLER_LEASE_SECONDS + 1),
|
||||
);
|
||||
let nacks = Arc::new(Mutex::new(0));
|
||||
let consumer = RedeliveringConsumer {
|
||||
id: Some(id),
|
||||
event: movie_discovered(),
|
||||
times: 1,
|
||||
nacks: Arc::clone(&nacks),
|
||||
};
|
||||
|
||||
WorkerService::new(Arc::new(consumer), vec![handler], store)
|
||||
.unwrap()
|
||||
.run(tokio::sync::watch::channel(false).1)
|
||||
.await;
|
||||
|
||||
assert_eq!(*calls.lock().unwrap(), 1);
|
||||
assert_eq!(*nacks.lock().unwrap(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_failed_handler_releases_its_claim_so_redelivery_retries() {
|
||||
let (handler, calls) = CountingHandler::new("flaky", 1);
|
||||
let store = InMemoryStore::new();
|
||||
let nacks = Arc::new(Mutex::new(0));
|
||||
let consumer = RedeliveringConsumer {
|
||||
id: Some(domain::events::EventId::new()),
|
||||
event: movie_discovered(),
|
||||
times: 3,
|
||||
nacks: Arc::clone(&nacks),
|
||||
};
|
||||
|
||||
WorkerService::new(Arc::new(consumer), vec![handler], store.clone())
|
||||
.unwrap()
|
||||
.run(tokio::sync::watch::channel(false).1)
|
||||
.await;
|
||||
|
||||
// Delivery 1 fails and releases; 2 succeeds; 3 is skipped as done.
|
||||
assert_eq!(*calls.lock().unwrap(), 2);
|
||||
assert_eq!(*nacks.lock().unwrap(), 1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user