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.
Files changed (135) hide show
  1. package/README.md +467 -1775
  2. package/package.json +12 -14
  3. package/src/client/client.js +664 -5
  4. package/src/managers/queueManagerOptimized.js +250 -47
  5. package/src/routes/ack.js +3 -3
  6. package/src/server.js +131 -3
  7. package/src/test/advanced-client-tests.js +761 -0
  8. package/src/test/advanced-pattern-tests.js +2 -2
  9. package/src/test/bus-mode-tests.js +6 -6
  10. package/src/test/core-tests.js +1 -1
  11. package/src/test/edge-case-tests.js +2 -2
  12. package/src/test/test-new.js +36 -1
  13. package/src/webapp-dist/assets/Analytics-DJrE4Rz2.css +1 -0
  14. package/src/webapp-dist/assets/Analytics-HEMXKmfJ.js +1 -0
  15. package/src/webapp-dist/assets/ConfirmDialog-YEoCdE7x.js +1 -0
  16. package/src/webapp-dist/assets/ConsumerGroups-CC5VJywp.css +1 -0
  17. package/src/webapp-dist/assets/ConsumerGroups-D8Z20HhZ.js +1 -0
  18. package/src/webapp-dist/assets/Dashboard-BRFOz6it.css +1 -0
  19. package/src/webapp-dist/assets/Dashboard-Bpx9I32h.js +1 -0
  20. package/src/webapp-dist/assets/LoadingSpinner-BPttXcbs.js +1 -0
  21. package/src/webapp-dist/assets/Messages-BOfVHKma.css +1 -0
  22. package/src/webapp-dist/assets/Messages-EKURCbLl.js +1 -0
  23. package/src/webapp-dist/assets/QueueDetail-BXXA7xoZ.css +1 -0
  24. package/src/webapp-dist/assets/QueueDetail-Bmxg0KiA.js +1 -0
  25. package/src/webapp-dist/assets/Queues-CvrJkrZT.css +1 -0
  26. package/src/webapp-dist/assets/Queues-yUx9_3-9.js +1 -0
  27. package/src/webapp-dist/assets/StatusBadge-C1bBtXHh.css +1 -0
  28. package/src/webapp-dist/assets/StatusBadge-C6N84XmO.js +1 -0
  29. package/src/webapp-dist/assets/analytics-BapTekLM.js +1 -0
  30. package/src/webapp-dist/assets/colors-5z6Q_kUr.js +18 -0
  31. package/src/webapp-dist/assets/index-B7w2bKT6.css +1 -0
  32. package/src/webapp-dist/assets/index-CUSnwYTH.js +31 -0
  33. package/src/webapp-dist/assets/messages-CRV6E-xU.js +1 -0
  34. package/src/webapp-dist/assets/queues-BdUbxfBC.js +1 -0
  35. package/src/webapp-dist/assets/resources-Bs9U-ceF.js +1 -0
  36. package/{webapp → src/webapp-dist}/index.html +2 -1
  37. package/API.md +0 -1226
  38. package/AUTH.md +0 -2044
  39. package/CHANGELOG_STATIC_SERVING.md +0 -213
  40. package/DEPLOYMENT.md +0 -425
  41. package/PROGRAMMATIC_SERVER.md +0 -239
  42. package/WEBAPP.md +0 -1889
  43. package/assets/dashboard-01.png +0 -0
  44. package/assets/dashboard.png +0 -0
  45. package/examples/batch-processing.js +0 -58
  46. package/examples/bus-mode.js +0 -239
  47. package/examples/continuous-consumer-optimized.js +0 -215
  48. package/examples/continuous-consumer.js +0 -159
  49. package/examples/continuous-producer.js +0 -343
  50. package/examples/mixed-mode.js +0 -277
  51. package/examples/multi-server-test.js +0 -305
  52. package/examples/programmatic-server.js +0 -58
  53. package/examples/server-with-frontend.js +0 -46
  54. package/examples/single.js +0 -64
  55. package/examples/smartchat-dealyed.js +0 -42
  56. package/examples/smartchat.js +0 -52
  57. package/examples/test-cache-invalidation.js +0 -119
  58. package/examples/test-cache-multi-server.js +0 -245
  59. package/examples/test-complete-client.js +0 -260
  60. package/examples/test-connection-recovery.js +0 -66
  61. package/examples/test-dashboard-api.js +0 -200
  62. package/examples/test-minimal-client.js +0 -112
  63. package/examples/test-queue-creation-policy.js +0 -137
  64. package/examples/test-traceid.js +0 -147
  65. package/server.log +0 -1
  66. package/test-keepalive-v2.sh +0 -22
  67. package/webapp/COLOR_GUIDE.md +0 -118
  68. package/webapp/README.md +0 -143
  69. package/webapp/package-lock.json +0 -3184
  70. package/webapp/package.json +0 -25
  71. package/webapp/postcss.config.js +0 -7
  72. package/webapp/public/assets/queen-logo-blue.svg +0 -210
  73. package/webapp/public/assets/queen-logo-cyan.svg +0 -210
  74. package/webapp/public/assets/queen-logo-indigo.svg +0 -210
  75. package/webapp/public/assets/queen-logo-orange.svg +0 -210
  76. package/webapp/public/assets/queen-logo-pink.svg +0 -210
  77. package/webapp/public/assets/queen-logo-purple.svg +0 -210
  78. package/webapp/public/assets/queen-logo-rose.svg +0 -239
  79. package/webapp/public/assets/queen-logo.svg +0 -263
  80. package/webapp/src/App.vue +0 -19
  81. package/webapp/src/api/analytics.js +0 -10
  82. package/webapp/src/api/client.js +0 -34
  83. package/webapp/src/api/consumers.js +0 -52
  84. package/webapp/src/api/health.js +0 -7
  85. package/webapp/src/api/messages.js +0 -26
  86. package/webapp/src/api/queues.js +0 -14
  87. package/webapp/src/api/resources.js +0 -8
  88. package/webapp/src/assets/styles/main.css +0 -357
  89. package/webapp/src/components/analytics/AnalyticsFilters.vue +0 -87
  90. package/webapp/src/components/analytics/AnalyticsMetrics.vue +0 -57
  91. package/webapp/src/components/analytics/MessageDistributionChart.vue +0 -111
  92. package/webapp/src/components/analytics/MessageFlowChart.vue +0 -173
  93. package/webapp/src/components/analytics/TimeRangeSelector.vue +0 -27
  94. package/webapp/src/components/analytics/TopQueuesChart.vue +0 -132
  95. package/webapp/src/components/common/ConfirmDialog.vue +0 -56
  96. package/webapp/src/components/common/LoadingSpinner.vue +0 -6
  97. package/webapp/src/components/common/MetricCard.vue +0 -43
  98. package/webapp/src/components/common/StatusBadge.vue +0 -45
  99. package/webapp/src/components/dashboard/MessageStatusCard.vue +0 -50
  100. package/webapp/src/components/dashboard/PerformanceCard.vue +0 -38
  101. package/webapp/src/components/dashboard/ThroughputChart.vue +0 -182
  102. package/webapp/src/components/dashboard/TopQueuesTable.vue +0 -53
  103. package/webapp/src/components/layout/AppLayout.vue +0 -110
  104. package/webapp/src/components/layout/AppSidebar.vue +0 -304
  105. package/webapp/src/components/messages/MessageDetailPanel.vue +0 -242
  106. package/webapp/src/components/messages/MessageFilters.vue +0 -101
  107. package/webapp/src/components/queue-detail/PartitionList.vue +0 -79
  108. package/webapp/src/components/queue-detail/PushMessageModal.vue +0 -175
  109. package/webapp/src/components/queue-detail/QueueConfig.vue +0 -63
  110. package/webapp/src/components/queue-detail/QueueDetailHeader.vue +0 -53
  111. package/webapp/src/components/queue-detail/RecentMessages.vue +0 -76
  112. package/webapp/src/components/queues/CreateQueueModal.vue +0 -193
  113. package/webapp/src/components/queues/QueueFilters.vue +0 -90
  114. package/webapp/src/composables/useApi.js +0 -34
  115. package/webapp/src/composables/useTheme.js +0 -36
  116. package/webapp/src/main.js +0 -11
  117. package/webapp/src/router/index.js +0 -42
  118. package/webapp/src/utils/colors.js +0 -96
  119. package/webapp/src/utils/formatters.js +0 -49
  120. package/webapp/src/views/Analytics.vue +0 -377
  121. package/webapp/src/views/ConsumerGroups.vue +0 -433
  122. package/webapp/src/views/Dashboard.vue +0 -418
  123. package/webapp/src/views/Messages.vue +0 -363
  124. package/webapp/src/views/QueueDetail.vue +0 -582
  125. package/webapp/src/views/Queues.vue +0 -496
  126. package/webapp/tailwind.config.js +0 -25
  127. package/webapp/vite.config.js +0 -10
  128. /package/{assets → src/webapp-dist/assets}/queen-logo-blue.svg +0 -0
  129. /package/{assets → src/webapp-dist/assets}/queen-logo-cyan.svg +0 -0
  130. /package/{assets → src/webapp-dist/assets}/queen-logo-indigo.svg +0 -0
  131. /package/{assets → src/webapp-dist/assets}/queen-logo-orange.svg +0 -0
  132. /package/{assets → src/webapp-dist/assets}/queen-logo-pink.svg +0 -0
  133. /package/{assets → src/webapp-dist/assets}/queen-logo-purple.svg +0 -0
  134. /package/{assets → src/webapp-dist/assets}/queen-logo-rose.svg +0 -0
  135. /package/{assets → src/webapp-dist/assets}/queen-logo.svg +0 -0
Binary file
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
-
@@ -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
- });