roman-1983/messenger-worker-registry
A Laravel package that provides a registry for messenger workers, making it easy to register, discover, and manage worker definitions in your application. Ideal for organizing background message consumers and keeping worker configuration consistent.
This package lives in Symfony Messenger territory, not Laravel queues. Before writing any code, confirm you actually run Messenger workers — a Laravel app using Redis/SQS/Horizon has no Symfony worker lifecycle to hook into, and this package will do nothing for you. It fits only if you run standalone messenger:consume processes (hybrid Symfony/Laravel estate, sidecar consumers, or a custom bridge).
Minimal onboarding:
src/ is small enough to audit in an hour. Answer three questions before proceeding: What storage backend does the registry use (Redis, Doctrine, cache)? Does it write heartbeats on a timer, on WorkerRunningEvent, or only on start? Does it clean up stale entries?composer.json constraints. Look at the pinned symfony/messenger and PHP versions — small packages often lock old versions that will fight your framework upgrade.composer require roman-1983/messenger-worker-registry.config/bundles.php. If not, import its service definition manually — check Configuration.php for the config key (likely under messenger_worker_registry:).messenger:consume worker, then query the registry (via injected service or a shipped console command) and confirm the worker appears with name, PID, host, and status. If the worker never registers, nothing downstream matters — debug that first.Health endpoint pattern (the primary use case). Expose the registry through a small controller or Laravel route: pull active workers, compare last_seen against now() - heartbeat_interval, and return a status summary. Let your external monitoring (Uptime, Datadog synthetic, a cron) poll it. This is the whole value proposition: one endpoint answering "which consumers are alive?" without scraping every pod.
Route::get('/health/workers', function (WorkerRegistry $registry) {
$workers = $registry->all();
$stale = array_filter($workers, fn ($w) => $w->lastSeen < now()->subSeconds(90));
return response()->json([
'total' => count($workers),
'stale' => count($stale),
'workers' => $workers,
]);
});
Scale-decision pattern. Before scaling a consumer deployment up, query the registry to see how many workers are actually alive per queue. This catches the classic "scaled up, but old pods are still registered as zombies" case during rolling restarts and blue/green deploys.
Graceful shutdown coordination. Register on worker start, deregister on WorkerRunningEvent when isWorkerIdle() fires, or in your SIGTERM handler. Wire this into supervisor/systemd stop logic so the registry reflects reality rather than accumulating ghosts. For canary verification after deploy, diff the registry between before and after — you should see exactly the workers you intended to replace.
Multi-queue keying. If you run workers per transport (--queues=high,low), key registry entries by queue name so dashboards can show worker counts per queue alongside depth. This is what makes per-queue capacity planning possible.
Wrap it behind your own interface. Define WorkerRegistry with methods like activeCount(string $queue): int, all(): array, isAlive(string $workerId): bool. Even if you only use one implementation, the wrapper is what lets you swap the package out later without a codebase-wide refactor — and given the maturity signals, you probably will.
Fail-open, always. Registry writes must never block job processing. If the backend is down, the worker should log and continue consuming; only the dashboard goes stale.
last_seen and run it from a scheduled command.Configuration.php. Services are usually autoconfigured; if not, you must tag the worker event subscriber manually or it silently never runs, and you'll see an empty registry with zero errors.KEYS messenger:* (or the equivalent Doctrine table) and look at raw values. If writes and reads disagree, you have a serialization or connection mismatch, not a code bug.WorkerRegistryInterface you can implement to swap Redis for Doctrine or decorate with a heartbeat wrapper. If none exists, that's a fork candidate — the small surface area here makes forking cheap.How can I help you explore Laravel packages today?