worker-que 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +337 -0
- package/DASHBOARD.md +254 -252
- package/README.md +362 -261
- package/package.json +2 -3
- package/DASHBOARD-QUICKSTART.md +0 -278
- package/SSL-QUICK-REFERENCE.md +0 -225
package/README.md
CHANGED
|
@@ -1,107 +1,37 @@
|
|
|
1
|
-
# que
|
|
1
|
+
# worker-que
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://github.com/Duke-Engineering/que-ts/actions/workflows/coverage.yml)
|
|
5
|
-
[](https://github.com/Duke-Engineering/que-ts/actions/workflows/security.yml)
|
|
6
|
-
[](https://badge.fury.io/js/que-ts)
|
|
3
|
+
> A production-ready TypeScript job queue library for PostgreSQL with real-time monitoring dashboard
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/worker-que)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **Transaction
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
|
|
21
|
-
## Installation
|
|
22
|
-
|
|
23
|
-
### From npm (when published)
|
|
24
|
-
```bash
|
|
25
|
-
npm install que-ts
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### From GitHub (development)
|
|
29
|
-
```bash
|
|
30
|
-
npm install github:Duke-Engineering/que-ts#master
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**Note**: When installing from GitHub, the package will automatically build from TypeScript source using the `prepare` script.
|
|
34
|
-
|
|
35
|
-
### Troubleshooting GitHub Installation
|
|
36
|
-
|
|
37
|
-
If you encounter "Cannot find module 'que-ts'" errors when installing from GitHub:
|
|
38
|
-
|
|
39
|
-
1. **Check the installation completed successfully**:
|
|
40
|
-
```bash
|
|
41
|
-
cd node_modules/que-ts
|
|
42
|
-
ls dist/ # Should show compiled JavaScript files
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
2. **Manual build if needed**:
|
|
46
|
-
```bash
|
|
47
|
-
cd node_modules/que-ts
|
|
48
|
-
npm run build
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
3. **Verify installation**:
|
|
52
|
-
```bash
|
|
53
|
-
cd node_modules/que-ts
|
|
54
|
-
node test-install.js
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
4. **Alternative: Use specific tag**:
|
|
58
|
-
```bash
|
|
59
|
-
npm install github:Duke-Engineering/que-ts#v1.0.0
|
|
60
|
-
```
|
|
12
|
+
- ⚡ **High Performance** - PostgreSQL-backed with advisory locks for reliable job processing
|
|
13
|
+
- 📊 **Real-time Dashboard** - Beautiful web UI for monitoring and managing jobs
|
|
14
|
+
- 🔐 **Enterprise Security** - SSL/TLS support with client certificates
|
|
15
|
+
- 🎯 **Priority Queues** - Multiple queues with configurable priorities
|
|
16
|
+
- 🔄 **Automatic Retries** - Exponential backoff for failed jobs
|
|
17
|
+
- 💾 **Transaction Support** - Enqueue jobs within database transactions
|
|
18
|
+
- 🌐 **Cross-Platform** - Compatible with Ruby Que and que-go
|
|
19
|
+
- 📝 **TypeScript Native** - Full type safety and IntelliSense support
|
|
61
20
|
|
|
62
21
|
## Quick Start
|
|
63
22
|
|
|
64
|
-
###
|
|
65
|
-
|
|
66
|
-
#### Option 1: Using Docker (Recommended for Development)
|
|
23
|
+
### Installation
|
|
67
24
|
|
|
68
25
|
```bash
|
|
69
|
-
|
|
70
|
-
npm run docker:up
|
|
71
|
-
|
|
72
|
-
# Run tests
|
|
73
|
-
npm test
|
|
74
|
-
|
|
75
|
-
# Stop when done
|
|
76
|
-
npm run docker:down
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
For detailed Docker usage, see [DOCKER.md](DOCKER.md).
|
|
80
|
-
|
|
81
|
-
#### Option 2: Manual Database Setup
|
|
82
|
-
|
|
83
|
-
Create the required database table:
|
|
84
|
-
|
|
85
|
-
```sql
|
|
86
|
-
CREATE TABLE que_jobs (
|
|
87
|
-
priority smallint NOT NULL DEFAULT 100,
|
|
88
|
-
run_at timestamptz NOT NULL DEFAULT now(),
|
|
89
|
-
job_id bigserial NOT NULL,
|
|
90
|
-
job_class text NOT NULL,
|
|
91
|
-
args json NOT NULL DEFAULT '[]'::json,
|
|
92
|
-
error_count integer NOT NULL DEFAULT 0,
|
|
93
|
-
last_error text,
|
|
94
|
-
queue text NOT NULL DEFAULT '',
|
|
95
|
-
PRIMARY KEY (queue, priority, run_at, job_id)
|
|
96
|
-
);
|
|
26
|
+
npm install worker-que pg express
|
|
97
27
|
```
|
|
98
28
|
|
|
99
29
|
### Basic Usage
|
|
100
30
|
|
|
101
31
|
```typescript
|
|
102
|
-
import { Client, Worker } from 'que
|
|
32
|
+
import { Client, Worker } from 'worker-que';
|
|
103
33
|
|
|
104
|
-
// Create
|
|
34
|
+
// Create client
|
|
105
35
|
const client = new Client({
|
|
106
36
|
host: 'localhost',
|
|
107
37
|
port: 5432,
|
|
@@ -113,12 +43,7 @@ const client = new Client({
|
|
|
113
43
|
// Enqueue jobs
|
|
114
44
|
await client.enqueue('SendEmail', ['user@example.com', 'Welcome!']);
|
|
115
45
|
|
|
116
|
-
|
|
117
|
-
priority: 10,
|
|
118
|
-
runAt: new Date(Date.now() + 60000) // Run in 1 minute
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// Create and start a worker
|
|
46
|
+
// Create worker
|
|
122
47
|
const worker = new Worker({
|
|
123
48
|
host: 'localhost',
|
|
124
49
|
port: 5432,
|
|
@@ -127,49 +52,56 @@ const worker = new Worker({
|
|
|
127
52
|
password: 'password'
|
|
128
53
|
});
|
|
129
54
|
|
|
130
|
-
// Register
|
|
55
|
+
// Register handlers
|
|
131
56
|
worker.register('SendEmail', async (job) => {
|
|
132
57
|
const [email, message] = job.args;
|
|
133
58
|
console.log(`Sending email to ${email}: ${message}`);
|
|
134
|
-
//
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
worker.register('ProcessPayment', async (job) => {
|
|
138
|
-
const paymentData = job.args[0];
|
|
139
|
-
console.log(`Processing payment of ${paymentData.amount} ${paymentData.currency}`);
|
|
140
|
-
// Payment processing logic here
|
|
59
|
+
// Your email logic here
|
|
141
60
|
});
|
|
142
61
|
|
|
143
|
-
// Start processing
|
|
62
|
+
// Start processing
|
|
144
63
|
await worker.work();
|
|
64
|
+
```
|
|
145
65
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
66
|
+
### Database Setup
|
|
67
|
+
|
|
68
|
+
Create the required table:
|
|
69
|
+
|
|
70
|
+
```sql
|
|
71
|
+
CREATE TABLE que_jobs (
|
|
72
|
+
priority smallint NOT NULL DEFAULT 100,
|
|
73
|
+
run_at timestamptz NOT NULL DEFAULT now(),
|
|
74
|
+
job_id bigserial NOT NULL,
|
|
75
|
+
job_class text NOT NULL,
|
|
76
|
+
args json NOT NULL DEFAULT '[]'::json,
|
|
77
|
+
error_count integer NOT NULL DEFAULT 0,
|
|
78
|
+
last_error text,
|
|
79
|
+
queue text NOT NULL DEFAULT '',
|
|
80
|
+
PRIMARY KEY (queue, priority, run_at, job_id)
|
|
81
|
+
);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or use the included migration:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
psql -U postgres -d myapp -f node_modules/worker-que/migrations/schema.sql
|
|
151
88
|
```
|
|
152
89
|
|
|
153
90
|
## Web Dashboard
|
|
154
91
|
|
|
155
|
-
|
|
92
|
+
Monitor and manage your jobs with the built-in web dashboard:
|
|
156
93
|
|
|
157
94
|
```typescript
|
|
158
95
|
import express from 'express';
|
|
159
96
|
import { Pool } from 'pg';
|
|
160
|
-
import { createDashboard } from 'que
|
|
97
|
+
import { createDashboard } from 'worker-que/dist/dashboard';
|
|
161
98
|
|
|
162
99
|
const app = express();
|
|
163
100
|
const pool = new Pool({ /* your config */ });
|
|
164
101
|
|
|
165
|
-
// Mount dashboard at /admin/queue
|
|
166
102
|
app.use('/admin/queue', createDashboard(pool, {
|
|
167
|
-
title: 'My
|
|
168
|
-
|
|
169
|
-
auth: (req, res, next) => {
|
|
170
|
-
// Add your authentication logic
|
|
171
|
-
return req.isAuthenticated();
|
|
172
|
-
}
|
|
103
|
+
title: 'My Job Queue',
|
|
104
|
+
auth: (req) => req.isAuthenticated() // Optional authentication
|
|
173
105
|
}));
|
|
174
106
|
|
|
175
107
|
app.listen(3000);
|
|
@@ -177,238 +109,407 @@ app.listen(3000);
|
|
|
177
109
|
```
|
|
178
110
|
|
|
179
111
|
**Dashboard Features:**
|
|
180
|
-
- 📊 Real-time statistics
|
|
181
|
-
- 📈 Visual analytics
|
|
182
|
-
- 🔍
|
|
183
|
-
- 🔄
|
|
184
|
-
-
|
|
112
|
+
- 📊 Real-time job statistics
|
|
113
|
+
- 📈 Visual analytics by queue and class
|
|
114
|
+
- 🔍 Filter and search jobs
|
|
115
|
+
- 🔄 Retry failed jobs
|
|
116
|
+
- 🗑️ Delete jobs
|
|
185
117
|
- 📱 Responsive design
|
|
186
118
|
|
|
187
|
-
|
|
119
|
+
[**→ Complete Dashboard Documentation**](./DASHBOARD.md)
|
|
188
120
|
|
|
189
|
-
##
|
|
121
|
+
## Core Concepts
|
|
190
122
|
|
|
191
|
-
|
|
123
|
+
### Jobs
|
|
192
124
|
|
|
193
|
-
|
|
125
|
+
Jobs are units of work stored in PostgreSQL:
|
|
194
126
|
|
|
195
|
-
|
|
127
|
+
```typescript
|
|
128
|
+
await client.enqueue('ProcessPayment', [
|
|
129
|
+
{ amount: 100, userId: 123 }
|
|
130
|
+
], {
|
|
131
|
+
priority: 10, // Lower = higher priority
|
|
132
|
+
queue: 'critical', // Queue name
|
|
133
|
+
runAt: new Date() // Scheduled time
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Workers
|
|
196
138
|
|
|
197
|
-
|
|
139
|
+
Workers poll for jobs and execute registered handlers:
|
|
198
140
|
|
|
199
141
|
```typescript
|
|
200
|
-
new
|
|
201
|
-
|
|
142
|
+
const worker = new Worker({
|
|
143
|
+
host: 'localhost',
|
|
144
|
+
port: 5432,
|
|
145
|
+
database: 'myapp'
|
|
146
|
+
}, {
|
|
147
|
+
queue: 'critical', // Process specific queue
|
|
148
|
+
interval: 1000 // Poll every 1 second
|
|
149
|
+
});
|
|
202
150
|
|
|
203
|
-
|
|
151
|
+
worker.register('ProcessPayment', async (job) => {
|
|
152
|
+
const [payment] = job.args;
|
|
153
|
+
// Process payment...
|
|
154
|
+
// Job automatically marked as done
|
|
155
|
+
});
|
|
204
156
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
- `lockJob(queue?: string): Promise<Job | null>`
|
|
208
|
-
- `close(): Promise<void>`
|
|
157
|
+
await worker.work();
|
|
158
|
+
```
|
|
209
159
|
|
|
210
|
-
###
|
|
160
|
+
### Error Handling
|
|
211
161
|
|
|
212
|
-
|
|
162
|
+
Failed jobs are automatically retried with exponential backoff:
|
|
163
|
+
|
|
164
|
+
- 1st retry: after 1 second
|
|
165
|
+
- 2nd retry: after 16 seconds
|
|
166
|
+
- 3rd retry: after 81 seconds
|
|
167
|
+
- 4th retry: after 256 seconds
|
|
213
168
|
|
|
214
169
|
```typescript
|
|
215
|
-
|
|
170
|
+
worker.register('RiskyJob', async (job) => {
|
|
171
|
+
try {
|
|
172
|
+
await riskyOperation();
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw error; // Job will be retried
|
|
175
|
+
}
|
|
176
|
+
});
|
|
216
177
|
```
|
|
217
178
|
|
|
218
|
-
|
|
179
|
+
## Advanced Features
|
|
219
180
|
|
|
220
|
-
|
|
221
|
-
- `work(): Promise<void>`
|
|
222
|
-
- `workOne(): Promise<boolean>`
|
|
223
|
-
- `shutdown(): Promise<void>`
|
|
181
|
+
### Priority Queues
|
|
224
182
|
|
|
225
|
-
|
|
183
|
+
Organize jobs by priority and queue:
|
|
226
184
|
|
|
227
|
-
|
|
185
|
+
```typescript
|
|
186
|
+
// High priority
|
|
187
|
+
await client.enqueue('UrgentTask', [data], { priority: 1 });
|
|
228
188
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
- `args: any[]`
|
|
235
|
-
- `errorCount: number`
|
|
236
|
-
- `lastError?: string`
|
|
189
|
+
// Critical queue
|
|
190
|
+
await client.enqueue('Payment', [data], {
|
|
191
|
+
priority: 1,
|
|
192
|
+
queue: 'critical'
|
|
193
|
+
});
|
|
237
194
|
|
|
238
|
-
|
|
195
|
+
// Background queue
|
|
196
|
+
await client.enqueue('Analytics', [data], {
|
|
197
|
+
priority: 500,
|
|
198
|
+
queue: 'background'
|
|
199
|
+
});
|
|
200
|
+
```
|
|
239
201
|
|
|
240
|
-
|
|
241
|
-
- `delete(): Promise<void>`
|
|
242
|
-
- `error(errorMessage: string): Promise<void>`
|
|
202
|
+
### Scheduled Jobs
|
|
243
203
|
|
|
244
|
-
|
|
204
|
+
Schedule jobs for future execution:
|
|
245
205
|
|
|
246
|
-
|
|
206
|
+
```typescript
|
|
207
|
+
// Run in 1 hour
|
|
208
|
+
const runAt = new Date(Date.now() + 3600000);
|
|
209
|
+
await client.enqueue('SendReminder', [data], { runAt });
|
|
210
|
+
|
|
211
|
+
// Daily at 9 AM
|
|
212
|
+
const tomorrow = new Date();
|
|
213
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
214
|
+
tomorrow.setHours(9, 0, 0, 0);
|
|
215
|
+
await client.enqueue('DailyReport', [data], { runAt: tomorrow });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Transaction Support
|
|
219
|
+
|
|
220
|
+
Enqueue jobs within database transactions:
|
|
247
221
|
|
|
248
222
|
```typescript
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
223
|
+
import { Pool } from 'pg';
|
|
224
|
+
|
|
225
|
+
const pool = new Pool({ /* config */ });
|
|
226
|
+
const client = new Client({ /* config */ });
|
|
227
|
+
|
|
228
|
+
const pgClient = await pool.connect();
|
|
229
|
+
try {
|
|
230
|
+
await pgClient.query('BEGIN');
|
|
231
|
+
|
|
232
|
+
// Database operations
|
|
233
|
+
await pgClient.query('INSERT INTO orders ...');
|
|
234
|
+
|
|
235
|
+
// Enqueue job in same transaction
|
|
236
|
+
await client.enqueueInTx(pgClient, 'SendOrderEmail', [orderId]);
|
|
237
|
+
|
|
238
|
+
await pgClient.query('COMMIT');
|
|
239
|
+
} catch (error) {
|
|
240
|
+
await pgClient.query('ROLLBACK');
|
|
241
|
+
throw error;
|
|
242
|
+
} finally {
|
|
243
|
+
pgClient.release();
|
|
258
244
|
}
|
|
259
245
|
```
|
|
260
246
|
|
|
261
|
-
###
|
|
247
|
+
### Multiple Workers
|
|
262
248
|
|
|
263
|
-
|
|
249
|
+
Run specialized workers for different queues:
|
|
264
250
|
|
|
265
251
|
```typescript
|
|
266
|
-
|
|
267
|
-
|
|
252
|
+
// Critical queue worker (fast polling)
|
|
253
|
+
const criticalWorker = new Worker(dbConfig, {
|
|
254
|
+
queue: 'critical',
|
|
255
|
+
interval: 100
|
|
256
|
+
});
|
|
268
257
|
|
|
269
|
-
//
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
database: 'mydb',
|
|
274
|
-
user: 'myuser',
|
|
275
|
-
password: 'mypassword',
|
|
276
|
-
ssl: {
|
|
277
|
-
rejectUnauthorized: true,
|
|
278
|
-
}
|
|
258
|
+
// Background queue worker (slow polling)
|
|
259
|
+
const backgroundWorker = new Worker(dbConfig, {
|
|
260
|
+
queue: 'background',
|
|
261
|
+
interval: 5000
|
|
279
262
|
});
|
|
280
263
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
264
|
+
criticalWorker.work();
|
|
265
|
+
backgroundWorker.work();
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### SSL/TLS Support
|
|
269
|
+
|
|
270
|
+
Secure database connections with client certificates:
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import * as fs from 'fs';
|
|
274
|
+
|
|
275
|
+
const client = new Client({
|
|
276
|
+
host: 'db.example.com',
|
|
288
277
|
ssl: {
|
|
289
278
|
rejectUnauthorized: true,
|
|
290
279
|
cert: fs.readFileSync('./certs/client-cert.pem'),
|
|
291
280
|
key: fs.readFileSync('./certs/client-key.pem'),
|
|
292
|
-
ca: fs.readFileSync('./certs/ca-cert.pem')
|
|
293
|
-
passphrase: 'optional-key-passphrase', // For encrypted keys
|
|
281
|
+
ca: fs.readFileSync('./certs/ca-cert.pem')
|
|
294
282
|
}
|
|
295
283
|
});
|
|
296
284
|
```
|
|
297
285
|
|
|
298
|
-
|
|
286
|
+
[**→ Complete SSL Documentation**](./SSL.md)
|
|
287
|
+
|
|
288
|
+
## API Reference
|
|
289
|
+
|
|
290
|
+
### Client
|
|
291
|
+
|
|
292
|
+
#### `new Client(config: ClientConfig)`
|
|
299
293
|
|
|
300
|
-
|
|
294
|
+
Creates a new client instance.
|
|
301
295
|
|
|
296
|
+
**Config Options:**
|
|
302
297
|
```typescript
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
298
|
+
{
|
|
299
|
+
host?: string;
|
|
300
|
+
port?: number;
|
|
301
|
+
database?: string;
|
|
302
|
+
user?: string;
|
|
303
|
+
password?: string;
|
|
304
|
+
ssl?: boolean | SSLConfig;
|
|
305
|
+
maxConnections?: number;
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### `client.enqueue(jobClass, args?, options?): Promise<Job>`
|
|
310
|
+
|
|
311
|
+
Enqueues a new job.
|
|
312
|
+
|
|
313
|
+
**Parameters:**
|
|
314
|
+
- `jobClass` - Job type identifier
|
|
315
|
+
- `args` - Job arguments (JSON array)
|
|
316
|
+
- `options` - Priority, queue, runAt
|
|
317
|
+
|
|
318
|
+
**Returns:** Job instance with ID and metadata
|
|
319
|
+
|
|
320
|
+
#### `client.enqueueInTx(pgClient, jobClass, args?, options?): Promise<Job>`
|
|
321
|
+
|
|
322
|
+
Enqueues a job within a transaction.
|
|
323
|
+
|
|
324
|
+
#### `client.lockJob(queue?): Promise<Job | null>`
|
|
325
|
+
|
|
326
|
+
Locks and returns the next available job.
|
|
327
|
+
|
|
328
|
+
#### `client.close(): Promise<void>`
|
|
329
|
+
|
|
330
|
+
Closes all database connections.
|
|
331
|
+
|
|
332
|
+
### Worker
|
|
333
|
+
|
|
334
|
+
#### `new Worker(config: ClientConfig, options?: WorkerOptions)`
|
|
335
|
+
|
|
336
|
+
Creates a new worker instance.
|
|
337
|
+
|
|
338
|
+
**Worker Options:**
|
|
339
|
+
```typescript
|
|
340
|
+
{
|
|
341
|
+
queue?: string; // Queue to process (default: '')
|
|
342
|
+
interval?: number; // Poll interval in ms (default: 60000)
|
|
306
343
|
maxAttempts?: number; // Max retry attempts (default: 5)
|
|
307
344
|
}
|
|
308
345
|
```
|
|
309
346
|
|
|
310
|
-
|
|
347
|
+
#### `worker.register(jobClass, handler): void`
|
|
348
|
+
|
|
349
|
+
Registers a job handler function.
|
|
350
|
+
|
|
351
|
+
#### `worker.work(): Promise<void>`
|
|
352
|
+
|
|
353
|
+
Starts continuous job processing.
|
|
354
|
+
|
|
355
|
+
#### `worker.workOne(): Promise<boolean>`
|
|
356
|
+
|
|
357
|
+
Processes a single job. Returns true if job was processed.
|
|
358
|
+
|
|
359
|
+
#### `worker.shutdown(): Promise<void>`
|
|
360
|
+
|
|
361
|
+
Gracefully shuts down the worker.
|
|
362
|
+
|
|
363
|
+
### Job
|
|
364
|
+
|
|
365
|
+
Job instances have these properties:
|
|
311
366
|
|
|
312
367
|
```typescript
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
368
|
+
{
|
|
369
|
+
id: number;
|
|
370
|
+
queue: string;
|
|
371
|
+
priority: number;
|
|
372
|
+
runAt: Date;
|
|
373
|
+
jobClass: string;
|
|
374
|
+
args: any[];
|
|
375
|
+
errorCount: number;
|
|
376
|
+
lastError?: string;
|
|
317
377
|
}
|
|
318
378
|
```
|
|
319
379
|
|
|
320
|
-
|
|
380
|
+
And these methods:
|
|
321
381
|
|
|
322
|
-
|
|
382
|
+
- `job.done()` - Mark as completed
|
|
383
|
+
- `job.error(message)` - Mark as failed
|
|
384
|
+
- `job.delete()` - Remove from queue
|
|
323
385
|
|
|
324
|
-
|
|
325
|
-
- 2nd retry: after 16 seconds
|
|
326
|
-
- 3rd retry: after 81 seconds
|
|
327
|
-
- 4th retry: after 256 seconds
|
|
328
|
-
- etc.
|
|
386
|
+
## Performance Tips
|
|
329
387
|
|
|
330
|
-
|
|
388
|
+
### Database Indexes
|
|
331
389
|
|
|
332
|
-
|
|
390
|
+
Add indexes for optimal performance:
|
|
333
391
|
|
|
334
|
-
|
|
392
|
+
```sql
|
|
393
|
+
CREATE INDEX idx_que_jobs_run_at ON que_jobs(run_at);
|
|
394
|
+
CREATE INDEX idx_que_jobs_error_count ON que_jobs(error_count);
|
|
395
|
+
CREATE INDEX idx_que_jobs_queue ON que_jobs(queue);
|
|
396
|
+
CREATE INDEX idx_que_jobs_job_class ON que_jobs(job_class);
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Connection Pooling
|
|
400
|
+
|
|
401
|
+
Configure appropriate pool sizes:
|
|
335
402
|
|
|
336
403
|
```typescript
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
queue: 'critical',
|
|
340
|
-
priority: 1
|
|
404
|
+
const client = new Client({
|
|
405
|
+
maxConnections: 20 // Adjust based on load
|
|
341
406
|
});
|
|
407
|
+
```
|
|
342
408
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
priority: 500
|
|
347
|
-
});
|
|
409
|
+
### Worker Tuning
|
|
410
|
+
|
|
411
|
+
Balance polling frequency with load:
|
|
348
412
|
|
|
349
|
-
|
|
350
|
-
|
|
413
|
+
```typescript
|
|
414
|
+
// High-frequency (for critical jobs)
|
|
415
|
+
new Worker(config, { interval: 100 });
|
|
416
|
+
|
|
417
|
+
// Low-frequency (for background jobs)
|
|
418
|
+
new Worker(config, { interval: 30000 });
|
|
351
419
|
```
|
|
352
420
|
|
|
353
421
|
## Cross-Language Compatibility
|
|
354
422
|
|
|
355
|
-
que
|
|
356
|
-
|
|
357
|
-
- **
|
|
358
|
-
- **[que-go](https://github.com/bgentry/que-go)** - Golang port (currently unmaintained)
|
|
423
|
+
worker-que is fully compatible with:
|
|
424
|
+
- [**Que (Ruby)**](https://github.com/chanks/que) - Original implementation
|
|
425
|
+
- [**que-go**](https://github.com/bgentry/que-go) - Go implementation
|
|
359
426
|
|
|
360
|
-
|
|
427
|
+
Jobs can be enqueued in one language and processed in another.
|
|
361
428
|
|
|
362
|
-
##
|
|
429
|
+
## Production Deployment
|
|
363
430
|
|
|
364
|
-
###
|
|
365
|
-
- **[Que (Ruby)](https://github.com/chanks/que)** - The original and most mature implementation
|
|
366
|
-
- **[que-go](https://github.com/bgentry/que-go)** - Go implementation (unmaintained, but stable)
|
|
367
|
-
- **[que-ts](https://github.com/Duke-Engineering/que-ts)** - This TypeScript/Node.js implementation
|
|
431
|
+
### Docker
|
|
368
432
|
|
|
369
|
-
|
|
433
|
+
```dockerfile
|
|
434
|
+
FROM node:18-alpine
|
|
435
|
+
WORKDIR /app
|
|
436
|
+
COPY package*.json ./
|
|
437
|
+
RUN npm ci --only=production
|
|
438
|
+
COPY . .
|
|
439
|
+
ENV NODE_ENV=production
|
|
440
|
+
EXPOSE 3000
|
|
441
|
+
CMD ["node", "dist/server.js"]
|
|
442
|
+
```
|
|
370
443
|
|
|
371
|
-
###
|
|
444
|
+
### Environment Variables
|
|
372
445
|
|
|
373
446
|
```bash
|
|
374
|
-
#
|
|
375
|
-
|
|
447
|
+
# Database
|
|
448
|
+
DB_HOST=postgres
|
|
449
|
+
DB_PORT=5432
|
|
450
|
+
DB_NAME=myapp
|
|
451
|
+
DB_USER=postgres
|
|
452
|
+
DB_PASSWORD=secret
|
|
453
|
+
|
|
454
|
+
# SSL
|
|
455
|
+
DB_SSL_ENABLED=true
|
|
456
|
+
DB_SSL_CERT=/path/to/client-cert.pem
|
|
457
|
+
DB_SSL_KEY=/path/to/client-key.pem
|
|
458
|
+
DB_SSL_CA=/path/to/ca-cert.pem
|
|
459
|
+
|
|
460
|
+
# Worker
|
|
461
|
+
WORKER_QUEUE=default
|
|
462
|
+
WORKER_INTERVAL=5000
|
|
463
|
+
WORKER_MAX_ATTEMPTS=5
|
|
464
|
+
|
|
465
|
+
# Dashboard
|
|
466
|
+
DASHBOARD_ENABLED=true
|
|
467
|
+
DASHBOARD_PORT=3000
|
|
468
|
+
DASHBOARD_AUTH_ENABLED=true
|
|
469
|
+
```
|
|
376
470
|
|
|
377
|
-
|
|
378
|
-
npm run docker:up
|
|
471
|
+
### Graceful Shutdown
|
|
379
472
|
|
|
380
|
-
|
|
381
|
-
|
|
473
|
+
```typescript
|
|
474
|
+
process.on('SIGTERM', async () => {
|
|
475
|
+
console.log('Shutting down...');
|
|
476
|
+
await worker.shutdown();
|
|
477
|
+
await client.close();
|
|
478
|
+
process.exit(0);
|
|
479
|
+
});
|
|
480
|
+
```
|
|
382
481
|
|
|
383
|
-
|
|
384
|
-
npm run test:watch
|
|
482
|
+
## Documentation
|
|
385
483
|
|
|
386
|
-
|
|
387
|
-
|
|
484
|
+
- [**Dashboard Guide**](./DASHBOARD.md) - Web UI setup and usage
|
|
485
|
+
- [**SSL Configuration**](./SSL.md) - Secure connections
|
|
486
|
+
- [**Docker Setup**](./DOCKER.md) - Container deployment
|
|
487
|
+
- [**Contributing**](./CONTRIBUTING.md) - Development guide
|
|
388
488
|
|
|
389
|
-
|
|
390
|
-
npm run lint
|
|
489
|
+
## Examples
|
|
391
490
|
|
|
392
|
-
|
|
393
|
-
npm run docker:down
|
|
394
|
-
```
|
|
491
|
+
Complete examples are available in the [`examples/`](./examples) directory:
|
|
395
492
|
|
|
396
|
-
|
|
493
|
+
- `basic-usage.ts` - Simple job queue
|
|
494
|
+
- `ssl-connection.ts` - SSL configurations
|
|
495
|
+
- `dashboard-server.ts` - Complete server with dashboard
|
|
397
496
|
|
|
398
|
-
|
|
399
|
-
- `npm run docker:down` - Stop containers
|
|
400
|
-
- `npm run docker:logs` - View PostgreSQL logs
|
|
401
|
-
- `npm run docker:clean` - Remove containers and volumes
|
|
402
|
-
- `npm run test:docker` - Full test cycle with Docker
|
|
497
|
+
## Support
|
|
403
498
|
|
|
404
|
-
|
|
499
|
+
- 📖 [Documentation](https://github.com/your-username/worker-que#readme)
|
|
500
|
+
- 🐛 [Issue Tracker](https://github.com/your-username/worker-que/issues)
|
|
501
|
+
- 💬 [Discussions](https://github.com/your-username/worker-que/discussions)
|
|
405
502
|
|
|
406
|
-
|
|
503
|
+
## License
|
|
407
504
|
|
|
408
|
-
|
|
505
|
+
[MIT](./LICENSE) © 2026
|
|
409
506
|
|
|
410
|
-
|
|
507
|
+
## Acknowledgments
|
|
411
508
|
|
|
412
|
-
|
|
509
|
+
worker-que is inspired by and compatible with:
|
|
510
|
+
- [Que](https://github.com/chanks/que) by Chris Hanks
|
|
511
|
+
- [que-go](https://github.com/bgentry/que-go) by Blake Gentry
|
|
512
|
+
|
|
513
|
+
---
|
|
413
514
|
|
|
414
|
-
|
|
515
|
+
**Built with ❤️ using TypeScript and PostgreSQL**
|