use crate::common;
use crate::common::inbox::HandlerFailed;
use std::time::Duration;
use reliar_core::MessageId;
use reliar_inbox::{Classify, FailureKind, InboxClaim, InboxScope, InboxStore};
use reliar_store_postgres::{PostgresInboxError, PostgresInboxSettings, PostgresInboxStore};
async fn fail_blocks_behind_an_uncommitted_claim_until_the_timeout_cancels_it_then_succeeds() {
let pool = common::fresh_db().await;
let store =
PostgresInboxStore::with_settings(pool.clone(), PostgresInboxSettings::default()).unwrap();
let scope = InboxScope::new("orders-projection").unwrap();
let id = MessageId::new();
let mut holder = pool.begin().await.unwrap();
let claim = store
.claim(&mut holder, &scope, crate::common::inbox::message(id))
.await
.unwrap();
assert_eq!(claim, InboxClaim::Claimed { attempt: 1 });
let settings = PostgresInboxSettings::default().statement_timeout(Duration::from_millis(200));
let timeout_store = PostgresInboxStore::with_settings(pool.clone(), settings).unwrap();
let result = tokio::time::timeout(
Duration::from_secs(5),
timeout_store.fail(&scope, crate::common::inbox::message(id), &HandlerFailed),
)
.await
.expect("must return well within the outer bound, not hang");
let err = result.expect_err("a canceled statement must surface as a typed error");
assert!(
matches!(err, PostgresInboxError::Database { .. }),
"expected Database, got {err:?}"
);
assert_eq!(
err.kind(),
FailureKind::Transient,
"a canceled statement is safe to retry"
);
holder.commit().await.unwrap();
timeout_store
.fail(&scope, crate::common::inbox::message(id), &HandlerFailed)
.await
.unwrap();
let record = store.find(&scope, id).await.unwrap().unwrap();
assert_eq!(
record.attempts, 1,
"the successful fail() recorded its attempt"
);
}
async fn a_generous_non_zero_timeout_behaves_identically_to_zero_for_fail_find_and_purge() {
let pool = common::fresh_db().await;
let settings = PostgresInboxSettings::default().statement_timeout(Duration::from_secs(30));
let store = PostgresInboxStore::with_settings(pool.clone(), settings).unwrap();
let scope = InboxScope::new("orders-projection").unwrap();
let id = MessageId::new();
store
.fail(&scope, crate::common::inbox::message(id), &HandlerFailed)
.await
.unwrap();
let record = store.find(&scope, id).await.unwrap().unwrap();
assert_eq!(record.attempts, 1, "fail behaves the same wrapped");
let report = store
.purge(
reliar_inbox::InboxPurgeRequest::default().incomplete_retention(Some(Duration::ZERO)),
)
.await
.unwrap();
assert_eq!(
report.incomplete_deleted, 1,
"purge behaves the same wrapped"
);
}
pub(crate) fn trials(rt: &'static tokio::runtime::Runtime) -> Vec<libtest_mimic::Trial> {
vec![
libtest_mimic::Trial::test(
"inbox_statement_timeout::fail_blocks_behind_an_uncommitted_claim_until_the_timeout_cancels_it_then_succeeds",
move || {
rt.block_on(fail_blocks_behind_an_uncommitted_claim_until_the_timeout_cancels_it_then_succeeds());
Ok(())
},
),
libtest_mimic::Trial::test(
"inbox_statement_timeout::a_generous_non_zero_timeout_behaves_identically_to_zero_for_fail_find_and_purge",
move || {
rt.block_on(
a_generous_non_zero_timeout_behaves_identically_to_zero_for_fail_find_and_purge(
),
);
Ok(())
},
),
]
}