queen-mq 0.2.0 → 0.2.11
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/README.md +165 -769
- package/{src → client-js}/benchmark/consumer.js +5 -3
- package/{src → client-js}/benchmark/producer.js +9 -4
- package/{src → client-js}/client/client.js +272 -6
- package/{src → client-js}/client/index.js +1 -4
- package/{src → client-js}/client/utils/http.js +3 -2
- package/{src → client-js}/client/utils/retry.js +7 -1
- package/{src → client-js}/test/core-tests.js +7 -2
- package/{src → client-js}/test/edge-case-tests.js +2 -1
- package/client-js/test/human.js +162 -0
- package/client-js/test/qos0-tests.js +334 -0
- package/{src → client-js}/test/test-new.js +42 -0
- package/package.json +8 -17
- package/init-db.js +0 -20
- package/src/cluster-server.js +0 -242
- package/src/config.js +0 -229
- package/src/database/connection.js +0 -129
- package/src/database/poolManager.js +0 -199
- package/src/database/schema-v2.sql +0 -278
- package/src/managers/eventManager.js +0 -59
- package/src/managers/queueManagerOptimized.js +0 -1634
- package/src/managers/resourceCache.js +0 -96
- package/src/managers/systemEventManager.js +0 -132
- package/src/routes/ack.js +0 -26
- package/src/routes/configure.js +0 -46
- package/src/routes/messages.js +0 -368
- package/src/routes/pop.js +0 -69
- package/src/routes/push.js +0 -28
- package/src/routes/resources.js +0 -330
- package/src/routes/status.js +0 -1037
- package/src/server.js +0 -1633
- package/src/test/MIGRATION_ISSUES.md +0 -174
- package/src/test/test.js +0 -4524
- package/src/utils/streaming.js +0 -231
- package/src/webapp-dist/assets/Analytics-DJrE4Rz2.css +0 -1
- package/src/webapp-dist/assets/Analytics-HEMXKmfJ.js +0 -1
- package/src/webapp-dist/assets/ConfirmDialog-YEoCdE7x.js +0 -1
- package/src/webapp-dist/assets/ConsumerGroups-CC5VJywp.css +0 -1
- package/src/webapp-dist/assets/ConsumerGroups-D8Z20HhZ.js +0 -1
- package/src/webapp-dist/assets/Dashboard-BRFOz6it.css +0 -1
- package/src/webapp-dist/assets/Dashboard-Bpx9I32h.js +0 -1
- package/src/webapp-dist/assets/LoadingSpinner-BPttXcbs.js +0 -1
- package/src/webapp-dist/assets/Messages-BOfVHKma.css +0 -1
- package/src/webapp-dist/assets/Messages-EKURCbLl.js +0 -1
- package/src/webapp-dist/assets/QueueDetail-BXXA7xoZ.css +0 -1
- package/src/webapp-dist/assets/QueueDetail-Bmxg0KiA.js +0 -1
- package/src/webapp-dist/assets/Queues-CvrJkrZT.css +0 -1
- package/src/webapp-dist/assets/Queues-yUx9_3-9.js +0 -1
- package/src/webapp-dist/assets/StatusBadge-C1bBtXHh.css +0 -1
- package/src/webapp-dist/assets/StatusBadge-C6N84XmO.js +0 -1
- package/src/webapp-dist/assets/analytics-BapTekLM.js +0 -1
- package/src/webapp-dist/assets/colors-5z6Q_kUr.js +0 -18
- package/src/webapp-dist/assets/index-B7w2bKT6.css +0 -1
- package/src/webapp-dist/assets/index-CUSnwYTH.js +0 -31
- package/src/webapp-dist/assets/messages-CRV6E-xU.js +0 -1
- package/src/webapp-dist/assets/queen-logo-blue.svg +0 -210
- package/src/webapp-dist/assets/queen-logo-cyan.svg +0 -210
- package/src/webapp-dist/assets/queen-logo-indigo.svg +0 -210
- package/src/webapp-dist/assets/queen-logo-orange.svg +0 -210
- package/src/webapp-dist/assets/queen-logo-pink.svg +0 -210
- package/src/webapp-dist/assets/queen-logo-purple.svg +0 -210
- package/src/webapp-dist/assets/queen-logo-rose.svg +0 -239
- package/src/webapp-dist/assets/queen-logo.svg +0 -263
- package/src/webapp-dist/assets/queues-BdUbxfBC.js +0 -1
- package/src/webapp-dist/assets/resources-Bs9U-ceF.js +0 -1
- package/src/webapp-dist/index.html +0 -15
- package/src/websocket/wsServer.js +0 -228
- /package/{src → client-js}/benchmark/consumer_multi.js +0 -0
- /package/{src → client-js}/benchmark/producer_multi.js +0 -0
- /package/{src → client-js}/client/utils/loadBalancer.js +0 -0
- /package/{src → client-js}/services/encryptionService.js +0 -0
- /package/{src → client-js}/services/evictionService.js +0 -0
- /package/{src → client-js}/services/retentionService.js +0 -0
- /package/{src → client-js}/services/startupSync.js +0 -0
- /package/{src → client-js}/test/README.md +0 -0
- /package/{src → client-js}/test/advanced-client-tests.js +0 -0
- /package/{src → client-js}/test/advanced-pattern-tests.js +0 -0
- /package/{src → client-js}/test/bus-mode-tests.js +0 -0
- /package/{src → client-js}/test/enterprise-tests.js +0 -0
- /package/{src → client-js}/test/partition-locking-tests.js +0 -0
- /package/{src → client-js}/test/utils.js +0 -0
- /package/{src → client-js}/test/window-buffer-test.js +0 -0
- /package/{src → client-js}/utils/logger.js +0 -0
- /package/{src → client-js}/utils/uuid.js +0 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Queen - PostgreSQL-backed Message Queue
|
|
1
|
+
# Queen MQ - PostgreSQL-backed C++ Message Queue
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
@@ -7,862 +7,258 @@
|
|
|
7
7
|
[](LICENSE.md)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
|
-
[Quick Start](
|
|
10
|
+
[Quick Start](#js-client-usage) • [Examples](#-examples) • [Webapp](#webapp) • [Server Setup](#install-server-and-configure-it) • [HTTP API](#raw-http-api)
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
<img src="assets/queen-logo.svg" alt="Queen Logo" width="120" />
|
|
13
|
+
<img src="assets/queen-logo-rose.svg" alt="Queen Logo" width="120" />
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
**Queen** is a production-ready message queue system that combines the reliability of PostgreSQL with the performance of modern async architectures. Built with uWebSockets.js for blazing-fast HTTP handling and designed for real-world workloads.
|
|
23
|
-
|
|
24
|
-
### Why Queen?
|
|
25
|
-
|
|
26
|
-
**🚀 Developer-First API**
|
|
27
|
-
- **Pipeline & Transaction APIs**: High-level fluent interfaces for complex workflows
|
|
28
|
-
- **4 core methods, infinite patterns**: `queue()`, `push()`, `take()`, `ack()`
|
|
29
|
-
- **Async iteration**: Process messages with familiar `for await` syntax
|
|
30
|
-
- **Batch processing**: Use `takeBatch()` for 250k+ msg/sec throughput on millions of messages
|
|
31
|
-
- **Smart addressing**: `orders/urgent@workers` - queue, partition, and consumer group in one
|
|
32
|
-
|
|
33
|
-
**⚡ Production-Ready Performance**
|
|
34
|
-
- **100,000+ msg/sec** throughput with cursor-based consumption
|
|
35
|
-
- **Constant-time batch operations** - O(batch_size) regardless of queue depth
|
|
36
|
-
- **Long polling** for event-driven, real-time message delivery
|
|
37
|
-
- **Partition locking** prevents duplicate processing across consumers
|
|
38
|
-
- **Connection pooling** and optimized batch operations
|
|
39
|
-
- **Parallel processing** across partitions with `withConcurrency()`
|
|
40
|
-
|
|
41
|
-
**🏗️ Flexible Architecture**
|
|
42
|
-
- **Queue Mode**: Competitive consumption (traditional work queue)
|
|
43
|
-
- **Bus Mode**: Pub/sub with consumer groups (event streaming)
|
|
44
|
-
- **Mixed Mode**: Combine both patterns in the same system
|
|
45
|
-
- **Partitions**: FIFO ordering with parallel processing
|
|
46
|
-
- **Exactly-Once Processing**: Lease-based locking with automatic validation
|
|
47
|
-
|
|
48
|
-
**🔒 Enterprise Features**
|
|
49
|
-
- **AES-256-GCM Encryption**: Protect sensitive data at rest
|
|
50
|
-
- **Message Retention**: Automatic cleanup policies
|
|
51
|
-
- **Message Eviction**: SLA enforcement for time-sensitive tasks
|
|
52
|
-
- **Dead Letter Queue**: Handle failed messages gracefully
|
|
53
|
-
- **Automatic Lease Renewal**: For long-running tasks
|
|
54
|
-
- **Atomic Transactions**: Multi-operation consistency
|
|
55
|
-
|
|
56
|
-
**📊 Built-in Observability**
|
|
57
|
-
- **Real-time Dashboard**: WebSocket-powered monitoring
|
|
58
|
-
- **Rich Analytics**: Throughput, lag, queue depth metrics
|
|
59
|
-
- **Message Browser**: Search, inspect, and retry messages
|
|
60
|
-
- **System Health**: Database, memory, and performance metrics
|
|
61
|
-
- **Cursor Tracking**: Monitor consumption progress per consumer group
|
|
62
|
-
|
|
63
|
-
### Use Cases
|
|
64
|
-
|
|
65
|
-
- **Task Queues**: Background jobs, email sending, data processing
|
|
66
|
-
- **Event Streaming**: Audit logs, analytics, multi-service event handling
|
|
67
|
-
- **Workflow Orchestration**: Multi-stage pipelines, saga patterns
|
|
68
|
-
- **Rate Limiting**: Throttle and batch time-sensitive operations
|
|
69
|
-
- **Priority Processing**: Handle urgent tasks before routine ones
|
|
20
|
+
## Introduction
|
|
70
21
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
## 📋 Table of Contents
|
|
74
|
-
|
|
75
|
-
- [First Queue](#first-queue)
|
|
76
|
-
- [Quick Start](#-quick-start)
|
|
77
|
-
- [Advanced Client APIs](#advanced-client-apis)
|
|
78
|
-
- [Standard Client Examples](#-client-examples)
|
|
79
|
-
- [Server Setup](#-server-setup)
|
|
80
|
-
- [Core Concepts](#-core-concepts)
|
|
81
|
-
- [HTTP API Reference](#-http-api-reference)
|
|
82
|
-
- [Dashboard](#-dashboard)
|
|
83
|
-
- [Configuration](#-configuration)
|
|
84
|
-
- [Full Examples](#-full-examples)
|
|
85
|
-
- [Testing](#-testing)
|
|
86
|
-
- [Contributing](#-contributing)
|
|
22
|
+
QueenMQ is a queue system written in C++ and backed by Postgres. Supports queues and consumer groups.
|
|
87
23
|
|
|
88
|
-
|
|
24
|
+
## JS Client usage
|
|
89
25
|
|
|
90
|
-
|
|
26
|
+
```js
|
|
27
|
+
import { Queen } from 'queen-mq'
|
|
91
28
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
baseUrl: 'http://localhost:6632' // Single server
|
|
97
|
-
// OR for multiple servers with load balancing:
|
|
98
|
-
// baseUrls: ['http://server1:6632', 'http://server2:6632'],
|
|
99
|
-
// loadBalancingStrategy: 'round-robin', // or 'random', 'least-connections'
|
|
100
|
-
// enableFailover: true
|
|
29
|
+
const client = new Queen({
|
|
30
|
+
baseUrls: ['http://localhost:6632'],
|
|
31
|
+
timeout: 30000,
|
|
32
|
+
retryAttempts: 3
|
|
101
33
|
});
|
|
102
34
|
|
|
103
|
-
|
|
104
|
-
await client.queue('tasks', {
|
|
105
|
-
leaseTime: 300, // 5 minutes to process each message
|
|
106
|
-
retryLimit: 3 // Retry up to 3 times
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Push a message
|
|
110
|
-
await client.push('tasks', {
|
|
111
|
-
action: 'send-email',
|
|
112
|
-
to: 'user@example.com'
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Process messages
|
|
116
|
-
for await (const message of client.take('tasks')) {
|
|
117
|
-
console.log('Processing:', message.data);
|
|
118
|
-
await client.ack(message);
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
That's it! You now have a working message queue system.
|
|
123
|
-
|
|
124
|
-
## 🏃 Quick Start
|
|
125
|
-
|
|
126
|
-
### Prerequisites
|
|
127
|
-
|
|
128
|
-
- **Node.js 22+**
|
|
129
|
-
- **PostgreSQL 12+**
|
|
130
|
-
|
|
131
|
-
### Installation
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
# Clone the repository
|
|
135
|
-
git clone https://github.com/smartpricing/queen
|
|
136
|
-
cd queen
|
|
137
|
-
|
|
138
|
-
# Install dependencies
|
|
139
|
-
nvm use 22
|
|
140
|
-
npm install
|
|
35
|
+
const queue = 'html-processing'
|
|
141
36
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
```
|
|
37
|
+
// Create a queue
|
|
38
|
+
await client.queue(queue, { leaseTime: 30 });
|
|
145
39
|
|
|
146
|
-
|
|
40
|
+
// Push some data, specifyng the partition
|
|
41
|
+
await client.push(`${queue}/customer-1828`, [ { id: 1 } ]);
|
|
147
42
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
export PG_PASSWORD=postgres
|
|
154
|
-
export PG_PORT=5432
|
|
155
|
-
|
|
156
|
-
# Enable encryption (optional)
|
|
157
|
-
export QUEEN_ENCRYPTION_KEY=$(openssl rand -hex 32)
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Start the Server
|
|
161
|
-
|
|
162
|
-
```bash
|
|
163
|
-
npm start
|
|
164
|
-
# Server starts on http://localhost:6632
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## Advanced Client APIs
|
|
170
|
-
|
|
171
|
-
### Pipeline API - Fluent Message Processing
|
|
172
|
-
|
|
173
|
-
The Pipeline API provides a chainable interface for complex message processing workflows:
|
|
174
|
-
|
|
175
|
-
```javascript
|
|
176
|
-
// Simple message processing (one at a time)
|
|
177
|
-
await client.pipeline('my-queue')
|
|
178
|
-
.take(100) // Take up to 100 messages
|
|
179
|
-
.process(async (message) => { // Process each message individually
|
|
180
|
-
console.log('Processing:', message.data);
|
|
181
|
-
return { processed: true };
|
|
182
|
-
})
|
|
183
|
-
.execute();
|
|
184
|
-
|
|
185
|
-
// Batch processing
|
|
186
|
-
await client.pipeline('my-queue')
|
|
187
|
-
.take(100)
|
|
188
|
-
.processBatch(async (messages) => { // Process messages as a batch
|
|
189
|
-
console.log(`Processing ${messages.length} messages`);
|
|
190
|
-
return messages.map(m => ({ processed: m.id }));
|
|
191
|
-
})
|
|
192
|
-
.execute();
|
|
193
|
-
|
|
194
|
-
// With automatic lease renewal for long-running tasks
|
|
195
|
-
await client.pipeline('my-queue')
|
|
196
|
-
.take(50)
|
|
197
|
-
.withAutoRenewal({ interval: 5000 }) // Renew lease every 5 seconds
|
|
198
|
-
.process(async (message) => {
|
|
199
|
-
// Long-running task - lease automatically renewed
|
|
200
|
-
// Without this, if task takes > leaseTime, message may be redelivered
|
|
201
|
-
await heavyComputation(message);
|
|
202
|
-
})
|
|
203
|
-
.execute();
|
|
204
|
-
|
|
205
|
-
// Parallel processing across partitions
|
|
206
|
-
await client.pipeline('my-queue')
|
|
207
|
-
.take(100)
|
|
208
|
-
.withConcurrency(4) // 4 parallel workers
|
|
209
|
-
.process(async (message) => {
|
|
210
|
-
await processMessage(message);
|
|
211
|
-
})
|
|
212
|
-
.repeat({ continuous: true }) // Keep running continuously (default)
|
|
213
|
-
.execute();
|
|
214
|
-
|
|
215
|
-
// With error handling
|
|
216
|
-
await client.pipeline('my-queue')
|
|
217
|
-
.take(100)
|
|
218
|
-
.process(async (message) => {
|
|
219
|
-
if (message.data.invalid) {
|
|
220
|
-
throw new Error('Invalid message format');
|
|
221
|
-
}
|
|
222
|
-
return await riskyOperation(message);
|
|
223
|
-
})
|
|
224
|
-
.onError(async (error, messages) => {
|
|
225
|
-
console.error('Processing failed:', error.message);
|
|
226
|
-
|
|
227
|
-
// Move failed messages to error queue
|
|
228
|
-
await client.push('error-queue', {
|
|
229
|
-
error: error.message,
|
|
230
|
-
messages: messages.map(m => m.data),
|
|
231
|
-
timestamp: Date.now()
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// ACK as failed (will retry based on retryLimit)
|
|
235
|
-
for (const msg of messages) {
|
|
236
|
-
await client.ack(msg, false, { error: error.message });
|
|
237
|
-
}
|
|
238
|
-
})
|
|
239
|
-
.execute();
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
#### Automatic Lease Renewal
|
|
243
|
-
|
|
244
|
-
By default, messages have a lease time (e.g., 5 minutes). If processing takes longer, the message may be redelivered to another consumer. Use `.withAutoRenewal()` to prevent this:
|
|
245
|
-
|
|
246
|
-
```javascript
|
|
247
|
-
// WITHOUT auto-renewal (default) - Risk of redelivery
|
|
248
|
-
await client.pipeline('video-processing')
|
|
249
|
-
.take(10)
|
|
250
|
-
.process(async (message) => {
|
|
251
|
-
// If this takes > leaseTime, message may be processed twice!
|
|
252
|
-
await longRunningTask(message);
|
|
253
|
-
})
|
|
254
|
-
.execute();
|
|
255
|
-
|
|
256
|
-
// WITH auto-renewal - Safe for long tasks
|
|
257
|
-
await client.pipeline('video-processing')
|
|
258
|
-
.take(10)
|
|
259
|
-
.withAutoRenewal({
|
|
260
|
-
interval: 30000 // Renew every 30 seconds (default)
|
|
261
|
-
})
|
|
262
|
-
.process(async (message) => {
|
|
263
|
-
// Lease automatically renewed while processing
|
|
264
|
-
await longRunningTask(message); // Safe even if takes hours
|
|
265
|
-
})
|
|
266
|
-
.execute();
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
**When to use:**
|
|
270
|
-
- Video/audio processing
|
|
271
|
-
- Large file operations
|
|
272
|
-
- Machine learning inference
|
|
273
|
-
- Any task that might exceed the lease time
|
|
274
|
-
|
|
275
|
-
**Important:**
|
|
276
|
-
- Auto-renewal stops when message is ACKed or process crashes
|
|
277
|
-
- Set interval < lease time (e.g., renew at 1/3 of lease time)
|
|
278
|
-
- Only works within pipeline API
|
|
279
|
-
|
|
280
|
-
#### Error Handling in Pipeline
|
|
43
|
+
// Consume data with iterators
|
|
44
|
+
for await (const msg of client.take(queue, { limit: 1 })) {
|
|
45
|
+
console.log(msg.data.id)
|
|
46
|
+
await client.ack(msg) // OR await client.ack(msg, false) for nack
|
|
47
|
+
}
|
|
281
48
|
|
|
282
|
-
|
|
49
|
+
// Consume data with iterators, getting the entire batch
|
|
50
|
+
for await (const messages of client.takeBatch(queue, { limit: 1, batch: 10, wait: true, timeout: 2000 })) {
|
|
51
|
+
const newMex = messages.map(x => x.data.id * 2)
|
|
52
|
+
await client.ack(messages) // OR await client.ack(messages, false) for nack
|
|
53
|
+
}
|
|
283
54
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
55
|
+
// Consume data with a consumer group
|
|
56
|
+
for await (const msg of client.take(`${queue}@analytics-data`, { limit: 2, batch: 2 })) {
|
|
57
|
+
// Do your computation and than ack with consumer group
|
|
58
|
+
await client.ack(msg, true, { group: 'analytics-data' });
|
|
59
|
+
}
|
|
289
60
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
.
|
|
294
|
-
|
|
295
|
-
|
|
61
|
+
// Consume data from any partition of the queue, continusly
|
|
62
|
+
// This "pipeline" is useful for doing exactly one processing
|
|
63
|
+
await client
|
|
64
|
+
.pipeline(queue)
|
|
65
|
+
.withAutoRenewal({
|
|
66
|
+
interval: 5000 // Renew lease every 5 seconds
|
|
67
|
+
})
|
|
68
|
+
.withConcurrency(5) // Five parallel promises
|
|
69
|
+
.take(10, {
|
|
70
|
+
wait: true, // Use long polling
|
|
71
|
+
timeout: 30000 // Long polling length in millisconds
|
|
296
72
|
})
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.onError(async (error, messages) => {
|
|
300
|
-
for (const msg of messages) {
|
|
301
|
-
if (msg.retryCount >= 3) {
|
|
302
|
-
await client.push('dlq', { original: msg, error: error.message });
|
|
303
|
-
await client.ack(msg, true); // Remove from queue
|
|
304
|
-
} else {
|
|
305
|
-
await client.ack(msg, false); // Retry
|
|
306
|
-
}
|
|
307
|
-
}
|
|
73
|
+
.processBatch(async (messages) => {
|
|
74
|
+
return messages.map(x => x.data.id * 2);
|
|
308
75
|
})
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
.
|
|
312
|
-
console.error(`Skipping ${messages.length} messages: ${error.message}`);
|
|
313
|
-
await client.ack(messages, true); // ACK as success to remove
|
|
76
|
+
.atomically((tx, originalMessages, processedMessages) => { // ack and push are transactional inside atomically
|
|
77
|
+
tx.ack(originalMessages);
|
|
78
|
+
tx.push('another-queue', processedMessages);
|
|
314
79
|
})
|
|
80
|
+
.repeat({ continuous: true })
|
|
81
|
+
.execute();
|
|
315
82
|
```
|
|
316
83
|
|
|
317
|
-
|
|
318
|
-
- With `atomically()`: atomic operations only run on success
|
|
319
|
-
- In `repeat()` mode: errors don't stop continuous processing
|
|
320
|
-
- Batch processing: error affects all messages in the batch
|
|
321
|
-
- Parallel workers: each worker has independent error handling
|
|
322
|
-
|
|
323
|
-
### Transaction API - Atomic Operations
|
|
324
|
-
|
|
325
|
-
Execute multiple operations atomically with lease validation:
|
|
326
|
-
|
|
327
|
-
```javascript
|
|
328
|
-
// Atomic ACK + PUSH
|
|
329
|
-
const messages = await client.takeSingleBatch('input-queue');
|
|
330
|
-
|
|
331
|
-
await client.transaction()
|
|
332
|
-
.ack(messages) // ACK input messages
|
|
333
|
-
.push('output-queue', processedResults) // Push to output
|
|
334
|
-
.extend(leaseId) // Extend another lease
|
|
335
|
-
.commit(); // Execute atomically
|
|
336
|
-
|
|
337
|
-
// Pipeline with custom atomic operations
|
|
338
|
-
await client.pipeline('my-queue')
|
|
339
|
-
.take(50)
|
|
340
|
-
.process(async (message) => {
|
|
341
|
-
return await transform(message);
|
|
342
|
-
})
|
|
343
|
-
.atomically((tx, originalMessages, processedMessages) => {
|
|
344
|
-
tx.ack(originalMessages)
|
|
345
|
-
.push('output-queue', processedMessages)
|
|
346
|
-
.push('audit-queue', {
|
|
347
|
-
timestamp: Date.now(),
|
|
348
|
-
count: processedMessages.length
|
|
349
|
-
});
|
|
350
|
-
})
|
|
351
|
-
.execute();
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
---
|
|
355
|
-
|
|
356
|
-
## 💻 Client Examples
|
|
357
|
-
|
|
358
|
-
The Queen client provides a minimalist API with just 4 methods that compose into any messaging pattern you need.
|
|
359
|
-
|
|
360
|
-
### Installation
|
|
361
|
-
|
|
362
|
-
```bash
|
|
363
|
-
npm install queen-mq
|
|
364
|
-
```
|
|
84
|
+
## 📚 Examples
|
|
365
85
|
|
|
366
86
|
### Basic Usage
|
|
87
|
+
- **[Basic Queue Operations](examples/01-basic-usage.js)** - Create queue, push, take, and ack messages
|
|
88
|
+
- **[Batch Operations](examples/02-batch-operations.js)** - Push and consume messages in batches
|
|
367
89
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const client = new Queen({
|
|
374
|
-
baseUrl: 'http://localhost:6632',
|
|
375
|
-
timeout: 30000,
|
|
376
|
-
retryAttempts: 3
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
// Configure with options
|
|
380
|
-
await client.queue('orders', {
|
|
381
|
-
priority: 10, // Higher priority queues processed first
|
|
382
|
-
leaseTime: 300, // 5 minutes to process each message
|
|
383
|
-
retryLimit: 3, // Retry up to 3 times
|
|
384
|
-
windowBuffer: 0, // No delay (immediate processing)
|
|
385
|
-
retentionSeconds: 86400, // Keep messages for 24 hours
|
|
386
|
-
completedRetentionSeconds: 3600, // Keep completed messages for 1 hour
|
|
387
|
-
partitions: 10, // Create 10 partitions for parallel processing
|
|
388
|
-
maxWaitTimeSeconds: 600 // Evict messages waiting > 10 minutes
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
// Configure with namespace and task for grouping
|
|
392
|
-
await client.queue('order-processing', {
|
|
393
|
-
priority: 10
|
|
394
|
-
}, {
|
|
395
|
-
namespace: 'ecommerce',
|
|
396
|
-
task: 'checkout'
|
|
397
|
-
});
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
#### 2. Push Messages
|
|
401
|
-
|
|
402
|
-
```javascript
|
|
403
|
-
// Single message
|
|
404
|
-
await client.push('orders', {
|
|
405
|
-
orderId: 12345,
|
|
406
|
-
amount: 99.99
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
// To a specific partition
|
|
410
|
-
await client.push('orders/urgent', {
|
|
411
|
-
orderId: 12346,
|
|
412
|
-
amount: 999.99,
|
|
413
|
-
priority: 'high'
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
// Batch messages
|
|
417
|
-
await client.push('orders', [
|
|
418
|
-
{ orderId: 12347, amount: 49.99 },
|
|
419
|
-
{ orderId: 12348, amount: 79.99 },
|
|
420
|
-
{ orderId: 12349, amount: 29.99 }
|
|
421
|
-
]);
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
#### 3. Take Messages (Async Iterator)
|
|
425
|
-
|
|
426
|
-
```javascript
|
|
427
|
-
// Process continuously with long polling
|
|
428
|
-
for await (const message of client.take('orders', {
|
|
429
|
-
wait: true, // Enable long polling
|
|
430
|
-
timeout: 30000, // 30 second timeout
|
|
431
|
-
batch: 10 // Fetch up to 10 at once
|
|
432
|
-
})) {
|
|
433
|
-
try {
|
|
434
|
-
await processOrder(message.data);
|
|
435
|
-
await client.ack(message); // Success
|
|
436
|
-
} catch (error) {
|
|
437
|
-
await client.ack(message, false, { error: error.message }); // Failure
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Process limited messages
|
|
442
|
-
for await (const message of client.take('orders', { limit: 100 })) {
|
|
443
|
-
await processOrder(message.data);
|
|
444
|
-
await client.ack(message);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Take from specific partition
|
|
448
|
-
for await (const message of client.take('orders/urgent')) {
|
|
449
|
-
await processUrgentOrder(message.data);
|
|
450
|
-
await client.ack(message);
|
|
451
|
-
}
|
|
90
|
+
### Advanced Features
|
|
91
|
+
- **[Queue Configuration](examples/06-queue-configuration.js)** - Configure maxSize, windowBuffer, delay, retryLimit, and priority
|
|
92
|
+
- **[Delayed Processing](examples/04-delayed-processing.js)** - Process messages after a delay
|
|
93
|
+
- **[Window Buffer](examples/05-window-buffer.js)** - Delay message availability after push
|
|
452
94
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
wait: true
|
|
457
|
-
})) {
|
|
458
|
-
// messages is an array of up to 1000 messages
|
|
459
|
-
console.log(`Processing batch of ${messages.length} messages`);
|
|
460
|
-
|
|
461
|
-
try {
|
|
462
|
-
// Process entire batch
|
|
463
|
-
await processBatch(messages.map(m => m.data));
|
|
464
|
-
|
|
465
|
-
// Acknowledge entire batch at once (efficient!)
|
|
466
|
-
await client.ack(messages); // Pass array for batch ack
|
|
467
|
-
} catch (error) {
|
|
468
|
-
// Mark entire batch as failed
|
|
469
|
-
await client.ack(messages, false, { error: error.message });
|
|
470
|
-
}
|
|
471
|
-
}
|
|
95
|
+
### Filtering & Routing
|
|
96
|
+
- **[Namespace & Task Filtering](examples/07-namespace-task-filtering.js)** - Route and filter messages by namespace and task
|
|
97
|
+
- **[Consumer Groups](examples/08-consumer-groups.js)** - Multiple consumer groups processing same messages
|
|
472
98
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
```
|
|
99
|
+
### Pipelines
|
|
100
|
+
- **[Transactional Pipelines](examples/03-transactional-pipeline.js)** - Atomic processing with ack and push in a transaction
|
|
476
101
|
|
|
477
|
-
|
|
102
|
+
### Event Streaming (QoS 0)
|
|
103
|
+
- **[Event Streaming](examples/09-event-streaming.js)** - At-most-once delivery with buffering and auto-ack
|
|
478
104
|
|
|
479
|
-
|
|
480
|
-
// Acknowledge success
|
|
481
|
-
await client.ack(message);
|
|
482
|
-
// or
|
|
483
|
-
await client.ack(message, true);
|
|
105
|
+
## QoS 0: At-Most-Once Event Streaming
|
|
484
106
|
|
|
485
|
-
|
|
486
|
-
await client.ack(message, false);
|
|
107
|
+
For high-throughput event streams, Queen supports **at-most-once delivery** with server-side buffering and auto-acknowledgment.
|
|
487
108
|
|
|
488
|
-
|
|
489
|
-
await client.ack(message, false, {
|
|
490
|
-
error: 'Payment gateway timeout'
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Batch acknowledgment (with lease validation)
|
|
494
|
-
await client.ack(messages); // Pass array for batch ack
|
|
495
|
-
```
|
|
109
|
+
### Server-Side Buffering
|
|
496
110
|
|
|
497
|
-
|
|
111
|
+
Batch events on the server for 10-100x reduction in database writes:
|
|
498
112
|
|
|
499
113
|
```javascript
|
|
500
|
-
//
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
// Renew by lease ID directly
|
|
505
|
-
await client.renewLease('lease-uuid-123');
|
|
506
|
-
|
|
507
|
-
// Renew multiple messages at once
|
|
508
|
-
const results = await client.renewLease(messages);
|
|
509
|
-
results.forEach(r => {
|
|
510
|
-
if (r.success) {
|
|
511
|
-
console.log(`Renewed ${r.leaseId} until ${r.newExpiresAt}`);
|
|
512
|
-
} else {
|
|
513
|
-
console.error(`Failed to renew ${r.leaseId}: ${r.error}`);
|
|
514
|
-
}
|
|
114
|
+
// Push with buffering (QoS 0)
|
|
115
|
+
await client.push('metrics', { cpu: 45, memory: 67 }, {
|
|
116
|
+
bufferMs: 100, // Server batches for 100ms
|
|
117
|
+
bufferMax: 100 // Or until 100 events
|
|
515
118
|
});
|
|
516
119
|
|
|
517
|
-
//
|
|
518
|
-
for await (const message of client.take('my-queue')) {
|
|
519
|
-
// Set up periodic renewal
|
|
520
|
-
const renewalInterval = setInterval(async () => {
|
|
521
|
-
await client.renewLease(message);
|
|
522
|
-
}, 30000); // Renew every 30 seconds
|
|
523
|
-
|
|
524
|
-
try {
|
|
525
|
-
await longRunningTask(message);
|
|
526
|
-
await client.ack(message);
|
|
527
|
-
} finally {
|
|
528
|
-
clearInterval(renewalInterval);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
120
|
+
// Result: 1000 events = ~10 DB writes (instead of 1000)
|
|
531
121
|
```
|
|
532
122
|
|
|
533
|
-
###
|
|
123
|
+
### Auto-Acknowledgment
|
|
534
124
|
|
|
535
|
-
|
|
125
|
+
Skip manual ack for fire-and-forget consumption:
|
|
536
126
|
|
|
537
127
|
```javascript
|
|
538
|
-
//
|
|
539
|
-
'
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
// Namespace/task filtering (cross-queue consumption)
|
|
545
|
-
'namespace:ecommerce' // All queues in namespace
|
|
546
|
-
'task:checkout' // All queues with task
|
|
547
|
-
'namespace:ecommerce/task:checkout' // Combined filter
|
|
548
|
-
'namespace:ecommerce/task:checkout@audit' // With consumer group
|
|
128
|
+
// Consume with auto-ack
|
|
129
|
+
for await (const msg of client.take('metrics', { autoAck: true })) {
|
|
130
|
+
updateDashboard(msg.data);
|
|
131
|
+
// No ack() needed - automatically acknowledged!
|
|
132
|
+
}
|
|
549
133
|
```
|
|
550
134
|
|
|
551
|
-
### Consumer
|
|
135
|
+
### Fan-Out Pattern (Consumer Groups)
|
|
552
136
|
|
|
553
|
-
|
|
137
|
+
Combine buffering + auto-ack + consumer groups for pub/sub:
|
|
554
138
|
|
|
555
139
|
```javascript
|
|
556
|
-
//
|
|
557
|
-
|
|
558
|
-
// Analytics service
|
|
559
|
-
for await (const event of client.take('events@analytics')) {
|
|
560
|
-
await updateAnalytics(event.data);
|
|
561
|
-
await client.ack(event, true, { group: 'analytics' });
|
|
562
|
-
}
|
|
140
|
+
// Publisher (buffered)
|
|
141
|
+
await client.push('events', { action: 'login' }, { bufferMs: 100 });
|
|
563
142
|
|
|
564
|
-
//
|
|
565
|
-
for await (const
|
|
566
|
-
|
|
567
|
-
await client.ack(event, true, { group: 'monitoring' });
|
|
143
|
+
// Multiple subscribers (each group gets all messages)
|
|
144
|
+
for await (const e of client.take('events@dashboard', { autoAck: true })) {
|
|
145
|
+
updateUI(e.data);
|
|
568
146
|
}
|
|
569
147
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
await logToAudit(event.data);
|
|
573
|
-
await client.ack(event, true, { group: 'audit' });
|
|
148
|
+
for await (const e of client.take('events@analytics', { autoAck: true })) {
|
|
149
|
+
trackEvent(e.data);
|
|
574
150
|
}
|
|
575
151
|
```
|
|
576
152
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
```javascript
|
|
580
|
-
let running = true;
|
|
153
|
+
### PostgreSQL Failover
|
|
581
154
|
|
|
582
|
-
|
|
583
|
-
console.log('Shutting down gracefully...');
|
|
584
|
-
running = false;
|
|
585
|
-
});
|
|
155
|
+
Queen automatically buffers messages to disk when PostgreSQL is unavailable - **zero message loss**:
|
|
586
156
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
157
|
+
- Normal pushes go directly to PostgreSQL (FIFO preserved)
|
|
158
|
+
- If PostgreSQL is down, messages buffered to file (macOS: `/tmp/queen`, Linux: `/var/lib/queen/buffers`)
|
|
159
|
+
- Automatic replay when PostgreSQL recovers
|
|
160
|
+
- Survives server crashes and restarts
|
|
161
|
+
- Directory auto-created on first run
|
|
593
162
|
|
|
594
|
-
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
---
|
|
598
|
-
|
|
599
|
-
## 🖥️ Server Setup
|
|
600
|
-
|
|
601
|
-
### Environment Variables
|
|
163
|
+
**No configuration needed** - failover is automatic!
|
|
602
164
|
|
|
165
|
+
**Custom directory:**
|
|
603
166
|
```bash
|
|
604
|
-
|
|
605
|
-
PORT=6632 # Server port
|
|
606
|
-
HOST=0.0.0.0 # Server host
|
|
607
|
-
WORKER_ID=srv-1 # Unique server instance ID
|
|
608
|
-
APP_NAME=queen-mq # Application name
|
|
609
|
-
|
|
610
|
-
# Database connection
|
|
611
|
-
PG_USER=postgres # PostgreSQL user
|
|
612
|
-
PG_HOST=localhost # PostgreSQL host
|
|
613
|
-
PG_DB=postgres # PostgreSQL database
|
|
614
|
-
PG_PASSWORD=postgres # PostgreSQL password
|
|
615
|
-
PG_PORT=5432 # PostgreSQL port
|
|
616
|
-
PG_USE_SSL=false # Enable SSL for PostgreSQL
|
|
617
|
-
DB_POOL_SIZE=150 # Connection pool size
|
|
618
|
-
DB_IDLE_TIMEOUT=30000 # Idle connection timeout (ms)
|
|
619
|
-
DB_CONNECTION_TIMEOUT=2000 # Connection timeout (ms)
|
|
620
|
-
DB_STATEMENT_TIMEOUT=30000 # Statement timeout (ms)
|
|
621
|
-
|
|
622
|
-
# Queue configuration
|
|
623
|
-
DEFAULT_TIMEOUT=30000 # Default pop timeout (ms)
|
|
624
|
-
MAX_TIMEOUT=60000 # Maximum pop timeout (ms)
|
|
625
|
-
DEFAULT_BATCH_SIZE=1 # Default batch size
|
|
626
|
-
BATCH_INSERT_SIZE=1000 # Batch insert size for push operations
|
|
627
|
-
DEFAULT_LEASE_TIME=300 # Default lease time (seconds)
|
|
628
|
-
DEFAULT_RETRY_LIMIT=3 # Default retry limit
|
|
629
|
-
DEFAULT_RETRY_DELAY=1000 # Default retry delay (ms)
|
|
630
|
-
DEFAULT_PRIORITY=0 # Default message priority
|
|
631
|
-
DEFAULT_WINDOW_BUFFER=0 # Default window buffer (seconds)
|
|
632
|
-
|
|
633
|
-
# Long polling configuration
|
|
634
|
-
QUEUE_POLL_INTERVAL=100 # Initial poll interval (ms)
|
|
635
|
-
QUEUE_MAX_POLL_INTERVAL=2000 # Max poll interval after backoff (ms)
|
|
636
|
-
QUEUE_BACKOFF_THRESHOLD=5 # Empty polls before backoff
|
|
637
|
-
QUEUE_BACKOFF_MULTIPLIER=2 # Exponential backoff multiplier
|
|
638
|
-
|
|
639
|
-
# Encryption
|
|
640
|
-
QUEEN_ENCRYPTION_KEY= # 32-byte hex key for AES-256-GCM encryption
|
|
641
|
-
|
|
642
|
-
# Retention & Eviction
|
|
643
|
-
DEFAULT_RETENTION_SECONDS=0 # Message retention (0 = disabled)
|
|
644
|
-
DEFAULT_COMPLETED_RETENTION_SECONDS=0 # Completed message retention
|
|
645
|
-
RETENTION_INTERVAL=300000 # Retention service interval (ms)
|
|
646
|
-
RETENTION_BATCH_SIZE=1000 # Retention batch size
|
|
647
|
-
EVICTION_INTERVAL=60000 # Eviction service interval (ms)
|
|
648
|
-
EVICTION_BATCH_SIZE=1000 # Eviction batch size
|
|
649
|
-
METRICS_RETENTION_DAYS=90 # Metrics retention period
|
|
650
|
-
|
|
651
|
-
# System Events
|
|
652
|
-
QUEEN_SYSTEM_EVENTS_ENABLED=false # Enable system event propagation
|
|
653
|
-
QUEEN_SYSTEM_EVENTS_BATCH_MS=10 # Event batching window (ms)
|
|
654
|
-
QUEEN_SYSTEM_EVENTS_SYNC_TIMEOUT=30000 # Startup sync timeout (ms)
|
|
655
|
-
|
|
656
|
-
# WebSocket configuration
|
|
657
|
-
WS_COMPRESSION=0 # WebSocket compression level
|
|
658
|
-
WS_MAX_PAYLOAD_LENGTH=16384 # Max WebSocket payload (bytes)
|
|
659
|
-
WS_IDLE_TIMEOUT=60 # WebSocket idle timeout (seconds)
|
|
660
|
-
WS_MAX_CONNECTIONS=1000 # Max WebSocket connections
|
|
661
|
-
WS_HEARTBEAT_INTERVAL=30000 # WebSocket heartbeat interval (ms)
|
|
662
|
-
|
|
663
|
-
# API configuration
|
|
664
|
-
MAX_BODY_SIZE=104857600 # Max request body size (100MB)
|
|
665
|
-
API_DEFAULT_LIMIT=100 # Default pagination limit
|
|
666
|
-
API_MAX_LIMIT=1000 # Max pagination limit
|
|
667
|
-
CORS_MAX_AGE=86400 # CORS max age (seconds)
|
|
668
|
-
CORS_ALLOWED_ORIGINS=* # CORS allowed origins
|
|
669
|
-
CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS
|
|
670
|
-
CORS_ALLOWED_HEADERS=Content-Type,Authorization
|
|
671
|
-
|
|
672
|
-
# Monitoring & Logging
|
|
673
|
-
ENABLE_REQUEST_COUNTING=true # Enable request metrics
|
|
674
|
-
ENABLE_MESSAGE_COUNTING=true # Enable message metrics
|
|
675
|
-
METRICS_ENDPOINT_ENABLED=true # Enable metrics endpoint
|
|
676
|
-
HEALTH_CHECK_ENABLED=true # Enable health check endpoint
|
|
677
|
-
ENABLE_LOGGING=true # Enable logging
|
|
678
|
-
LOG_LEVEL=info # Log level (debug|info|warn|error)
|
|
679
|
-
LOG_FORMAT=json # Log format (json|text)
|
|
167
|
+
FILE_BUFFER_DIR=/custom/path ./bin/queen-server
|
|
680
168
|
```
|
|
681
169
|
|
|
682
|
-
###
|
|
170
|
+
### When to Use
|
|
683
171
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
database: {
|
|
690
|
-
connectionString: 'postgresql://...',
|
|
691
|
-
poolSize: 20
|
|
692
|
-
},
|
|
693
|
-
encryption: {
|
|
694
|
-
key: 'your-32-byte-hex-key'
|
|
695
|
-
},
|
|
696
|
-
features: {
|
|
697
|
-
retention: true,
|
|
698
|
-
eviction: true
|
|
699
|
-
}
|
|
700
|
-
});
|
|
172
|
+
| Feature | Use For | Don't Use For |
|
|
173
|
+
|---------|---------|---------------|
|
|
174
|
+
| **Buffering** | Metrics, logs, analytics, UI updates | Critical tasks, payments |
|
|
175
|
+
| **Auto-Ack** | Fire-and-forget events, notifications | Tasks requiring retry logic |
|
|
176
|
+
| **Failover** | Everything (automatic) | N/A - always beneficial |
|
|
701
177
|
|
|
702
|
-
|
|
703
|
-
```
|
|
178
|
+
## Webapp
|
|
704
179
|
|
|
705
|
-
|
|
180
|
+
A modern Vue 3 web interface for managing and monitoring Queen MQ.
|
|
706
181
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
5. **Retry**: Failed messages are retried with exponential backoff
|
|
716
|
-
|
|
717
|
-
### Partition & Lease Management
|
|
718
|
-
|
|
719
|
-
- Each queue can have multiple partitions for parallel processing
|
|
720
|
-
- Consumers acquire exclusive leases on partitions
|
|
721
|
-
- Leases include unique IDs for validation and fencing
|
|
722
|
-
- Automatic lease renewal for long-running tasks
|
|
723
|
-
- Dead letter queue for exhausted retries
|
|
724
|
-
|
|
725
|
-
### Scalability
|
|
726
|
-
|
|
727
|
-
- Horizontal scaling with multiple server instances
|
|
728
|
-
- Partition-based parallelism
|
|
729
|
-
- Connection pooling and query optimization
|
|
730
|
-
- WebSocket support for real-time updates
|
|
731
|
-
- Efficient batch operations
|
|
732
|
-
|
|
733
|
-
---
|
|
734
|
-
|
|
735
|
-
## 📚 HTTP API Reference
|
|
736
|
-
|
|
737
|
-
### Queue Management
|
|
182
|
+
**Features:**
|
|
183
|
+
- 📊 Real-time dashboard with system metrics
|
|
184
|
+
- 📈 Message throughput visualization
|
|
185
|
+
- 🔍 Queue management and monitoring
|
|
186
|
+
- 👥 Consumer group tracking
|
|
187
|
+
- 💬 Message browser
|
|
188
|
+
- 📉 Analytics and insights
|
|
189
|
+
- 🌓 Dark/light theme support
|
|
738
190
|
|
|
191
|
+
**Quick Start:**
|
|
739
192
|
```bash
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
-d '{
|
|
744
|
-
"queue": "my-queue",
|
|
745
|
-
"options": {
|
|
746
|
-
"maxRetries": 3,
|
|
747
|
-
"visibilityTimeout": 30000
|
|
748
|
-
}
|
|
749
|
-
}'
|
|
750
|
-
|
|
751
|
-
# Delete queue
|
|
752
|
-
curl -X DELETE http://localhost:6632/api/v1/configure/my-queue
|
|
193
|
+
cd webapp
|
|
194
|
+
npm install
|
|
195
|
+
npm run dev
|
|
753
196
|
```
|
|
754
197
|
|
|
755
|
-
|
|
198
|
+
The dashboard will be available at `http://localhost:4000`
|
|
756
199
|
|
|
757
|
-
|
|
758
|
-
# Push messages
|
|
759
|
-
curl -X POST http://localhost:6632/api/v1/push \
|
|
760
|
-
-H "Content-Type: application/json" \
|
|
761
|
-
-d '{
|
|
762
|
-
"queue": "my-queue",
|
|
763
|
-
"messages": [
|
|
764
|
-
{ "data": "message 1" },
|
|
765
|
-
{ "data": "message 2", "priority": 100 }
|
|
766
|
-
]
|
|
767
|
-
}'
|
|
768
|
-
|
|
769
|
-
# Pop messages (take)
|
|
770
|
-
curl -X POST http://localhost:6632/api/v1/pop \
|
|
771
|
-
-H "Content-Type: application/json" \
|
|
772
|
-
-d '{
|
|
773
|
-
"queue": "my-queue",
|
|
774
|
-
"batch": 10,
|
|
775
|
-
"visibilityTimeout": 30000
|
|
776
|
-
}'
|
|
777
|
-
|
|
778
|
-
# Acknowledge messages
|
|
779
|
-
curl -X POST http://localhost:6632/api/v1/ack \
|
|
780
|
-
-H "Content-Type: application/json" \
|
|
781
|
-
-d '{
|
|
782
|
-
"queue": "my-queue",
|
|
783
|
-
"transactionId": "msg-transaction-id",
|
|
784
|
-
"status": "completed",
|
|
785
|
-
"leaseId": "lease-uuid"
|
|
786
|
-
}'
|
|
787
|
-
```
|
|
200
|
+
See [webapp/README.md](webapp/README.md) for more details.
|
|
788
201
|
|
|
789
|
-
|
|
202
|
+
## Install server and configure it
|
|
790
203
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
"queue": "input-queue",
|
|
800
|
-
"transactionId": "msg-id",
|
|
801
|
-
"status": "completed"
|
|
802
|
-
},
|
|
803
|
-
{
|
|
804
|
-
"type": "push",
|
|
805
|
-
"queue": "output-queue",
|
|
806
|
-
"messages": [{"data": "processed"}]
|
|
807
|
-
}
|
|
808
|
-
],
|
|
809
|
-
"requiredLeases": ["lease-uuid-1", "lease-uuid-2"]
|
|
810
|
-
}'
|
|
811
|
-
|
|
812
|
-
# Extend lease
|
|
813
|
-
curl -X POST http://localhost:6632/api/v1/lease/lease-uuid/extend \
|
|
814
|
-
-H "Content-Type: application/json" \
|
|
815
|
-
-d '{}'
|
|
816
|
-
|
|
817
|
-
# Get queue status
|
|
818
|
-
curl http://localhost:6632/api/v1/status/my-queue
|
|
204
|
+
### Quick Start
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
cd server
|
|
208
|
+
make clean
|
|
209
|
+
make deps
|
|
210
|
+
make build-only
|
|
211
|
+
DB_POOL_SIZE=50 ./bin/queen-server
|
|
819
212
|
```
|
|
820
213
|
|
|
821
|
-
|
|
214
|
+
**📖 Complete Build & Tuning Guide:** [server/README.md](server/README.md)
|
|
822
215
|
|
|
823
|
-
|
|
216
|
+
Includes:
|
|
217
|
+
- Build instructions and optimization
|
|
218
|
+
- Performance tuning (worker threads, database pool)
|
|
219
|
+
- Production deployment (systemd, Docker, load balancing)
|
|
220
|
+
- Troubleshooting common issues
|
|
221
|
+
- Benchmarking guides
|
|
824
222
|
|
|
825
|
-
|
|
223
|
+
### Environment Variables
|
|
826
224
|
|
|
827
|
-
|
|
828
|
-
- Real-time queue metrics
|
|
829
|
-
- Message browser with search
|
|
830
|
-
- Consumer group monitoring
|
|
831
|
-
- System health indicators
|
|
832
|
-
- Performance graphs
|
|
225
|
+
[The full list of environment variables is here](server/ENV_VARIABLES.md)
|
|
833
226
|
|
|
834
|
-
###
|
|
835
|
-
```
|
|
836
|
-
|
|
837
|
-
ws.on('message', (data) => {
|
|
838
|
-
const event = JSON.parse(data);
|
|
839
|
-
console.log('Queue event:', event);
|
|
840
|
-
});
|
|
227
|
+
### With Docker
|
|
228
|
+
```sh
|
|
229
|
+
./build.sh
|
|
841
230
|
```
|
|
842
231
|
|
|
843
|
-
|
|
232
|
+
### Running on k8s
|
|
844
233
|
|
|
845
|
-
|
|
234
|
+
[Running in k8s](server/k8s-example.yaml)
|
|
846
235
|
|
|
847
|
-
|
|
848
|
-
# Run test suite
|
|
849
|
-
npm test
|
|
236
|
+
## 🔌 Raw HTTP API
|
|
850
237
|
|
|
851
|
-
|
|
852
|
-
npm test -- --grep "Pipeline"
|
|
238
|
+
You can use Queen directly from HTTP without the JS client.
|
|
853
239
|
|
|
854
|
-
|
|
855
|
-
npm run benchmark
|
|
856
|
-
```
|
|
240
|
+
[Here the complete list of API endpoints](API.md)
|
|
857
241
|
|
|
858
|
-
|
|
242
|
+
## ⚠️ Known Issues & Roadmap
|
|
859
243
|
|
|
860
|
-
|
|
244
|
+
### Server Startup Timing (Critical)
|
|
245
|
+
**Issue:** Worker initialization timeout (30s → 3600s) now matches file buffer recovery timeout. This is a temporary fix.
|
|
861
246
|
|
|
862
|
-
|
|
247
|
+
**Problem:** During startup, if the file buffer has many events to recover (e.g., after a long PostgreSQL outage), Worker 0 performs blocking recovery that can take up to 1 hour. The acceptor waits for all workers to initialize before starting to accept connections.
|
|
863
248
|
|
|
864
|
-
|
|
249
|
+
**Current Fix:** Worker initialization timeout increased to 3600s to prevent premature timeout.
|
|
865
250
|
|
|
866
|
-
|
|
251
|
+
**Better Solution Needed:**
|
|
252
|
+
- Make recovery non-blocking while preserving FIFO ordering guarantees
|
|
253
|
+
- Implement progressive readiness with memory-buffered queue during recovery
|
|
254
|
+
- Add configurable recovery timeout with graceful degradation
|
|
255
|
+
- See: `server/src/services/file_buffer.cpp:212` (MAX_STARTUP_RECOVERY_SECONDS)
|
|
256
|
+
- See: `server/src/acceptor_server.cpp:1876` (worker initialization timeout)
|
|
867
257
|
|
|
868
|
-
|
|
258
|
+
### Other TODO Items
|
|
259
|
+
- retention jobs
|
|
260
|
+
- reconsume
|
|
261
|
+
- fix frontend
|
|
262
|
+
- pg async
|
|
263
|
+
- pg reconnect
|
|
264
|
+
- memory leak?
|