VOOZH about

URL: https://deepwiki.com/hypervel/queue/7.3-transaction-aware-dispatching

⇱ Transaction-Aware Dispatching | hypervel/queue | DeepWiki


Loading...
Menu

Transaction-Aware Dispatching

Purpose and Scope

This page documents the transaction-aware dispatching feature, which allows jobs to be deferred until database transactions commit. This ensures that queued jobs only execute when their triggering database operations have been successfully persisted, preventing jobs from processing data that may be rolled back.

For information about the general job lifecycle, see Job Lifecycle. For queue driver implementations, see Queue Drivers.


Overview

Transaction-aware dispatching solves a critical race condition in queue-based systems: when a job is dispatched within a database transaction, the job may begin processing before the transaction commits. If the transaction later rolls back, the job will operate on data that no longer exists.

The queue system provides three mechanisms to defer job dispatching until transactions commit:

MechanismScopePriority
ShouldQueueAfterCommit interfacePer-job classHighest
$afterCommit propertyPer-job instanceMedium
after_commit configurationPer-connectionLowest

Sources: src/Queue.php48-50 src/Queue.php308-319


The ShouldQueueAfterCommit Interface

Interface Contract

The ShouldQueueAfterCommit interface is a marker interface with no methods. Jobs implement this interface to declare that they should always wait for transaction commits:


Usage Pattern


Sources: src/Queue.php310


Alternative Mechanisms

Property-Based Control

Individual job instances can override transaction behavior using the $afterCommit property:


This provides runtime flexibility without requiring interface implementation.

Sources: src/Queue.php314-316

Connection-Level Configuration

Queue connections can be configured to defer all jobs by default:


Available on these drivers:

  • database
  • beanstalkd
  • sqs
  • redis

Sources: publish/queue.php64 publish/queue.php73 publish/queue.php90 publish/queue.php105


Decision Flow

The system evaluates whether to defer a job using this priority order:


Dispatch Decision Logic

Sources: src/Queue.php308-319


Implementation Architecture

Core Components


Transaction-Aware Component Relationships

Sources: src/Queue.php279-301 src/CoroutineQueue.php23-38


Sequence Diagram


Transaction-Aware Job Dispatch Sequence

Sources: src/Queue.php281-293 src/CoroutineQueue.php26-32


Implementation in Queue Base Class

The Queue abstract class provides the core logic in the shouldDispatchAfterCommit() method:

src/Queue.php308-319


Priority Resolution

  1. Interface check (highest priority): If the job implements ShouldQueueAfterCommit, return true
  2. Property check (medium priority): If the job is an object with an afterCommit property, return its value
  3. Configuration fallback (lowest priority): Return the connection's $dispatchAfterCommit configuration

Sources: src/Queue.php308-319


Implementation in enqueueUsing()

The enqueueUsing() method wraps the actual queueing operation with transaction awareness:

src/Queue.php279-301

Key steps when shouldDispatchAfterCommit() returns true:

  1. Retrieves TransactionManager from container
  2. Registers a callback via addCallback()
  3. The callback contains the full queueing operation including events
  4. Returns the result of addCallback() (not the job ID)

When shouldDispatchAfterCommit() returns false, the job is dispatched immediately.

Sources: src/Queue.php281-293


Implementation in CoroutineQueue

The CoroutineQueue driver provides its own implementation in the push() method:

src/CoroutineQueue.php23-38


Unlike the base class, CoroutineQueue defers the actual execution (via executeJob()) rather than the enqueueing, since it executes jobs immediately in coroutines.

Sources: src/CoroutineQueue.php23-38


Connector Configuration

The CoroutineConnector passes the after_commit configuration when creating queue instances:

src/Connectors/CoroutineConnector.php15-18


This configuration value becomes the $dispatchAfterCommit property used in the fallback logic.

Sources: src/Connectors/CoroutineConnector.php17


Configuration Reference

Connection Configuration

Each queue connection can specify after_commit behavior:

ConnectionConfiguration KeyDefault Value
databaseafter_commitfalse
beanstalkdafter_commitfalse
sqsafter_commitfalse
redisafter_commitfalse

The sync and coroutine drivers accept this configuration but handle it differently since they execute jobs immediately.

Sources: publish/queue.php64 publish/queue.php73 publish/queue.php90 publish/queue.php105


Behavior Across Queue Drivers

Database, Redis, Beanstalkd, SQS Drivers

These drivers use the Queue::enqueueUsing() method, which:

  • Defers the push() operation to the driver's backend
  • Defers the JobQueueing and JobQueued events
  • Returns null immediately (the actual job ID is only available after commit)

Synchronous Driver

The SyncQueue driver executes jobs immediately and does not support transaction-aware dispatching (since jobs are not actually queued).

Coroutine Driver

The CoroutineQueue driver defers the entire job execution (not just the queueing) until transaction commit. This is because coroutine jobs execute immediately in a background coroutine rather than being persisted to a queue.

Sources: src/CoroutineQueue.php23-38


TransactionManager Integration

Dependency Requirement

Transaction-aware dispatching requires the TransactionManager service to be available in the container:


Without TransactionManager, jobs dispatch immediately even if they implement ShouldQueueAfterCommit.

Callback Registration

The TransactionManager::addCallback() method registers callbacks that execute on transaction commit:


Sources: src/Queue.php284-292 src/CoroutineQueue.php29-32


Code Entity Map


Code Entity Relationships for Transaction-Aware Dispatching

Sources: src/Queue.php48-50 src/Queue.php308-319 src/Queue.php279-301 src/CoroutineQueue.php23-38 src/Connectors/CoroutineConnector.php15-18


Best Practices

When to Use Transaction-Aware Dispatching

Use CaseRecommended Approach
Job processes newly created database recordsImplement ShouldQueueAfterCommit
Job depends on transaction successImplement ShouldQueueAfterCommit
Job sends notifications about data changesImplement ShouldQueueAfterCommit
Job performs idempotent operationsStandard dispatching is sufficient
Job operates on external systems onlyStandard dispatching is sufficient

Configuration Strategy

Connection-level configuration is appropriate when:

  • All jobs on a connection have similar transaction requirements
  • You want to enforce transaction awareness by default
  • Jobs are primarily CRUD-related

Interface-based control is appropriate when:

  • Only specific job types need transaction awareness
  • You want explicit, type-safe contracts
  • Jobs have varying transaction requirements

Property-based control is appropriate when:

  • Runtime decisions determine transaction behavior
  • The same job class needs different behaviors in different contexts

Sources: src/Queue.php308-319


Caveats and Considerations

Return Value Differences

When a job is deferred:

  • Immediate dispatch returns the job ID
  • Deferred dispatch returns null (the ID is only known after commit)

Event Timing

The JobQueueing and JobQueued events are also deferred until transaction commit. Listeners to these events will not execute until after the transaction completes.

Nested Transactions

The behavior with nested transactions depends on the TransactionManager implementation. Typically, callbacks execute only when the outermost transaction commits.

TransactionManager Availability

If TransactionManager is not registered in the container, transaction-aware dispatching silently falls back to immediate dispatch. No exception is thrown.

Sources: src/Queue.php282-283 src/CoroutineQueue.php27-28


Summary

Transaction-aware dispatching provides three levels of control:

  1. Interface level: ShouldQueueAfterCommit for type-safe, class-level declaration
  2. Property level: $afterCommit for runtime, instance-level control
  3. Configuration level: after_commit for connection-wide defaults

The system integrates with TransactionManager to register callbacks that execute on transaction commit, ensuring jobs only process when their triggering data changes are persisted. Different queue drivers handle deferred dispatching appropriately for their execution model.

Sources: src/Queue.php308-319 src/Queue.php279-301 src/CoroutineQueue.php23-38

Refresh this wiki

On this page