queen-mq 0.3.1 โ 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -144
- package/client-js/client-v2/Queen.js +28 -0
- package/client-js/client-v2/README.md +6 -2
- package/client-js/client-v2/builders/QueueBuilder.js +7 -1
- package/client-js/client-v2/builders/TransactionBuilder.js +20 -4
- package/client-js/client-v2/stream/StreamBuilder.js +72 -0
- package/client-js/client-v2/stream/StreamConsumer.js +156 -0
- package/client-js/client-v2/stream/Window.js +140 -0
- package/client-js/test-v2/MAINTENANCE_TEST.md +148 -0
- package/client-js/test-v2/consume.js +1 -0
- package/client-js/test-v2/maintenance.js +261 -0
- package/client-js/test-v2/retention.js +69 -0
- package/client-js/test-v2/run.js +7 -1
- package/package.json +1 -1
- package/client-js/client/client.js +0 -1536
- package/client-js/client/index.js +0 -4
- package/client-js/client/utils/http.js +0 -173
- package/client-js/client/utils/loadBalancer.js +0 -152
- package/client-js/client/utils/retry.js +0 -41
- package/client-js/services/encryptionService.js +0 -82
- package/client-js/services/evictionService.js +0 -160
- package/client-js/services/retentionService.js +0 -162
- package/client-js/services/startupSync.js +0 -35
- package/client-js/test/README.md +0 -224
- package/client-js/test/advanced-client-tests.js +0 -761
- package/client-js/test/advanced-pattern-tests.js +0 -1137
- package/client-js/test/bus-mode-tests.js +0 -361
- package/client-js/test/core-tests.js +0 -457
- package/client-js/test/edge-case-tests.js +0 -562
- package/client-js/test/enterprise-tests.js +0 -637
- package/client-js/test/human.js +0 -162
- package/client-js/test/partition-locking-tests.js +0 -545
- package/client-js/test/partition-transaction-tests.js +0 -482
- package/client-js/test/qos0-tests.js +0 -334
- package/client-js/test/test-new.js +0 -370
- package/client-js/test/utils.js +0 -169
- package/client-js/test/window-buffer-test.js +0 -114
- package/client-js/utils/logger.js +0 -44
- package/client-js/utils/uuid.js +0 -5
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { Queen } from '../client-v2/index.js';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
|
|
4
|
+
export async function test_maintenance_mode(client = null) {
|
|
5
|
+
console.log('๐งช Testing Maintenance Mode with File Buffer\n');
|
|
6
|
+
|
|
7
|
+
const queen = client
|
|
8
|
+
|
|
9
|
+
// Use port 6632 (default Queen port) for API calls
|
|
10
|
+
const API_URL = 'http://localhost:6632';
|
|
11
|
+
const QUEUE_NAME = 'test-maintenance-queue';
|
|
12
|
+
const MESSAGES_PER_SECOND = 10;
|
|
13
|
+
const MAINTENANCE_DURATION_SECONDS = 10;
|
|
14
|
+
|
|
15
|
+
let producedCount = 0;
|
|
16
|
+
let receivedCount = 0;
|
|
17
|
+
let consumerActive = false;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// 0. Ensure maintenance mode is OFF before starting
|
|
21
|
+
console.log('๐งน Step 0: Ensuring clean state...');
|
|
22
|
+
await axios.post(`${API_URL}/api/v1/system/maintenance`, { enabled: false });
|
|
23
|
+
|
|
24
|
+
let initialStatus = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
25
|
+
console.log(` Initial status:`, initialStatus.data);
|
|
26
|
+
|
|
27
|
+
if (initialStatus.data.bufferedMessages > 0) {
|
|
28
|
+
console.log(` โ ๏ธ Found ${initialStatus.data.bufferedMessages} buffered messages from previous runs`);
|
|
29
|
+
console.log(' Waiting for buffer to drain (checking every 2 seconds)...');
|
|
30
|
+
|
|
31
|
+
// Wait for buffer to drain completely
|
|
32
|
+
let waitTime = 0;
|
|
33
|
+
const maxWait = 60; // Max 60 seconds
|
|
34
|
+
while (waitTime < maxWait) {
|
|
35
|
+
await sleep(2000);
|
|
36
|
+
waitTime += 2;
|
|
37
|
+
const status = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
38
|
+
console.log(` ${waitTime}s - Buffered: ${status.data.bufferedMessages}`);
|
|
39
|
+
|
|
40
|
+
if (status.data.bufferedMessages === 0) {
|
|
41
|
+
console.log(' โ
Buffer drained completely');
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (waitTime >= maxWait) {
|
|
47
|
+
console.log(` โ ๏ธ Timeout waiting for buffer to drain. Continuing anyway...`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
console.log('');
|
|
51
|
+
|
|
52
|
+
// 1. Configure queue
|
|
53
|
+
console.log(`๐ Step 1: Configuring queue "${QUEUE_NAME}"...`);
|
|
54
|
+
await queen.queue(QUEUE_NAME).config({
|
|
55
|
+
leaseTime: 60,
|
|
56
|
+
}).create();
|
|
57
|
+
console.log('โ
Queue configured\n');
|
|
58
|
+
|
|
59
|
+
// 2. Start consumer
|
|
60
|
+
console.log('๐ฅ Step 2: Starting consumer...');
|
|
61
|
+
consumerActive = true;
|
|
62
|
+
|
|
63
|
+
const consumePromise = (async () => {
|
|
64
|
+
while (consumerActive) {
|
|
65
|
+
try {
|
|
66
|
+
const messages = await queen.queue(QUEUE_NAME).batch(10).wait(true).limit(1).each().pop();
|
|
67
|
+
|
|
68
|
+
if (messages && messages.length > 0) {
|
|
69
|
+
receivedCount += messages.length;
|
|
70
|
+
console.log(` ๐จ Received ${messages.length} messages (total: ${receivedCount})`);
|
|
71
|
+
|
|
72
|
+
// Acknowledge messages
|
|
73
|
+
await queen.ack(messages);
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (consumerActive) {
|
|
77
|
+
console.error(' โ Consumer error:', err.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
|
|
83
|
+
console.log('โ
Consumer started\n');
|
|
84
|
+
|
|
85
|
+
// 3. Start producer (10 messages/second)
|
|
86
|
+
console.log(`๐ค Step 3: Starting producer (${MESSAGES_PER_SECOND} msgs/sec)...`);
|
|
87
|
+
let producerRunning = true;
|
|
88
|
+
|
|
89
|
+
const producerLoop = async () => {
|
|
90
|
+
while (producerRunning) {
|
|
91
|
+
try {
|
|
92
|
+
// Push batch of 10 messages
|
|
93
|
+
const batch = [];
|
|
94
|
+
for (let i = 0; i < MESSAGES_PER_SECOND; i++) {
|
|
95
|
+
batch.push({
|
|
96
|
+
data: {
|
|
97
|
+
messageNumber: producedCount + i,
|
|
98
|
+
timestamp: Date.now()
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Push and wait for confirmation
|
|
104
|
+
await queen.queue(QUEUE_NAME).push(batch);
|
|
105
|
+
producedCount += batch.length; // Only increment after successful push
|
|
106
|
+
|
|
107
|
+
// Wait 1 second before next batch
|
|
108
|
+
await sleep(1000);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error(' โ Producer error:', err.message);
|
|
111
|
+
await sleep(1000); // Wait before retry
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const producerPromise = producerLoop();
|
|
117
|
+
|
|
118
|
+
console.log('โ
Producer started\n');
|
|
119
|
+
|
|
120
|
+
// Wait 3 seconds for messages to flow normally
|
|
121
|
+
console.log('โฑ๏ธ Step 4: Waiting 3 seconds for normal message flow...');
|
|
122
|
+
await sleep(3000);
|
|
123
|
+
console.log(` Produced: ${producedCount}, Received: ${receivedCount}\n`);
|
|
124
|
+
|
|
125
|
+
const receivedBeforeMaintenance = receivedCount;
|
|
126
|
+
|
|
127
|
+
// 4. Enable maintenance mode
|
|
128
|
+
console.log('๐ง Step 5: Enabling MAINTENANCE MODE...');
|
|
129
|
+
const maintenanceResponse = await axios.post(`${API_URL}/api/v1/system/maintenance`, {
|
|
130
|
+
enabled: true
|
|
131
|
+
});
|
|
132
|
+
console.log(` Response:`, maintenanceResponse.data);
|
|
133
|
+
console.log('โ
Maintenance mode enabled\n');
|
|
134
|
+
|
|
135
|
+
// Wait 2 seconds and check that consumer stops receiving
|
|
136
|
+
console.log('โฑ๏ธ Step 6: Waiting 5 seconds - consumer should stop receiving...');
|
|
137
|
+
await sleep(5000);
|
|
138
|
+
const receivedDuringWait = receivedCount - receivedBeforeMaintenance;
|
|
139
|
+
console.log(` Messages received during maintenance: ${receivedDuringWait}`);
|
|
140
|
+
console.log(` Total: Produced=${producedCount}, Received=${receivedCount}\n`);
|
|
141
|
+
|
|
142
|
+
// 5. Keep producing during maintenance
|
|
143
|
+
console.log(`โฑ๏ธ Step 7: Producing during maintenance for ${MAINTENANCE_DURATION_SECONDS} seconds...`);
|
|
144
|
+
console.log(' (Messages should go to file buffer)\n');
|
|
145
|
+
await sleep(MAINTENANCE_DURATION_SECONDS * 1000);
|
|
146
|
+
|
|
147
|
+
const producedDuringMaintenance = producedCount;
|
|
148
|
+
const receivedAfterMaintenance = receivedCount;
|
|
149
|
+
const messagesBuffered = producedDuringMaintenance - receivedAfterMaintenance;
|
|
150
|
+
|
|
151
|
+
console.log(` Maintenance period complete:`);
|
|
152
|
+
console.log(` - Total produced: ${producedCount}`);
|
|
153
|
+
console.log(` - Total received: ${receivedCount}`);
|
|
154
|
+
console.log(` - Buffered (should be ~${MESSAGES_PER_SECOND * MAINTENANCE_DURATION_SECONDS}): ${messagesBuffered}\n`);
|
|
155
|
+
|
|
156
|
+
// 6. Check maintenance status
|
|
157
|
+
console.log('๐ Step 8: Checking maintenance status...');
|
|
158
|
+
const statusResponse = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
159
|
+
console.log(` Status:`, statusResponse.data);
|
|
160
|
+
console.log('');
|
|
161
|
+
|
|
162
|
+
// 7. Disable maintenance mode
|
|
163
|
+
console.log('โ
Step 9: Disabling MAINTENANCE MODE...');
|
|
164
|
+
const disableResponse = await axios.post(`${API_URL}/api/v1/system/maintenance`, {
|
|
165
|
+
enabled: false
|
|
166
|
+
});
|
|
167
|
+
console.log(` Response:`, disableResponse.data);
|
|
168
|
+
console.log(' (File buffer should start draining to database)\n');
|
|
169
|
+
|
|
170
|
+
// 8. Wait for messages to resume and stop producer
|
|
171
|
+
console.log('โฑ๏ธ Step 10: Waiting for messages to resume...');
|
|
172
|
+
await sleep(2000);
|
|
173
|
+
|
|
174
|
+
const receivedAfterResume = receivedCount;
|
|
175
|
+
const messagesResumed = receivedAfterResume - receivedAfterMaintenance;
|
|
176
|
+
console.log(` Messages received after resuming: ${messagesResumed}`);
|
|
177
|
+
console.log('โ
Messages are flowing again!\n');
|
|
178
|
+
|
|
179
|
+
// Stop producer
|
|
180
|
+
console.log('๐ Step 11: Stopping producer...');
|
|
181
|
+
producerRunning = false;
|
|
182
|
+
await producerPromise; // Wait for producer to actually stop
|
|
183
|
+
|
|
184
|
+
const finalProducedCount = producedCount;
|
|
185
|
+
console.log(` Final produced count: ${finalProducedCount}\n`);
|
|
186
|
+
|
|
187
|
+
// 9. Wait for drain to complete
|
|
188
|
+
console.log('โฑ๏ธ Step 12: Waiting 10 seconds for file buffer to drain...');
|
|
189
|
+
for (let i = 0; i < 10; i++) {
|
|
190
|
+
await sleep(1000);
|
|
191
|
+
console.log(` ${i + 1}s - Received: ${receivedCount}/${finalProducedCount} (${((receivedCount/finalProducedCount)*100).toFixed(1)}%)`);
|
|
192
|
+
|
|
193
|
+
// Check if we've received all messages
|
|
194
|
+
if (receivedCount >= finalProducedCount) {
|
|
195
|
+
console.log(' โ
All messages received!\n');
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 10. Stop consumer
|
|
201
|
+
console.log('๐ Step 13: Stopping consumer...');
|
|
202
|
+
consumerActive = false;
|
|
203
|
+
await consumePromise;
|
|
204
|
+
console.log('โ
Consumer stopped\n');
|
|
205
|
+
|
|
206
|
+
// 11. Final verification
|
|
207
|
+
console.log('๐ Final Verification:\n');
|
|
208
|
+
console.log(` Total Produced: ${finalProducedCount}`);
|
|
209
|
+
console.log(` Total Received: ${receivedCount}`);
|
|
210
|
+
console.log(` Difference: ${finalProducedCount - receivedCount}`);
|
|
211
|
+
|
|
212
|
+
if (receivedCount === finalProducedCount) {
|
|
213
|
+
console.log('\nโ
SUCCESS: All messages accounted for!');
|
|
214
|
+
console.log(' Maintenance mode works correctly with file buffer.\n');
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
message: `All ${finalProducedCount} messages accounted for during maintenance mode test`
|
|
219
|
+
};
|
|
220
|
+
} else if (receivedCount < finalProducedCount) {
|
|
221
|
+
const missing = finalProducedCount - receivedCount;
|
|
222
|
+
console.log(`\nโ ๏ธ WARNING: Missing ${missing} messages`);
|
|
223
|
+
console.log(' They may still be in the file buffer or queue.');
|
|
224
|
+
console.log(' Check /var/lib/queen/buffers/ for remaining files.\n');
|
|
225
|
+
|
|
226
|
+
// Check buffer status
|
|
227
|
+
const finalStatus = await axios.get(`${API_URL}/api/v1/system/maintenance`);
|
|
228
|
+
console.log(' Buffer status:', finalStatus.data);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
message: `Missing ${missing} messages (produced: ${finalProducedCount}, received: ${receivedCount})`
|
|
233
|
+
};
|
|
234
|
+
} else {
|
|
235
|
+
console.log(`\nโ ERROR: Received more messages than produced! (${receivedCount} > ${finalProducedCount})`);
|
|
236
|
+
console.log(' This should not happen - possible duplicate issue.\n');
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
success: false,
|
|
240
|
+
message: `Received more than produced (${receivedCount} > ${finalProducedCount})`
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.error('\nโ Test failed:', err.message);
|
|
246
|
+
console.error(err.stack);
|
|
247
|
+
|
|
248
|
+
// Clean up
|
|
249
|
+
consumerActive = false;
|
|
250
|
+
|
|
251
|
+
return { success: false, message: `Test threw error: ${err.message}` };
|
|
252
|
+
} finally {
|
|
253
|
+
// Clean up
|
|
254
|
+
consumerActive = false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function sleep(ms) {
|
|
259
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
260
|
+
}
|
|
261
|
+
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
// To pass this test the server needs to
|
|
3
|
+
// be started with RETENTION_INTERVAL=2000
|
|
4
|
+
export async function retentionTest(client) {
|
|
5
|
+
const queueName = 'test-queue-retention-01';
|
|
6
|
+
const queue = await client
|
|
7
|
+
.queue(queueName)
|
|
8
|
+
.config({
|
|
9
|
+
retentionEnabled: true,
|
|
10
|
+
retentionSeconds: 10,
|
|
11
|
+
completedRetentionSeconds: 5
|
|
12
|
+
})
|
|
13
|
+
.create()
|
|
14
|
+
if (!queue.configured) {
|
|
15
|
+
return { success: false, message: 'Queue not created' }
|
|
16
|
+
}
|
|
17
|
+
for (let i = 0; i < 100; i++) {
|
|
18
|
+
const res = await client
|
|
19
|
+
.queue(queueName)
|
|
20
|
+
.push([{ data: { message: i } }])
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await new Promise(resolve => setTimeout(resolve, 15000))
|
|
24
|
+
|
|
25
|
+
const messages = await client
|
|
26
|
+
.queue(queueName)
|
|
27
|
+
.batch(100)
|
|
28
|
+
.wait(false)
|
|
29
|
+
.pop()
|
|
30
|
+
|
|
31
|
+
if (messages.length === 0) {
|
|
32
|
+
return { success: true, message: 'Messages cleaned up' }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { success: false }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
// To pass this test the server needs to
|
|
40
|
+
// be started with RETENTION_INTERVAL=2000
|
|
41
|
+
export async function retentionTestMaxTime(client) {
|
|
42
|
+
const queueName = 'test-queue-retention-02';
|
|
43
|
+
const queue = await client
|
|
44
|
+
.queue(queueName)
|
|
45
|
+
.config({ retentionEnabled: true, maxWaitTimeSeconds: 10 })
|
|
46
|
+
.create()
|
|
47
|
+
if (!queue.configured) {
|
|
48
|
+
return { success: false, message: 'Queue not created with retention enabled' }
|
|
49
|
+
}
|
|
50
|
+
for (let i = 0; i < 100; i++) {
|
|
51
|
+
const res = await client
|
|
52
|
+
.queue(queueName)
|
|
53
|
+
.push([{ data: { message: i } }])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await new Promise(resolve => setTimeout(resolve, 15000))
|
|
57
|
+
|
|
58
|
+
const messages = await client
|
|
59
|
+
.queue(queueName)
|
|
60
|
+
.batch(100)
|
|
61
|
+
.wait(false)
|
|
62
|
+
.pop()
|
|
63
|
+
|
|
64
|
+
if (messages.length === 0) {
|
|
65
|
+
return { success: true, message: 'Messages cleaned up' }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { success: false }
|
|
69
|
+
}
|
package/client-js/test-v2/run.js
CHANGED
|
@@ -9,6 +9,8 @@ import * as dlqTests from './dlq.js'
|
|
|
9
9
|
import * as completeTests from './complete.js'
|
|
10
10
|
import * as transactionTests from './transaction.js'
|
|
11
11
|
import * as subscriptionTests from './subscription.js'
|
|
12
|
+
import * as maintenanceTests from './maintenance.js'
|
|
13
|
+
import * as retentionTests from './retention.js'
|
|
12
14
|
import * as errorHandlingTests from './ai_error_handling.js'
|
|
13
15
|
import * as leaseRenewalTests from './ai_lease_renewal.js'
|
|
14
16
|
import * as resourcesTests from './ai_resources.js'
|
|
@@ -17,6 +19,7 @@ import * as priorityTests from './ai_priority.js'
|
|
|
17
19
|
import * as ttlRetentionTests from './ai_ttl_retention.js'
|
|
18
20
|
import * as mixedScenariosTests from './ai_mixed_scenarios.js'
|
|
19
21
|
|
|
22
|
+
|
|
20
23
|
// Test configuration
|
|
21
24
|
export const TEST_CONFIG = {
|
|
22
25
|
baseUrls: ['http://localhost:6632'],
|
|
@@ -92,7 +95,9 @@ async function main() {
|
|
|
92
95
|
dlqTests,
|
|
93
96
|
completeTests,
|
|
94
97
|
transactionTests,
|
|
95
|
-
subscriptionTests
|
|
98
|
+
subscriptionTests,
|
|
99
|
+
retentionTests,
|
|
100
|
+
maintenanceTests
|
|
96
101
|
]
|
|
97
102
|
|
|
98
103
|
const aiTests = [
|
|
@@ -155,6 +160,7 @@ async function main() {
|
|
|
155
160
|
|
|
156
161
|
for (const test of testsToRun) {
|
|
157
162
|
try {
|
|
163
|
+
console.log('Running test:', test.name)
|
|
158
164
|
const result = await test(client)
|
|
159
165
|
const message = result.message || 'Test completed successfully'
|
|
160
166
|
addRestResult(result.success, test.name, message)
|