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,437 @@
1
+ <template>
2
+ <div class="messages-view">
3
+ <Card class="messages-card">
4
+ <template #content>
5
+ <DataTable
6
+ :value="messages"
7
+ :loading="loading"
8
+ :paginator="true"
9
+ :rows="20"
10
+ :rowsPerPageOptions="[20, 50, 100]"
11
+ responsiveLayout="scroll"
12
+ class="dark-table-v3"
13
+ >
14
+ <template #header>
15
+ <div class="table-header">
16
+ <h2>Message Browser</h2>
17
+ <div class="table-actions">
18
+ <Select
19
+ v-model="selectedQueue"
20
+ :options="queues || []"
21
+ optionLabel="name"
22
+ optionValue="name"
23
+ placeholder="Select Queue"
24
+ class="queue-dropdown"
25
+ @change="fetchMessages"
26
+ />
27
+ <Select
28
+ v-model="selectedStatus"
29
+ :options="statusOptions"
30
+ optionLabel="label"
31
+ optionValue="value"
32
+ placeholder="Status"
33
+ class="status-dropdown"
34
+ @change="fetchMessages"
35
+ />
36
+ <Button
37
+ icon="pi pi-refresh"
38
+ @click="fetchMessages"
39
+ :loading="loading"
40
+ class="p-button-text"
41
+ v-tooltip="'Refresh'"
42
+ />
43
+ </div>
44
+ </div>
45
+ </template>
46
+
47
+ <Column field="transactionId" header="Transaction ID">
48
+ <template #body="{ data }">
49
+ <span class="transaction-id">{{ data.transactionId.substring(0, 8) }}...</span>
50
+ </template>
51
+ </Column>
52
+
53
+ <Column field="queue" header="Queue">
54
+ <template #body="{ data }">
55
+ <div class="queue-name-cell">
56
+ <div class="queue-icon-small">Q</div>
57
+ <span>{{ data.queue }}</span>
58
+ </div>
59
+ </template>
60
+ </Column>
61
+
62
+ <Column field="status" header="Status">
63
+ <template #body="{ data }">
64
+ <span :class="`status-${data.status}`">{{ data.status }}</span>
65
+ </template>
66
+ </Column>
67
+
68
+ <Column field="created" header="Created">
69
+ <template #body="{ data }">
70
+ <span class="timestamp">{{ formatDate(data.created) }}</span>
71
+ </template>
72
+ </Column>
73
+
74
+ <Column field="attempts" header="Attempts">
75
+ <template #body="{ data }">
76
+ <Tag :value="data.attempts || 0" :severity="getAttemptSeverity(data.attempts)" />
77
+ </template>
78
+ </Column>
79
+
80
+ <Column header="Actions" :exportable="false">
81
+ <template #body="{ data }">
82
+ <Button
83
+ icon="pi pi-eye"
84
+ class="p-button-text p-button-sm action-btn"
85
+ @click="viewMessage(data)"
86
+ v-tooltip="'View Details'"
87
+ />
88
+ <Button
89
+ icon="pi pi-replay"
90
+ class="p-button-text p-button-sm action-btn"
91
+ @click="retryMessage(data)"
92
+ v-tooltip="'Retry'"
93
+ :disabled="data.status !== 'failed'"
94
+ />
95
+ </template>
96
+ </Column>
97
+ </DataTable>
98
+ </template>
99
+ </Card>
100
+
101
+ <!-- Message Detail Dialog -->
102
+ <Dialog
103
+ v-model:visible="showDetail"
104
+ :header="`Message: ${selectedMessage?.transactionId}`"
105
+ :style="{ width: '50vw' }"
106
+ :modal="true"
107
+ class="message-dialog"
108
+ >
109
+ <div v-if="selectedMessage" class="message-detail">
110
+ <div class="detail-row">
111
+ <span class="detail-label">Transaction ID:</span>
112
+ <span class="detail-value">{{ selectedMessage.transactionId }}</span>
113
+ </div>
114
+ <div class="detail-row">
115
+ <span class="detail-label">Queue:</span>
116
+ <span class="detail-value">{{ selectedMessage.queue }}</span>
117
+ </div>
118
+ <div class="detail-row">
119
+ <span class="detail-label">Status:</span>
120
+ <span :class="`status-${selectedMessage.status}`">{{ selectedMessage.status }}</span>
121
+ </div>
122
+ <div class="detail-row">
123
+ <span class="detail-label">Created:</span>
124
+ <span class="detail-value">{{ formatDate(selectedMessage.created) }}</span>
125
+ </div>
126
+ <div class="detail-row">
127
+ <span class="detail-label">Payload:</span>
128
+ </div>
129
+ <pre class="payload-viewer">{{ JSON.stringify(selectedMessage.payload, null, 2) }}</pre>
130
+ </div>
131
+ </Dialog>
132
+ </div>
133
+ </template>
134
+
135
+ <script setup>
136
+ import { ref, onMounted } from 'vue'
137
+ import { useToast } from 'primevue/usetoast'
138
+ import DataTable from 'primevue/datatable'
139
+ import Column from 'primevue/column'
140
+ import Button from 'primevue/button'
141
+ import Select from 'primevue/select'
142
+ import Tag from 'primevue/tag'
143
+ import Card from 'primevue/card'
144
+ import Dialog from 'primevue/dialog'
145
+ import api from '../services/api.js'
146
+
147
+ const toast = useToast()
148
+ const loading = ref(false)
149
+ const messages = ref([])
150
+ const queues = ref([])
151
+ const selectedQueue = ref(null)
152
+ const selectedStatus = ref(null)
153
+ const selectedMessage = ref(null)
154
+ const showDetail = ref(false)
155
+
156
+ const statusOptions = [
157
+ { label: 'All', value: null },
158
+ { label: 'Pending', value: 'pending' },
159
+ { label: 'Processing', value: 'processing' },
160
+ { label: 'Completed', value: 'completed' },
161
+ { label: 'Failed', value: 'failed' },
162
+ { label: 'Dead Letter', value: 'dead_letter' }
163
+ ]
164
+
165
+ const fetchQueues = async () => {
166
+ try {
167
+ const data = await api.getQueues()
168
+ queues.value = data.queues || []
169
+ } catch (error) {
170
+ console.error('Failed to fetch queues:', error)
171
+ }
172
+ }
173
+
174
+ const fetchMessages = async () => {
175
+ try {
176
+ loading.value = true
177
+
178
+ // Build API parameters
179
+ const params = {}
180
+ if (selectedQueue.value) {
181
+ params.queue = selectedQueue.value
182
+ }
183
+ if (selectedStatus.value) {
184
+ params.status = selectedStatus.value
185
+ }
186
+
187
+ // Fetch real messages from API
188
+ const data = await api.getMessages(params)
189
+
190
+ if (data && Array.isArray(data.messages)) {
191
+ // Process real API data
192
+ messages.value = data.messages.map(message => ({
193
+ transactionId: message.transactionId || message.id,
194
+ queue: message.queue,
195
+ status: message.status,
196
+ created: message.createdAt || message.created,
197
+ attempts: message.attempts || message.retryCount || 0,
198
+ payload: message.payload || message.data || {}
199
+ }))
200
+ } else if (data && Array.isArray(data)) {
201
+ // Handle if API returns array directly
202
+ messages.value = data.map(message => ({
203
+ transactionId: message.transactionId || message.id,
204
+ queue: message.queue,
205
+ status: message.status,
206
+ created: message.createdAt || message.created,
207
+ attempts: message.attempts || message.retryCount || 0,
208
+ payload: message.payload || message.data || {}
209
+ }))
210
+ } else {
211
+ // No data available
212
+ messages.value = []
213
+ }
214
+
215
+ } catch (error) {
216
+ console.error('Failed to fetch messages:', error)
217
+ toast.add({
218
+ severity: 'error',
219
+ summary: 'Error',
220
+ detail: 'Failed to load messages',
221
+ life: 3000
222
+ })
223
+
224
+ // Show empty state on error
225
+ messages.value = []
226
+ } finally {
227
+ loading.value = false
228
+ }
229
+ }
230
+
231
+ const viewMessage = (message) => {
232
+ selectedMessage.value = message
233
+ showDetail.value = true
234
+ }
235
+
236
+ const retryMessage = async (message) => {
237
+ try {
238
+ // API call to retry message
239
+ toast.add({
240
+ severity: 'success',
241
+ summary: 'Success',
242
+ detail: 'Message queued for retry',
243
+ life: 3000
244
+ })
245
+ } catch (error) {
246
+ toast.add({
247
+ severity: 'error',
248
+ summary: 'Error',
249
+ detail: 'Failed to retry message',
250
+ life: 3000
251
+ })
252
+ }
253
+ }
254
+
255
+ const formatDate = (dateString) => {
256
+ if (!dateString) return '-'
257
+ const date = new Date(dateString)
258
+ return date.toLocaleString()
259
+ }
260
+
261
+ const getAttemptSeverity = (attempts) => {
262
+ if (!attempts || attempts === 0) return 'success'
263
+ if (attempts <= 2) return 'warning'
264
+ return 'danger'
265
+ }
266
+
267
+ onMounted(() => {
268
+ fetchQueues()
269
+ fetchMessages()
270
+ })
271
+ </script>
272
+
273
+ <style scoped>
274
+ .messages-view {
275
+ padding: 0;
276
+ }
277
+
278
+ .messages-card {
279
+ background: transparent !important;
280
+ border: 1px solid rgba(255, 255, 255, 0.1);
281
+ border-radius: 16px;
282
+ box-shadow: none !important;
283
+ }
284
+
285
+ :deep(.p-card-content) {
286
+ padding: 0;
287
+ }
288
+
289
+ .table-header {
290
+ display: flex;
291
+ justify-content: space-between;
292
+ align-items: center;
293
+ padding: 1.5rem;
294
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
295
+ }
296
+
297
+ .table-header h2 {
298
+ font-size: 1.25rem;
299
+ font-weight: 600;
300
+ color: var(--surface-700);
301
+ margin: 0;
302
+ }
303
+
304
+ .table-actions {
305
+ display: flex;
306
+ gap: 1rem;
307
+ align-items: center;
308
+ }
309
+
310
+ .queue-dropdown,
311
+ .status-dropdown {
312
+ min-width: 150px;
313
+ }
314
+
315
+ :deep(.queue-dropdown .p-dropdown),
316
+ :deep(.status-dropdown .p-dropdown) {
317
+ background: rgba(255, 255, 255, 0.05);
318
+ border: 1px solid rgba(255, 255, 255, 0.1);
319
+ }
320
+
321
+ .transaction-id {
322
+ font-family: 'Courier New', monospace;
323
+ font-size: 0.875rem;
324
+ color: var(--primary-500);
325
+ }
326
+
327
+ .queue-name-cell {
328
+ display: flex;
329
+ align-items: center;
330
+ gap: 0.5rem;
331
+ }
332
+
333
+ .queue-icon-small {
334
+ width: 24px;
335
+ height: 24px;
336
+ border-radius: 6px;
337
+ background: linear-gradient(135deg, #ec4899 0%, #db2777 100%);
338
+ display: flex;
339
+ align-items: center;
340
+ justify-content: center;
341
+ color: white;
342
+ font-weight: 600;
343
+ font-size: 0.75rem;
344
+ }
345
+
346
+ .timestamp {
347
+ color: var(--surface-500);
348
+ font-size: 0.875rem;
349
+ }
350
+
351
+ .action-btn {
352
+ color: var(--surface-500) !important;
353
+ }
354
+
355
+ .action-btn:hover {
356
+ background: rgba(236, 72, 153, 0.1) !important;
357
+ color: var(--primary-500) !important;
358
+ }
359
+
360
+ /* Message Detail Dialog */
361
+ .message-dialog :deep(.p-dialog) {
362
+ background: var(--surface-50);
363
+ border: 1px solid rgba(255, 255, 255, 0.1);
364
+ }
365
+
366
+ .message-dialog :deep(.p-dialog-header) {
367
+ background: var(--surface-0);
368
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
369
+ }
370
+
371
+ .message-detail {
372
+ padding: 1rem;
373
+ }
374
+
375
+ .detail-row {
376
+ display: flex;
377
+ align-items: center;
378
+ gap: 1rem;
379
+ margin-bottom: 1rem;
380
+ }
381
+
382
+ .detail-label {
383
+ font-weight: 600;
384
+ color: var(--surface-400);
385
+ min-width: 120px;
386
+ }
387
+
388
+ .detail-value {
389
+ color: var(--surface-600);
390
+ }
391
+
392
+ .payload-viewer {
393
+ background: var(--surface-0);
394
+ border: 1px solid rgba(255, 255, 255, 0.1);
395
+ border-radius: 8px;
396
+ padding: 1rem;
397
+ color: var(--surface-600);
398
+ font-family: 'Courier New', monospace;
399
+ font-size: 0.875rem;
400
+ overflow-x: auto;
401
+ }
402
+
403
+ /* DataTable dark theme overrides */
404
+ :deep(.dark-table-v3) {
405
+ background: transparent !important;
406
+ border: none !important;
407
+ }
408
+
409
+ :deep(.dark-table-v3 .p-datatable-header) {
410
+ background: transparent !important;
411
+ border: none !important;
412
+ }
413
+
414
+ :deep(.dark-table-v3 .p-datatable-thead > tr > th) {
415
+ background: transparent !important;
416
+ color: var(--surface-400) !important;
417
+ border-color: rgba(255, 255, 255, 0.1) !important;
418
+ }
419
+
420
+ :deep(.dark-table-v3 .p-datatable-tbody > tr) {
421
+ background: transparent !important;
422
+ }
423
+
424
+ :deep(.dark-table-v3 .p-datatable-tbody > tr:hover) {
425
+ background: rgba(236, 72, 153, 0.05) !important;
426
+ }
427
+
428
+ :deep(.dark-table-v3 .p-datatable-tbody > tr > td) {
429
+ color: var(--surface-600) !important;
430
+ border-color: rgba(255, 255, 255, 0.05) !important;
431
+ }
432
+
433
+ :deep(.p-paginator) {
434
+ background: transparent !important;
435
+ border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
436
+ }
437
+ </style>