Problem
With MySQL REPEATABLE READ, WorkflowStub::make(ExampleWorkflow::class, $callerSuppliedId) can throw ModelNotFoundException for an instance that another transaction has already committed.
Observed with the published Workflow 2.0.14 package, PHP 8.4.25 and MySQL 8.4.5. Reproduced using separate PHP processes/connections, not an in-memory model comparison.
Reproduction
- Connection B begins a transaction and executes a normal SELECT, establishing a REPEATABLE READ snapshot before the workflow instance exists.
- Connection A reserves the caller-supplied workflow ID, starts its run, and commits.
- Without ending its transaction, B calls
WorkflowStub::make for the same class and ID, intending to use DuplicateStartPolicy::ReturnExistingActive.
- B's
insertOrIgnore sees the existing unique ID. The following nonlocking findOrFail still uses B's older snapshot and cannot see that row.
The exception occurs in src/V2/WorkflowStub.php::reserveCallerSuppliedInstance(), before attemptStart() can apply the duplicate-start policy.
Expected
Reservation resolves the already-committed identity, then the normal type and duplicate-start policies apply. No extra instance, run, or task is created; incompatible types must still be rejected.
Scope And Acceptance
- Use a current database read where reservation reconciles the unique-ID conflict; do not globally lower isolation or bypass workflow identity validation.
- Add a native-MySQL, separate-connection regression with a pre-existing snapshot and a concurrent committed reservation/start.
- Cover duplicate start, competing workflow types, and rollback behavior; SQLite alone cannot prove this behavior.
- Keep the fix focused on caller-ID reservation. This is scheduled normal-priority engine work, not a request for a new orchestration layer.
A consuming application can avoid repeating reservation by reading its already-accepted workflow identity under its existing transaction lock. That workaround does not fix this public API edge case.
Problem
With MySQL REPEATABLE READ,
WorkflowStub::make(ExampleWorkflow::class, $callerSuppliedId)can throwModelNotFoundExceptionfor an instance that another transaction has already committed.Observed with the published Workflow 2.0.14 package, PHP 8.4.25 and MySQL 8.4.5. Reproduced using separate PHP processes/connections, not an in-memory model comparison.
Reproduction
WorkflowStub::makefor the same class and ID, intending to useDuplicateStartPolicy::ReturnExistingActive.insertOrIgnoresees the existing unique ID. The following nonlockingfindOrFailstill uses B's older snapshot and cannot see that row.The exception occurs in
src/V2/WorkflowStub.php::reserveCallerSuppliedInstance(), beforeattemptStart()can apply the duplicate-start policy.Expected
Reservation resolves the already-committed identity, then the normal type and duplicate-start policies apply. No extra instance, run, or task is created; incompatible types must still be rejected.
Scope And Acceptance
A consuming application can avoid repeating reservation by reading its already-accepted workflow identity under its existing transaction lock. That workaround does not fix this public API edge case.