queen-mq 0.4.0 → 0.6.4

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 (47) hide show
  1. package/README.md +520 -273
  2. package/{client-js/client-v2 → client-v2}/Queen.js +38 -0
  3. package/{client-js/client-v2 → client-v2}/README.md +61 -8
  4. package/package.json +5 -8
  5. package/{client-js/test-v2 → test-v2}/GETTING_STARTED.md +27 -0
  6. package/test-v2/README_SUBSCRIPTION_TESTS.md +201 -0
  7. package/{client-js/test-v2 → test-v2}/consume.js +10 -0
  8. package/{client-js/test-v2 → test-v2}/dlq.js +1 -1
  9. package/{client-js/test-v2 → test-v2}/load.js +2 -0
  10. package/{client-js/test-v2 → test-v2}/subscription.js +198 -18
  11. package/{client-js/test-v2 → test-v2}/transaction.js +66 -4
  12. package/LICENSE.md +0 -202
  13. package/client-js/benchmark/consumer.js +0 -209
  14. package/client-js/benchmark/consumer_multi.js +0 -216
  15. package/client-js/benchmark/producer.js +0 -80
  16. package/client-js/benchmark/producer_multi.js +0 -115
  17. /package/{client-js/client-v2 → client-v2}/LOGGING.md +0 -0
  18. /package/{client-js/client-v2 → client-v2}/buffer/BufferManager.js +0 -0
  19. /package/{client-js/client-v2 → client-v2}/buffer/MessageBuffer.js +0 -0
  20. /package/{client-js/client-v2 → client-v2}/builders/QueueBuilder.js +0 -0
  21. /package/{client-js/client-v2 → client-v2}/builders/TransactionBuilder.js +0 -0
  22. /package/{client-js/client-v2 → client-v2}/consumer/ConsumerManager.js +0 -0
  23. /package/{client-js/client-v2 → client-v2}/http/HttpClient.js +0 -0
  24. /package/{client-js/client-v2 → client-v2}/http/LoadBalancer.js +0 -0
  25. /package/{client-js/client-v2 → client-v2}/index.js +0 -0
  26. /package/{client-js/client-v2 → client-v2}/stream/StreamBuilder.js +0 -0
  27. /package/{client-js/client-v2 → client-v2}/stream/StreamConsumer.js +0 -0
  28. /package/{client-js/client-v2 → client-v2}/stream/Window.js +0 -0
  29. /package/{client-js/client-v2 → client-v2}/utils/defaults.js +0 -0
  30. /package/{client-js/client-v2 → client-v2}/utils/logger.js +0 -0
  31. /package/{client-js/client-v2 → client-v2}/utils/validation.js +0 -0
  32. /package/{client-js/test-v2 → test-v2}/AI_TEST_SUMMARY.md +0 -0
  33. /package/{client-js/test-v2 → test-v2}/MAINTENANCE_TEST.md +0 -0
  34. /package/{client-js/test-v2 → test-v2}/ai_buffering.js +0 -0
  35. /package/{client-js/test-v2 → test-v2}/ai_error_handling.js +0 -0
  36. /package/{client-js/test-v2 → test-v2}/ai_lease_renewal.js +0 -0
  37. /package/{client-js/test-v2 → test-v2}/ai_mixed_scenarios.js +0 -0
  38. /package/{client-js/test-v2 → test-v2}/ai_priority.js +0 -0
  39. /package/{client-js/test-v2 → test-v2}/ai_resources.js +0 -0
  40. /package/{client-js/test-v2 → test-v2}/ai_ttl_retention.js +0 -0
  41. /package/{client-js/test-v2 → test-v2}/complete.js +0 -0
  42. /package/{client-js/test-v2 → test-v2}/maintenance.js +0 -0
  43. /package/{client-js/test-v2 → test-v2}/pop.js +0 -0
  44. /package/{client-js/test-v2 → test-v2}/push.js +0 -0
  45. /package/{client-js/test-v2 → test-v2}/queue.js +0 -0
  46. /package/{client-js/test-v2 → test-v2}/retention.js +0 -0
  47. /package/{client-js/test-v2 → test-v2}/run.js +0 -0
@@ -1,209 +0,0 @@
1
- import { Queen } from '../client/client.js';
2
- import fs from 'fs';
3
-
4
- const QUEUE_NAME = 'benchmark-queue-001';
5
- const NUMBER_OF_CONSUMERS = 10;
6
- const STARTING_PARTITION = parseInt(process.argv[2]) || 0;
7
- const BATCH_SIZE = 10000;
8
- const CONSUME_MODE = null//'partition';
9
-
10
- // Global metrics tracking
11
- const metrics = {
12
- startTime: null,
13
- endTime: null,
14
- totalProcessed: 0,
15
- consumers: [],
16
- batchTimes: [],
17
- ackTimes: [],
18
- popTimes: [],
19
- firstMessageTime: null,
20
- lastMessageTime: null
21
- };
22
-
23
- async function consumer(consumerId, partition) {
24
- const q = new Queen({
25
- baseUrls: [ 'http://localhost:6632'],
26
- timeout: 30000
27
- });
28
-
29
- const consumerMetrics = {
30
- consumerId,
31
- partition,
32
- messagesProcessed: 0,
33
- batchesProcessed: 0,
34
- batchTimes: [],
35
- ackTimes: [],
36
- popTimes: [],
37
- startTime: Date.now(),
38
- endTime: null
39
- };
40
-
41
- const target = CONSUME_MODE === 'partition' ? `${QUEUE_NAME}/${partition}` : `${QUEUE_NAME}`;
42
-
43
- metrics.consumers.push(consumerMetrics);
44
- if (target === `${QUEUE_NAME}`) {
45
- console.log(`[Consumer ${consumerId}] Started, consuming all partitions`);
46
- } else {
47
- console.log(`[Consumer ${consumerId}] Started, consuming partition ${partition}`);
48
- }
49
-
50
- try {
51
- for await (const messages of q.takeBatch(target, { //`namespace:benchmark`,
52
- wait: true,
53
- timeout: 30000,
54
- batch: BATCH_SIZE,
55
- idleTimeout: 5000
56
- })) {
57
- if (!metrics.firstMessageTime) {
58
- metrics.firstMessageTime = Date.now();
59
- }
60
- metrics.lastMessageTime = Date.now();
61
- const batchStartTime = Date.now();
62
- const popTime = batchStartTime - (consumerMetrics.endTime || consumerMetrics.startTime);
63
- consumerMetrics.popTimes.push(popTime);
64
- metrics.popTimes.push(popTime);
65
-
66
- try {
67
- // Validate message ordering
68
- const partitionLastIds = new Map();
69
-
70
- for (const message of messages) {
71
- if (!message.data || typeof message.data.id !== 'number') {
72
- throw new Error(`Message missing or invalid data.id: ${JSON.stringify(message)}`);
73
- }
74
-
75
- const partition = message.partition || 'default';
76
- const messageId = message.data.id;
77
-
78
- if (partitionLastIds.has(partition)) {
79
- const lastId = partitionLastIds.get(partition);
80
- if (messageId !== lastId + 1) {
81
- console.log(message.partition, message);
82
- throw new Error(`Message ordering violation in partition '${partition}': expected id ${lastId + 1}, got ${messageId}`);
83
- }
84
- }
85
-
86
- partitionLastIds.set(partition, messageId);
87
- }
88
-
89
- // Acknowledge all messages
90
- const ackStartTime = Date.now();
91
- await q.ack(messages);
92
- const ackTime = Date.now() - ackStartTime;
93
- consumerMetrics.ackTimes.push(ackTime);
94
- metrics.ackTimes.push(ackTime);
95
-
96
- const batchEndTime = Date.now();
97
- const batchDuration = batchEndTime - batchStartTime;
98
-
99
- consumerMetrics.messagesProcessed += messages.length;
100
- consumerMetrics.batchesProcessed++;
101
- consumerMetrics.batchTimes.push(batchDuration);
102
- consumerMetrics.endTime = batchEndTime;
103
-
104
- metrics.totalProcessed += messages.length;
105
- metrics.batchTimes.push(batchDuration);
106
-
107
- const overallDuration = (batchEndTime - metrics.startTime) / 1000;
108
- const currentRate = (metrics.totalProcessed / overallDuration).toFixed(0);
109
-
110
- console.log(new Date().toISOString(), `[C${consumerId}] Batch: ${messages.length} msgs | Time: ${(batchDuration/1000).toFixed(3)}s (pop:${(popTime/1000).toFixed(3)}s ack:${(ackTime/1000).toFixed(3)}s) | Total: ${metrics.totalProcessed} | Rate: ${currentRate} msg/s`);
111
- metrics.lastMessageTime = Date.now();
112
- } catch (error) {
113
- console.error(new Date().toISOString(), `[Consumer ${consumerId}] Error processing batch:`, error);
114
- await q.ack(messages, false, { error: error.message });
115
- }
116
- }
117
- } catch (error) {
118
- console.error(`[Consumer ${consumerId}] Fatal error:`, error);
119
- }
120
-
121
- consumerMetrics.endTime = Date.now();
122
- console.log(`[Consumer ${consumerId}] Finished. Processed ${consumerMetrics.messagesProcessed} messages in ${consumerMetrics.batchesProcessed} batches`);
123
- }
124
-
125
- // Start metrics
126
- metrics.startTime = Date.now();
127
-
128
- // Launch all consumers
129
- const consumerPromises = [];
130
- //
131
- for (let i = 0; i < NUMBER_OF_CONSUMERS; i++) {
132
- consumerPromises.push(consumer(i, i ));
133
- }
134
-
135
- // Wait for all consumers to complete
136
- await Promise.all(consumerPromises);
137
- metrics.endTime = Date.now();
138
-
139
- // Calculate actual processing time (excluding idle timeout)
140
- // Find the time when the last batch was actually completed
141
- let lastBatchCompletedTime = metrics.startTime;
142
- metrics.consumers.forEach(c => {
143
- if (c.endTime && c.batchesProcessed > 0) {
144
- // c.endTime is when consumer exited (after idle timeout)
145
- // Actual last batch time = c.endTime - (idle timeout we didn't track)
146
- // Better: use the sum of all processing times
147
- const totalBatchTime = c.batchTimes.reduce((a,b)=>a+b,0);
148
- const totalPopTime = c.popTimes.reduce((a,b)=>a+b,0);
149
- const actualEndTime = c.startTime + totalBatchTime + totalPopTime;
150
- if (actualEndTime > lastBatchCompletedTime) {
151
- lastBatchCompletedTime = actualEndTime;
152
- }
153
- }
154
- });
155
-
156
- const totalDuration = (metrics.endTime - metrics.startTime) / 1000;
157
- const actualProcessingDuration = (lastBatchCompletedTime - metrics.startTime) / 1000;
158
- const avgThroughput = (metrics.totalProcessed / actualProcessingDuration).toFixed(0);
159
-
160
- console.log('\n' + '='.repeat(80));
161
- console.log('📊 BENCHMARK RESULTS');
162
- console.log('='.repeat(80));
163
- console.log(`Total Messages Processed: ${metrics.totalProcessed.toLocaleString()}`);
164
- console.log(`Total Wall Clock Time: ${totalDuration.toFixed(2)}s (includes idle timeout)`);
165
- console.log(`Actual Processing Time: ${actualProcessingDuration.toFixed(2)}s`);
166
- console.log(`Idle/Wait Time: ${(totalDuration - actualProcessingDuration).toFixed(2)}s`);
167
- console.log(`Average Throughput: ${avgThroughput} msg/s (based on processing time)`);
168
- console.log(`Number of Consumers: ${NUMBER_OF_CONSUMERS}`);
169
- console.log(`Total Batches: ${metrics.batchTimes.length}`);
170
- console.log(`Total Time: ${metrics.lastMessageTime - metrics.firstMessageTime}ms`);
171
- console.log(`Total msg/s: ${metrics.totalProcessed / (metrics.lastMessageTime - metrics.firstMessageTime) * 1000} msg/s`);
172
-
173
- if (metrics.batchTimes.length > 0) {
174
- const avgBatchTime = (metrics.batchTimes.reduce((a, b) => a + b, 0) / metrics.batchTimes.length / 1000).toFixed(3);
175
- const minBatchTime = (Math.min(...metrics.batchTimes) / 1000).toFixed(3);
176
- const maxBatchTime = (Math.max(...metrics.batchTimes) / 1000).toFixed(3);
177
-
178
- console.log(`\nBatch Times:`);
179
- console.log(` Average: ${avgBatchTime}s`);
180
- console.log(` Min: ${minBatchTime}s`);
181
- console.log(` Max: ${maxBatchTime}s`);
182
- }
183
-
184
- if (metrics.popTimes.length > 0) {
185
- const avgPopTime = (metrics.popTimes.reduce((a, b) => a + b, 0) / metrics.popTimes.length / 1000).toFixed(3);
186
- console.log(`\nAverage POP Time: ${avgPopTime}s`);
187
- }
188
-
189
- if (metrics.ackTimes.length > 0) {
190
- const avgAckTime = (metrics.ackTimes.reduce((a, b) => a + b, 0) / metrics.ackTimes.length / 1000).toFixed(3);
191
- console.log(`Average ACK Time: ${avgAckTime}s`);
192
- }
193
-
194
- console.log(`\nPer-Consumer Stats:`);
195
- metrics.consumers.forEach(c => {
196
- // Calculate actual processing time (sum of all batch times + pop times)
197
- const totalBatchTime = c.batchTimes.reduce((a,b)=>a+b,0);
198
- const totalPopTime = c.popTimes.reduce((a,b)=>a+b,0);
199
- const actualProcessingTime = (totalBatchTime + totalPopTime) / 1000;
200
-
201
- const wallClockTime = ((c.endTime - c.startTime) / 1000).toFixed(2);
202
- const rate = (c.messagesProcessed / actualProcessingTime).toFixed(0);
203
- const avgBatch = c.batchTimes.length > 0 ? (totalBatchTime / c.batchTimes.length / 1000).toFixed(3) : 'N/A';
204
- const idleTime = (wallClockTime - actualProcessingTime).toFixed(2);
205
-
206
- console.log(` [C${c.consumerId}] ${c.messagesProcessed.toLocaleString()} msgs | Active: ${actualProcessingTime.toFixed(2)}s (${rate} msg/s) | Idle: ${idleTime}s | ${c.batchesProcessed} batches | avg: ${avgBatch}s`);
207
- });
208
-
209
- console.log('='.repeat(80));
@@ -1,216 +0,0 @@
1
- import { Queen } from '../client/client.js';
2
- import fs from 'fs';
3
-
4
- const NAMESPACE = 'benchmark';
5
- const NUMBER_OF_CONSUMERS = 10;
6
- const BATCH_SIZE = 1;
7
-
8
- // Global metrics tracking
9
- const metrics = {
10
- startTime: null,
11
- endTime: null,
12
- totalProcessed: 0,
13
- consumers: [],
14
- batchTimes: [],
15
- ackTimes: [],
16
- popTimes: [],
17
- firstMessageTime: null,
18
- lastMessageTime: null
19
- };
20
-
21
- async function consumer(consumerId) {
22
- const q = new Queen({
23
- baseUrls: ['http://localhost:6632'],
24
- timeout: 30000
25
- });
26
-
27
- const consumerMetrics = {
28
- consumerId,
29
- messagesProcessed: 0,
30
- batchesProcessed: 0,
31
- batchTimes: [],
32
- ackTimes: [],
33
- popTimes: [],
34
- startTime: Date.now(),
35
- endTime: null,
36
- queuesConsumed: new Set(),
37
- partitionsConsumed: new Set()
38
- };
39
-
40
- const target = `namespace:${NAMESPACE}`;
41
-
42
- metrics.consumers.push(consumerMetrics);
43
- console.log(`[Consumer ${consumerId}] Started, consuming from namespace '${NAMESPACE}'`);
44
-
45
- try {
46
- for await (const messages of q.takeBatch(target, {
47
- wait: true,
48
- timeout: 15000,
49
- batch: BATCH_SIZE,
50
- idleTimeout: 5000
51
- })) {
52
- if (!metrics.firstMessageTime) {
53
- metrics.firstMessageTime = Date.now();
54
- }
55
- metrics.lastMessageTime = Date.now();
56
- const batchStartTime = Date.now();
57
- const popTime = batchStartTime - (consumerMetrics.endTime || consumerMetrics.startTime);
58
- consumerMetrics.popTimes.push(popTime);
59
- metrics.popTimes.push(popTime);
60
-
61
- try {
62
- // Track which queues and partitions this consumer is consuming from
63
- for (const message of messages) {
64
- if (message.queue) consumerMetrics.queuesConsumed.add(message.queue);
65
- if (message.partition !== undefined) consumerMetrics.partitionsConsumed.add(`${message.queue}:${message.partition}`);
66
- }
67
-
68
- // Validate message ordering per queue/partition
69
- const queuePartitionLastIds = new Map();
70
-
71
- for (const message of messages) {
72
- if (!message.data || typeof message.data.id !== 'number') {
73
- throw new Error(`Message missing or invalid data.id: ${JSON.stringify(message)}`);
74
- }
75
-
76
- // Use queue and partition to ensure proper ordering within each queue/partition
77
- const queuePartitionKey = `${message.queue || 'default'}:${message.partition || 'default'}`;
78
- const messageId = message.data.id;
79
-
80
- if (queuePartitionLastIds.has(queuePartitionKey)) {
81
- const lastId = queuePartitionLastIds.get(queuePartitionKey);
82
- if (messageId !== lastId + 1) {
83
- console.log(`Queue: ${message.queue}, Partition: ${message.partition}, Message:`, message);
84
- throw new Error(`Message ordering violation in '${queuePartitionKey}': expected id ${lastId + 1}, got ${messageId}`);
85
- }
86
- }
87
-
88
- queuePartitionLastIds.set(queuePartitionKey, messageId);
89
- }
90
-
91
- // Acknowledge all messages
92
- const ackStartTime = Date.now();
93
- await q.ack(messages);
94
- const ackTime = Date.now() - ackStartTime;
95
- consumerMetrics.ackTimes.push(ackTime);
96
- metrics.ackTimes.push(ackTime);
97
-
98
- const batchEndTime = Date.now();
99
- const batchDuration = batchEndTime - batchStartTime;
100
-
101
- consumerMetrics.messagesProcessed += messages.length;
102
- consumerMetrics.batchesProcessed++;
103
- consumerMetrics.batchTimes.push(batchDuration);
104
- consumerMetrics.endTime = batchEndTime;
105
-
106
- metrics.totalProcessed += messages.length;
107
- metrics.batchTimes.push(batchDuration);
108
-
109
- const overallDuration = (batchEndTime - metrics.startTime) / 1000;
110
- const currentRate = (metrics.totalProcessed / overallDuration).toFixed(0);
111
-
112
- console.log(new Date().toISOString(), `[C${consumerId}] Batch: ${messages.length} msgs | Time: ${(batchDuration/1000).toFixed(3)}s (pop:${(popTime/1000).toFixed(3)}s ack:${(ackTime/1000).toFixed(3)}s) | Total: ${metrics.totalProcessed} | Rate: ${currentRate} msg/s`);
113
- metrics.lastMessageTime = Date.now();
114
- } catch (error) {
115
- console.error(new Date().toISOString(), `[Consumer ${consumerId}] Error processing batch:`, error);
116
- await q.ack(messages, false, { error: error.message });
117
- }
118
- }
119
- } catch (error) {
120
- console.error(`[Consumer ${consumerId}] Fatal error:`, error);
121
- }
122
-
123
- consumerMetrics.endTime = Date.now();
124
- console.log(`[Consumer ${consumerId}] Finished. Processed ${consumerMetrics.messagesProcessed} messages in ${consumerMetrics.batchesProcessed} batches`);
125
- }
126
-
127
- // Start metrics
128
- metrics.startTime = Date.now();
129
-
130
- // Launch all consumers (they will all consume from the namespace)
131
- console.log(`Launching ${NUMBER_OF_CONSUMERS} consumers for namespace '${NAMESPACE}'...\n`);
132
- const consumerPromises = [];
133
- for (let i = 0; i < NUMBER_OF_CONSUMERS; i++) {
134
- consumerPromises.push(consumer(i));
135
- }
136
-
137
- // Wait for all consumers to complete
138
- await Promise.all(consumerPromises);
139
- metrics.endTime = Date.now();
140
-
141
- // Calculate actual processing time (excluding idle timeout)
142
- // Find the time when the last batch was actually completed
143
- let lastBatchCompletedTime = metrics.startTime;
144
- metrics.consumers.forEach(c => {
145
- if (c.endTime && c.batchesProcessed > 0) {
146
- // c.endTime is when consumer exited (after idle timeout)
147
- // Actual last batch time = c.endTime - (idle timeout we didn't track)
148
- // Better: use the sum of all processing times
149
- const totalBatchTime = c.batchTimes.reduce((a,b)=>a+b,0);
150
- const totalPopTime = c.popTimes.reduce((a,b)=>a+b,0);
151
- const actualEndTime = c.startTime + totalBatchTime + totalPopTime;
152
- if (actualEndTime > lastBatchCompletedTime) {
153
- lastBatchCompletedTime = actualEndTime;
154
- }
155
- }
156
- });
157
-
158
- const totalDuration = (metrics.endTime - metrics.startTime) / 1000;
159
- const actualProcessingDuration = (lastBatchCompletedTime - metrics.startTime) / 1000;
160
- const avgThroughput = (metrics.totalProcessed / actualProcessingDuration).toFixed(0);
161
-
162
- console.log('\n' + '='.repeat(80));
163
- console.log('📊 BENCHMARK RESULTS');
164
- console.log('='.repeat(80));
165
- console.log(`Total Messages Processed: ${metrics.totalProcessed.toLocaleString()}`);
166
- console.log(`Total Wall Clock Time: ${totalDuration.toFixed(2)}s (includes idle timeout)`);
167
- console.log(`Actual Processing Time: ${actualProcessingDuration.toFixed(2)}s`);
168
- console.log(`Idle/Wait Time: ${(totalDuration - actualProcessingDuration).toFixed(2)}s`);
169
- console.log(`Average Throughput: ${avgThroughput} msg/s (based on processing time)`);
170
- console.log(`Number of Consumers: ${NUMBER_OF_CONSUMERS}`);
171
- console.log(`Total Batches: ${metrics.batchTimes.length}`);
172
- console.log(`Total Time: ${metrics.lastMessageTime - metrics.firstMessageTime}ms`);
173
- console.log(`Total msg/s: ${metrics.totalProcessed / (metrics.lastMessageTime - metrics.firstMessageTime) * 1000} msg/s`);
174
-
175
- if (metrics.batchTimes.length > 0) {
176
- const avgBatchTime = (metrics.batchTimes.reduce((a, b) => a + b, 0) / metrics.batchTimes.length / 1000).toFixed(3);
177
- const minBatchTime = (Math.min(...metrics.batchTimes) / 1000).toFixed(3);
178
- const maxBatchTime = (Math.max(...metrics.batchTimes) / 1000).toFixed(3);
179
-
180
- console.log(`\nBatch Times:`);
181
- console.log(` Average: ${avgBatchTime}s`);
182
- console.log(` Min: ${minBatchTime}s`);
183
- console.log(` Max: ${maxBatchTime}s`);
184
- }
185
-
186
- if (metrics.popTimes.length > 0) {
187
- const avgPopTime = (metrics.popTimes.reduce((a, b) => a + b, 0) / metrics.popTimes.length / 1000).toFixed(3);
188
- console.log(`\nAverage POP Time: ${avgPopTime}s`);
189
- }
190
-
191
- if (metrics.ackTimes.length > 0) {
192
- const avgAckTime = (metrics.ackTimes.reduce((a, b) => a + b, 0) / metrics.ackTimes.length / 1000).toFixed(3);
193
- console.log(`Average ACK Time: ${avgAckTime}s`);
194
- }
195
-
196
- console.log(`\nPer-Consumer Stats:`);
197
- metrics.consumers.forEach(c => {
198
- // Calculate actual processing time (sum of all batch times + pop times)
199
- const totalBatchTime = c.batchTimes.reduce((a,b)=>a+b,0);
200
- const totalPopTime = c.popTimes.reduce((a,b)=>a+b,0);
201
- const actualProcessingTime = (totalBatchTime + totalPopTime) / 1000;
202
-
203
- const wallClockTime = ((c.endTime - c.startTime) / 1000).toFixed(2);
204
- const rate = (c.messagesProcessed / actualProcessingTime).toFixed(0);
205
- const avgBatch = c.batchTimes.length > 0 ? (totalBatchTime / c.batchTimes.length / 1000).toFixed(3) : 'N/A';
206
- const idleTime = (wallClockTime - actualProcessingTime).toFixed(2);
207
-
208
- const queuesStr = c.queuesConsumed.size > 0 ? `queues:[${Array.from(c.queuesConsumed).sort().join(',')}]` : '';
209
- const partitionsStr = c.partitionsConsumed.size > 0 ? `partitions:${c.partitionsConsumed.size}` : '';
210
- const consumedFrom = [queuesStr, partitionsStr].filter(Boolean).join(' ');
211
-
212
- console.log(` [C${c.consumerId}] ${c.messagesProcessed.toLocaleString()} msgs | Active: ${actualProcessingTime.toFixed(2)}s (${rate} msg/s) | Idle: ${idleTime}s | ${c.batchesProcessed} batches | avg: ${avgBatch}s | ${consumedFrom}`);
213
- });
214
-
215
- console.log('='.repeat(80));
216
-
@@ -1,80 +0,0 @@
1
- import { Queen } from '../client/client.js';
2
- import fs from 'fs';
3
-
4
- const TOTAL_MESSAGES = 1000000;
5
- const PARTITIONS = 10;
6
- const MAX_BATCH_SIZE = 100; // Reduced from 10000
7
- const PUSH_PARALLEL = 10; // Reduced from 10 to avoid lock contention
8
- const QUEUE_NAME = 'benchmark-queue-001';
9
-
10
- const q = new Queen({
11
- baseUrls: ['http://localhost:6632'],
12
- timeout: 120000
13
- });
14
-
15
-
16
- try {
17
- await q.queueDelete(QUEUE_NAME);
18
- await q.queue(QUEUE_NAME, {
19
- leaseTime: 10,
20
- retryLimit: 3
21
- }, { namespace: 'benchmark' });
22
- } catch (e) {
23
- console.error(e);
24
- }
25
-
26
-
27
-
28
- let batches = []
29
- const messagesPerPartition = Math.floor(TOTAL_MESSAGES / PARTITIONS);
30
-
31
- // Create batches for each partition
32
- const partitionBatches = [];
33
- for (let i = 0; i < PARTITIONS; i++) {
34
- partitionBatches[i] = [];
35
- let messages = [];
36
- for (let j = 0; j < messagesPerPartition; j++) {
37
- messages.push({
38
- id: j,
39
- //message: `Message ${globalMessageId}`,
40
- //timestamp: new Date().toISOString()
41
- });
42
- if (messages.length >= MAX_BATCH_SIZE) {
43
- console.log(`Pushing ${messages.length} messages for partition ${i}`);
44
- partitionBatches[i].push({
45
- partition: i,
46
- messages: messages
47
- });
48
- messages = [];
49
- }
50
- }
51
- // Only add the remaining batch if it has messages
52
- if (messages.length > 0) {
53
- console.log(`Pushing ${messages.length} messages for partition ${i}`);
54
- partitionBatches[i].push({
55
- partition: i,
56
- messages: messages
57
- });
58
- }
59
- }
60
-
61
- // Interleave batches from different partitions to avoid lock contention
62
- // This ensures parallel pushes go to different partitions
63
- const maxBatchesPerPartition = Math.max(...partitionBatches.map(pb => pb.length));
64
- for (let batchIndex = 0; batchIndex < maxBatchesPerPartition; batchIndex++) {
65
- for (let partition = 0; partition < PARTITIONS; partition++) {
66
- if (partitionBatches[partition][batchIndex]) {
67
- batches.push(partitionBatches[partition][batchIndex]);
68
- }
69
- }
70
- }
71
-
72
- const start = Date.now();
73
- console.log(`Total batches: ${batches.length}, pushing ${PUSH_PARALLEL} at a time`);
74
- for (let i = 0; i < batches.length; i += PUSH_PARALLEL) {
75
- const batchGroup = batches.slice(i, i + PUSH_PARALLEL);
76
- console.log(`Pushing batch group ${Math.floor(i/PUSH_PARALLEL) + 1}/${Math.ceil(batches.length/PUSH_PARALLEL)}: ${batchGroup.map(b => `partition ${b.partition} (${b.messages.length} msgs)`).join(', ')}`);
77
- await Promise.all(batchGroup.map(batch => q.push(`${QUEUE_NAME}/${batch.partition}`, batch.messages)));
78
- }
79
- const end = Date.now();
80
- console.log(`Pushed ${TOTAL_MESSAGES} messages in ${(end - start) / 1000}s, ${(TOTAL_MESSAGES / ((end - start) / 1000)).toFixed(2)} msg/s`);
@@ -1,115 +0,0 @@
1
- import { Queen } from '../client/client.js';
2
- import fs from 'fs';
3
-
4
- const TOTAL_MESSAGES = 100000;
5
- const NUM_QUEUES = 50; // X: Number of queues
6
- const PARTITIONS_PER_QUEUE = 10; // Y: Partitions per queue
7
- const MAX_BATCH_SIZE = 10;
8
- const PUSH_PARALLEL = 10;
9
-
10
- const q = new Queen({
11
- baseUrls: ['http://localhost:6632'],
12
- timeout: 120000
13
- });
14
-
15
- // Create X queues, each with Y partitions, all in 'benchmark' namespace
16
- console.log(`Creating ${NUM_QUEUES} queues with ${PARTITIONS_PER_QUEUE} partitions each...`);
17
- for (let queueIdx = 0; queueIdx < NUM_QUEUES; queueIdx++) {
18
- const queueName = `benchmark-queue-${String(queueIdx + 1).padStart(2, '0')}`;
19
- try {
20
- //await q.queueDelete(queueName);
21
- } catch (e) {
22
- // Queue might not exist
23
- }
24
- await q.queue(queueName, {
25
- leaseTime: 10,
26
- retryLimit: 3
27
- }, { namespace: 'benchmark' });
28
- console.log(` Created queue: ${queueName}`);
29
- }
30
-
31
-
32
- // Calculate total partitions across all queues
33
- const TOTAL_PARTITIONS = NUM_QUEUES * PARTITIONS_PER_QUEUE;
34
- const messagesPerPartition = Math.floor(TOTAL_MESSAGES / TOTAL_PARTITIONS);
35
-
36
- console.log(`\nDistributing ${TOTAL_MESSAGES} messages across ${NUM_QUEUES} queues × ${PARTITIONS_PER_QUEUE} partitions = ${TOTAL_PARTITIONS} total partitions`);
37
- console.log(`Messages per partition: ${messagesPerPartition}\n`);
38
-
39
- // Create batches for each queue/partition combination
40
- const queuePartitionBatches = [];
41
- for (let queueIdx = 0; queueIdx < NUM_QUEUES; queueIdx++) {
42
- for (let partitionIdx = 0; partitionIdx < PARTITIONS_PER_QUEUE; partitionIdx++) {
43
- const key = `q${queueIdx}_p${partitionIdx}`;
44
- queuePartitionBatches.push({ queueIdx, partitionIdx, batches: [] });
45
-
46
- let messages = [];
47
- for (let msgIdx = 0; msgIdx < messagesPerPartition; msgIdx++) {
48
- messages.push({
49
- id: msgIdx,
50
- queue: queueIdx,
51
- partition: partitionIdx
52
- });
53
-
54
- if (messages.length >= MAX_BATCH_SIZE) {
55
- queuePartitionBatches[queuePartitionBatches.length - 1].batches.push([...messages]);
56
- messages = [];
57
- }
58
- }
59
-
60
- // Add remaining messages
61
- if (messages.length > 0) {
62
- queuePartitionBatches[queuePartitionBatches.length - 1].batches.push([...messages]);
63
- }
64
- }
65
- }
66
-
67
- // Flatten and interleave batches to distribute load evenly
68
- let batches = [];
69
- const maxBatchesPerQueuePartition = Math.max(...queuePartitionBatches.map(qp => qp.batches.length));
70
- for (let batchIdx = 0; batchIdx < maxBatchesPerQueuePartition; batchIdx++) {
71
- for (const qp of queuePartitionBatches) {
72
- if (qp.batches[batchIdx]) {
73
- batches.push({
74
- queueIdx: qp.queueIdx,
75
- partitionIdx: qp.partitionIdx,
76
- messages: qp.batches[batchIdx]
77
- });
78
- }
79
- }
80
- }
81
-
82
- const start = Date.now();
83
- console.log(`Total batches: ${batches.length}, pushing ${PUSH_PARALLEL} at a time\n`);
84
-
85
- for (let i = 0; i < batches.length; i += PUSH_PARALLEL) {
86
- const batchGroup = batches.slice(i, i + PUSH_PARALLEL);
87
- const batchGroupNum = Math.floor(i/PUSH_PARALLEL) + 1;
88
- const totalBatchGroups = Math.ceil(batches.length/PUSH_PARALLEL);
89
-
90
- console.log(`[${batchGroupNum}/${totalBatchGroups}] Pushing: ${batchGroup.map(b => {
91
- const queueName = `benchmark-queue-${String(b.queueIdx + 1).padStart(2, '0')}`;
92
- return `${queueName}/p${b.partitionIdx} (${b.messages.length} msgs)`;
93
- }).join(', ')}`);
94
-
95
- await Promise.all(batchGroup.map(batch => {
96
- const queueName = `benchmark-queue-${String(batch.queueIdx + 1).padStart(2, '0')}`;
97
- return q.push(`${queueName}/${batch.partitionIdx}`, batch.messages);
98
- }));
99
- }
100
-
101
- const end = Date.now();
102
- const duration = (end - start) / 1000;
103
- const throughput = (TOTAL_MESSAGES / duration).toFixed(2);
104
-
105
- console.log('\n' + '='.repeat(80));
106
- console.log('📤 PRODUCER RESULTS');
107
- console.log('='.repeat(80));
108
- console.log(`Total Messages Pushed: ${TOTAL_MESSAGES.toLocaleString()}`);
109
- console.log(`Queues: ${NUM_QUEUES}`);
110
- console.log(`Partitions per Queue: ${PARTITIONS_PER_QUEUE}`);
111
- console.log(`Total Partitions: ${TOTAL_PARTITIONS}`);
112
- console.log(`Duration: ${duration.toFixed(2)}s`);
113
- console.log(`Throughput: ${throughput} msg/s`);
114
- console.log('='.repeat(80));
115
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes