queen-mq 0.1.1 → 0.1.3
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/API.md +1226 -0
- package/AUTH.md +2044 -0
- package/LICENSE.md +202 -0
- package/PROGRAMMATIC_SERVER.md +190 -0
- package/README.md +303 -53
- package/WEBAPP.md +1889 -0
- package/assets/dashboard-01.png +0 -0
- package/assets/queen-logo-blue.svg +210 -0
- package/assets/queen-logo-cyan.svg +210 -0
- package/assets/queen-logo-indigo.svg +210 -0
- package/assets/queen-logo-orange.svg +210 -0
- package/assets/queen-logo-pink.svg +210 -0
- package/assets/queen-logo-purple.svg +210 -0
- package/assets/queen-logo-rose.svg +239 -0
- package/assets/queen-logo.svg +263 -0
- package/examples/batch-processing.js +58 -0
- package/examples/programmatic-server.js +58 -0
- package/examples/test-cache-multi-server.js +2 -2
- package/examples/test-connection-recovery.js +66 -0
- package/examples/test-dashboard-api.js +200 -0
- package/package.json +4 -2
- package/server.log +1 -0
- package/src/benchmark/consumer.js +207 -0
- package/src/benchmark/consumer_multi.js +216 -0
- package/src/benchmark/producer.js +75 -0
- package/src/benchmark/producer_multi.js +115 -0
- package/src/client/client.js +219 -17
- package/src/client/index.js +4 -3
- package/src/cluster-server.js +242 -0
- package/src/config.js +19 -5
- package/src/database/connection.js +42 -16
- package/src/database/poolManager.js +7 -0
- package/src/database/schema-v2.sql +194 -130
- package/src/managers/queueManagerOptimized.js +852 -933
- package/src/managers/systemEventManager.js +8 -3
- package/src/routes/messages.js +127 -57
- package/src/routes/pop.js +27 -43
- package/src/routes/resources.js +61 -27
- package/src/routes/status.js +1037 -0
- package/src/server.js +704 -642
- package/src/services/evictionService.js +57 -28
- package/src/services/retentionService.js +44 -11
- package/src/services/startupSync.js +1 -1
- package/src/test/advanced-pattern-tests.js +6 -6
- package/src/test/bus-mode-tests.js +24 -11
- package/src/test/core-tests.js +110 -0
- package/src/test/edge-case-tests.js +12 -5
- package/src/test/enterprise-tests.js +48 -15
- package/src/test/test-new.js +3 -1
- package/src/test/test.js +1 -1
- package/src/test/utils.js +1 -1
- package/src/test/window-buffer-test.js +114 -0
- package/src/utils/streaming.js +231 -0
- package/src/utils/uuid.js +2 -2
- package/src/websocket/wsServer.js +10 -3
- package/test-keepalive-v2.sh +22 -0
- package/webapp/COLOR_GUIDE.md +118 -0
- package/webapp/README.md +143 -0
- package/webapp/index.html +14 -0
- package/webapp/package-lock.json +3184 -0
- package/webapp/package.json +25 -0
- package/webapp/postcss.config.js +7 -0
- package/webapp/public/assets/queen-logo-blue.svg +210 -0
- package/webapp/public/assets/queen-logo-cyan.svg +210 -0
- package/webapp/public/assets/queen-logo-indigo.svg +210 -0
- package/webapp/public/assets/queen-logo-orange.svg +210 -0
- package/webapp/public/assets/queen-logo-pink.svg +210 -0
- package/webapp/public/assets/queen-logo-purple.svg +210 -0
- package/webapp/public/assets/queen-logo-rose.svg +239 -0
- package/webapp/public/assets/queen-logo.svg +263 -0
- package/webapp/src/App.vue +19 -0
- package/webapp/src/api/analytics.js +10 -0
- package/webapp/src/api/client.js +29 -0
- package/webapp/src/api/consumers.js +52 -0
- package/webapp/src/api/health.js +7 -0
- package/webapp/src/api/messages.js +26 -0
- package/webapp/src/api/queues.js +14 -0
- package/webapp/src/api/resources.js +8 -0
- package/webapp/src/assets/styles/main.css +357 -0
- package/webapp/src/components/analytics/AnalyticsFilters.vue +87 -0
- package/webapp/src/components/analytics/AnalyticsMetrics.vue +57 -0
- package/webapp/src/components/analytics/MessageDistributionChart.vue +111 -0
- package/webapp/src/components/analytics/MessageFlowChart.vue +173 -0
- package/webapp/src/components/analytics/TimeRangeSelector.vue +27 -0
- package/webapp/src/components/analytics/TopQueuesChart.vue +132 -0
- package/webapp/src/components/common/ConfirmDialog.vue +56 -0
- package/webapp/src/components/common/LoadingSpinner.vue +6 -0
- package/webapp/src/components/common/MetricCard.vue +43 -0
- package/webapp/src/components/common/StatusBadge.vue +45 -0
- package/webapp/src/components/dashboard/MessageStatusCard.vue +50 -0
- package/webapp/src/components/dashboard/PerformanceCard.vue +38 -0
- package/webapp/src/components/dashboard/ThroughputChart.vue +182 -0
- package/webapp/src/components/dashboard/TopQueuesTable.vue +53 -0
- package/webapp/src/components/layout/AppLayout.vue +110 -0
- package/webapp/src/components/layout/AppSidebar.vue +304 -0
- package/webapp/src/components/messages/MessageDetailPanel.vue +242 -0
- package/webapp/src/components/messages/MessageFilters.vue +101 -0
- package/webapp/src/components/queue-detail/PartitionList.vue +79 -0
- package/webapp/src/components/queue-detail/PushMessageModal.vue +175 -0
- package/webapp/src/components/queue-detail/QueueConfig.vue +63 -0
- package/webapp/src/components/queue-detail/QueueDetailHeader.vue +53 -0
- package/webapp/src/components/queue-detail/RecentMessages.vue +76 -0
- package/webapp/src/components/queues/CreateQueueModal.vue +193 -0
- package/webapp/src/components/queues/QueueFilters.vue +90 -0
- package/webapp/src/composables/useApi.js +34 -0
- package/webapp/src/composables/useTheme.js +36 -0
- package/webapp/src/main.js +11 -0
- package/webapp/src/router/index.js +42 -0
- package/webapp/src/utils/colors.js +96 -0
- package/webapp/src/utils/formatters.js +49 -0
- package/webapp/src/views/Analytics.vue +377 -0
- package/webapp/src/views/ConsumerGroups.vue +433 -0
- package/webapp/src/views/Dashboard.vue +418 -0
- package/webapp/src/views/Messages.vue +363 -0
- package/webapp/src/views/QueueDetail.vue +582 -0
- package/webapp/src/views/Queues.vue +496 -0
- package/webapp/tailwind.config.js +25 -0
- package/webapp/vite.config.js +10 -0
- package/dashboard/.vscode/extensions.json +0 -3
- package/dashboard/README.md +0 -5
- package/dashboard/index.html +0 -14
- package/dashboard/package-lock.json +0 -1458
- package/dashboard/package.json +0 -25
- package/dashboard/public/vite.svg +0 -1
- package/dashboard/src/App.vue +0 -29
- package/dashboard/src/assets/styles/main.css +0 -908
- package/dashboard/src/assets/vue.svg +0 -1
- package/dashboard/src/components/cards/MetricCard.vue +0 -298
- package/dashboard/src/components/charts/QueueDepthChart.vue +0 -276
- package/dashboard/src/components/charts/QueueLagChart.vue +0 -436
- package/dashboard/src/components/charts/ThroughputChart.vue +0 -302
- package/dashboard/src/components/common/ActivityFeed.vue +0 -251
- package/dashboard/src/components/layout/AppHeader.vue +0 -208
- package/dashboard/src/components/layout/AppLayout.vue +0 -88
- package/dashboard/src/components/layout/AppSidebar.vue +0 -261
- package/dashboard/src/main.js +0 -44
- package/dashboard/src/router.js +0 -54
- package/dashboard/src/services/api.js +0 -187
- package/dashboard/src/services/websocket.js +0 -167
- package/dashboard/src/utils/constants.js +0 -56
- package/dashboard/src/utils/helpers.js +0 -118
- package/dashboard/src/views/Analytics.vue +0 -912
- package/dashboard/src/views/Dashboard.vue +0 -906
- package/dashboard/src/views/Messages.vue +0 -437
- package/dashboard/src/views/QueueDetail.vue +0 -501
- package/dashboard/src/views/Queues.vue +0 -333
- package/dashboard/vite.config.js +0 -30
- package/src/client/queenClient.js +0 -513
- package/src/routes/analytics.js +0 -812
package/README.md
CHANGED
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
[Quick Start](#-quick-start) • [Client Examples](#-client-examples) • [Server Setup](#-server-setup) • [Core Concepts](#-core-concepts) • [API Reference](#-http-api-reference) • [Dashboard](#-dashboard)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
<img src="assets/queen-logo.svg" alt="Queen Logo" width="120" />
|
|
14
|
+
</p>
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
</div>
|
|
15
17
|
|
|
16
18
|
---
|
|
17
19
|
|
|
@@ -22,12 +24,14 @@
|
|
|
22
24
|
### Why Queen?
|
|
23
25
|
|
|
24
26
|
**🚀 Developer-First API**
|
|
25
|
-
- **4 methods, infinite patterns**: `queue()`, `push()`, `take()`, `ack()`
|
|
27
|
+
- **4 core methods, infinite patterns**: `queue()`, `push()`, `take()`, `ack()`
|
|
26
28
|
- **Async iteration**: Process messages with familiar `for await` syntax
|
|
29
|
+
- **Batch processing**: Use `takeBatch()` for 250k+ msg/sec throughput on millions of messages
|
|
27
30
|
- **Smart addressing**: `orders/urgent@workers` - queue, partition, and consumer group in one
|
|
28
31
|
|
|
29
32
|
**⚡ Production-Ready Performance**
|
|
30
|
-
- **
|
|
33
|
+
- **100,000+ msg/sec** throughput with cursor-based consumption
|
|
34
|
+
- **Constant-time batch operations** - O(batch_size) regardless of queue depth
|
|
31
35
|
- **Long polling** for event-driven, real-time message delivery
|
|
32
36
|
- **Partition locking** prevents duplicate processing across consumers
|
|
33
37
|
- **Connection pooling** and optimized batch operations
|
|
@@ -49,6 +53,7 @@
|
|
|
49
53
|
- **Rich Analytics**: Throughput, lag, queue depth metrics
|
|
50
54
|
- **Message Browser**: Search, inspect, and retry messages
|
|
51
55
|
- **System Health**: Database, memory, and performance metrics
|
|
56
|
+
- **Cursor Tracking**: Monitor consumption progress per consumer group
|
|
52
57
|
|
|
53
58
|
### Use Cases
|
|
54
59
|
|
|
@@ -67,6 +72,7 @@
|
|
|
67
72
|
- [Client Examples](#-client-examples)
|
|
68
73
|
- [Server Setup](#-server-setup)
|
|
69
74
|
- [Core Concepts](#-core-concepts)
|
|
75
|
+
- [Cursor-Based Consumption Strategy](#-cursor-based-consumption-strategy)
|
|
70
76
|
- [HTTP API Reference](#-http-api-reference)
|
|
71
77
|
- [Dashboard](#-dashboard)
|
|
72
78
|
- [Configuration](#-configuration)
|
|
@@ -128,6 +134,33 @@ npm install
|
|
|
128
134
|
node init-db.js
|
|
129
135
|
```
|
|
130
136
|
|
|
137
|
+
### Running the Server
|
|
138
|
+
|
|
139
|
+
**Option 1: Standalone Server (Traditional)**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm start
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Option 2: Programmatic Server (New in v0.1.2)**
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
import { QueenServer } from 'queen-mq';
|
|
149
|
+
|
|
150
|
+
// Start server with default config
|
|
151
|
+
const server = await QueenServer();
|
|
152
|
+
|
|
153
|
+
// Or with custom options
|
|
154
|
+
const server = await QueenServer({
|
|
155
|
+
port: 3000,
|
|
156
|
+
host: '127.0.0.1'
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
console.log(`Server running at http://${server.host}:${server.port}`);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
This allows you to embed Queen MQ directly in your application, run multiple instances, or easily start/stop servers in tests. See [PROGRAMMATIC_SERVER.md](./PROGRAMMATIC_SERVER.md) for more details.
|
|
163
|
+
|
|
131
164
|
### Set Environment (Optional)
|
|
132
165
|
|
|
133
166
|
```bash
|
|
@@ -252,6 +285,26 @@ for await (const message of client.take('orders/urgent')) {
|
|
|
252
285
|
await processUrgentOrder(message.data);
|
|
253
286
|
await client.ack(message);
|
|
254
287
|
}
|
|
288
|
+
|
|
289
|
+
// Use takeBatch to get arrays of messages (higher throughput)
|
|
290
|
+
for await (const messages of client.takeBatch('orders', {
|
|
291
|
+
batch: 1000, // Fetch 1000 at a time
|
|
292
|
+
wait: true
|
|
293
|
+
})) {
|
|
294
|
+
// messages is an array of up to 1000 messages
|
|
295
|
+
console.log(`Processing batch of ${messages.length} messages`);
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
// Process entire batch
|
|
299
|
+
await processBatch(messages.map(m => m.data));
|
|
300
|
+
|
|
301
|
+
// Acknowledge entire batch at once (efficient!)
|
|
302
|
+
await client.ack(messages); // Pass array for batch ack
|
|
303
|
+
} catch (error) {
|
|
304
|
+
// Mark entire batch as failed
|
|
305
|
+
await client.ack(messages, false, { error: error.message });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
255
308
|
```
|
|
256
309
|
|
|
257
310
|
#### 4. Acknowledge Messages
|
|
@@ -313,7 +366,7 @@ for await (const message of client.take('tasks', {
|
|
|
313
366
|
#### Batch Processing
|
|
314
367
|
|
|
315
368
|
```javascript
|
|
316
|
-
//
|
|
369
|
+
// Method 1: Manual batching with take()
|
|
317
370
|
const batch = [];
|
|
318
371
|
for await (const message of client.take('analytics', { batch: 100 })) {
|
|
319
372
|
batch.push(message);
|
|
@@ -328,6 +381,15 @@ for await (const message of client.take('analytics', { batch: 100 })) {
|
|
|
328
381
|
batch.length = 0;
|
|
329
382
|
}
|
|
330
383
|
}
|
|
384
|
+
|
|
385
|
+
// Method 2: Direct batch processing with takeBatch() (RECOMMENDED)
|
|
386
|
+
for await (const messages of client.takeBatch('analytics', { batch: 1000 })) {
|
|
387
|
+
// messages is already an array!
|
|
388
|
+
await processBatch(messages.map(m => m.data));
|
|
389
|
+
|
|
390
|
+
// Single batch acknowledgment (much faster!)
|
|
391
|
+
await client.ack(messages);
|
|
392
|
+
}
|
|
331
393
|
```
|
|
332
394
|
|
|
333
395
|
#### Parallel Processing with Partitions
|
|
@@ -881,6 +943,190 @@ await client.queue('time-sensitive', {
|
|
|
881
943
|
|
|
882
944
|
---
|
|
883
945
|
|
|
946
|
+
## 🚀 Cursor-Based Consumption Strategy
|
|
947
|
+
|
|
948
|
+
Queen uses a **cursor-based consumption model** for optimal performance at scale, providing O(batch_size) constant-time operations regardless of queue depth.
|
|
949
|
+
|
|
950
|
+
### How It Works
|
|
951
|
+
|
|
952
|
+
Traditional message queues scan through all messages to find pending ones, leading to performance degradation as messages accumulate. Queen's cursor-based approach maintains a position marker (cursor) for each consumer, allowing direct access to the next batch of messages.
|
|
953
|
+
|
|
954
|
+
**Partition Cursors:**
|
|
955
|
+
|
|
956
|
+
Each partition maintains a cursor position per consumer group:
|
|
957
|
+
- `last_consumed_created_at`: Timestamp of last consumed message
|
|
958
|
+
- `last_consumed_id`: UUID of last consumed message (tie-breaker for same timestamp)
|
|
959
|
+
- `total_messages_consumed`: Running count of consumed messages
|
|
960
|
+
|
|
961
|
+
The cursor always moves **forward** in time, ensuring strict FIFO ordering.
|
|
962
|
+
|
|
963
|
+
### The takeBatch Method
|
|
964
|
+
|
|
965
|
+
Queen provides two consumption methods:
|
|
966
|
+
|
|
967
|
+
**1. `take()` - Individual message iterator:**
|
|
968
|
+
```javascript
|
|
969
|
+
// Processes messages one at a time
|
|
970
|
+
for await (const message of client.take('orders', { batch: 1000 })) {
|
|
971
|
+
await processOrder(message.data);
|
|
972
|
+
await client.ack(message);
|
|
973
|
+
}
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
**2. `takeBatch()` - Array iterator (HIGH PERFORMANCE):**
|
|
977
|
+
```javascript
|
|
978
|
+
// Yields arrays of messages - achieves 100k+ msg/s throughput
|
|
979
|
+
for await (const messages of client.takeBatch('orders', { batch: 1000 })) {
|
|
980
|
+
// messages is an array of up to 1000 message objects
|
|
981
|
+
await processBatch(messages.map(m => m.data));
|
|
982
|
+
|
|
983
|
+
// Batch acknowledge - single DB transaction for all messages
|
|
984
|
+
await client.ack(messages);
|
|
985
|
+
}
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
**Under the hood**, both methods use cursor-based batch retrieval:
|
|
989
|
+
|
|
990
|
+
```sql
|
|
991
|
+
-- Cursor-based query (simplified)
|
|
992
|
+
SELECT * FROM messages
|
|
993
|
+
WHERE partition_id = $1
|
|
994
|
+
AND id > $2::uuid -- Start after last cursor position
|
|
995
|
+
ORDER BY created_at ASC, id ASC
|
|
996
|
+
LIMIT $3 -- Batch size
|
|
997
|
+
FOR UPDATE SKIP LOCKED
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
**Key characteristics:**
|
|
1001
|
+
1. **Constant-time**: Performance stays consistent whether you've consumed 0% or 99% of messages
|
|
1002
|
+
2. **FIFO guarantee**: Messages always returned in creation order
|
|
1003
|
+
3. **Lock-free scanning**: `SKIP LOCKED` prevents contention between consumers
|
|
1004
|
+
4. **Efficient**: No table scans - direct cursor-based access using UUIDv7 (time-ordered)
|
|
1005
|
+
|
|
1006
|
+
**Performance tip:** Use `takeBatch()` with large batch sizes (1,000-10,000) for maximum throughput. The server fetches messages in batches regardless, but `takeBatch()` gives you the array directly, allowing bulk processing and batch acknowledgment in a single operation.
|
|
1007
|
+
|
|
1008
|
+
### Batch Acknowledgment Semantics
|
|
1009
|
+
|
|
1010
|
+
Queen handles batch acknowledgments intelligently:
|
|
1011
|
+
|
|
1012
|
+
**Partial Success** (some messages succeed, some fail):
|
|
1013
|
+
```javascript
|
|
1014
|
+
// Batch: 10,000 messages
|
|
1015
|
+
// Success: 9,999 messages
|
|
1016
|
+
// Failed: 1 message
|
|
1017
|
+
|
|
1018
|
+
// Behavior:
|
|
1019
|
+
// ✅ Cursor advances past all 10,000 messages
|
|
1020
|
+
// ✅ Failed message moved to Dead Letter Queue
|
|
1021
|
+
// ✅ Next take() starts from message 10,001
|
|
1022
|
+
// ✅ FIFO maintained, no redelivery of successful messages
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
**Total Batch Failure** (all messages fail):
|
|
1026
|
+
```javascript
|
|
1027
|
+
// Batch: 10,000 messages
|
|
1028
|
+
// Success: 0 messages
|
|
1029
|
+
// Failed: 10,000 messages
|
|
1030
|
+
|
|
1031
|
+
// Behavior:
|
|
1032
|
+
// ❌ Cursor DOES NOT advance
|
|
1033
|
+
// ❌ Messages NOT moved to DLQ
|
|
1034
|
+
// ✅ Lease released
|
|
1035
|
+
// ✅ Next take() gets SAME batch (retry)
|
|
1036
|
+
// ✅ Allows recovery from transient failures
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
This design handles transient failures (network issues, service outages) gracefully while preventing poison messages from blocking the queue.
|
|
1040
|
+
|
|
1041
|
+
### Performance Comparison
|
|
1042
|
+
|
|
1043
|
+
| Operation | Traditional Approach | Cursor Approach | Improvement |
|
|
1044
|
+
|-----------|---------------------|-----------------|-------------|
|
|
1045
|
+
| Pop @ 0% consumed | O(partition_size) | O(batch_size) | Same |
|
|
1046
|
+
| Pop @ 50% consumed | O(partition_size) | O(batch_size) | **10-100x faster** |
|
|
1047
|
+
| Pop @ 99% consumed | O(partition_size) | O(batch_size) | **100-1000x faster** |
|
|
1048
|
+
|
|
1049
|
+
**Real-world benchmark** (1M messages):
|
|
1050
|
+
```
|
|
1051
|
+
Traditional:
|
|
1052
|
+
Early batches: 300ms per pop
|
|
1053
|
+
Late batches: 3500ms per pop (10x degradation)
|
|
1054
|
+
|
|
1055
|
+
Cursor-based:
|
|
1056
|
+
Early batches: 150ms per pop
|
|
1057
|
+
Late batches: 200ms per pop (constant!)
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
### Dead Letter Queue
|
|
1061
|
+
|
|
1062
|
+
Individual message failures are moved to the Dead Letter Queue for inspection and manual intervention:
|
|
1063
|
+
|
|
1064
|
+
```javascript
|
|
1065
|
+
// Monitor DLQ
|
|
1066
|
+
const response = await fetch('http://localhost:6632/api/v1/analytics/dlq');
|
|
1067
|
+
const dlqMessages = await response.json();
|
|
1068
|
+
|
|
1069
|
+
// Inspect failed messages
|
|
1070
|
+
for (const msg of dlqMessages) {
|
|
1071
|
+
console.log(`Failed: ${msg.error_message}`);
|
|
1072
|
+
|
|
1073
|
+
// After fixing issue, can re-push if needed
|
|
1074
|
+
await client.push(msg.queue, fixedPayload);
|
|
1075
|
+
}
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
**DLQ Query:**
|
|
1079
|
+
```sql
|
|
1080
|
+
SELECT * FROM queen.dead_letter_queue
|
|
1081
|
+
WHERE consumer_group = 'my-group'
|
|
1082
|
+
ORDER BY failed_at DESC
|
|
1083
|
+
LIMIT 100;
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
### Batch Size Guidelines
|
|
1087
|
+
|
|
1088
|
+
Choose batch sizes based on your workload:
|
|
1089
|
+
|
|
1090
|
+
**Smaller batches (100-1,000):**
|
|
1091
|
+
- ✅ Faster individual batch processing
|
|
1092
|
+
- ✅ Less impact if entire batch fails
|
|
1093
|
+
- ✅ Lower memory footprint
|
|
1094
|
+
- ❌ More network round-trips
|
|
1095
|
+
|
|
1096
|
+
**Larger batches (5,000-10,000):**
|
|
1097
|
+
- ✅ Higher throughput (100,000+ msg/sec achievable)
|
|
1098
|
+
- ✅ Fewer network round-trips
|
|
1099
|
+
- ✅ Better database efficiency
|
|
1100
|
+
- ❌ More messages retry if entire batch fails
|
|
1101
|
+
- ❌ Higher memory usage
|
|
1102
|
+
|
|
1103
|
+
**Recommendation:** Start with 1,000-2,000 for balanced performance. Increase to 5,000-10,000 for maximum throughput with reliable processing.
|
|
1104
|
+
|
|
1105
|
+
### Monitoring Cursor Progress
|
|
1106
|
+
|
|
1107
|
+
Track consumption progress via SQL:
|
|
1108
|
+
|
|
1109
|
+
```sql
|
|
1110
|
+
-- View cursor positions
|
|
1111
|
+
SELECT
|
|
1112
|
+
p.name as partition,
|
|
1113
|
+
pc.consumer_group,
|
|
1114
|
+
pc.total_messages_consumed,
|
|
1115
|
+
pc.total_batches_consumed,
|
|
1116
|
+
pc.last_consumed_at,
|
|
1117
|
+
EXTRACT(EPOCH FROM (NOW() - pc.last_consumed_at)) as seconds_since_last_consume
|
|
1118
|
+
FROM queen.partition_cursors pc
|
|
1119
|
+
JOIN queen.partitions p ON p.id = pc.partition_id
|
|
1120
|
+
ORDER BY pc.last_consumed_at DESC;
|
|
1121
|
+
|
|
1122
|
+
-- Monitor DLQ
|
|
1123
|
+
SELECT COUNT(*) as failed_count, consumer_group
|
|
1124
|
+
FROM queen.dead_letter_queue
|
|
1125
|
+
GROUP BY consumer_group;
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
---
|
|
1129
|
+
|
|
884
1130
|
## 🔌 HTTP API Reference
|
|
885
1131
|
|
|
886
1132
|
Base URL: `http://localhost:6632/api/v1`
|
|
@@ -1067,6 +1313,12 @@ POST /api/v1/messages/{transactionId}/dlq
|
|
|
1067
1313
|
DELETE /api/v1/queues/{queue}/clear
|
|
1068
1314
|
```
|
|
1069
1315
|
|
|
1316
|
+
**Delete queue:**
|
|
1317
|
+
```
|
|
1318
|
+
DELETE /api/v1/resources/queues/{queue}
|
|
1319
|
+
```
|
|
1320
|
+
_Note: Deletes the queue and all its partitions, messages, and related data._
|
|
1321
|
+
|
|
1070
1322
|
### System Health
|
|
1071
1323
|
|
|
1072
1324
|
**Health check:**
|
|
@@ -1108,10 +1360,12 @@ See [API.md](API.md) for complete API documentation.
|
|
|
1108
1360
|
|
|
1109
1361
|
Queen includes a comprehensive web dashboard for monitoring and management.
|
|
1110
1362
|
|
|
1363
|
+
[Dashboard](/assets/dashboard-01.png)
|
|
1364
|
+
|
|
1111
1365
|
### Access
|
|
1112
1366
|
|
|
1113
1367
|
1. Start the server: `npm start`
|
|
1114
|
-
2. Open browser: `http://localhost:
|
|
1368
|
+
2. Open browser: `http://localhost:4000`
|
|
1115
1369
|
3. WebSocket connection provides real-time updates
|
|
1116
1370
|
|
|
1117
1371
|
### Features
|
|
@@ -1496,7 +1750,7 @@ Promise.all([
|
|
|
1496
1750
|
await emitEvents();
|
|
1497
1751
|
```
|
|
1498
1752
|
|
|
1499
|
-
### Example 4: Batch Processing
|
|
1753
|
+
### Example 4: Batch Processing (High Throughput)
|
|
1500
1754
|
|
|
1501
1755
|
```javascript
|
|
1502
1756
|
import { Queen } from 'queen-mq';
|
|
@@ -1515,7 +1769,7 @@ await client.queue('data-processing', {
|
|
|
1515
1769
|
// Producer: Send data
|
|
1516
1770
|
async function sendData() {
|
|
1517
1771
|
const records = [];
|
|
1518
|
-
for (let i = 0; i <
|
|
1772
|
+
for (let i = 0; i < 100000; i++) {
|
|
1519
1773
|
records.push({ id: i, value: Math.random() });
|
|
1520
1774
|
}
|
|
1521
1775
|
|
|
@@ -1523,45 +1777,34 @@ async function sendData() {
|
|
|
1523
1777
|
await client.push('data-processing/analytics', records);
|
|
1524
1778
|
}
|
|
1525
1779
|
|
|
1526
|
-
// Consumer:
|
|
1780
|
+
// Consumer: HIGH PERFORMANCE batch processor using takeBatch()
|
|
1527
1781
|
async function batchProcessor() {
|
|
1528
|
-
const BATCH_SIZE =
|
|
1529
|
-
const batch = [];
|
|
1782
|
+
const BATCH_SIZE = 5000; // Large batches for 100k+ msg/s throughput
|
|
1530
1783
|
|
|
1531
|
-
|
|
1784
|
+
// takeBatch() yields arrays directly - no manual batching needed!
|
|
1785
|
+
for await (const messages of client.takeBatch('data-processing/analytics', {
|
|
1532
1786
|
batch: BATCH_SIZE,
|
|
1533
1787
|
wait: true,
|
|
1534
1788
|
timeout: 30000
|
|
1535
1789
|
})) {
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
console.log(`✓ Batch complete`);
|
|
1555
|
-
batch.length = 0;
|
|
1556
|
-
} catch (error) {
|
|
1557
|
-
console.error('Batch processing failed:', error);
|
|
1558
|
-
|
|
1559
|
-
// Mark all as failed
|
|
1560
|
-
for (const msg of batch) {
|
|
1561
|
-
await client.ack(msg, false);
|
|
1562
|
-
}
|
|
1563
|
-
batch.length = 0;
|
|
1564
|
-
}
|
|
1790
|
+
try {
|
|
1791
|
+
console.log(`Processing batch of ${messages.length} records`);
|
|
1792
|
+
|
|
1793
|
+
// Extract data
|
|
1794
|
+
const records = messages.map(m => m.data);
|
|
1795
|
+
|
|
1796
|
+
// Bulk process (single DB operation)
|
|
1797
|
+
await bulkInsertToDatabase(records);
|
|
1798
|
+
|
|
1799
|
+
// Batch acknowledge (single DB transaction!)
|
|
1800
|
+
await client.ack(messages);
|
|
1801
|
+
|
|
1802
|
+
console.log(`✓ Batch complete in single transaction`);
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
console.error('Batch processing failed:', error);
|
|
1805
|
+
|
|
1806
|
+
// Mark entire batch as failed (single transaction)
|
|
1807
|
+
await client.ack(messages, false, { error: error.message });
|
|
1565
1808
|
}
|
|
1566
1809
|
}
|
|
1567
1810
|
}
|
|
@@ -1569,6 +1812,12 @@ async function batchProcessor() {
|
|
|
1569
1812
|
// Run
|
|
1570
1813
|
await sendData();
|
|
1571
1814
|
await batchProcessor();
|
|
1815
|
+
|
|
1816
|
+
// Performance characteristics:
|
|
1817
|
+
// - Batch size 5000: ~100,000 messages/second
|
|
1818
|
+
// - Single DB transaction per batch (fetch + ack)
|
|
1819
|
+
// - Constant memory usage
|
|
1820
|
+
// - No performance degradation as queue grows
|
|
1572
1821
|
```
|
|
1573
1822
|
|
|
1574
1823
|
### Example 5: Scheduled Jobs
|
|
@@ -1886,33 +2135,34 @@ Apache License 2.0 - see [LICENSE.md](LICENSE.md) for details.
|
|
|
1886
2135
|
|
|
1887
2136
|
## 📈 Performance
|
|
1888
2137
|
|
|
1889
|
-
**Benchmarks** (PostgreSQL 16, Node.js 22):
|
|
1890
|
-
- **Throughput**:
|
|
2138
|
+
**Benchmarks** (PostgreSQL 16, Node.js 22, cursor-based consumption):
|
|
2139
|
+
- **Throughput**: 100,000+ messages/second with batch operations
|
|
1891
2140
|
- **Latency**: < 10ms for immediate pop operations
|
|
2141
|
+
- **Constant-time consumption**: O(batch_size) regardless of queue depth
|
|
1892
2142
|
- **Concurrent Connections**: 1,000+ long polling connections
|
|
1893
2143
|
- **Database**: Optimized with proper indexing and connection pooling
|
|
1894
2144
|
|
|
1895
|
-
**
|
|
2145
|
+
**Cursor-Based Architecture Benefits:**
|
|
2146
|
+
- **No performance degradation**: Consistent speed whether queue has 1K or 1B messages
|
|
2147
|
+
- **Predictable latency**: 150-200ms per batch throughout entire queue lifecycle
|
|
2148
|
+
- **Efficient batch processing**: Direct cursor access eliminates table scans
|
|
2149
|
+
- **Scalable to billions**: UUIDv7-based cursor positioning
|
|
2150
|
+
|
|
2151
|
+
**Additional Optimization Features:**
|
|
1896
2152
|
- Connection pooling with configurable size
|
|
1897
2153
|
- Resource caching for queue/partition lookups
|
|
1898
|
-
- Batch operations for bulk inserts/updates
|
|
2154
|
+
- Batch operations for bulk inserts/updates (up to 10,000 messages per batch)
|
|
1899
2155
|
- Optimized SQL queries with proper indexes
|
|
1900
2156
|
- Event-driven architecture for minimal polling overhead
|
|
1901
2157
|
- Long polling for real-time message delivery
|
|
2158
|
+
- SKIP LOCKED for lock-free concurrent consumption
|
|
1902
2159
|
|
|
1903
2160
|
---
|
|
1904
2161
|
|
|
1905
2162
|
## 🎯 Roadmap
|
|
1906
2163
|
|
|
1907
|
-
- [ ] **Horizontal Scaling**: Better support for multiple server instances
|
|
1908
2164
|
- [ ] **Message Scheduling**: Cron-like scheduling for recurring jobs
|
|
1909
|
-
- [ ] **Priority Lanes**: Dynamic priority adjustment based on load
|
|
1910
|
-
- [ ] **Metrics Export**: Prometheus/Grafana integration
|
|
1911
|
-
- [ ] **Admin API**: REST API for queue management
|
|
1912
2165
|
- [ ] **Client Libraries**: Python, Go, Java clients
|
|
1913
|
-
- [ ] **Message Tracing**: Distributed tracing integration
|
|
1914
|
-
- [ ] **Queue Templates**: Pre-configured queue patterns
|
|
1915
|
-
- [ ] **GraphQL API**: Alternative to REST API
|
|
1916
2166
|
- [ ] **Kubernetes Operator**: Native K8s support
|
|
1917
2167
|
|
|
1918
2168
|
---
|
|
@@ -1921,6 +2171,6 @@ Apache License 2.0 - see [LICENSE.md](LICENSE.md) for details.
|
|
|
1921
2171
|
|
|
1922
2172
|
**Queen Message Queue System** - Built for performance, reliability, and developer happiness 🚀
|
|
1923
2173
|
|
|
1924
|
-
Made with ❤️ by [
|
|
2174
|
+
Made with ❤️ by [Smartness](https://github.com/smartpricing)
|
|
1925
2175
|
|
|
1926
2176
|
</div>
|