121 lines
4.4 KiB
Rust
121 lines
4.4 KiB
Rust
use super::*;
|
|
use std::sync::Arc;
|
|
|
|
async fn locked_claim(case: &Case, scope: &Scope<'_>) -> (Executor, Executor, Claim, bool) {
|
|
let mut owner = case.executor(scope).await;
|
|
let waiting = case.executor(scope).await;
|
|
job::enqueue(&mut owner, scope, submission("locked"), NOW)
|
|
.await
|
|
.unwrap();
|
|
let claim = job::claim(&mut owner, scope, NOW, 100)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
let transaction = owner.begin(scope).await.unwrap();
|
|
owner
|
|
.query(
|
|
scope,
|
|
"SELECT id FROM _dever_job WHERE id=$1 FOR UPDATE",
|
|
vec![Parameter::Text(claim.id.clone())],
|
|
1,
|
|
|row| row.get::<_, String>(0),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
(owner, waiting, claim, transaction)
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "requires explicit isolated database.postgres_test"]
|
|
fn lease_expiration_after_row_lock_wait_rolls_back_business_writes() {
|
|
fixture(async |case, scope| {
|
|
let (mut owner, mut waiting, claim, transaction) = locked_claim(case, scope).await;
|
|
waiting
|
|
.initialize(scope, "CREATE TABLE business(id BIGINT PRIMARY KEY)")
|
|
.await
|
|
.unwrap();
|
|
let business = waiting.begin(scope).await.unwrap();
|
|
waiting
|
|
.execute(scope, "INSERT INTO business VALUES (1)", vec![])
|
|
.await
|
|
.unwrap();
|
|
let (result, ()) =
|
|
tokio::join!(job::complete(&mut waiting, scope, &claim, NOW + 1), async {
|
|
tokio::time::sleep(Duration::from_millis(250)).await;
|
|
owner.finish(scope, transaction, true).await.unwrap();
|
|
},);
|
|
assert!(
|
|
result.is_err(),
|
|
"lease checked before the row lock was acquired"
|
|
);
|
|
assert!(waiting.finish(scope, business, true).await.is_err());
|
|
assert_eq!(case.count(scope, "SELECT COUNT(*) FROM business").await, 0);
|
|
let retry = job::claim(&mut waiting, scope, NOW + 1000, 1000)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
assert_ne!(retry.token, claim.token);
|
|
assert!(
|
|
job::complete(&mut waiting, scope, &claim, NOW + 1001)
|
|
.await
|
|
.is_err()
|
|
);
|
|
job::complete(&mut waiting, scope, &retry, NOW + 1001)
|
|
.await
|
|
.unwrap();
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "requires explicit isolated database.postgres_test"]
|
|
fn claim_clock_includes_table_lock_wait() {
|
|
fixture(async |case, scope| {
|
|
let mut owner = case.executor(scope).await;
|
|
let mut waiting = case.executor(scope).await;
|
|
job::enqueue(&mut owner, scope, submission("wait"), NOW)
|
|
.await
|
|
.unwrap();
|
|
let transaction = owner.begin(scope).await.unwrap();
|
|
owner
|
|
.initialize(scope, "LOCK TABLE _dever_job IN ACCESS EXCLUSIVE MODE")
|
|
.await
|
|
.unwrap();
|
|
let (result, ()) = tokio::join!(job::claim(&mut waiting, scope, NOW, 100), async {
|
|
tokio::time::sleep(Duration::from_millis(250)).await;
|
|
owner.finish(scope, transaction, true).await.unwrap();
|
|
},);
|
|
assert!(result.unwrap().unwrap().lease_until >= NOW + 300);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "requires explicit isolated database.postgres_test"]
|
|
fn cancellation_discards_waiting_transaction_and_preserves_recovery() {
|
|
fixture(async |case, scope| {
|
|
let (mut owner, mut waiting, claim, transaction) = locked_claim(case, scope).await;
|
|
let claim = Arc::new(claim);
|
|
let copy = claim.clone();
|
|
let (started, ready) = tokio::sync::oneshot::channel();
|
|
let task = scope
|
|
.spawn(move |child| async move {
|
|
started.send(()).unwrap();
|
|
job::complete(&mut waiting, &child, ©, NOW + 1).await
|
|
})
|
|
.unwrap();
|
|
ready.await.unwrap();
|
|
tokio::time::sleep(Duration::from_millis(50)).await;
|
|
assert!(task.stop().await.unwrap().is_none());
|
|
owner.finish(scope, transaction, true).await.unwrap();
|
|
let mut replacement = case.executor(scope).await;
|
|
let next = job::claim(&mut replacement, scope, NOW + 1000, 1000)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
assert_eq!(next.id, claim.id);
|
|
assert_ne!(next.token, claim.token);
|
|
job::complete(&mut replacement, scope, &next, NOW + 1001)
|
|
.await
|
|
.unwrap();
|
|
});
|
|
}
|