queen-mq 0.15.0 → 0.16.0-beta.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 +13 -10
- package/client-v2/Queen.js +5 -2
- package/client-v2/README.md +9 -6
- package/client-v2/utils/logger.js +50 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Queen MQ is a PostgreSQL-backed message queue system with a powerful feature set
|
|
|
22
22
|
- **Consumer Groups** - Kafka-style consumer groups for scalability
|
|
23
23
|
- **Flexible Semantics** - Exactly-once, at-least-once, and at-most-once delivery
|
|
24
24
|
- **Transactions** - Atomic operations across push and ack
|
|
25
|
-
- **High Performance**
|
|
25
|
+
- **High Performance** — 104K msg/s push, 165K msg/s fan-out with consumer groups on a single 32-core node ([benchmarks](https://github.com/queen-mq/queen/tree/master/benchmark-queen/2026-04-26))
|
|
26
26
|
- **Subscription Modes** - Process from beginning, new messages only, or from timestamp
|
|
27
27
|
- **Dead Letter Queue** - Automatic failure handling and monitoring
|
|
28
28
|
- **Message Tracing** - Debug distributed workflows with trace timelines
|
|
@@ -179,7 +179,7 @@ const queen = new Queen({
|
|
|
179
179
|
urls: ['http://server1:6632', 'http://server2:6632'],
|
|
180
180
|
timeoutMillis: 30000,
|
|
181
181
|
retryAttempts: 3,
|
|
182
|
-
loadBalancingStrategy: '
|
|
182
|
+
loadBalancingStrategy: 'affinity', // or 'round-robin', 'session'
|
|
183
183
|
enableFailover: true
|
|
184
184
|
})
|
|
185
185
|
```
|
|
@@ -564,8 +564,10 @@ await queen.close() // Flush buffers and close connections
|
|
|
564
564
|
timeoutMillis: 30000,
|
|
565
565
|
retryAttempts: 3,
|
|
566
566
|
retryDelayMillis: 1000,
|
|
567
|
-
loadBalancingStrategy: 'round-robin'
|
|
568
|
-
|
|
567
|
+
loadBalancingStrategy: 'affinity', // 'affinity' | 'round-robin' | 'session'
|
|
568
|
+
affinityHashRing: 128,
|
|
569
|
+
enableFailover: true,
|
|
570
|
+
healthRetryAfterMillis: 5000
|
|
569
571
|
}
|
|
570
572
|
```
|
|
571
573
|
|
|
@@ -653,17 +655,18 @@ const messages: Message<OrderData>[] = await queen.queue('orders').pop()
|
|
|
653
655
|
|
|
654
656
|
## Documentation
|
|
655
657
|
|
|
656
|
-
- **[Complete V2 Guide](client-v2/README.md)**
|
|
657
|
-
- **[HTTP API Reference](https://github.com/
|
|
658
|
-
- **[Server Guide](https://github.com/
|
|
659
|
-
- **[Architecture
|
|
658
|
+
- **[Complete V2 Guide](client-v2/README.md)** — full tutorial with all features
|
|
659
|
+
- **[HTTP API Reference](https://github.com/queen-mq/queen/blob/master/server/API.md)** — raw HTTP endpoints
|
|
660
|
+
- **[Server Guide](https://github.com/queen-mq/queen/blob/master/server/README.md)** — server setup and configuration
|
|
661
|
+
- **[Architecture & internals](https://queenmq.com/architecture.html)** — published architecture overview
|
|
662
|
+
- **[libqueen design notes](https://github.com/queen-mq/queen/blob/master/cdocs/LIBQUEEN_IMPROVEMENTS.md)** — adaptive engine deep-dive
|
|
660
663
|
|
|
661
664
|
---
|
|
662
665
|
|
|
663
666
|
## Support
|
|
664
667
|
|
|
665
|
-
- **GitHub:** [
|
|
666
|
-
- **Issues:** [GitHub Issues](https://github.com/
|
|
668
|
+
- **GitHub:** [queen-mq/queen](https://github.com/queen-mq/queen)
|
|
669
|
+
- **Issues:** [GitHub Issues](https://github.com/queen-mq/queen/issues)
|
|
667
670
|
- **LinkedIn:** [Smartness](https://www.linkedin.com/company/smartness-com/)
|
|
668
671
|
|
|
669
672
|
---
|
package/client-v2/Queen.js
CHANGED
|
@@ -23,9 +23,12 @@ export class Queen {
|
|
|
23
23
|
#admin = null
|
|
24
24
|
|
|
25
25
|
constructor(config = {}) {
|
|
26
|
-
// Configure custom logger before anything else
|
|
26
|
+
// Configure custom logger before anything else.
|
|
27
|
+
// Opt into structured field logging with `structuredLogs: true` (the logger
|
|
28
|
+
// must accept a leading merge object, e.g. pino/bunyan); defaults to the
|
|
29
|
+
// backward-compatible single-string format.
|
|
27
30
|
if (config && typeof config === 'object' && !Array.isArray(config) && config.logger) {
|
|
28
|
-
logger.configure(config.logger)
|
|
31
|
+
logger.configure(config.logger, { structured: config.structuredLogs === true })
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
logger.log('Queen.constructor', { config: typeof config === 'object' && !Array.isArray(config) ? { ...config, urls: config.urls?.length || 0 } : { type: typeof config } })
|
package/client-v2/README.md
CHANGED
|
@@ -1788,11 +1788,13 @@ await queen.close()
|
|
|
1788
1788
|
### Client Defaults
|
|
1789
1789
|
```javascript
|
|
1790
1790
|
{
|
|
1791
|
-
timeoutMillis: 30000,
|
|
1791
|
+
timeoutMillis: 30000, // 30 seconds
|
|
1792
1792
|
retryAttempts: 3,
|
|
1793
1793
|
retryDelayMillis: 1000,
|
|
1794
|
-
loadBalancingStrategy: 'round-robin'
|
|
1795
|
-
|
|
1794
|
+
loadBalancingStrategy: 'affinity', // 'affinity' | 'round-robin' | 'session'
|
|
1795
|
+
affinityHashRing: 128,
|
|
1796
|
+
enableFailover: true,
|
|
1797
|
+
healthRetryAfterMillis: 5000
|
|
1796
1798
|
}
|
|
1797
1799
|
```
|
|
1798
1800
|
|
|
@@ -1993,9 +1995,10 @@ process.on('SIGINT', async () => {
|
|
|
1993
1995
|
You now know everything about Queen v2! 🎉
|
|
1994
1996
|
|
|
1995
1997
|
**Additional resources:**
|
|
1996
|
-
- [API Documentation](
|
|
1997
|
-
- [Test Examples](../test-v2/)
|
|
1998
|
-
- [Architecture
|
|
1998
|
+
- [API Documentation](../../../server/API.md) — complete HTTP API reference
|
|
1999
|
+
- [Test Examples](../test-v2/) — runnable test cases
|
|
2000
|
+
- [Architecture overview](https://queenmq.com/architecture.html) — published deep-dive
|
|
2001
|
+
- [libqueen design notes](../../../cdocs/LIBQUEEN_IMPROVEMENTS.md) — server internals
|
|
1999
2002
|
|
|
2000
2003
|
**Need help?** Check out the test files in `test-v2/` - they're full of working examples!
|
|
2001
2004
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Logger utility for Queen Client v2
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Supports pluggable logger backends (pino, winston, bunyan, etc.).
|
|
5
5
|
* When no custom logger is configured, falls back to console-based logging
|
|
6
6
|
* gated by the QUEEN_CLIENT_LOG environment variable (Node.js)
|
|
@@ -20,6 +20,9 @@ const LOG_ENABLED = (() => {
|
|
|
20
20
|
})()
|
|
21
21
|
|
|
22
22
|
let customLogger = null
|
|
23
|
+
// When true, details are passed to the custom logger as structured fields
|
|
24
|
+
// (pino/bunyan style: `info(fields, message)`). Opt-in for backward compatibility.
|
|
25
|
+
let structuredMode = false
|
|
23
26
|
|
|
24
27
|
function getTimestamp() {
|
|
25
28
|
return new Date().toISOString()
|
|
@@ -36,22 +39,62 @@ function formatLog(operation, details, level = 'INFO') {
|
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* Configure a custom logger backend.
|
|
42
|
+
*
|
|
39
43
|
* The logger must implement: info(msg), warn(msg), error(msg).
|
|
40
44
|
* debug(msg) is optional and falls back to info(msg) if missing.
|
|
41
45
|
* When a custom logger is set, it is always active (no env var gating).
|
|
46
|
+
*
|
|
47
|
+
* By default (backward compatible) the logger is called with a single formatted
|
|
48
|
+
* string: `info('[operation] {json}')`.
|
|
49
|
+
*
|
|
50
|
+
* Pass `{ structured: true }` to instead call the logger pino/bunyan style —
|
|
51
|
+
* `info(fields, message)` — so `details` become structured, queryable fields
|
|
52
|
+
* (the `operation` is also added as a field and used as the message). Use this
|
|
53
|
+
* only with loggers that accept a leading merge object (e.g. pino, bunyan).
|
|
54
|
+
*
|
|
55
|
+
* The built-in console fallback (no custom logger) is unaffected by this option.
|
|
56
|
+
*
|
|
42
57
|
* @param {object} logger - Logger instance (e.g. pino(), winston.createLogger())
|
|
58
|
+
* @param {{ structured?: boolean }} [options]
|
|
43
59
|
*/
|
|
44
|
-
export function configure(logger) {
|
|
60
|
+
export function configure(logger, options = {}) {
|
|
45
61
|
if (logger && typeof logger.info !== 'function') {
|
|
46
62
|
throw new Error('Custom logger must implement info(), warn(), and error() methods')
|
|
47
63
|
}
|
|
48
64
|
customLogger = logger
|
|
65
|
+
structuredMode = options != null && options.structured === true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Emit a log through the configured custom logger.
|
|
70
|
+
* - structuredMode: `details` keys become top-level fields, `operation` is added
|
|
71
|
+
* as a field, and the operation is the message (pino/bunyan style).
|
|
72
|
+
* - default (legacy): a single `[operation] {json}` string is passed.
|
|
73
|
+
* Missing level methods fall back to info().
|
|
74
|
+
*/
|
|
75
|
+
function emitToCustomLogger(method, operation, details) {
|
|
76
|
+
const fn = customLogger[method] || customLogger.info
|
|
77
|
+
|
|
78
|
+
if (structuredMode) {
|
|
79
|
+
let fields
|
|
80
|
+
if (details && typeof details === 'object' && !Array.isArray(details)) {
|
|
81
|
+
fields = { operation, ...details }
|
|
82
|
+
} else if (details !== undefined) {
|
|
83
|
+
fields = { operation, details }
|
|
84
|
+
} else {
|
|
85
|
+
fields = { operation }
|
|
86
|
+
}
|
|
87
|
+
fn.call(customLogger, fields, operation)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const detailsStr = typeof details === 'object' ? JSON.stringify(details) : details
|
|
92
|
+
fn.call(customLogger, `[${operation}] ${detailsStr}`)
|
|
49
93
|
}
|
|
50
94
|
|
|
51
95
|
export function log(operation, details) {
|
|
52
96
|
if (customLogger) {
|
|
53
|
-
|
|
54
|
-
customLogger.info(`[${operation}] ${detailsStr}`)
|
|
97
|
+
emitToCustomLogger('info', operation, details)
|
|
55
98
|
return
|
|
56
99
|
}
|
|
57
100
|
if (!LOG_ENABLED) return
|
|
@@ -60,9 +103,7 @@ export function log(operation, details) {
|
|
|
60
103
|
|
|
61
104
|
export function debug(operation, details) {
|
|
62
105
|
if (customLogger) {
|
|
63
|
-
|
|
64
|
-
const fn = customLogger.debug || customLogger.info
|
|
65
|
-
fn.call(customLogger, `[${operation}] ${detailsStr}`)
|
|
106
|
+
emitToCustomLogger('debug', operation, details)
|
|
66
107
|
return
|
|
67
108
|
}
|
|
68
109
|
if (!LOG_ENABLED) return
|
|
@@ -71,8 +112,7 @@ export function debug(operation, details) {
|
|
|
71
112
|
|
|
72
113
|
export function warn(operation, details) {
|
|
73
114
|
if (customLogger) {
|
|
74
|
-
|
|
75
|
-
customLogger.warn(`[${operation}] ${detailsStr}`)
|
|
115
|
+
emitToCustomLogger('warn', operation, details)
|
|
76
116
|
return
|
|
77
117
|
}
|
|
78
118
|
if (!LOG_ENABLED) return
|
|
@@ -81,8 +121,7 @@ export function warn(operation, details) {
|
|
|
81
121
|
|
|
82
122
|
export function error(operation, details) {
|
|
83
123
|
if (customLogger) {
|
|
84
|
-
|
|
85
|
-
customLogger.error(`[${operation}] ${detailsStr}`)
|
|
124
|
+
emitToCustomLogger('error', operation, details)
|
|
86
125
|
return
|
|
87
126
|
}
|
|
88
127
|
if (!LOG_ENABLED) return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "queen-mq",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0-beta.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Partitioned message queue on PostgreSQL — broker client + fluent streaming SDK (windows, joins, gates) in one package",
|
|
6
6
|
"main": "client-v2/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
|
32
|
-
"url": "https://github.com/
|
|
32
|
+
"url": "https://github.com/queen-mq/queen"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"message-queue",
|