NAME
Punk::Queue::Worker - a worker child
SYNOPSIS
my $q = Punk::Queue->new(dsn => ...);
$q->task(add => sub { my ($job, $a, $b) = @_; $a + $b });
$q->worker(queues => ['default', 'mail'])->run;
# or, supervised:
# punk-queue worker -q default,mail -j 4 --app app.pl
DESCRIPTION
One claiming process: register a worker row, claim jobs, run them, heartbeat, exit cleanly on TERM or INT. Exactly one job is in flight at a time - the concurrency knob is the supervisor's -j (one process, one row, per unit of concurrency), not per-child parallelism.
The two execution paths
With Hyperman installed, the worker builds a private event loop and runs jobs from inside it. That is not a performance feature; it is what puts a live loop under the task body, so a Punk::Future (or anything loop-aware) the task creates awaits in loop mode - and because the worker's own heartbeat shares the loop, a task that awaits keeps the worker's timers firing.
Without Hyperman - or with PUNK_QUEUE_NO_HM_ABI=1, the test seam - the worker sleeps in poll(2) instead. Fully functional; task-created futures fall back to block mode.
What a task can return
A plain value becomes the result. A die becomes the failure. A job whose task name has no registered body fails with a clear message rather than taking the worker down - a queue holding work this process does not know is normal during a rolling deploy.
How an idle worker waits
On PostgreSQL, every enqueue emits a notification on its queue's channel and the worker holds a second, dedicated LISTEN connection - never the work handle, whose socket carries claim traffic. A new job wakes the worker in milliseconds however long the dequeue interval; the interval (default 5 seconds, interval) is only a backstop. The LISTEN connection is monitored for the quiet death PostgreSQL actually exhibits - the socket goes readable, pg_notifies returns nothing, and Active still claims health - and is reconnected, re-subscribed and re-watched automatically.
On SQLite there is nothing to listen to, so the worker polls with decorrelated jitter: sleeps grow from 50ms toward a 1-second cap by a randomised factor, so a pool of children does not hammer the file in lockstep, and any claim resets the sleep to 50ms.
Either way the sleep is clamped to the delayed-job horizon - the time until the next delayed job in the subscribed queues becomes claimable - so a delay => 3 job starts on time even under a long interval.
Environment
PUNK_QUEUE_ONESHOT=1 claims at most one job, runs it, and exits - how the test suite drives a real worker without hanging. PUNK_QUEUE_NO_HM_ABI=1 forces the poll path.
METHODS
run
Run until stopped. Returns the number of jobs performed. Options were given at construction: queues, tasks (restrict to these task names), interval (idle claim interval, default 0.5s), heartbeat_interval (default 10s), max_jobs (recycle after N jobs; 0 = unlimited - the leak containment story for large app classes).
id
The worker row id this run registered, once running.
queue
The Punk::Queue this worker claims from.
SEE ALSO
Punk::Queue, Punk::Queue::Command.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)