queen-mq 0.2.0 → 0.2.22

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.
Files changed (85) hide show
  1. package/README.md +186 -763
  2. package/{src → client-js}/benchmark/consumer.js +6 -4
  3. package/{src → client-js}/benchmark/producer.js +9 -4
  4. package/{src → client-js}/client/client.js +277 -7
  5. package/{src → client-js}/client/index.js +1 -4
  6. package/{src → client-js}/client/utils/http.js +3 -2
  7. package/{src → client-js}/client/utils/retry.js +7 -1
  8. package/{src → client-js}/test/README.md +30 -9
  9. package/{src → client-js}/test/core-tests.js +7 -2
  10. package/{src → client-js}/test/edge-case-tests.js +2 -1
  11. package/client-js/test/human.js +162 -0
  12. package/client-js/test/partition-transaction-tests.js +482 -0
  13. package/client-js/test/qos0-tests.js +334 -0
  14. package/{src → client-js}/test/test-new.js +55 -0
  15. package/package.json +8 -17
  16. package/init-db.js +0 -20
  17. package/src/cluster-server.js +0 -242
  18. package/src/config.js +0 -229
  19. package/src/database/connection.js +0 -129
  20. package/src/database/poolManager.js +0 -199
  21. package/src/database/schema-v2.sql +0 -278
  22. package/src/managers/eventManager.js +0 -59
  23. package/src/managers/queueManagerOptimized.js +0 -1634
  24. package/src/managers/resourceCache.js +0 -96
  25. package/src/managers/systemEventManager.js +0 -132
  26. package/src/routes/ack.js +0 -26
  27. package/src/routes/configure.js +0 -46
  28. package/src/routes/messages.js +0 -368
  29. package/src/routes/pop.js +0 -69
  30. package/src/routes/push.js +0 -28
  31. package/src/routes/resources.js +0 -330
  32. package/src/routes/status.js +0 -1037
  33. package/src/server.js +0 -1633
  34. package/src/test/MIGRATION_ISSUES.md +0 -174
  35. package/src/test/test.js +0 -4524
  36. package/src/utils/streaming.js +0 -231
  37. package/src/webapp-dist/assets/Analytics-DJrE4Rz2.css +0 -1
  38. package/src/webapp-dist/assets/Analytics-HEMXKmfJ.js +0 -1
  39. package/src/webapp-dist/assets/ConfirmDialog-YEoCdE7x.js +0 -1
  40. package/src/webapp-dist/assets/ConsumerGroups-CC5VJywp.css +0 -1
  41. package/src/webapp-dist/assets/ConsumerGroups-D8Z20HhZ.js +0 -1
  42. package/src/webapp-dist/assets/Dashboard-BRFOz6it.css +0 -1
  43. package/src/webapp-dist/assets/Dashboard-Bpx9I32h.js +0 -1
  44. package/src/webapp-dist/assets/LoadingSpinner-BPttXcbs.js +0 -1
  45. package/src/webapp-dist/assets/Messages-BOfVHKma.css +0 -1
  46. package/src/webapp-dist/assets/Messages-EKURCbLl.js +0 -1
  47. package/src/webapp-dist/assets/QueueDetail-BXXA7xoZ.css +0 -1
  48. package/src/webapp-dist/assets/QueueDetail-Bmxg0KiA.js +0 -1
  49. package/src/webapp-dist/assets/Queues-CvrJkrZT.css +0 -1
  50. package/src/webapp-dist/assets/Queues-yUx9_3-9.js +0 -1
  51. package/src/webapp-dist/assets/StatusBadge-C1bBtXHh.css +0 -1
  52. package/src/webapp-dist/assets/StatusBadge-C6N84XmO.js +0 -1
  53. package/src/webapp-dist/assets/analytics-BapTekLM.js +0 -1
  54. package/src/webapp-dist/assets/colors-5z6Q_kUr.js +0 -18
  55. package/src/webapp-dist/assets/index-B7w2bKT6.css +0 -1
  56. package/src/webapp-dist/assets/index-CUSnwYTH.js +0 -31
  57. package/src/webapp-dist/assets/messages-CRV6E-xU.js +0 -1
  58. package/src/webapp-dist/assets/queen-logo-blue.svg +0 -210
  59. package/src/webapp-dist/assets/queen-logo-cyan.svg +0 -210
  60. package/src/webapp-dist/assets/queen-logo-indigo.svg +0 -210
  61. package/src/webapp-dist/assets/queen-logo-orange.svg +0 -210
  62. package/src/webapp-dist/assets/queen-logo-pink.svg +0 -210
  63. package/src/webapp-dist/assets/queen-logo-purple.svg +0 -210
  64. package/src/webapp-dist/assets/queen-logo-rose.svg +0 -239
  65. package/src/webapp-dist/assets/queen-logo.svg +0 -263
  66. package/src/webapp-dist/assets/queues-BdUbxfBC.js +0 -1
  67. package/src/webapp-dist/assets/resources-Bs9U-ceF.js +0 -1
  68. package/src/webapp-dist/index.html +0 -15
  69. package/src/websocket/wsServer.js +0 -228
  70. /package/{src → client-js}/benchmark/consumer_multi.js +0 -0
  71. /package/{src → client-js}/benchmark/producer_multi.js +0 -0
  72. /package/{src → client-js}/client/utils/loadBalancer.js +0 -0
  73. /package/{src → client-js}/services/encryptionService.js +0 -0
  74. /package/{src → client-js}/services/evictionService.js +0 -0
  75. /package/{src → client-js}/services/retentionService.js +0 -0
  76. /package/{src → client-js}/services/startupSync.js +0 -0
  77. /package/{src → client-js}/test/advanced-client-tests.js +0 -0
  78. /package/{src → client-js}/test/advanced-pattern-tests.js +0 -0
  79. /package/{src → client-js}/test/bus-mode-tests.js +0 -0
  80. /package/{src → client-js}/test/enterprise-tests.js +0 -0
  81. /package/{src → client-js}/test/partition-locking-tests.js +0 -0
  82. /package/{src → client-js}/test/utils.js +0 -0
  83. /package/{src → client-js}/test/window-buffer-test.js +0 -0
  84. /package/{src → client-js}/utils/logger.js +0 -0
  85. /package/{src → client-js}/utils/uuid.js +0 -0
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Queen - PostgreSQL-backed Message Queue System
1
+ # Queen MQ - PostgreSQL-backed C++ Message Queue
2
2
 
3
3
  <div align="center">
4
4
 
@@ -7,862 +7,285 @@
7
7
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE.md)
8
8
  [![Node](https://img.shields.io/badge/node-%3E%3D22.0.0-brightgreen.svg)](https://nodejs.org/)
9
9
 
10
- [Quick Start](#-quick-start) • [Client Examples](#-client-examples) • [Server Setup](#-server-setup) • [Core Concepts](#-core-concepts) • [API Reference](#-http-api-reference) • [Dashboard](#-dashboard)
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
- ## 🎯 Introduction
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)
87
-
88
- ---
22
+ QueenMQ is a queue system written in C++ and backed by Postgres. Supports queues and consumer groups.
89
23
 
90
- ## First Queue
24
+ ## JS Client usage
91
25
 
92
- ```javascript
93
- import { Queen } from 'queen-mq';
94
-
95
- const client = new Queen({
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
101
- });
102
-
103
- // Configure queue
104
- await client.queue('tasks', {
105
- leaseTime: 300, // 5 minutes to process each message
106
- retryLimit: 3 // Retry up to 3 times
107
- });
26
+ ```js
27
+ import { Queen } from 'queen-mq'
108
28
 
109
- // Push a message
110
- await client.push('tasks', {
111
- action: 'send-email',
112
- to: 'user@example.com'
29
+ const client = new Queen({
30
+ baseUrls: ['http://localhost:6632'],
31
+ timeout: 30000,
32
+ retryAttempts: 3
113
33
  });
114
34
 
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
141
-
142
- # Initialize database schema
143
- node init-db.js
144
- ```
145
-
146
- ### Set Environment (Optional)
147
-
148
- ```bash
149
- # Database connection
150
- export PG_USER=postgres
151
- export PG_HOST=localhost
152
- export PG_DB=postgres
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
35
+ const queue = 'html-processing'
170
36
 
171
- ### Pipeline API - Fluent Message Processing
37
+ // Create a queue
38
+ await client.queue(queue, { leaseTime: 30 });
172
39
 
173
- The Pipeline API provides a chainable interface for complex message processing workflows:
40
+ // Push some data, specifyng the partition
41
+ await client.push(`${queue}/customer-1828`, [ { id: 1 } ]);
174
42
 
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
- The `onError` handler provides robust error recovery:
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
- **Key Behaviors:**
285
- - Catches errors from `process()` or `processBatch()`
286
- - Receives the error and affected messages
287
- - Pipeline continues after error handler (doesn't stop)
288
- - Without `onError`, errors stop the pipeline
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
- **Error Recovery Strategies:**
291
- ```javascript
292
- // Strategy 1: Retry with backoff
293
- .onError(async (error, messages) => {
294
- // ACK as failed - will retry based on retryLimit
295
- await client.ack(messages, false);
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
- // Strategy 2: Move to Dead Letter Queue
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
- // Strategy 3: Skip and continue
311
- .onError(async (error, messages) => {
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
- **Important Notes:**
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
- #### 1. Configure a Queue
369
-
370
- ```javascript
371
- import { Queen } from 'queen-mq';
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
- }
452
-
453
- // Use takeBatch to get arrays of messages (higher throughput)
454
- for await (const messages of client.takeBatch('orders', {
455
- batch: 1000, // Fetch 1000 at a time
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
- }
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
472
94
 
473
- // Take single batch (convenience method)
474
- const messages = await client.takeSingleBatch('orders', { batch: 100 });
475
- ```
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
476
98
 
477
- #### 4. Acknowledge Messages
99
+ ### Pipelines
100
+ - **[Transactional Pipelines](examples/03-transactional-pipeline.js)** - Atomic processing with ack and push in a transaction
478
101
 
479
- ```javascript
480
- // Acknowledge success
481
- await client.ack(message);
482
- // or
483
- await client.ack(message, true);
102
+ ### Event Streaming (QoS 0)
103
+ - **[Event Streaming](examples/09-event-streaming.js)** - At-most-once delivery with buffering and auto-ack
484
104
 
485
- // Acknowledge failure (will retry based on retryLimit)
486
- await client.ack(message, false);
105
+ ## QoS 0: At-Most-Once Event Streaming
487
106
 
488
- // Acknowledge with error context
489
- await client.ack(message, false, {
490
- error: 'Payment gateway timeout'
491
- });
107
+ For high-throughput event streams, Queen supports **at-most-once delivery** with server-side buffering and auto-acknowledgment.
492
108
 
493
- // Batch acknowledgment (with lease validation)
494
- await client.ack(messages); // Pass array for batch ack
495
- ```
109
+ ### Server-Side Buffering
496
110
 
497
- #### 5. Renew Leases (for long-running tasks)
111
+ Batch events on the server for 10-100x reduction in database writes:
498
112
 
499
113
  ```javascript
500
- // Renew lease for a single message
501
- const result = await client.renewLease(message);
502
- console.log(`Lease renewed until: ${result.newExpiresAt}`);
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
- // Example: Manual renewal during long processing
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
- ### Address Notation
123
+ ### Auto-Acknowledgment
534
124
 
535
- Queen uses a simple addressing scheme that encodes queue, partition, and consumer group:
125
+ Skip manual ack for fire-and-forget consumption:
536
126
 
537
127
  ```javascript
538
- // Basic addresses
539
- 'orders' // Queue (Default partition)
540
- 'orders/urgent' // Queue with specific partition
541
- 'orders@workers' // Queue with consumer group (bus mode)
542
- 'orders/urgent@workers' // Full address: queue + partition + group
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 Patterns
135
+ ### Fan-Out Pattern (Consumer Groups)
552
136
 
553
- #### Consumer Groups (Bus Mode)
137
+ Combine buffering + auto-ack + consumer groups for pub/sub:
554
138
 
555
139
  ```javascript
556
- // Multiple services process the same messages independently
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
- // Monitoring service (gets same messages)
565
- for await (const event of client.take('events@monitoring')) {
566
- await checkThresholds(event.data);
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
- // Audit service (also gets same messages)
571
- for await (const event of client.take('events@audit')) {
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
- #### Graceful Shutdown
153
+ ### PostgreSQL Failover
578
154
 
579
- ```javascript
580
- let running = true;
581
-
582
- process.on('SIGTERM', () => {
583
- console.log('Shutting down gracefully...');
584
- running = false;
585
- });
586
-
587
- for await (const message of client.take('orders')) {
588
- if (!running) break;
589
-
590
- await processOrder(message.data);
591
- await client.ack(message);
592
- }
593
-
594
- console.log('Shutdown complete');
595
- ```
596
-
597
- ---
155
+ Queen automatically buffers messages to disk when PostgreSQL is unavailable - **zero message loss**:
598
156
 
599
- ## 🖥️ Server Setup
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
600
162
 
601
- ### Environment Variables
163
+ **No configuration needed** - failover is automatic!
602
164
 
165
+ **Custom directory:**
603
166
  ```bash
604
- # Server configuration
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
- ### Programmatic Server
683
-
684
- ```javascript
685
- import { createQueenServer } from 'queen-mq/server';
686
-
687
- const server = await createQueenServer({
688
- port: 6632,
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
- });
701
-
702
- await server.start();
703
- ```
170
+ ### When to Use
704
171
 
705
- ---
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 |
706
177
 
707
- ## 🔑 Core Concepts
178
+ ## Architecture
708
179
 
709
- ### Message Flow
180
+ Queen uses a high-performance **acceptor/worker pattern** with uWebSockets, combining non-blocking I/O for HTTP/WebSocket with a dedicated thread pool for database operations.
710
181
 
711
- 1. **Push**: Messages are encrypted and stored in PostgreSQL with metadata
712
- 2. **Pop/Take**: Messages are leased to consumers with automatic lock management
713
- 3. **Process**: Consumers process messages with automatic lease renewal
714
- 4. **ACK**: Messages are marked complete and leases are released
715
- 5. **Retry**: Failed messages are retried with exponential backoff
182
+ **View the interactive architecture diagram:** [architecture.svg](./assets/architecture.svg)
716
183
 
717
- ### Partition & Lease Management
184
+ **Key Components:**
185
+ - **UWS Acceptor**: Single thread listening on port 6632, round-robin distributes to workers
186
+ - **UWS Workers**: N event loop threads (default: 10) handling HTTP routes and WebSocket
187
+ - **Response Timers**: Per-worker timers (25ms tick) drain response queue back to clients
188
+ - **DB ThreadPool**: Separate pool for blocking PostgreSQL operations
189
+ - **Poll Workers**: 2 reserved threads for long-polling with adaptive backoff (100ms→2000ms)
190
+ - **Poll Intention Registry**: Thread-safe store for long-poll requests
191
+ - **Database Pool**: 150 shared PostgreSQL connections (libpq) with mutex/condition variable
192
+ - **Response Queue**: Thread-safe queue decoupling DB results from event loop responses
718
193
 
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
194
+ **Request Flow:**
195
+ 1. Client → Acceptor → Worker (event loop)
196
+ 2. Worker registers response, submits job to DB ThreadPool
197
+ 3. DB thread executes query, pushes result to Response Queue
198
+ 4. Worker's response timer drains queue, sends HTTP response
724
199
 
725
- ### Scalability
200
+ **Long-Polling Flow:**
201
+ 1. No immediate messages? Register intention in Registry
202
+ 2. Poll Workers wake every 50ms, group intentions by queue/partition/consumer
203
+ 3. Rate-limited DB queries (100ms initial, exponential backoff to 2s)
204
+ 4. Messages distributed to waiting clients via Response Queue
205
+ 5. Timeouts detected by Poll Workers, send 204 No Content
726
206
 
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
207
+ This architecture provides high concurrency, efficient connection pooling, and minimal latency for both immediate and long-polling requests.
732
208
 
733
- ---
209
+ ## Webapp
734
210
 
735
- ## 📚 HTTP API Reference
211
+ A modern Vue 3 web interface for managing and monitoring Queen MQ.
736
212
 
737
- ### Queue Management
213
+ **Features:**
214
+ - 📊 Real-time dashboard with system metrics
215
+ - 📈 Message throughput visualization
216
+ - 🔍 Queue management and monitoring
217
+ - 👥 Consumer group tracking
218
+ - 💬 Message browser
219
+ - 📉 Analytics and insights
220
+ - 🌓 Dark/light theme support
738
221
 
222
+ **Quick Start:**
739
223
  ```bash
740
- # Create/configure queue
741
- curl -X POST http://localhost:6632/api/v1/configure \
742
- -H "Content-Type: application/json" \
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
224
+ cd webapp
225
+ npm install
226
+ npm run dev
753
227
  ```
754
228
 
755
- ### Message Operations
229
+ The dashboard will be available at `http://localhost:4000`
756
230
 
757
- ```bash
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
- ```
231
+ See [webapp/README.md](webapp/README.md) for more details.
788
232
 
789
- ### Advanced Operations
233
+ ## Install server and configure it
790
234
 
791
- ```bash
792
- # Atomic transaction
793
- curl -X POST http://localhost:6632/api/v1/transaction \
794
- -H "Content-Type: application/json" \
795
- -d '{
796
- "operations": [
797
- {
798
- "type": "ack",
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
235
+ ### Quick Start
236
+
237
+ ```sh
238
+ cd server
239
+ make clean
240
+ make deps
241
+ make build-only
242
+ DB_POOL_SIZE=50 ./bin/queen-server
819
243
  ```
820
244
 
821
- ---
245
+ **📖 Complete Build & Tuning Guide:** [server/README.md](server/README.md)
822
246
 
823
- ## 📊 Dashboard
247
+ Includes:
248
+ - Build instructions and optimization
249
+ - Performance tuning (worker threads, database pool)
250
+ - Production deployment (systemd, Docker, load balancing)
251
+ - Troubleshooting common issues
252
+ - Benchmarking guides
824
253
 
825
- Access the real-time dashboard at `http://localhost:6632/`
254
+ ### Environment Variables
826
255
 
827
- ### Features
828
- - Real-time queue metrics
829
- - Message browser with search
830
- - Consumer group monitoring
831
- - System health indicators
832
- - Performance graphs
256
+ [The full list of environment variables is here](server/ENV_VARIABLES.md)
833
257
 
834
- ### WebSocket Events
835
- ```javascript
836
- const ws = new WebSocket('ws://localhost:6632/ws/dashboard');
837
- ws.on('message', (data) => {
838
- const event = JSON.parse(data);
839
- console.log('Queue event:', event);
840
- });
258
+ ### With Docker
259
+ ```sh
260
+ ./build.sh
841
261
  ```
842
262
 
843
- ---
844
-
845
- ## 🧪 Testing
263
+ ### Running on k8s
846
264
 
847
- ```bash
848
- # Run test suite
849
- npm test
265
+ [Running in k8s](server/k8s-example.yaml)
850
266
 
851
- # Run specific test
852
- npm test -- --grep "Pipeline"
267
+ ## 🔌 Raw HTTP API
853
268
 
854
- # Benchmark
855
- npm run benchmark
856
- ```
269
+ You can use Queen directly from HTTP without the JS client.
857
270
 
858
- ---
271
+ [Here the complete list of API endpoints](API.md)
859
272
 
860
- ## 🤝 Contributing
273
+ ## ⚠️ Known Issues & Roadmap
861
274
 
862
- See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
275
+ ### Server Startup Timing (Critical)
276
+ **Issue:** Worker initialization timeout (30s → 3600s) now matches file buffer recovery timeout. This is a temporary fix.
863
277
 
864
- ---
278
+ **Better Solution Needed:**
279
+ - Make recovery non-blocking while preserving FIFO ordering guarantees
280
+ - Implement progressive readiness with memory-buffered queue during recovery
281
+ - Add configurable recovery timeout with graceful degradation
282
+ - See: `server/src/services/file_buffer.cpp:212` (MAX_STARTUP_RECOVERY_SECONDS)
283
+ - See: `server/src/acceptor_server.cpp:1876` (worker initialization timeout)
865
284
 
866
- ## 📄 License
867
285
 
868
- Apache 2.0 - see [LICENSE.md](LICENSE.md)
286
+ ### Other TODO Items
287
+ - retention jobs
288
+ - reconsume
289
+ - new client
290
+ - auth
291
+ - streaming engine