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
package/CACHE.md ADDED
@@ -0,0 +1,519 @@
1
+ # Queen Internal Event Propagation System
2
+
3
+ ## Overview
4
+ A general-purpose internal event system using Queen's own infrastructure to propagate system events (configuration changes, cache invalidations, etc.) across multiple server instances.
5
+
6
+ ## Architecture
7
+
8
+ ### Event Flow
9
+ ```
10
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
11
+ │ Server #1 │ │ Server #2 │ │ Server #3 │
12
+ │ │ │ │ │ │
13
+ │ Event Producer │ │ Event Consumer │ │ Event Consumer │
14
+ │ Event Consumer │ │ │ │ │
15
+ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘
16
+ │ │ │
17
+ └───────────┬───────────┴───────────────────────┘
18
+ ▼
19
+ ┌───────────────────────────┐
20
+ │ __system_events__ │
21
+ │ Queue │
22
+ └───────────────────────────┘
23
+ ```
24
+
25
+ ### Event Structure
26
+ ```javascript
27
+ {
28
+ eventType: 'queue.updated', // queue.created, queue.updated, queue.deleted
29
+ entityType: 'queue', // queue, partition, consumer_group
30
+ entityId: 'user-tasks', // queue name or other identifier
31
+ changes: { // What changed
32
+ retryLimit: { old: 3, new: 5 },
33
+ priority: { old: 0, new: 10 }
34
+ },
35
+ timestamp: 1234567890,
36
+ sourceServer: 'server_1234',
37
+ version: 1 // Event schema version
38
+ }
39
+ ```
40
+
41
+ ## Implementation
42
+
43
+ ### Phase 1: Core Event System
44
+
45
+ #### 1.1 System Event Manager
46
+ **File**: `src/managers/systemEventManager.js`
47
+ ```javascript
48
+ import { generateUUID } from '../utils/uuid.js';
49
+
50
+ export const SYSTEM_QUEUE = '__system_events__';
51
+
52
+ export const EventTypes = {
53
+ // Queue events
54
+ QUEUE_CREATED: 'queue.created',
55
+ QUEUE_UPDATED: 'queue.updated',
56
+ QUEUE_DELETED: 'queue.deleted',
57
+
58
+ // Partition events
59
+ PARTITION_CREATED: 'partition.created',
60
+ PARTITION_DELETED: 'partition.deleted',
61
+
62
+ // Future events
63
+ CONSUMER_GROUP_CREATED: 'consumer_group.created',
64
+ CONSUMER_GROUP_UPDATED: 'consumer_group.updated'
65
+ };
66
+
67
+ export class SystemEventManager {
68
+ constructor(pool, serverInstanceId) {
69
+ this.pool = pool;
70
+ this.serverInstanceId = serverInstanceId;
71
+ this.handlers = new Map();
72
+ this.eventQueue = [];
73
+ this.batchTimer = null;
74
+ }
75
+
76
+ // Register event handlers
77
+ on(eventType, handler) {
78
+ if (!this.handlers.has(eventType)) {
79
+ this.handlers.set(eventType, []);
80
+ }
81
+ this.handlers.get(eventType).push(handler);
82
+ }
83
+
84
+ // Emit event locally and to system queue
85
+ async emit(eventType, data) {
86
+ const event = {
87
+ id: generateUUID(),
88
+ eventType,
89
+ ...data,
90
+ timestamp: Date.now(),
91
+ sourceServer: this.serverInstanceId,
92
+ version: 1
93
+ };
94
+
95
+ // Handle locally first (immediate consistency for originating server)
96
+ await this.handleEventLocally(event);
97
+
98
+ // Queue for batch publishing
99
+ this.eventQueue.push(event);
100
+ this.scheduleBatch();
101
+ }
102
+
103
+ // Process incoming event from system queue
104
+ async processSystemEvent(event) {
105
+ // Skip own events (already processed locally)
106
+ if (event.sourceServer === this.serverInstanceId) {
107
+ return;
108
+ }
109
+
110
+ await this.handleEventLocally(event);
111
+ }
112
+
113
+ async handleEventLocally(event) {
114
+ const handlers = this.handlers.get(event.eventType) || [];
115
+ for (const handler of handlers) {
116
+ try {
117
+ await handler(event);
118
+ } catch (error) {
119
+ console.error(`Handler failed for ${event.eventType}:`, error);
120
+ }
121
+ }
122
+ }
123
+
124
+ scheduleBatch() {
125
+ if (this.batchTimer) return;
126
+
127
+ this.batchTimer = setTimeout(() => {
128
+ this.publishBatch();
129
+ }, 10); // 10ms batching
130
+ }
131
+
132
+ async publishBatch() {
133
+ if (this.eventQueue.length === 0) return;
134
+
135
+ const events = [...this.eventQueue];
136
+ this.eventQueue = [];
137
+ this.batchTimer = null;
138
+
139
+ try {
140
+ // Direct database insert to avoid circular dependency
141
+ const client = await this.pool.connect();
142
+ try {
143
+ for (const event of events) {
144
+ await this.insertSystemEvent(client, event);
145
+ }
146
+ } finally {
147
+ client.release();
148
+ }
149
+ } catch (error) {
150
+ console.error('Failed to publish system events:', error);
151
+ }
152
+ }
153
+
154
+ async insertSystemEvent(client, event) {
155
+ // Direct insert bypassing queue manager to avoid circular dependency
156
+ await client.query(`
157
+ INSERT INTO queen.messages (
158
+ id, transaction_id, partition_id, payload,
159
+ created_at, trace_id
160
+ )
161
+ SELECT
162
+ $1, $2, p.id, $3, NOW(), $4
163
+ FROM queen.partitions p
164
+ JOIN queen.queues q ON p.queue_id = q.id
165
+ WHERE q.name = $5 AND p.name = 'Default'
166
+ `, [
167
+ generateUUID(),
168
+ generateUUID(),
169
+ JSON.stringify(event),
170
+ event.id,
171
+ SYSTEM_QUEUE
172
+ ]);
173
+ }
174
+ }
175
+ ```
176
+
177
+ #### 1.2 Server Startup Synchronization
178
+ **File**: `src/services/startupSync.js`
179
+ ```javascript
180
+ export async function syncSystemEvents(client, eventManager) {
181
+ console.log('Synchronizing system events...');
182
+
183
+ let processed = 0;
184
+ let hasMore = true;
185
+
186
+ while (hasMore) {
187
+ // Pop and process system events in batches
188
+ const result = await client.pop({
189
+ queue: SYSTEM_QUEUE,
190
+ batch: 100,
191
+ wait: false
192
+ });
193
+
194
+ if (!result.messages || result.messages.length === 0) {
195
+ hasMore = false;
196
+ break;
197
+ }
198
+
199
+ for (const message of result.messages) {
200
+ await eventManager.processSystemEvent(message.payload);
201
+ await client.ack(message.transactionId, 'completed');
202
+ processed++;
203
+ }
204
+ }
205
+
206
+ console.log(`Synchronized ${processed} system events`);
207
+ return processed;
208
+ }
209
+ ```
210
+
211
+ #### 1.3 Resource Cache Integration
212
+ **File**: `src/managers/resourceCache.js` (additions)
213
+ ```javascript
214
+ export const createResourceCache = () => {
215
+ // ... existing code ...
216
+
217
+ // Register event handlers
218
+ const registerEventHandlers = (eventManager) => {
219
+ // Invalidate cache on queue changes
220
+ eventManager.on(EventTypes.QUEUE_UPDATED, (event) => {
221
+ invalidateQueue(event.entityId);
222
+ });
223
+
224
+ eventManager.on(EventTypes.QUEUE_DELETED, (event) => {
225
+ invalidateQueue(event.entityId);
226
+ });
227
+
228
+ eventManager.on(EventTypes.PARTITION_CREATED, (event) => {
229
+ invalidate(event.queueName, event.entityId);
230
+ });
231
+
232
+ eventManager.on(EventTypes.PARTITION_DELETED, (event) => {
233
+ invalidate(event.queueName, event.entityId);
234
+ });
235
+ };
236
+
237
+ return {
238
+ // ... existing methods ...
239
+ registerEventHandlers
240
+ };
241
+ };
242
+ ```
243
+
244
+ ### Phase 2: Queue Manager Integration
245
+
246
+ #### 2.1 Emit Events on Configuration Changes
247
+ **File**: `src/managers/queueManagerOptimized.js` (modifications)
248
+ ```javascript
249
+ const configureQueue = async (queueName, options = {}, namespace = null, task = null) => {
250
+ return withTransaction(pool, async (client) => {
251
+ // Check if queue exists (for update vs create detection)
252
+ const existingQueue = await client.query(
253
+ 'SELECT * FROM queen.queues WHERE name = $1',
254
+ [queueName]
255
+ );
256
+ const isUpdate = existingQueue.rows.length > 0;
257
+
258
+ // ... existing configuration code ...
259
+
260
+ // Detect what changed
261
+ const changes = {};
262
+ if (isUpdate) {
263
+ const old = existingQueue.rows[0];
264
+ for (const [key, value] of Object.entries(options)) {
265
+ const dbKey = optionMappings[key];
266
+ if (old[dbKey] !== value) {
267
+ changes[key] = { old: old[dbKey], new: value };
268
+ }
269
+ }
270
+ }
271
+
272
+ // Emit appropriate event
273
+ await eventManager.emit(
274
+ isUpdate ? EventTypes.QUEUE_UPDATED : EventTypes.QUEUE_CREATED,
275
+ {
276
+ entityType: 'queue',
277
+ entityId: queueName,
278
+ changes: isUpdate ? changes : options,
279
+ namespace,
280
+ task
281
+ }
282
+ );
283
+
284
+ // Local cache invalidation (immediate)
285
+ resourceCache.invalidateQueue(queueName);
286
+
287
+ return result;
288
+ });
289
+ };
290
+ ```
291
+
292
+ ### Phase 3: Server Integration
293
+
294
+ #### 3.1 Server Initialization
295
+ **File**: `src/server.js` (modifications)
296
+ ```javascript
297
+ import { SystemEventManager, SYSTEM_QUEUE } from './managers/systemEventManager.js';
298
+ import { syncSystemEvents } from './services/startupSync.js';
299
+
300
+ // Generate unique server instance ID
301
+ const SERVER_INSTANCE_ID = `server_${process.pid}_${Date.now()}`;
302
+
303
+ // Initialize system event manager
304
+ const systemEventManager = new SystemEventManager(pool, SERVER_INSTANCE_ID);
305
+
306
+ // Register cache event handlers
307
+ resourceCache.registerEventHandlers(systemEventManager);
308
+
309
+ // Initialize system queue
310
+ async function initializeSystemQueue() {
311
+ try {
312
+ // Direct database operation to create system queue
313
+ await pool.query(`
314
+ INSERT INTO queen.queues (name, ttl, priority, max_queue_size)
315
+ VALUES ($1, 300, 100, 10000)
316
+ ON CONFLICT (name) DO UPDATE SET
317
+ ttl = EXCLUDED.ttl,
318
+ priority = EXCLUDED.priority,
319
+ max_queue_size = EXCLUDED.max_queue_size
320
+ `, [SYSTEM_QUEUE]);
321
+
322
+ await pool.query(`
323
+ INSERT INTO queen.partitions (queue_id, name)
324
+ SELECT id, 'Default' FROM queen.queues WHERE name = $1
325
+ ON CONFLICT (queue_id, name) DO NOTHING
326
+ `, [SYSTEM_QUEUE]);
327
+ } catch (error) {
328
+ console.error('Failed to initialize system queue:', error);
329
+ throw error;
330
+ }
331
+ }
332
+
333
+ // Startup sequence
334
+ async function startServer() {
335
+ console.log('Starting Queen server...');
336
+
337
+ // 1. Initialize database connection
338
+ await testDatabaseConnection();
339
+
340
+ // 2. Initialize system queue
341
+ await initializeSystemQueue();
342
+
343
+ // 3. Create temporary client for synchronization
344
+ const syncClient = createQueenClient({
345
+ baseUrl: `http://localhost:${PORT}`
346
+ });
347
+
348
+ // 4. Synchronize system events (catch up on missed events)
349
+ await syncSystemEvents(syncClient, systemEventManager);
350
+
351
+ // 5. Start consuming system events
352
+ const systemEventConsumer = syncClient.consume({
353
+ queue: SYSTEM_QUEUE,
354
+ consumerGroup: SERVER_INSTANCE_ID,
355
+ handler: async (message) => {
356
+ await systemEventManager.processSystemEvent(message.payload);
357
+ },
358
+ options: {
359
+ batch: 10,
360
+ wait: true
361
+ }
362
+ });
363
+
364
+ // 6. Start HTTP server
365
+ app.listen(PORT, '0.0.0.0', (listenSocket) => {
366
+ if (listenSocket) {
367
+ console.log(`Queen server running on port ${PORT}`);
368
+ console.log(`Server Instance ID: ${SERVER_INSTANCE_ID}`);
369
+ }
370
+ });
371
+ }
372
+
373
+ // Graceful shutdown
374
+ process.on('SIGTERM', async () => {
375
+ console.log('Shutting down...');
376
+ systemEventConsumer?.stop();
377
+ await pool.end();
378
+ process.exit(0);
379
+ });
380
+ ```
381
+
382
+ #### 3.2 Bypass Cache for System Queue
383
+ **File**: `src/managers/queueManagerOptimized.js` (modification)
384
+ ```javascript
385
+ const ensureResources = async (client, queueName, partitionName = 'Default', namespace = null, task = null) => {
386
+ // CRITICAL: System queues bypass cache completely
387
+ if (queueName.startsWith('__system_')) {
388
+ // Direct database query for system queues
389
+ const result = await client.query(`
390
+ SELECT
391
+ q.id as queue_id,
392
+ q.name as queue_name,
393
+ p.id as partition_id,
394
+ q.*
395
+ FROM queen.queues q
396
+ JOIN queen.partitions p ON p.queue_id = q.id
397
+ WHERE q.name = $1 AND p.name = $2
398
+ `, [queueName, partitionName]);
399
+
400
+ if (result.rows.length === 0) {
401
+ throw new Error(`System queue ${queueName} not found`);
402
+ }
403
+
404
+ const row = result.rows[0];
405
+ return {
406
+ queueId: row.queue_id,
407
+ queueName: row.queue_name,
408
+ partitionId: row.partition_id,
409
+ queueConfig: {
410
+ leaseTime: row.lease_time,
411
+ retryLimit: row.retry_limit,
412
+ // ... other config fields
413
+ },
414
+ encryptionEnabled: false, // System queues never encrypted
415
+ maxWaitTimeSeconds: row.max_wait_time_seconds
416
+ };
417
+ }
418
+
419
+ // ... existing cache logic for regular queues ...
420
+ };
421
+ ```
422
+
423
+ ## Configuration
424
+
425
+ ### Environment Variables
426
+ ```bash
427
+ # System event configuration
428
+ QUEEN_SYSTEM_EVENTS_ENABLED=true # Enable system event propagation
429
+ QUEEN_SYSTEM_EVENTS_BATCH_MS=10 # Batching window for events
430
+ QUEEN_SYSTEM_EVENTS_SYNC_TIMEOUT=30000 # Timeout for startup synchronization
431
+
432
+ # Cache configuration (unchanged)
433
+ QUEEN_CACHE_TTL=60000 # Cache TTL when events are enabled
434
+ ```
435
+
436
+ ## Monitoring
437
+
438
+ ### Key Metrics
439
+ ```javascript
440
+ // System event metrics
441
+ {
442
+ systemEventsPublished: counter,
443
+ systemEventsProcessed: counter,
444
+ systemEventLag: gauge,
445
+ startupSyncDuration: histogram,
446
+ eventProcessingErrors: counter
447
+ }
448
+ ```
449
+
450
+ ### Health Checks
451
+ ```javascript
452
+ // Add to health endpoint
453
+ async function checkSystemEventHealth() {
454
+ const result = await pool.query(`
455
+ SELECT COUNT(*) as pending
456
+ FROM queen.messages m
457
+ JOIN queen.partitions p ON m.partition_id = p.id
458
+ JOIN queen.queues q ON p.queue_id = q.id
459
+ WHERE q.name = $1
460
+ AND m.status = 'pending'
461
+ `, [SYSTEM_QUEUE]);
462
+
463
+ return {
464
+ systemEventQueueDepth: result.rows[0].pending,
465
+ healthy: result.rows[0].pending < 1000
466
+ };
467
+ }
468
+ ```
469
+
470
+ ## Migration Plan
471
+
472
+ ### Step 1: Deploy with Feature Flag Off
473
+ ```bash
474
+ QUEEN_SYSTEM_EVENTS_ENABLED=false
475
+ ```
476
+
477
+ ### Step 2: Enable and Monitor
478
+ ```bash
479
+ QUEEN_SYSTEM_EVENTS_ENABLED=true
480
+ QUEEN_CACHE_TTL=60000 # Re-enable caching
481
+ ```
482
+
483
+ ### Step 3: Optimize
484
+ - Increase cache TTL once confidence is established
485
+ - Tune batching windows based on event volume
486
+
487
+ ## Rollback Plan
488
+
489
+ ### Immediate Rollback
490
+ ```bash
491
+ # Disable system events
492
+ QUEEN_SYSTEM_EVENTS_ENABLED=false
493
+ QUEEN_CACHE_TTL=0 # Disable cache
494
+ ```
495
+
496
+ ### Gradual Rollback
497
+ ```bash
498
+ # Keep events but reduce cache TTL
499
+ QUEEN_SYSTEM_EVENTS_ENABLED=true
500
+ QUEEN_CACHE_TTL=5000 # 5 second cache
501
+ ```
502
+
503
+ ## Future Extensions
504
+
505
+ The event system can be extended for:
506
+ - **Audit Logging**: Track all configuration changes
507
+ - **Metrics Collection**: Aggregate queue statistics
508
+ - **Distributed Locks**: Coordinate operations across servers
509
+ - **Leader Election**: Designate primary server for certain operations
510
+ - **Schema Migrations**: Coordinate database updates
511
+ - **Feature Flags**: Propagate feature flag changes
512
+
513
+ ## Success Criteria
514
+
515
+ - [ ] System events published for all queue operations
516
+ - [ ] All servers process events and update caches
517
+ - [ ] Startup synchronization completes in < 5 seconds
518
+ - [ ] No circular dependencies with system queue
519
+ - [ ] Cache consistency across all servers within 100ms