queen-mq 0.1.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 (90) hide show
  1. package/API.md +1116 -0
  2. package/CACHE.md +519 -0
  3. package/DASHBOARD-V3.md +478 -0
  4. package/DASHBOARD.md +382 -0
  5. package/MOD_QUEUE.md +453 -0
  6. package/PARTITION_LOCKING_DESIGN.md +989 -0
  7. package/PLAN.md +707 -0
  8. package/QUERY_ANALSYS.md +72 -0
  9. package/QUEUE_BUS.md +334 -0
  10. package/README.md +1495 -0
  11. package/V2-PLAN.md +236 -0
  12. package/assets/dashboard.png +0 -0
  13. package/dashboard/.vscode/extensions.json +3 -0
  14. package/dashboard/README.md +5 -0
  15. package/dashboard/index.html +14 -0
  16. package/dashboard/package-lock.json +1458 -0
  17. package/dashboard/package.json +25 -0
  18. package/dashboard/public/vite.svg +1 -0
  19. package/dashboard/src/App.vue +29 -0
  20. package/dashboard/src/assets/styles/main.css +908 -0
  21. package/dashboard/src/assets/vue.svg +1 -0
  22. package/dashboard/src/components/cards/MetricCard.vue +298 -0
  23. package/dashboard/src/components/charts/QueueDepthChart.vue +276 -0
  24. package/dashboard/src/components/charts/QueueLagChart.vue +436 -0
  25. package/dashboard/src/components/charts/ThroughputChart.vue +302 -0
  26. package/dashboard/src/components/common/ActivityFeed.vue +251 -0
  27. package/dashboard/src/components/layout/AppHeader.vue +208 -0
  28. package/dashboard/src/components/layout/AppLayout.vue +88 -0
  29. package/dashboard/src/components/layout/AppSidebar.vue +261 -0
  30. package/dashboard/src/main.js +44 -0
  31. package/dashboard/src/router.js +54 -0
  32. package/dashboard/src/services/api.js +187 -0
  33. package/dashboard/src/services/websocket.js +167 -0
  34. package/dashboard/src/utils/constants.js +56 -0
  35. package/dashboard/src/utils/helpers.js +118 -0
  36. package/dashboard/src/views/Analytics.vue +912 -0
  37. package/dashboard/src/views/Dashboard.vue +906 -0
  38. package/dashboard/src/views/Messages.vue +437 -0
  39. package/dashboard/src/views/QueueDetail.vue +501 -0
  40. package/dashboard/src/views/Queues.vue +333 -0
  41. package/dashboard/vite.config.js +30 -0
  42. package/debug-namespace.js +110 -0
  43. package/docs/long-polling.md +159 -0
  44. package/docs/multi-server-cache-solutions.md +185 -0
  45. package/docs/performance-tuning.md +222 -0
  46. package/examples/bus-mode.js +239 -0
  47. package/examples/continuous-consumer-optimized.js +215 -0
  48. package/examples/continuous-consumer.js +159 -0
  49. package/examples/continuous-producer.js +343 -0
  50. package/examples/mixed-mode.js +277 -0
  51. package/examples/multi-server-test.js +305 -0
  52. package/examples/single.js +64 -0
  53. package/examples/smartchat-dealyed.js +42 -0
  54. package/examples/smartchat.js +52 -0
  55. package/examples/test-cache-invalidation.js +119 -0
  56. package/examples/test-cache-multi-server.js +245 -0
  57. package/examples/test-minimal-client.js +112 -0
  58. package/examples/test-queue-creation-policy.js +137 -0
  59. package/init-db.js +20 -0
  60. package/package.json +36 -0
  61. package/src/client/client.js +291 -0
  62. package/src/client/index.js +6 -0
  63. package/src/client/queenClient.js +513 -0
  64. package/src/client/utils/http.js +172 -0
  65. package/src/client/utils/loadBalancer.js +152 -0
  66. package/src/client/utils/retry.js +35 -0
  67. package/src/config.js +215 -0
  68. package/src/database/connection.js +103 -0
  69. package/src/database/poolManager.js +192 -0
  70. package/src/database/schema-v2.sql +214 -0
  71. package/src/managers/eventManager.js +59 -0
  72. package/src/managers/queueManagerOptimized.js +1512 -0
  73. package/src/managers/resourceCache.js +96 -0
  74. package/src/managers/systemEventManager.js +127 -0
  75. package/src/routes/ack.js +26 -0
  76. package/src/routes/analytics.js +812 -0
  77. package/src/routes/configure.js +46 -0
  78. package/src/routes/messages.js +298 -0
  79. package/src/routes/pop.js +85 -0
  80. package/src/routes/push.js +28 -0
  81. package/src/routes/resources.js +296 -0
  82. package/src/server.js +1286 -0
  83. package/src/services/encryptionService.js +82 -0
  84. package/src/services/evictionService.js +131 -0
  85. package/src/services/retentionService.js +129 -0
  86. package/src/services/startupSync.js +35 -0
  87. package/src/test/test.js +4521 -0
  88. package/src/utils/logger.js +44 -0
  89. package/src/utils/uuid.js +5 -0
  90. package/src/websocket/wsServer.js +221 -0
@@ -0,0 +1,215 @@
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
+ });
@@ -0,0 +1,159 @@
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
+ });