Expand description
The write transaction: one database transaction around everything a write does.
A repository write commits on its own, statement by statement: a create commits, and only
then does the service run its after-hooks and publish its events. A crash between the two
loses the effect, and a hook that fails afterwards cannot undo the row. in_write_tx closes
that gap. It opens one transaction, publishes it as a task-local, and commits it only when the
whole write succeeds; every repository write path and every scoped statement helper then runs
on it instead of committing by itself.
Behaviour without a write transaction is exactly what it was: a hand-written caller that never opens one sees no change.
§Which connection
- When a request scope holds a dedicated connection
(
with_request_scopeor the org request scope), the transaction is begun on that connection. The fence variables it carries (company, org units, acting unit, audit attribution) were set at session level, so they hold inside the transaction without being set again, and ID-only lookups stay fenced. - Otherwise a pool connection is taken, the transaction is begun, and the company scope of
the task, the org scope and the audit attribution are bound transaction-locally, as
bind_current_companydoes for a bulk write.
Row-level security therefore decides inside the transaction exactly what it decided outside. A row it refuses refuses the write and, since Postgres aborts the transaction, the whole of it.
§Nesting and savepoints
A write transaction opened while one is already active joins it as a savepoint: if the inner work fails, only the inner work is undone and the outer transaction can carry on; if the inner work succeeds, its effects wait for the outer commit. The bulk repository paths use the same mechanism, so a bulk write that fails inside a service’s transaction rolls back its own rows and reports the error without poisoning anything the caller already holds.
§Cancellation
If the future running inside in_write_tx is dropped (a client disconnect, a timeout layer),
the transaction is rolled back by a task spawned from the guard’s Drop, and the connection
is closed instead of returned to the pool if that rollback fails. A transaction never returns
to the pool open.
§Reading through it
While a write transaction is active, request_conn answers with its
connection, so every scoped read and write helper of this crate, and every repository method,
sees the transaction’s own uncommitted rows. Code that opens a connection of its own (a bare
pool.begin() inside a hook) does not, and cannot see them: that is the one behaviour a hook
has to know about.
Structs§
- WriteTx
- The write transaction of the current task: a handle to the connection it runs on.
- Write
TxConn - The connection of a write transaction, locked. Dereferences to the Postgres connection.
Enums§
- Write
TxFailure - Why a write transaction run through
in_write_tx_resultfailed: the work’s own error, or the database while beginning, binding the scope or committing.
Functions§
- current_
write_ tx - The write transaction of the current task, if
in_write_txopened one. - in_
write_ tx - Run
finside one transaction: committed whenfreturnsOk, rolled back when it returnsError is dropped. - in_
write_ tx_ result in_write_txfor work whose error type cannot absorb asqlx::Error: the two kinds of failure stay apart inWriteTxFailure, so a caller maps each as it must.- in_
write_ tx_ scope - Whether the current task runs inside a write transaction.