queen-mq 0.6.3 → 0.7.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.
- package/README.md +500 -327
- package/{client-js/client-v2 → client-v2}/Queen.js +5 -2
- package/{client-js/client-v2 → client-v2}/README.md +53 -2
- package/{client-js/client-v2 → client-v2}/builders/QueueBuilder.js +24 -1
- package/{client-js/client-v2 → client-v2}/consumer/ConsumerManager.js +25 -4
- package/{client-js/client-v2 → client-v2}/http/HttpClient.js +24 -12
- package/client-v2/http/LoadBalancer.js +271 -0
- package/{client-js/client-v2 → client-v2}/utils/defaults.js +4 -2
- package/package.json +6 -8
- package/{client-js/test-v2 → test-v2}/maintenance.js +1 -1
- package/LICENSE.md +0 -202
- package/client-js/benchmark/consumer.js +0 -209
- package/client-js/benchmark/consumer_multi.js +0 -216
- package/client-js/benchmark/producer.js +0 -80
- package/client-js/benchmark/producer_multi.js +0 -115
- package/client-js/client-v2/http/LoadBalancer.js +0 -50
- /package/{client-js/client-v2 → client-v2}/LOGGING.md +0 -0
- /package/{client-js/client-v2 → client-v2}/buffer/BufferManager.js +0 -0
- /package/{client-js/client-v2 → client-v2}/buffer/MessageBuffer.js +0 -0
- /package/{client-js/client-v2 → client-v2}/builders/TransactionBuilder.js +0 -0
- /package/{client-js/client-v2 → client-v2}/index.js +0 -0
- /package/{client-js/client-v2 → client-v2}/stream/StreamBuilder.js +0 -0
- /package/{client-js/client-v2 → client-v2}/stream/StreamConsumer.js +0 -0
- /package/{client-js/client-v2 → client-v2}/stream/Window.js +0 -0
- /package/{client-js/client-v2 → client-v2}/utils/logger.js +0 -0
- /package/{client-js/client-v2 → client-v2}/utils/validation.js +0 -0
- /package/{client-js/test-v2 → test-v2}/AI_TEST_SUMMARY.md +0 -0
- /package/{client-js/test-v2 → test-v2}/GETTING_STARTED.md +0 -0
- /package/{client-js/test-v2 → test-v2}/MAINTENANCE_TEST.md +0 -0
- /package/{client-js/test-v2 → test-v2}/README_SUBSCRIPTION_TESTS.md +0 -0
- /package/{client-js/test-v2 → test-v2}/ai_buffering.js +0 -0
- /package/{client-js/test-v2 → test-v2}/ai_error_handling.js +0 -0
- /package/{client-js/test-v2 → test-v2}/ai_lease_renewal.js +0 -0
- /package/{client-js/test-v2 → test-v2}/ai_mixed_scenarios.js +0 -0
- /package/{client-js/test-v2 → test-v2}/ai_priority.js +0 -0
- /package/{client-js/test-v2 → test-v2}/ai_resources.js +0 -0
- /package/{client-js/test-v2 → test-v2}/ai_ttl_retention.js +0 -0
- /package/{client-js/test-v2 → test-v2}/complete.js +0 -0
- /package/{client-js/test-v2 → test-v2}/consume.js +0 -0
- /package/{client-js/test-v2 → test-v2}/dlq.js +0 -0
- /package/{client-js/test-v2 → test-v2}/load.js +0 -0
- /package/{client-js/test-v2 → test-v2}/pop.js +0 -0
- /package/{client-js/test-v2 → test-v2}/push.js +0 -0
- /package/{client-js/test-v2 → test-v2}/queue.js +0 -0
- /package/{client-js/test-v2 → test-v2}/retention.js +0 -0
- /package/{client-js/test-v2 → test-v2}/run.js +0 -0
- /package/{client-js/test-v2 → test-v2}/subscription.js +0 -0
- /package/{client-js/test-v2 → test-v2}/transaction.js +0 -0
|
@@ -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
|
-
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Load balancer for distributing requests across multiple servers
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export class LoadBalancer {
|
|
6
|
-
#urls
|
|
7
|
-
#strategy
|
|
8
|
-
#currentIndex = 0
|
|
9
|
-
#sessionMap = new Map()
|
|
10
|
-
#sessionId
|
|
11
|
-
|
|
12
|
-
constructor(urls, strategy = 'round-robin') {
|
|
13
|
-
this.#urls = urls.map(url => url.replace(/\/$/, '')) // Remove trailing slashes
|
|
14
|
-
this.#strategy = strategy
|
|
15
|
-
this.#sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
getNextUrl(sessionKey = null) {
|
|
19
|
-
const key = sessionKey || this.#sessionId
|
|
20
|
-
|
|
21
|
-
if (this.#strategy === 'session') {
|
|
22
|
-
// Session affinity: stick to the same server per session
|
|
23
|
-
if (!this.#sessionMap.has(key)) {
|
|
24
|
-
const assignedIndex = this.#currentIndex
|
|
25
|
-
this.#sessionMap.set(key, assignedIndex)
|
|
26
|
-
this.#currentIndex = (this.#currentIndex + 1) % this.#urls.length
|
|
27
|
-
}
|
|
28
|
-
return this.#urls[this.#sessionMap.get(key)]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Round robin: cycle through URLs
|
|
32
|
-
const url = this.#urls[this.#currentIndex]
|
|
33
|
-
this.#currentIndex = (this.#currentIndex + 1) % this.#urls.length
|
|
34
|
-
return url
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
getAllUrls() {
|
|
38
|
-
return [...this.#urls]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
getStrategy() {
|
|
42
|
-
return this.#strategy
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
reset() {
|
|
46
|
-
this.#currentIndex = 0
|
|
47
|
-
this.#sessionMap.clear()
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|