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)