Skip to main content

RowLock

Trait RowLock 

Source
pub trait RowLock: Dialect {
    // Required method
    fn lock_claim(
        &self,
        spec: &TableSpec<'_>,
        shape: ClaimShape,
    ) -> Result<Statement, StatementError>;
}
Expand description

The row lock form of a dialect: the claim that locks its rows until its transaction ends.

A table that declares neither #[field(locked_until)] nor advisory_lock takes its rows this way. The claim selects the claimable rows under a lock, the handler runs while the transaction holds them, and the settlement ends the transaction; after a crash the database rolls back and the rows return at once. The transaction opens at the table’s isolation level, with the statement Dialect::begin gives. A dialect implements this trait where its database locks rows for a transaction, and a table in the row lock form does not compile against a dialect that does not. Postgres and MySql implement it; Sqlite does not, as its writer locks the whole database.

§Examples

use ruststream_sqlx_dialect::{
    ClaimShape, Dialect, Param, RowLock, Statement, StatementError, TableSpec,
};

/// SQL Server, a database without a built-in dialect.
#[derive(Debug)]
pub struct Mssql;

// A table without `locked_until` or `advisory_lock` mounts on `Mssql`. The claim holds an
// update lock on each row it takes until its transaction ends, and skips the rows another claim
// holds.
impl RowLock for Mssql {
    fn lock_claim(
        &self,
        _spec: &TableSpec<'_>,
        shape: ClaimShape,
    ) -> Result<Statement, StatementError> {
        let selected = match shape {
            ClaimShape::Rows => "[job_id], [attempt], [payload]",
            ClaimShape::Ids => "[job_id]",
            ClaimShape::Roles => {
                "[job_id] AS [id], [attempt] AS [attempt], [payload] AS [payload]"
            }
        };
        Ok(Statement::new(
            format!(
                "SELECT TOP (@p1) {selected} FROM [email_jobs] \
                 WITH (UPDLOCK, READPAST, ROWLOCK) ORDER BY [job_id]"
            ),
            [Param::Limit],
        ))
    }
}

Required Methods§

Source

fn lock_claim( &self, spec: &TableSpec<'_>, shape: ClaimShape, ) -> Result<Statement, StatementError>

The statement that claims up to Param::Limit rows of the subscription’s group, in claim order, and locks them until the claim’s transaction ends.

A table with FIFO groups keeps one row of a group in work. Its claim takes the group’s head, the first unfinished row of the group in claim order, and takes nothing while the head is in work (another claim’s transaction holds it) or not yet due. It binds no Param::Limit. The claim’s transaction first takes the group with the statement fifo_guard gives and holds it until the delivery settles, so a row that enters the group ahead of the head in work waits too.

§Errors

StatementError::FormMismatch for a table in another form. A dialect of the service’s own that builds no claim for FIFO groups returns StatementError::UnsupportedFifo for a table with them.

§Examples
use ruststream_sqlx_dialect::{
    ClaimShape, Dialect, Param, RowLock, Statement, StatementError, TableSpec,
};

/// SQL Server, a database without a built-in dialect.
#[derive(Debug)]
pub struct Mssql;

impl RowLock for Mssql {
    // The due rows of the subscription's group in `email_jobs`, in claim order. `UPDLOCK`
    // keeps two claims apart until the transaction ends, `READPAST` skips the rows another
    // claim holds, and `ROWLOCK` keeps the lock to the rows themselves.
    fn lock_claim(
        &self,
        spec: &TableSpec<'_>,
        _shape: ClaimShape,
    ) -> Result<Statement, StatementError> {
        // This claim keeps no group in order.
        if spec.is_fifo() {
            return Err(StatementError::UnsupportedFifo { dialect: self.name() });
        }
        Ok(Statement::new(
            "SELECT TOP (@p3) [job_id], [attempt], [payload] FROM [email_jobs] \
             WITH (UPDLOCK, READPAST, ROWLOCK) \
             WHERE [name] = @p1 AND [retry_after] <= @p2 \
             ORDER BY [priority], [retry_after], [job_id]",
            [Param::Group, Param::Now, Param::Limit],
        ))
    }
}

Dyn Compatibility§

This trait is dyn compatible.

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

Implementors§