Add PostgreSQL DurableStore substrate for WP-03

Introduce remora-durable with pool/migrations, a RemoraApiMutation-shaped
ledger, claim-fenced outbox projection, CAS helpers, and keyset cursors,
plus a kind Job acceptance suite against the hermetic fixture.
This commit is contained in:
ms2k 2026-07-16 03:56:44 +09:00
commit 957d170150

View file

@ -0,0 +1,42 @@
use std::future::Future;
use std::pin::Pin;
use sqlx::{PgPool, Postgres, Transaction};
use crate::error::DurableError;
pub type PgTx<'a> = Transaction<'a, Postgres>;
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub async fn with_transaction<T, F>(pool: &PgPool, work: F) -> Result<T, DurableError>
where
F: for<'c> FnOnce(&'c mut PgTx<'_>) -> BoxFuture<'c, Result<T, DurableError>>,
{
let mut tx = pool.begin().await?;
match work(&mut tx).await {
Ok(value) => {
tx.commit().await?;
Ok(value)
}
Err(error) => {
let _ = tx.rollback().await;
Err(error)
}
}
}
/// Bound how long this transaction waits for row or advisory locks.
pub async fn set_lock_timeout(tx: &mut PgTx<'_>) -> Result<(), DurableError> {
sqlx::query("SET LOCAL lock_timeout = '5s'")
.execute(tx.as_mut())
.await?;
Ok(())
}
pub async fn advisory_xact_lock(tx: &mut PgTx<'_>, key: i64) -> Result<(), DurableError> {
sqlx::query("SELECT pg_advisory_xact_lock($1)")
.bind(key)
.execute(tx.as_mut())
.await?;
Ok(())
}