use sqlx::Pool;
use crate::inbox::database::QueueDatabase;
use crate::inbox::engine::{self, Claimed, Claiming, Events, Leasing, Now};
use crate::inbox::queue::Queue;
use crate::inbox::subscriber::{Failed, begin, take_group};
use crate::inbox::tx::PoolTx;
pub(crate) async fn claim_leased<DB, Row>(
pool: &Pool<DB>,
cx: &Claiming,
now: Now,
ids: &mut Row::Ids,
rows: &mut Vec<Claimed<Row>>,
) -> Result<Row::Token, Failed>
where
DB: QueueDatabase,
Row: Events<DB>,
{
let queue = cx.queue;
let claim_failed = |source| -> Failed {
let statement = queue.prepared.claim.map_or("claim", |claim| claim.sql);
(statement, source)
};
if queue.prepared.stamps || queue.prepared.fifo_guard.is_some() {
let mut tx = begin(pool, queue).await?;
let claimed = async {
let lease = Row::lease(queue, now).map_err(claim_failed)?;
let taken = take_group::<DB, Row>(&mut tx, cx, Some(&lease)).await?;
if taken {
Row::claim(&mut tx, cx, Some(&lease), ids, rows)
.await
.map_err(claim_failed)?;
if queue.prepared.stamps {
stamp_rows::<DB, Row>(&mut tx, cx, &lease, rows).await?;
}
}
Ok::<_, Failed>((lease.expiry, taken))
}
.await;
return match claimed {
Ok((lease, true)) => {
tx.commit().await.map_err(|source| ("COMMIT", source))?;
Ok(lease)
}
Ok((lease, false)) => {
let _ = tx.rollback().await;
Ok(lease)
}
Err(failed) => {
let _ = tx.rollback().await;
Err(failed)
}
};
}
let mut conn = pool.acquire().await.map_err(|source| ("acquire", source))?;
let lease = Row::lease(queue, now).map_err(claim_failed)?;
Row::claim(&mut conn, cx, Some(&lease), ids, rows)
.await
.map_err(claim_failed)?;
Ok(lease.expiry)
}
pub(crate) async fn begin_work<DB: QueueDatabase>(
pool: &Pool<DB>,
queue: &Queue,
) -> Result<PoolTx<DB>, Failed> {
let begin = queue.prepared.begin_work;
PoolTx::begin(pool, begin)
.await
.map_err(|source| (begin.unwrap_or("BEGIN"), source))
}
async fn stamp_rows<DB, Row>(
conn: &mut DB::Connection,
cx: &Claiming,
lease: &Leasing<Row::Token>,
rows: &mut Vec<Claimed<Row>>,
) -> Result<(), Failed>
where
DB: QueueDatabase,
Row: Events<DB>,
{
let mut index = 0;
while let Some(claimed) = rows.get(index) {
let stamped = engine::stamp::<DB, Row>(conn, cx, claimed.id::<DB>(), lease)
.await
.map_err(|source| {
let statement = cx.queue.prepared.stamp.map_or("stamp", |stamp| stamp.sql);
(statement, source)
})?;
if stamped {
index += 1;
} else {
rows.remove(index);
}
}
Ok(())
}