enqueue/dbal
Doctrine DBAL transport for Enqueue/Queue Interop: use an SQL database as a message broker to send and consume messages via Doctrine DBAL. Part of the Enqueue ecosystem; docs and support available via site and community channels.
Installation Add the package via Composer:
composer require enqueue/dbal
Ensure doctrine/dbal is installed (required dependency).
Basic Configuration
Register the transport in your Laravel service provider (e.g., AppServiceProvider):
use Enqueue\Dbal\DbalConnectionFactory;
use Enqueue\Client\Extension\Context;
use Doctrine\DBAL\Connection;
public function register()
{
$connection = new Connection([
'url' => env('DATABASE_URL'),
'driver' => 'pdo_mysql',
'host' => env('DB_HOST'),
'dbname' => env('DB_NAME'),
'user' => env('DB_USER'),
'password' => env('DB_PASSWORD'),
]);
$this->app->singleton('enqueue.dbal.connection', function () use ($connection) {
return DbalConnectionFactory::createWithContext(
new Context(),
$connection
);
});
}
First Use Case: Publishing a Message Inject the connection into a service and publish a message:
use Enqueue\Client\Producer;
public function publishMessage(Producer $producer)
{
$producer->send(new Message('Hello, Queue!', ['priority' => 1]));
}
Bootstrap Consumer Define a consumer in a Laravel command:
use Enqueue\Client\Consumer;
use Enqueue\Dbal\DbalConnectionFactory;
class QueueWorker extends Command
{
protected $signature = 'queue:work';
public function handle()
{
$connection = app('enqueue.dbal.connection');
$consumer = new Consumer($connection);
$consumer->setCallback(function (Message $message, Context $context) {
// Process message logic
$this->info('Processed: ' . $message->getBody());
return new Response(200);
});
$consumer->run();
}
}
Handling Failures
Use setErrorCallback to log or retry failed messages:
$consumer->setErrorCallback(function (Message $message, Context $context, $e) {
Log::error('Failed to process message: ' . $e->getMessage());
return new Response(500);
});
Illuminate\Queue\QueueManager to include the DBAL transport.DbalConnectionFactory’s createBatch() for bulk operations.Connection instance across consumers to avoid overhead.Transaction Isolation
DBAL transport relies on database transactions. Ensure your database supports REPEATABLE READ or higher isolation levels to avoid phantom reads during message processing.
Locking Mechanism
Messages are locked using database rows. Long-running tasks may cause locks to expire (default: 30 seconds). Adjust lock_ttl in the connection factory if needed:
DbalConnectionFactory::createWithContext(
new Context(['lock_ttl' => 60]), // 60-second lock
$connection
);
Schema Migrations
The package creates tables automatically, but manual schema changes (e.g., altering columns) may break functionality. Avoid altering enqueue_jobs or enqueue_lock tables.
enqueue_jobs for stuck messages:
SELECT * FROM enqueue_jobs WHERE status = 'waiting';
$connection->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Custom Message Serialization
Override the default JSON serializer by extending Enqueue\Client\Message and injecting a custom serializer into the Context:
$context = new Context(['serializer' => new CustomSerializer()]);
Queue Priorities
Use the priority field in the message body to implement priority queues. The DBAL transport supports this natively.
Dead Letter Queue (DLQ)
Implement a DLQ by extending the ErrorCallback to move failed messages to a separate table or queue. Example:
$consumer->setErrorCallback(function (Message $message, Context $context, $e) {
$dlqProducer = new Producer($connection);
$dlqProducer->send(new Message($message->getBody(), [
'original_priority' => $message->getProperty('priority'),
'failed_at' => now(),
]));
return new Response(500);
});
How can I help you explore Laravel packages today?