mcfedr/resque-queue-driver-bundle
A Symfony bundle that integrates a Resque queue driver, letting Symfony applications dispatch background jobs to Resque workers. It wires the mcfedr Resque queue library into Symfony's service container, making it easy to configure connections and process asynchronous tasks.
This bundle is a thin Symfony bridge between mcfedr/queue-manager-bundle and the PHP Resque port (resque/resque). It doesn't implement queuing itself: it registers a queue driver that serializes a job class name plus arguments and pushes them onto Redis lists (resque:queue:<name>), which forking Resque workers then consume. In practice you get a generic "enqueue a job" abstraction in Symfony and delegate all the actual work, retries, and monitoring semantics to Resque.
Before writing code, verify two things:
composer require mcfedr/resque-queue-driver-bundle in a scratch project first and inspect what it resolves to; on Symfony 4+ you will likely need --ignore-platform-reqs, a fork, or a different solution entirely.pcntl/posix extensions if you intend to use Resque's forking worker model.Minimal setup:
composer require mcfedr/resque-queue-driver-bundle resque/resque
Register the bundle in AppKernel (and the queue manager bundle it depends on), then configure the Redis connection and named queues. The exact keys live in the bundle's DependencyInjection/Configuration.php — read that file before trusting any snippet, including this illustrative one:
# config.yml (illustrative — confirm keys against Configuration.php)
mcfedr_queue_manager:
queues:
default:
driver: resque
options:
queue: default
The first use case to prove the wiring end-to-end is a trivial job:
class SendWelcomeEmail
{
public function perform(): void
{
// args arrive as properties/constructor values depending on how you enqueue
}
}
Enqueue it through the queue manager service from a controller, then start a worker with QUEUE=default ./bin/resque (or whatever entry point your Resque install provides) and confirm the job disappears from resque:queue:default in Redis.
Where to look first: the bundle's Configuration.php and service definitions, the mcfedr/queue-manager-bundle driver interface, and the resque/resque README for worker environment variables (QUEUE, REDIS_BACKEND, PREFIX, INTERVAL).
Pass identifiers, not entities. Resque JSON-encodes arguments. Hydrate the entity inside perform() using a repository or service. This keeps payloads small, avoids stale data in a queue, and survives schema changes between enqueue and execution.
Enqueue through the injected driver, not Resque::enqueue directly. The whole point of the wrapper is that your application code depends on the queue-manager abstraction rather than the static Resque API. Inject the manager service (or a thin domain-specific "job dispatcher" around it) into controllers and services. If you later migrate to Symfony Messenger, only the dispatcher changes.
Keep job classes as thin adapters. A job's perform() should do argument validation and immediately delegate to a real service:
class GenerateThumbnail
{
private $thumbnailer;
public function __construct(Thumbnailer $thumbnailer)
{
$this->thumbnailer = $thumbnailer;
}
public function perform(string $path, int $width): void
{
$this->thumbnailer->generate($path, $width);
}
}
This makes the job unit-testable without Redis or a worker, which is the single biggest day-to-day productivity win. If the bundle's worker instantiates jobs by class name rather than through the container, wrap the container lookup in a job factory or a service-locator-backed constructor so tests can inject fakes.
One worker process per queue, managed by Supervisor (or systemd). Don't run QUEUE=* in production: a stuck job in one queue starves every other queue. Give each queue its own supervised process with autorestart=true, restart on deploy, and separate queues for high/low priority work.
Namespace queues per environment and application. Set Resque's PREFIX or include the environment in the queue name (myapp_prod_emails). Without this, a staging worker happily consumes production jobs when both point at the same Redis instance.
Retries belong in the job, mostly. Resque's failure backend captures exceptions, but the PHP ecosystem has nothing as mature as Ruby's resque-retry. Implement retry explicitly: catch exceptions in perform(), increment an attempt counter passed in the argument array, and re-enqueue with a delay (a resque:delayed-style sorted set is common, or a simple sleep/requeue via a scheduled job). Cap attempts and route exhausted jobs to a dead-letter queue.
Test the payload boundary. Unit-test perform() directly. For integration tests, either point at a throwaway Redis database (REDIS_BACKEND=redis://localhost:6379/15) and run a worker synchronously in the test, or substitute an in-memory driver implementation of the queue-manager interface.
composer.json, commit a lock file, and decide early whether you're prepared to fork and patch. For new projects, Symfony Messenger's Redis transport or enqueue/enqueue-bundle are far safer bets; for existing projects, isolate the dependency behind your own dispatcher interface so it's replaceable.perform() or configure the job to bootstrap its own connections. This is the classic "random MySQL errors under load" bug with PHP Resque.restart, or a deploy hook), or you'll debug phantom behavior for an afternoon.DateTime instances (serialize to ISO 8601 strings). Associative arrays become objects in some Resque consumers — be deliberate about array_values() vs maps.services.yml rather than fighting the bundle's container config.FLUSHDB on the wrong database is a real incident, not a hypothetical.LLEN resque:queue:<name> (is it enqueued?), LRANGE ... 0 5 (is the payload class/args what you expect?), KEYS 'resque:*failed*' (did it fail?), worker stdout/logs (did it run?), and ps aux | grep resque (is a worker even alive?). Nine times out of ten the answer is at step one.How can I help you explore Laravel packages today?