Skip to main content

Lock

Trait Lock 

Source
pub trait Lock<DB: Database>: InboxRow {
    // Required method
    fn lock(
        conn: &mut DB::Connection,
        key: &str,
    ) -> impl Future<Output = Result<bool, Error>> + Send;
}
Expand description

Takes the advisory lock on a row’s key for the session that will hold its delivery, without waiting: the lock event of the advisory lock form.

The dialect takes the lock itself on Postgres and MySQL, and the process keeps the keys in work on SQLite. A table names lock and unlock as its own together (custom(lock, unlock), or own::Lock and own::Unlock by hand) to run the service’s own SQL for them instead, as a database without a built-in dialect does, MSSQL with sp_getapplock and sp_releaseapplock; the dialect still selects the candidates and takes each row. A claim calls it once per candidate with the key the template renders, on the connection that holds the delivery; Unlock releases the lock when the delivery settles, and where the delivery is dropped unsettled the connection closes after that unlock. With a lock of the service’s own the process keeps no registry of keys, on SQLite too.

The dialect’s candidate select cannot see a lock of the service’s own, so a claim reads as many candidates past its limit as the subscription has deliveries in work: a key its deliveries hold does not hold back the rows behind it. A key the service’s lock holds elsewhere, in another process or system, is passed over within that margin only: while such keys head the claim order, the claim may end empty and wait for its next poll.

§Examples

use ruststream_sqlx::{Inbox, Lock, Unlock};
use sqlx::{PgConnection, Postgres};

#[derive(Inbox, sqlx::FromRow)]
#[inbox(table = "jobs", advisory_lock = "jobs-{id}", custom(lock, unlock))]
pub struct Job {
    #[field(id)]
    id: i64,
    #[field(payload)]
    payload: Vec<u8>,
}

// The pair MSSQL writes with `sp_getapplock` and `sp_releaseapplock`, written here with
// Postgres's session locks on a 32-bit hash of the key.
impl Lock<Postgres> for Job {
    async fn lock(conn: &mut PgConnection, key: &str) -> Result<bool, sqlx::Error> {
        sqlx::query_scalar("SELECT pg_try_advisory_lock(hashtext($1))")
            .bind(key)
            .fetch_one(conn)
            .await
    }
}

impl Unlock<Postgres> for Job {
    async fn unlock(conn: &mut PgConnection, key: &str) -> Result<bool, sqlx::Error> {
        sqlx::query_scalar("SELECT pg_advisory_unlock(hashtext($1))")
            .bind(key)
            .fetch_one(conn)
            .await
    }
}

Required Methods§

Source

fn lock( conn: &mut DB::Connection, key: &str, ) -> impl Future<Output = Result<bool, Error>> + Send

Tries the advisory lock on key for the session of conn, without waiting: true when it took the lock. The session holds it until Unlock releases it or the session ends.

§Errors

The database’s error; the claim fails, its connection closes, and the next claim waits one second.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§