JobDispatchedInTransactionUsesAfterCommitRule
class JobDispatchedInTransactionUsesAfterCommitRule implements Rule
A queued job dispatched inside a DB::transaction(...) closure must defer its
dispatch until the transaction commits, by chaining ->afterCommit() on the
dispatch, declaring $afterCommit = true, implementing ShouldQueueAfterCommit,
or using a queue connection that does either.
A queued job pushed during an open transaction can be picked up by a worker
before the transaction commits (a fast worker racing the still open connection):
the job then loads rows that are not visible yet and fails, or silently operates
on half written state. Worse, if the transaction rolls back the job still runs
against data that never existed. afterCommit holds the dispatch until the
outermost transaction commits, and drops it entirely on rollback.
Unlike upstream, which assumes Laravel's queue config, the rule reports only a
dispatch whose queue connection is known to push outside the transaction: Hypervel
defaults after_commit to true for every driver except sync and database, and a
database queue on the transaction's own connection inserts inside it. Anything the
analysis cannot establish (an environment-dependent connection, a custom driver,
failover, an enclosing transaction or a job constructor that changes its settings)
stays silent, as do a deliberate beforeCommit(), the outbox and afterResponse().
Scope and limits:
- Only the transaction callback's closure or arrow function form is inspected.
The manual
DB::beginTransaction()...DB::commit()form has no closure to bound the analysis and is not covered. Job::dispatch(...),Job::dispatchIf(...),Job::dispatchUnless(...), thedispatch()helper andBus::dispatch()are checked, for a job named in the call. A literal condition that fails dispatches nothing. Synchronous dispatch (dispatchSync,dispatch_sync) runs inline within the transaction by design.- A dispatch's own settings only count when they are syntactically chained on it.
- Unlike upstream, the walk does not enter nested closures, functions or
classes: they run later, never or at an unknown time (a
DB::afterCommit()callback, for example). A nested transaction is checked from its own call.
Traits
Constants
| private CHAIN_SETTINGS |
The setting each dispatch chain method changes, by lowercase method name. |
| private SETTING_PROPERTIES |
The job properties holding those settings. |
| private SETTING_FREE_METHODS |
The Queueable methods a job constructor may call without changing those settings. |
| private ASYNC_DRIVERS |
The after_commit default each built-in connector applies, for the drivers that can enqueue outside the transaction. |
Properties
| protected Container|null | $container | from HasContainer |
Methods
Create a new rule instance.
Get the node type the rule inspects.
Report queued jobs dispatched inside a transaction that do not wait for it to commit.
Details
in
HasContainer at line 19
void
setContainer(Container $container)
Set the container used for resolution.
in
HasContainer at line 27
Container
getContainer()
Return the current container.
in
HasContainer at line 35
mixed
resolve(string $abstract)
Resolve the given type from the container.
at line 191
__construct(ReflectionProvider $reflectionProvider, ConfigParser $configParser, InitializerExprTypeResolver $initializerExprTypeResolver, ConstructorParser $constructorParser, RuntimeInputs $runtimeInputs)
Create a new rule instance.
at line 203
string
getNodeType()
Get the node type the rule inspects.
at line 215
array
processNode(Node $node, Scope $scope)
Report queued jobs dispatched inside a transaction that do not wait for it to commit.