power-queues
Version:
Base classes for implementing custom queues in redis under high load conditions based on nestjs.
146 lines (115 loc) • 6.2 kB
Markdown
# power-queues
A production-ready, **Redis-backed queue runner** with visibility timeouts, delayed scheduling, chainable queues, retries with exponential backoff + jitter, and heartbeat renewals — built on top of a thin ```PowerRedis``` abstraction.
This module exposes a base abstract class PowerQueue which you extend and implement. It supports:
- FIFO reservation (`LMOVE LEFT->RIGHT` preferred, `RPOPLPUSH` fallback) via Lua scripts or client loop.
- **Visibility timeout (VT)** with periodic heartbeat (ZSET scores store deadlines).
- **Delayed tasks** (ZSET with future timestamps) and promotion to ready list.
- **Retry policy** with exponential backoff + jitter and a **fail** queue after max attempts.
- **Chain mode**: automatically forward a task through a list of queues and call lifecycle hooks.
- Iteration loop with promote/requeue passes, concurrency slicing, and status counters with TTL.
It provides the core logic for **visibility timeouts**, **task retries**, **delayed scheduling**, **chain forwarding**, and **concurrent workers** — without any external dependencies.
> Built on top of [**PowerRedis**](https://github.com/ihor-bielchenko/power-redis)
> Uses utilities from [**full-utils**](https://github.com/ihor-bielchenko/full-utils)
## API (with examples)
Below are brief excerpts. Full JSDoc: power-queues.docs.ihor.bielchenko.com.
## Overview
PowerQueue is not a “ready-made” message broker like BullMQ — it’s a **foundation** for building your own
custom Redis-based queues with full control over task lifecycle and execution flow.
It manages:
- `LIST` for **ready tasks** (RPUSH producers, LMOVE/RPOPLPUSH consumers)
- `LIST` for **processing tasks** (temporary visibility list)
- `ZSET` for **visibility timeouts** (invisible tasks until acknowledged or expired)
- `ZSET` for **delayed scheduling** (future tasks activation)
Ideal for:
- high-performance Redis-based backends,
- telemetry and distributed job systems,
- retryable or delayed workloads,
- cron-like task promotion and requeueing.
---
## Key Features
- **Visibility timeout** — automatic requeue if worker crashes or fails to ack in time
- **Delayed tasks** — schedule jobs for future execution (e.g., 5 minutes later)
- **Concurrent processing** — process multiple jobs in parallel
- **Retry logic** — exponential backoff with configurable limits
- **Lifecycle hooks** — extend and customize every phase
- **Task chaining** — forward results between queues
- **Lua-optimized reservation** — atomic batch pulls via LMOVE or RPOPLPUSH fallback
- **Zero dependencies** — built purely on Node.js + PowerRedis + full-utils
## Installation
```bash
npm install power-queue
# or
yarn add power-queue
```
PowerQueue works with any Redis client compatible with IORedis interface.
## Example Usage
### 1. Create your custom queue
```javascript
import { PowerQueue } from 'power-queue';
class EmailQueue extends PowerQueue {
/**
* Process a single task payload.
*/
async execute(task) {
console.log('📨 Sending email:', task.payload);
// Simulate async work
await this.wait(200);
// Optionally return result
return { status: 'sent', timestamp: Date.now() };
}
}
export const emailQueue = new EmailQueue({
redis: { host: '127.0.0.1', port: 6379 },
});
```
### 2. Enqueue a new task
```javascript
await emailQueue.enqueue('emails', {
to: 'user@example.com',
subject: 'Welcome!',
body: 'Hello and thanks for joining!',
});
```
### 3. Start processing
```javascript
emailQueue.start('emails');
```
That’s it — PowerQueue will:
- Pull tasks from Redis LIST atomically
- Move them into processing + visibility sets
- Retry or requeue on error or timeout
- Handle parallel processing with full isolation
## Configuration Options
Each property is strongly typed and fully documented in TypeDoc.
| Property | Type | Default | Description |
| ---------------------- | ----------------------------------- | ------- | ---------------------------------------------- |
| `iterationTimeout` | `number` | `1000` | Delay (ms) between polling loops |
| `portionLength` | `number` | `1000` | Max number of tasks per batch pull |
| `expireStatusSec` | `number` | `300` | TTL for progress tracking |
| `maxAttempts` | `number` | `1` | Max retry attempts before marking as failed |
| `concurrency` | `number` | `32` | Parallel runners per queue |
| `visibilityTimeoutSec` | `number` | `60` | How long a task stays invisible before requeue |
| `retryBaseSec` | `number` | `1` | Base delay for exponential backoff |
| `retryMaxSec` | `number` | `3600` | Max retry delay |
| `runners` | `Map<string, { running: boolean }>` | — | Active queue execution flags |
| `processingRaw` | `Map<string, string>` | — | Tracks raw Redis entries for acknowledgment |
| `heartbeatTimers` | `Map<string, NodeJS.Timeout>` | — | Maintains active visibility extensions |
## Design Principles
### Minimalism
Only core Redis primitives are used — no unnecessary abstractions or event emitters.
### Predictability
All queue state is transparent and Redis-inspectable:
- ```LIST``` for ready and processing states
- ```ZSET``` for timeouts and scheduling
### Resilience
Every step (reserve, ack, retry, promote) can recover after process restart.
### Extensibility
Implement custom behaviors by overriding:
```javascript
async beforeExecute(task) {}
async afterExecute(task, result) {}
async onError(task, error) {}
async onRetry(task, delaySec) {}
```
## License
Use freely in your own projects. Add proper notices if you publish a package (MIT/Apache-2.0, etc.).