queen-mq 0.1.5 ā 0.2.0
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 +467 -1775
- package/package.json +12 -14
- package/src/client/client.js +664 -5
- package/src/managers/queueManagerOptimized.js +250 -47
- package/src/routes/ack.js +3 -3
- package/src/server.js +131 -3
- package/src/test/advanced-client-tests.js +761 -0
- package/src/test/advanced-pattern-tests.js +2 -2
- package/src/test/bus-mode-tests.js +6 -6
- package/src/test/core-tests.js +1 -1
- package/src/test/edge-case-tests.js +2 -2
- package/src/test/test-new.js +36 -1
- package/src/webapp-dist/assets/Analytics-DJrE4Rz2.css +1 -0
- package/src/webapp-dist/assets/Analytics-HEMXKmfJ.js +1 -0
- package/src/webapp-dist/assets/ConfirmDialog-YEoCdE7x.js +1 -0
- package/src/webapp-dist/assets/ConsumerGroups-CC5VJywp.css +1 -0
- package/src/webapp-dist/assets/ConsumerGroups-D8Z20HhZ.js +1 -0
- package/src/webapp-dist/assets/Dashboard-BRFOz6it.css +1 -0
- package/src/webapp-dist/assets/Dashboard-Bpx9I32h.js +1 -0
- package/src/webapp-dist/assets/LoadingSpinner-BPttXcbs.js +1 -0
- package/src/webapp-dist/assets/Messages-BOfVHKma.css +1 -0
- package/src/webapp-dist/assets/Messages-EKURCbLl.js +1 -0
- package/src/webapp-dist/assets/QueueDetail-BXXA7xoZ.css +1 -0
- package/src/webapp-dist/assets/QueueDetail-Bmxg0KiA.js +1 -0
- package/src/webapp-dist/assets/Queues-CvrJkrZT.css +1 -0
- package/src/webapp-dist/assets/Queues-yUx9_3-9.js +1 -0
- package/src/webapp-dist/assets/StatusBadge-C1bBtXHh.css +1 -0
- package/src/webapp-dist/assets/StatusBadge-C6N84XmO.js +1 -0
- package/src/webapp-dist/assets/analytics-BapTekLM.js +1 -0
- package/src/webapp-dist/assets/colors-5z6Q_kUr.js +18 -0
- package/src/webapp-dist/assets/index-B7w2bKT6.css +1 -0
- package/src/webapp-dist/assets/index-CUSnwYTH.js +31 -0
- package/src/webapp-dist/assets/messages-CRV6E-xU.js +1 -0
- package/src/webapp-dist/assets/queues-BdUbxfBC.js +1 -0
- package/src/webapp-dist/assets/resources-Bs9U-ceF.js +1 -0
- package/{webapp ā src/webapp-dist}/index.html +2 -1
- package/API.md +0 -1226
- package/AUTH.md +0 -2044
- package/CHANGELOG_STATIC_SERVING.md +0 -213
- package/DEPLOYMENT.md +0 -425
- package/PROGRAMMATIC_SERVER.md +0 -239
- package/WEBAPP.md +0 -1889
- package/assets/dashboard-01.png +0 -0
- package/assets/dashboard.png +0 -0
- package/examples/batch-processing.js +0 -58
- package/examples/bus-mode.js +0 -239
- package/examples/continuous-consumer-optimized.js +0 -215
- package/examples/continuous-consumer.js +0 -159
- package/examples/continuous-producer.js +0 -343
- package/examples/mixed-mode.js +0 -277
- package/examples/multi-server-test.js +0 -305
- package/examples/programmatic-server.js +0 -58
- package/examples/server-with-frontend.js +0 -46
- package/examples/single.js +0 -64
- package/examples/smartchat-dealyed.js +0 -42
- package/examples/smartchat.js +0 -52
- package/examples/test-cache-invalidation.js +0 -119
- package/examples/test-cache-multi-server.js +0 -245
- package/examples/test-complete-client.js +0 -260
- package/examples/test-connection-recovery.js +0 -66
- package/examples/test-dashboard-api.js +0 -200
- package/examples/test-minimal-client.js +0 -112
- package/examples/test-queue-creation-policy.js +0 -137
- package/examples/test-traceid.js +0 -147
- package/server.log +0 -1
- package/test-keepalive-v2.sh +0 -22
- package/webapp/COLOR_GUIDE.md +0 -118
- package/webapp/README.md +0 -143
- package/webapp/package-lock.json +0 -3184
- package/webapp/package.json +0 -25
- package/webapp/postcss.config.js +0 -7
- package/webapp/public/assets/queen-logo-blue.svg +0 -210
- package/webapp/public/assets/queen-logo-cyan.svg +0 -210
- package/webapp/public/assets/queen-logo-indigo.svg +0 -210
- package/webapp/public/assets/queen-logo-orange.svg +0 -210
- package/webapp/public/assets/queen-logo-pink.svg +0 -210
- package/webapp/public/assets/queen-logo-purple.svg +0 -210
- package/webapp/public/assets/queen-logo-rose.svg +0 -239
- package/webapp/public/assets/queen-logo.svg +0 -263
- package/webapp/src/App.vue +0 -19
- package/webapp/src/api/analytics.js +0 -10
- package/webapp/src/api/client.js +0 -34
- package/webapp/src/api/consumers.js +0 -52
- package/webapp/src/api/health.js +0 -7
- package/webapp/src/api/messages.js +0 -26
- package/webapp/src/api/queues.js +0 -14
- package/webapp/src/api/resources.js +0 -8
- package/webapp/src/assets/styles/main.css +0 -357
- package/webapp/src/components/analytics/AnalyticsFilters.vue +0 -87
- package/webapp/src/components/analytics/AnalyticsMetrics.vue +0 -57
- package/webapp/src/components/analytics/MessageDistributionChart.vue +0 -111
- package/webapp/src/components/analytics/MessageFlowChart.vue +0 -173
- package/webapp/src/components/analytics/TimeRangeSelector.vue +0 -27
- package/webapp/src/components/analytics/TopQueuesChart.vue +0 -132
- package/webapp/src/components/common/ConfirmDialog.vue +0 -56
- package/webapp/src/components/common/LoadingSpinner.vue +0 -6
- package/webapp/src/components/common/MetricCard.vue +0 -43
- package/webapp/src/components/common/StatusBadge.vue +0 -45
- package/webapp/src/components/dashboard/MessageStatusCard.vue +0 -50
- package/webapp/src/components/dashboard/PerformanceCard.vue +0 -38
- package/webapp/src/components/dashboard/ThroughputChart.vue +0 -182
- package/webapp/src/components/dashboard/TopQueuesTable.vue +0 -53
- package/webapp/src/components/layout/AppLayout.vue +0 -110
- package/webapp/src/components/layout/AppSidebar.vue +0 -304
- package/webapp/src/components/messages/MessageDetailPanel.vue +0 -242
- package/webapp/src/components/messages/MessageFilters.vue +0 -101
- package/webapp/src/components/queue-detail/PartitionList.vue +0 -79
- package/webapp/src/components/queue-detail/PushMessageModal.vue +0 -175
- package/webapp/src/components/queue-detail/QueueConfig.vue +0 -63
- package/webapp/src/components/queue-detail/QueueDetailHeader.vue +0 -53
- package/webapp/src/components/queue-detail/RecentMessages.vue +0 -76
- package/webapp/src/components/queues/CreateQueueModal.vue +0 -193
- package/webapp/src/components/queues/QueueFilters.vue +0 -90
- package/webapp/src/composables/useApi.js +0 -34
- package/webapp/src/composables/useTheme.js +0 -36
- package/webapp/src/main.js +0 -11
- package/webapp/src/router/index.js +0 -42
- package/webapp/src/utils/colors.js +0 -96
- package/webapp/src/utils/formatters.js +0 -49
- package/webapp/src/views/Analytics.vue +0 -377
- package/webapp/src/views/ConsumerGroups.vue +0 -433
- package/webapp/src/views/Dashboard.vue +0 -418
- package/webapp/src/views/Messages.vue +0 -363
- package/webapp/src/views/QueueDetail.vue +0 -582
- package/webapp/src/views/Queues.vue +0 -496
- package/webapp/tailwind.config.js +0 -25
- package/webapp/vite.config.js +0 -10
- /package/{assets ā src/webapp-dist/assets}/queen-logo-blue.svg +0 -0
- /package/{assets ā src/webapp-dist/assets}/queen-logo-cyan.svg +0 -0
- /package/{assets ā src/webapp-dist/assets}/queen-logo-indigo.svg +0 -0
- /package/{assets ā src/webapp-dist/assets}/queen-logo-orange.svg +0 -0
- /package/{assets ā src/webapp-dist/assets}/queen-logo-pink.svg +0 -0
- /package/{assets ā src/webapp-dist/assets}/queen-logo-purple.svg +0 -0
- /package/{assets ā src/webapp-dist/assets}/queen-logo-rose.svg +0 -0
- /package/{assets ā src/webapp-dist/assets}/queen-logo.svg +0 -0
package/assets/dashboard-01.png
DELETED
|
Binary file
|
package/assets/dashboard.png
DELETED
|
Binary file
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Queen } from '../src/client/client.js';
|
|
2
|
-
|
|
3
|
-
const q = new Queen({ baseUrl: 'http://localhost:6632' });
|
|
4
|
-
|
|
5
|
-
await q.queue('demo-queue', {
|
|
6
|
-
leaseTime: 30,
|
|
7
|
-
retryLimit: 3
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
console.log('=== Example 1: take() - Individual message processing ===');
|
|
11
|
-
let count1 = 0;
|
|
12
|
-
for await (const message of q.take('demo-queue', {
|
|
13
|
-
batch: 10, // Fetch 10 at a time internally
|
|
14
|
-
limit: 25, // Process 25 total
|
|
15
|
-
wait: false
|
|
16
|
-
})) {
|
|
17
|
-
console.log(`Message ${++count1}:`, message.data);
|
|
18
|
-
await q.ack(message);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
console.log('\n=== Example 2: takeBatch() - Batch processing ===');
|
|
22
|
-
let count2 = 0;
|
|
23
|
-
for await (const messages of q.takeBatch('demo-queue', {
|
|
24
|
-
batch: 10, // Fetch 10 at a time
|
|
25
|
-
limit: 25, // Process 25 total (across batches)
|
|
26
|
-
wait: false
|
|
27
|
-
})) {
|
|
28
|
-
console.log(`Batch ${++count2}: ${messages.length} messages`);
|
|
29
|
-
|
|
30
|
-
// Process entire batch
|
|
31
|
-
for (const msg of messages) {
|
|
32
|
-
console.log(` - ${msg.data}`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Acknowledge entire batch at once
|
|
36
|
-
await q.ack(messages);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
console.log('\n=== Example 3: High-throughput batch processing ===');
|
|
40
|
-
let totalProcessed = 0;
|
|
41
|
-
const startTime = Date.now();
|
|
42
|
-
|
|
43
|
-
for await (const messages of q.takeBatch('demo-queue', {
|
|
44
|
-
batch: 1000, // Large batches for throughput
|
|
45
|
-
wait: true, // Long polling
|
|
46
|
-
idleTimeout: 5000 // Exit after 5s of no messages
|
|
47
|
-
})) {
|
|
48
|
-
totalProcessed += messages.length;
|
|
49
|
-
await q.ack(messages);
|
|
50
|
-
|
|
51
|
-
const rate = totalProcessed / ((Date.now() - startTime) / 1000);
|
|
52
|
-
console.log(`Processed ${messages.length} | Total: ${totalProcessed} | Rate: ${rate.toFixed(0)} msg/s`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log(`\nCompleted: ${totalProcessed} messages`);
|
|
56
|
-
|
|
57
|
-
await q.close();
|
|
58
|
-
|
package/examples/bus-mode.js
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Bus Mode Example for Queen V3
|
|
5
|
-
*
|
|
6
|
-
* This example demonstrates the new bus/pub-sub functionality where
|
|
7
|
-
* multiple consumer groups can receive ALL messages independently,
|
|
8
|
-
* similar to Kafka's consumer groups.
|
|
9
|
-
*
|
|
10
|
-
* Each consumer group maintains its own progress through the message stream.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { createQueenClient } from '../src/client/index.js';
|
|
14
|
-
|
|
15
|
-
async function main() {
|
|
16
|
-
const client = createQueenClient({
|
|
17
|
-
baseUrl: process.env.QUEEN_URL || 'http://localhost:6632',
|
|
18
|
-
timeout: 35000
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const queue = 'events';
|
|
22
|
-
|
|
23
|
-
console.log('š Starting Bus Mode Example');
|
|
24
|
-
console.log('š¦ Queue:', queue);
|
|
25
|
-
console.log('');
|
|
26
|
-
console.log('This example will:');
|
|
27
|
-
console.log('1. Push some test messages to the queue');
|
|
28
|
-
console.log('2. Start 3 different consumer groups');
|
|
29
|
-
console.log('3. Show how each group receives ALL messages independently');
|
|
30
|
-
console.log('');
|
|
31
|
-
|
|
32
|
-
// First, push some test messages
|
|
33
|
-
console.log('š¤ Pushing test messages...');
|
|
34
|
-
const testMessages = [];
|
|
35
|
-
for (let i = 1; i <= 5; i++) {
|
|
36
|
-
testMessages.push({
|
|
37
|
-
queue,
|
|
38
|
-
payload: {
|
|
39
|
-
id: i,
|
|
40
|
-
type: 'test-event',
|
|
41
|
-
timestamp: new Date().toISOString(),
|
|
42
|
-
data: `Message ${i}`
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
await client.push({ items: testMessages });
|
|
48
|
-
console.log(`ā
Pushed ${testMessages.length} messages\n`);
|
|
49
|
-
|
|
50
|
-
// Track messages received by each consumer group
|
|
51
|
-
const received = {
|
|
52
|
-
analytics: [],
|
|
53
|
-
notifications: [],
|
|
54
|
-
audit: []
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// Consumer Group 1: Analytics Service
|
|
58
|
-
console.log('šµ Starting Analytics Consumer Group...');
|
|
59
|
-
const analyticsConsumer = client.consume({
|
|
60
|
-
queue,
|
|
61
|
-
consumerGroup: 'analytics-service',
|
|
62
|
-
handler: async (message) => {
|
|
63
|
-
console.log(` [Analytics] Received message ${message.data.id}: ${message.data.data}`);
|
|
64
|
-
received.analytics.push(message.data.id);
|
|
65
|
-
|
|
66
|
-
// Simulate processing
|
|
67
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
68
|
-
},
|
|
69
|
-
options: {
|
|
70
|
-
batch: 2,
|
|
71
|
-
wait: false // Don't wait for new messages in this example
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// Consumer Group 2: Notification Service
|
|
76
|
-
console.log('š¢ Starting Notifications Consumer Group...');
|
|
77
|
-
const notificationConsumer = client.consume({
|
|
78
|
-
queue,
|
|
79
|
-
consumerGroup: 'notification-service',
|
|
80
|
-
handler: async (message) => {
|
|
81
|
-
console.log(` [Notifications] Received message ${message.data.id}: ${message.data.data}`);
|
|
82
|
-
received.notifications.push(message.data.id);
|
|
83
|
-
|
|
84
|
-
// Simulate processing
|
|
85
|
-
await new Promise(resolve => setTimeout(resolve, 150));
|
|
86
|
-
},
|
|
87
|
-
options: {
|
|
88
|
-
batch: 1,
|
|
89
|
-
wait: false
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Consumer Group 3: Audit Log Service
|
|
94
|
-
console.log('š” Starting Audit Log Consumer Group...');
|
|
95
|
-
const auditConsumer = client.consume({
|
|
96
|
-
queue,
|
|
97
|
-
consumerGroup: 'audit-log',
|
|
98
|
-
handler: async (message) => {
|
|
99
|
-
console.log(` [Audit] Received message ${message.data.id}: ${message.data.data}`);
|
|
100
|
-
received.audit.push(message.data.id);
|
|
101
|
-
|
|
102
|
-
// Simulate processing
|
|
103
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
104
|
-
},
|
|
105
|
-
options: {
|
|
106
|
-
batch: 3,
|
|
107
|
-
wait: false
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
// Wait for all messages to be processed
|
|
112
|
-
console.log('\nā³ Processing messages...\n');
|
|
113
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
114
|
-
|
|
115
|
-
// Stop all consumers
|
|
116
|
-
analyticsConsumer();
|
|
117
|
-
notificationConsumer();
|
|
118
|
-
auditConsumer();
|
|
119
|
-
|
|
120
|
-
// Display results
|
|
121
|
-
console.log('\nš Results:');
|
|
122
|
-
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
123
|
-
console.log(`Analytics Service received: [${received.analytics.sort().join(', ')}]`);
|
|
124
|
-
console.log(`Notification Service received: [${received.notifications.sort().join(', ')}]`);
|
|
125
|
-
console.log(`Audit Log Service received: [${received.audit.sort().join(', ')}]`);
|
|
126
|
-
console.log('');
|
|
127
|
-
|
|
128
|
-
// Verify all groups received all messages
|
|
129
|
-
const allReceived =
|
|
130
|
-
received.analytics.length === testMessages.length &&
|
|
131
|
-
received.notifications.length === testMessages.length &&
|
|
132
|
-
received.audit.length === testMessages.length;
|
|
133
|
-
|
|
134
|
-
if (allReceived) {
|
|
135
|
-
console.log('ā
SUCCESS: All consumer groups received ALL messages!');
|
|
136
|
-
console.log(' This demonstrates bus/pub-sub mode where each consumer group');
|
|
137
|
-
console.log(' gets its own copy of every message, independently.');
|
|
138
|
-
} else {
|
|
139
|
-
console.log('ā ļø Some messages were not received by all groups.');
|
|
140
|
-
console.log(' This might be due to timing. Try running again.');
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
console.log('\nšÆ Key Differences from Queue Mode:');
|
|
144
|
-
console.log(' ⢠In Queue Mode: Messages are consumed by ONE worker');
|
|
145
|
-
console.log(' ⢠In Bus Mode: Messages are consumed by ALL consumer groups');
|
|
146
|
-
console.log(' ⢠Each group maintains its own progress/offset');
|
|
147
|
-
console.log(' ⢠Perfect for: analytics, notifications, audit logs, etc.');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Demonstrate subscription modes
|
|
151
|
-
async function demonstrateSubscriptionModes() {
|
|
152
|
-
const client = createQueenClient({
|
|
153
|
-
baseUrl: process.env.QUEEN_URL || 'http://localhost:6632'
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
const queue = 'subscription-test';
|
|
157
|
-
|
|
158
|
-
console.log('\n\nš
Demonstrating Subscription Modes');
|
|
159
|
-
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
160
|
-
|
|
161
|
-
// Push some historical messages
|
|
162
|
-
console.log('š¤ Pushing historical messages...');
|
|
163
|
-
for (let i = 1; i <= 3; i++) {
|
|
164
|
-
await client.push({
|
|
165
|
-
items: [{
|
|
166
|
-
queue,
|
|
167
|
-
payload: { id: i, type: 'historical', data: `Historical message ${i}` }
|
|
168
|
-
}]
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
console.log('ā
Historical messages pushed\n');
|
|
173
|
-
|
|
174
|
-
// Consumer 1: Consume all messages (default)
|
|
175
|
-
console.log('šµ Consumer Group "all-messages" (default mode):');
|
|
176
|
-
const allMessages = [];
|
|
177
|
-
const consumer1 = client.consume({
|
|
178
|
-
queue,
|
|
179
|
-
consumerGroup: 'all-messages',
|
|
180
|
-
handler: async (msg) => {
|
|
181
|
-
console.log(` Received: ${msg.data.type} - ${msg.data.data}`);
|
|
182
|
-
allMessages.push(msg.data.id);
|
|
183
|
-
},
|
|
184
|
-
options: { wait: false }
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
188
|
-
consumer1();
|
|
189
|
-
|
|
190
|
-
// Consumer 2: Only new messages from subscription time
|
|
191
|
-
console.log('\nš¢ Consumer Group "new-only" (subscriptionMode: "new"):');
|
|
192
|
-
const newOnly = [];
|
|
193
|
-
const consumer2 = client.consume({
|
|
194
|
-
queue,
|
|
195
|
-
consumerGroup: 'new-only',
|
|
196
|
-
handler: async (msg) => {
|
|
197
|
-
console.log(` Received: ${msg.data.type} - ${msg.data.data}`);
|
|
198
|
-
newOnly.push(msg.data.id);
|
|
199
|
-
},
|
|
200
|
-
options: {
|
|
201
|
-
wait: false,
|
|
202
|
-
subscriptionMode: 'new'
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Push new messages after subscription
|
|
207
|
-
console.log('\nš¤ Pushing new messages...');
|
|
208
|
-
for (let i = 4; i <= 6; i++) {
|
|
209
|
-
await client.push({
|
|
210
|
-
items: [{
|
|
211
|
-
queue,
|
|
212
|
-
payload: { id: i, type: 'new', data: `New message ${i}` }
|
|
213
|
-
}]
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
218
|
-
consumer2();
|
|
219
|
-
|
|
220
|
-
// Display results
|
|
221
|
-
console.log('\nš Subscription Mode Results:');
|
|
222
|
-
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
223
|
-
console.log(`"all-messages" group received: [${allMessages.sort().join(', ')}] (all 6 messages)`);
|
|
224
|
-
console.log(`"new-only" group received: [${newOnly.sort().join(', ')}] (only new messages)`);
|
|
225
|
-
console.log('');
|
|
226
|
-
console.log('ā
This demonstrates how consumer groups can choose when to start consuming!');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Run the examples
|
|
230
|
-
main()
|
|
231
|
-
.then(() => demonstrateSubscriptionModes())
|
|
232
|
-
.then(() => {
|
|
233
|
-
console.log('\n⨠Bus mode examples completed!');
|
|
234
|
-
process.exit(0);
|
|
235
|
-
})
|
|
236
|
-
.catch(error => {
|
|
237
|
-
console.error('ā Error:', error);
|
|
238
|
-
process.exit(1);
|
|
239
|
-
});
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Optimized Continuous Consumer Example
|
|
5
|
-
*
|
|
6
|
-
* This optimized version processes messages in parallel batches
|
|
7
|
-
* with batch acknowledgments for maximum throughput.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { createQueenClient } from '../src/client/index.js';
|
|
11
|
-
|
|
12
|
-
async function main() {
|
|
13
|
-
// Create client with configuration
|
|
14
|
-
const client = createQueenClient({
|
|
15
|
-
baseUrl: process.env.QUEEN_URL || 'http://localhost:6632',
|
|
16
|
-
timeout: 35000 // Client timeout for non-polling requests
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const namespace = process.env.NS || 'smartchat';
|
|
20
|
-
const task = process.env.TASK || 'translations';
|
|
21
|
-
const queue = process.env.QUEUE || 'email';
|
|
22
|
-
|
|
23
|
-
// Optimized settings for high throughput
|
|
24
|
-
const batchSize = parseInt(process.env.BATCH_SIZE) || 50; // Reasonable batch size
|
|
25
|
-
const parallelWorkers = parseInt(process.env.WORKERS) || 10; // Process in parallel
|
|
26
|
-
const pollTimeout = parseInt(process.env.POLL_TIMEOUT) || 5000; // Shorter timeout for faster response
|
|
27
|
-
|
|
28
|
-
console.log('š Starting OPTIMIZED continuous consumer');
|
|
29
|
-
console.log(`š¦ Queue: ${namespace}/${task}/${queue}`);
|
|
30
|
-
console.log('ā” Performance settings:');
|
|
31
|
-
console.log(` - Batch size: ${batchSize}`);
|
|
32
|
-
console.log(` - Parallel workers: ${parallelWorkers}`);
|
|
33
|
-
console.log(` - Poll timeout: ${pollTimeout}ms`);
|
|
34
|
-
console.log('');
|
|
35
|
-
|
|
36
|
-
let stats = {
|
|
37
|
-
messages: 0,
|
|
38
|
-
errors: 0,
|
|
39
|
-
startTime: Date.now(),
|
|
40
|
-
lastMessageTime: null,
|
|
41
|
-
processingTimes: []
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Process message function
|
|
45
|
-
async function processMessage(message) {
|
|
46
|
-
// Your actual processing logic here
|
|
47
|
-
// For demo, just simulate minimal work
|
|
48
|
-
return new Promise(resolve => setImmediate(resolve));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Optimized consume function with parallel processing
|
|
52
|
-
async function consumeOptimized() {
|
|
53
|
-
let running = true;
|
|
54
|
-
const activeWorkers = new Set();
|
|
55
|
-
|
|
56
|
-
// Handle shutdown
|
|
57
|
-
process.on('SIGINT', () => {
|
|
58
|
-
console.log('\n\nš Shutting down consumer...');
|
|
59
|
-
running = false;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
while (running) {
|
|
63
|
-
try {
|
|
64
|
-
// Wait if we have too many active workers
|
|
65
|
-
while (activeWorkers.size >= parallelWorkers && running) {
|
|
66
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!running) break;
|
|
70
|
-
|
|
71
|
-
// Start a new worker
|
|
72
|
-
const worker = (async () => {
|
|
73
|
-
try {
|
|
74
|
-
// Pop messages with optimized settings
|
|
75
|
-
const result = await client.pop({
|
|
76
|
-
ns: namespace,
|
|
77
|
-
task: task,
|
|
78
|
-
queue: queue,
|
|
79
|
-
wait: true,
|
|
80
|
-
timeout: pollTimeout,
|
|
81
|
-
batch: batchSize
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (result.messages && result.messages.length > 0) {
|
|
85
|
-
const startTime = Date.now();
|
|
86
|
-
|
|
87
|
-
// Process messages in parallel within the batch
|
|
88
|
-
const processPromises = result.messages.map(async (message) => {
|
|
89
|
-
try {
|
|
90
|
-
await processMessage(message);
|
|
91
|
-
stats.messages++;
|
|
92
|
-
stats.lastMessageTime = Date.now();
|
|
93
|
-
return { transactionId: message.transactionId, status: 'completed' };
|
|
94
|
-
} catch (error) {
|
|
95
|
-
stats.errors++;
|
|
96
|
-
console.error(`Error processing message ${message.id}:`, error.message);
|
|
97
|
-
return { transactionId: message.transactionId, status: 'failed', error: error.message };
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Wait for all messages in batch to process
|
|
102
|
-
const acknowledgments = await Promise.all(processPromises);
|
|
103
|
-
|
|
104
|
-
// Batch acknowledge all messages at once
|
|
105
|
-
try {
|
|
106
|
-
await client.ackBatch(acknowledgments);
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error('Batch ACK failed:', error);
|
|
109
|
-
// Fall back to individual ACKs if batch fails
|
|
110
|
-
for (const ack of acknowledgments) {
|
|
111
|
-
try {
|
|
112
|
-
await client.ack(ack.transactionId, ack.status, ack.error);
|
|
113
|
-
} catch (e) {
|
|
114
|
-
console.error(`Individual ACK failed for ${ack.transactionId}:`, e.message);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const processingTime = Date.now() - startTime;
|
|
120
|
-
stats.processingTimes.push(processingTime);
|
|
121
|
-
|
|
122
|
-
// Keep only last 100 processing times for average calculation
|
|
123
|
-
if (stats.processingTimes.length > 100) {
|
|
124
|
-
stats.processingTimes.shift();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
console.log(`ā
Processed batch of ${result.messages.length} messages in ${processingTime}ms`);
|
|
128
|
-
}
|
|
129
|
-
} catch (error) {
|
|
130
|
-
if (error.name !== 'AbortError') {
|
|
131
|
-
console.error('Worker error:', error);
|
|
132
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
133
|
-
}
|
|
134
|
-
} finally {
|
|
135
|
-
activeWorkers.delete(worker);
|
|
136
|
-
}
|
|
137
|
-
})();
|
|
138
|
-
|
|
139
|
-
activeWorkers.add(worker);
|
|
140
|
-
} catch (error) {
|
|
141
|
-
console.error('Main loop error:', error);
|
|
142
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Wait for all workers to complete
|
|
147
|
-
await Promise.all(Array.from(activeWorkers));
|
|
148
|
-
|
|
149
|
-
// Final stats
|
|
150
|
-
const uptime = Math.floor((Date.now() - stats.startTime) / 1000);
|
|
151
|
-
const avgProcessingTime = stats.processingTimes.length > 0
|
|
152
|
-
? stats.processingTimes.reduce((a, b) => a + b, 0) / stats.processingTimes.length
|
|
153
|
-
: 0;
|
|
154
|
-
|
|
155
|
-
console.log('\nš Final Statistics:');
|
|
156
|
-
console.log(` Total runtime: ${formatUptime(uptime)}`);
|
|
157
|
-
console.log(` Messages processed: ${stats.messages}`);
|
|
158
|
-
console.log(` Throughput: ${(stats.messages / uptime).toFixed(2)} msg/s`);
|
|
159
|
-
console.log(` Average batch processing time: ${avgProcessingTime.toFixed(2)}ms`);
|
|
160
|
-
console.log(` Errors encountered: ${stats.errors}`);
|
|
161
|
-
|
|
162
|
-
process.exit(0);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Print statistics every 10 seconds
|
|
166
|
-
const statsInterval = setInterval(() => {
|
|
167
|
-
if (stats.messages === 0) return;
|
|
168
|
-
|
|
169
|
-
const uptime = Math.floor((Date.now() - stats.startTime) / 1000);
|
|
170
|
-
const throughput = stats.messages / uptime;
|
|
171
|
-
const avgProcessingTime = stats.processingTimes.length > 0
|
|
172
|
-
? stats.processingTimes.reduce((a, b) => a + b, 0) / stats.processingTimes.length
|
|
173
|
-
: 0;
|
|
174
|
-
|
|
175
|
-
console.log('\nš Statistics:');
|
|
176
|
-
console.log(` Uptime: ${formatUptime(uptime)}`);
|
|
177
|
-
console.log(` Messages processed: ${stats.messages}`);
|
|
178
|
-
console.log(` Throughput: ${throughput.toFixed(2)} msg/s`);
|
|
179
|
-
console.log(` Average batch processing: ${avgProcessingTime.toFixed(2)}ms`);
|
|
180
|
-
console.log(` Errors: ${stats.errors}`);
|
|
181
|
-
|
|
182
|
-
if (stats.lastMessageTime) {
|
|
183
|
-
const idle = Math.floor((Date.now() - stats.lastMessageTime) / 1000);
|
|
184
|
-
if (idle > 2) {
|
|
185
|
-
console.log(` ā ļø Idle time: ${idle}s`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
console.log('');
|
|
189
|
-
}, 10000);
|
|
190
|
-
|
|
191
|
-
console.log('ā
Consumer started. Press Ctrl+C to stop.\n');
|
|
192
|
-
|
|
193
|
-
// Start consuming
|
|
194
|
-
await consumeOptimized();
|
|
195
|
-
clearInterval(statsInterval);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function formatUptime(seconds) {
|
|
199
|
-
const hours = Math.floor(seconds / 3600);
|
|
200
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
201
|
-
const secs = seconds % 60;
|
|
202
|
-
|
|
203
|
-
const parts = [];
|
|
204
|
-
if (hours > 0) parts.push(`${hours}h`);
|
|
205
|
-
if (minutes > 0) parts.push(`${minutes}m`);
|
|
206
|
-
parts.push(`${secs}s`);
|
|
207
|
-
|
|
208
|
-
return parts.join(' ');
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Run the consumer
|
|
212
|
-
main().catch(error => {
|
|
213
|
-
console.error('ā Fatal error:', error);
|
|
214
|
-
process.exit(1);
|
|
215
|
-
});
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Continuous Consumer Example for Queen V2
|
|
5
|
-
*
|
|
6
|
-
* This example demonstrates how the Queen client performs continuous
|
|
7
|
-
* long polling without delays when timeouts occur. The consumer will
|
|
8
|
-
* immediately start a new request when the previous one times out,
|
|
9
|
-
* ensuring no messages are missed and minimizing latency.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { createQueenClient } from '../src/client/index.js';
|
|
13
|
-
|
|
14
|
-
async function main() {
|
|
15
|
-
// Create client with configuration
|
|
16
|
-
const client = createQueenClient({
|
|
17
|
-
baseUrl: process.env.QUEEN_URL || 'http://localhost:6632',
|
|
18
|
-
timeout: 35000 // Client timeout for non-polling requests
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// V2: Using queue and optional partition
|
|
22
|
-
const queue = process.env.QUEUE || 'email-translations';
|
|
23
|
-
const partition = process.env.PARTITION; // Optional: if not set, consumes from any partition
|
|
24
|
-
const namespace = process.env.NAMESPACE; // Optional: for filtering
|
|
25
|
-
const task = process.env.TASK; // Optional: for filtering
|
|
26
|
-
|
|
27
|
-
console.log('š Starting continuous consumer (V2)');
|
|
28
|
-
if (queue) {
|
|
29
|
-
console.log(`š¦ Queue: ${queue}${partition ? '/' + partition : ' (any partition)'}`);
|
|
30
|
-
} else if (namespace || task) {
|
|
31
|
-
console.log(`š Filtering by: ${namespace ? 'namespace=' + namespace : ''} ${task ? 'task=' + task : ''}`);
|
|
32
|
-
}
|
|
33
|
-
console.log('ā±ļø Long polling timeout: 30 seconds');
|
|
34
|
-
console.log('š Will immediately retry on timeout (no delays)\n');
|
|
35
|
-
|
|
36
|
-
let stats = {
|
|
37
|
-
messages: 0,
|
|
38
|
-
errors: 0,
|
|
39
|
-
startTime: Date.now(),
|
|
40
|
-
lastMessageTime: null,
|
|
41
|
-
partitionCounts: {}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Start the consumer
|
|
45
|
-
const stop = client.consume({
|
|
46
|
-
queue: queue,
|
|
47
|
-
partition: partition,
|
|
48
|
-
namespace: namespace,
|
|
49
|
-
task: task,
|
|
50
|
-
handler: async (message) => {
|
|
51
|
-
stats.messages++;
|
|
52
|
-
stats.lastMessageTime = Date.now();
|
|
53
|
-
|
|
54
|
-
// Track messages per partition
|
|
55
|
-
const partitionKey = `${message.queue}/${message.partition}`;
|
|
56
|
-
stats.partitionCounts[partitionKey] = (stats.partitionCounts[partitionKey] || 0) + 1;
|
|
57
|
-
|
|
58
|
-
console.log(`āļø [${new Date().toISOString()}] Message received:`, {
|
|
59
|
-
queue: message.queue,
|
|
60
|
-
partition: message.partition,
|
|
61
|
-
transactionId: message.transactionId,
|
|
62
|
-
retryCount: message.retryCount,
|
|
63
|
-
data: message.data
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Simulate processing
|
|
67
|
-
await processMessage(message);
|
|
68
|
-
},
|
|
69
|
-
options: {
|
|
70
|
-
wait: true, // Enable long polling
|
|
71
|
-
timeout: 30000, // 30 second server timeout
|
|
72
|
-
batch: 10, // Process up to 10 messages at once
|
|
73
|
-
stopOnError: false // Continue processing on errors
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Print statistics every minute
|
|
78
|
-
const statsInterval = setInterval(() => {
|
|
79
|
-
const uptime = Math.floor((Date.now() - stats.startTime) / 1000);
|
|
80
|
-
const rate = stats.messages / (uptime / 60);
|
|
81
|
-
|
|
82
|
-
console.log('\nš Statistics:');
|
|
83
|
-
console.log(` Uptime: ${formatUptime(uptime)}`);
|
|
84
|
-
console.log(` Messages processed: ${stats.messages}`);
|
|
85
|
-
console.log(` Processing rate: ${rate.toFixed(2)} msg/min`);
|
|
86
|
-
console.log(` Errors: ${stats.errors}`);
|
|
87
|
-
|
|
88
|
-
// Show partition distribution
|
|
89
|
-
if (Object.keys(stats.partitionCounts).length > 0) {
|
|
90
|
-
console.log(' Distribution by partition:');
|
|
91
|
-
for (const [partition, count] of Object.entries(stats.partitionCounts)) {
|
|
92
|
-
const percentage = ((count / stats.messages) * 100).toFixed(1);
|
|
93
|
-
console.log(` ${partition}: ${count} messages (${percentage}%)`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (stats.lastMessageTime) {
|
|
98
|
-
const idle = Math.floor((Date.now() - stats.lastMessageTime) / 1000);
|
|
99
|
-
console.log(` Idle time: ${idle}s`);
|
|
100
|
-
}
|
|
101
|
-
console.log('');
|
|
102
|
-
}, 60000);
|
|
103
|
-
|
|
104
|
-
// Handle shutdown
|
|
105
|
-
process.on('SIGINT', () => {
|
|
106
|
-
console.log('\n\nš Shutting down consumer...');
|
|
107
|
-
|
|
108
|
-
clearInterval(statsInterval);
|
|
109
|
-
stop();
|
|
110
|
-
|
|
111
|
-
const uptime = Math.floor((Date.now() - stats.startTime) / 1000);
|
|
112
|
-
console.log('\nš Final Statistics:');
|
|
113
|
-
console.log(` Total runtime: ${formatUptime(uptime)}`);
|
|
114
|
-
console.log(` Messages processed: ${stats.messages}`);
|
|
115
|
-
console.log(` Errors encountered: ${stats.errors}`);
|
|
116
|
-
|
|
117
|
-
if (Object.keys(stats.partitionCounts).length > 0) {
|
|
118
|
-
console.log(' Final partition distribution:');
|
|
119
|
-
for (const [partition, count] of Object.entries(stats.partitionCounts)) {
|
|
120
|
-
const percentage = ((count / stats.messages) * 100).toFixed(1);
|
|
121
|
-
console.log(` ${partition}: ${count} messages (${percentage}%)`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
process.exit(0);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
console.log('ā
Consumer started. Press Ctrl+C to stop.\n');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async function processMessage(message) {
|
|
132
|
-
// Simulate some processing work
|
|
133
|
-
const processingTime = Math.random() * 1000 + 500; // 0.5-1.5 seconds
|
|
134
|
-
await new Promise(resolve => setTimeout(resolve, processingTime));
|
|
135
|
-
|
|
136
|
-
// Randomly simulate occasional processing errors (5% chance)
|
|
137
|
-
if (Math.random() < 0.05) {
|
|
138
|
-
throw new Error('Simulated processing error');
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function formatUptime(seconds) {
|
|
143
|
-
const hours = Math.floor(seconds / 3600);
|
|
144
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
145
|
-
const secs = seconds % 60;
|
|
146
|
-
|
|
147
|
-
const parts = [];
|
|
148
|
-
if (hours > 0) parts.push(`${hours}h`);
|
|
149
|
-
if (minutes > 0) parts.push(`${minutes}m`);
|
|
150
|
-
parts.push(`${secs}s`);
|
|
151
|
-
|
|
152
|
-
return parts.join(' ');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Run the consumer
|
|
156
|
-
main().catch(error => {
|
|
157
|
-
console.error('ā Fatal error:', error);
|
|
158
|
-
process.exit(1);
|
|
159
|
-
});
|