Expected Behavior
A workflow execution mutation should acquire the execution-row lock and verify the expected previous record version or next event ID in the same conditional UPDATE whenever the condition succeeds.
Actual Behavior
applyWorkflowMutationTx and applyWorkflowSnapshotTxAsReset first call lockAndCheckExecution, which performs a SELECT ... FOR UPDATE, and then issue an unconditional UPDATE of the same executions row.
The successful path therefore requires two SQL statements against the same row even though the update itself acquires the required lock. Under high workflow-transition concurrency, the extra statement adds database work and lock hold time.
The existing conflict details can be preserved without paying for the read on every successful mutation: include the expected prior value in the UPDATE predicate, and only perform the current lock/read when the conditional update affects zero rows. That conflict-only read can continue returning the existing typed condition errors with the observed version and next event ID.
Steps to Reproduce the Problem
- Trace a successful SQL workflow mutation or reset.
- Observe
WriteLockExecutions followed by UpdateExecutions for the same execution row.
- Express the existing condition in the update predicate and observe that a successful update both validates the condition and holds the row lock for the transaction.
Specifications
- Version: current
main (9eadc90f60fd02fa2ee688bf5b29919b7fbb0dda)
- Platform: PostgreSQL, MySQL, and SQLite SQL persistence plugins
Expected Behavior
A workflow execution mutation should acquire the execution-row lock and verify the expected previous record version or next event ID in the same conditional
UPDATEwhenever the condition succeeds.Actual Behavior
applyWorkflowMutationTxandapplyWorkflowSnapshotTxAsResetfirst calllockAndCheckExecution, which performs aSELECT ... FOR UPDATE, and then issue an unconditionalUPDATEof the sameexecutionsrow.The successful path therefore requires two SQL statements against the same row even though the update itself acquires the required lock. Under high workflow-transition concurrency, the extra statement adds database work and lock hold time.
The existing conflict details can be preserved without paying for the read on every successful mutation: include the expected prior value in the
UPDATEpredicate, and only perform the current lock/read when the conditional update affects zero rows. That conflict-only read can continue returning the existing typed condition errors with the observed version and next event ID.Steps to Reproduce the Problem
WriteLockExecutionsfollowed byUpdateExecutionsfor the same execution row.Specifications
main(9eadc90f60fd02fa2ee688bf5b29919b7fbb0dda)