Async-Redis
view release on metacpan or search on metacpan
examples/async-job-queue/SPEC.md view on Meta::CPAN
# Async Job Queue Example Specification
## Goal
Create a small CLI example that makes Async::Redis concurrency visible without
requiring a web framework or extra UI.
The example should prove three things:
- Redis list commands can model a simple job queue.
- multiple async workers can process jobs concurrently in one Perl process.
- a heartbeat task keeps running while workers wait in `BLPOP` and while other
tasks are sleeping or doing Redis I/O.
## Location
- executable: `examples/async-job-queue/app.pl`
- documentation: `examples/async-job-queue/README.md`
- examples index update: `examples/README.md`
## User Story
As a new Async::Redis user, I can run one command and see jobs being queued,
claimed by workers, processed concurrently, and reported by a heartbeat while
the process remains responsive.
## Runtime Behavior
The app should:
- connect to Redis using `REDIS_HOST` and `REDIS_PORT`, defaulting to
`localhost` and `6379`
- clean only its own demo keys at startup
- enqueue a burst of jobs immediately so the queue depth is visible
- start a fixed number of worker coroutines
- start one heartbeat coroutine
- exit automatically after all jobs are processed
- clean up its worker unblock sentinels before exit
Default values:
- jobs: `10`
- workers: `2`
- simulated work delay: `1.5` seconds
- heartbeat interval: `0.25` seconds
- queue key: `async-job-queue:jobs`
- processed counter key: `async-job-queue:processed`
- in-flight set key: `async-job-queue:in-flight`
## Async Design
Use separate Redis connections for roles that can block independently:
- one producer/controller connection
- one stats connection
- one Redis connection per worker
Workers must use `BLPOP` so the example demonstrates a real Redis blocking
operation. Each worker connection may sit inside `BLPOP` without preventing the
producer, heartbeat, or other workers from making progress.
Workers should simulate job processing with `Future::IO->sleep($delay)` rather
than CPU work. The point is event-loop concurrency, not parallel CPU execution.
The app should coordinate all tasks with futures, for example:
- producer future enqueues all jobs
- worker futures loop until they receive a stop sentinel
- heartbeat future loops until the processed count reaches the target
- main future waits for producer, workers, and heartbeat to finish
## Queue Semantics
Startup:
- delete `async-job-queue:jobs`
- delete `async-job-queue:processed`
- delete `async-job-queue:in-flight`
- push jobs `job-1` through `job-N`
Worker loop:
- `BLPOP async-job-queue:jobs 0`
- if the value is a stop sentinel, exit
- add the job to `async-job-queue:in-flight`
- print that the worker started the job
- `await Future::IO->sleep($delay)`
- remove the job from `async-job-queue:in-flight`
- increment `async-job-queue:processed`
- print that the worker finished the job
Shutdown:
- after all real jobs are processed, push one stop sentinel per worker
- workers consume sentinels and exit
- final summary prints elapsed time, processed count, and expected sequential
( run in 0.700 second using v1.01-cache-2.11-cpan-df04353d9ac )