comq 0.15.3 → 0.15.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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/readme.md +3 -6
- package/source/io.js +8 -42
- package/types/io.d.ts +2 -4
- package/test/io.request.timeout.test.js +0 -61
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [0.15.4](https://github.com/toa-io/comq/compare/v0.15.3...v0.15.4) (2026-08-16)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* remove request() timeout ([#246](https://github.com/toa-io/comq/issues/246)) ([d432388](https://github.com/toa-io/comq/commit/d4323889bcad867a870d0ed351b92098f6aad551))
|
|
7
|
+
|
|
1
8
|
## [0.15.3](https://github.com/toa-io/comq/compare/v0.15.2...v0.15.3) (2026-08-04)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -103,11 +103,10 @@ await io.reply('add_numbers', ({ a, b }) => (a + b))
|
|
|
103
103
|
|
|
104
104
|
## Request
|
|
105
105
|
|
|
106
|
-
`async IO.request(queue: string, payload: any, encoding?: string
|
|
106
|
+
`async IO.request(queue: string, payload: any, encoding?: string): any`
|
|
107
107
|
|
|
108
108
|
Send encoded Request message with `replyTo` and `correlationId` properties set and
|
|
109
|
-
return decoded Reply content.
|
|
110
|
-
Reply arrives in time; encoding may be omitted when only `timeout` is passed.
|
|
109
|
+
return decoded Reply content. The promise stays pending until the Reply arrives.
|
|
111
110
|
|
|
112
111
|
On the initial call, queues for Requests and Replies are asserted.
|
|
113
112
|
|
|
@@ -115,7 +114,6 @@ On the initial call, queues for Requests and Replies are asserted.
|
|
|
115
114
|
|
|
116
115
|
```javascript
|
|
117
116
|
const sum = await io.request('add_numbers', { a: 1, b: 2 })
|
|
118
|
-
const sumOrTimeout = await io.request('add_numbers', { a: 1, b: 2 }, 5000)
|
|
119
117
|
```
|
|
120
118
|
|
|
121
119
|
## Consumption
|
|
@@ -332,8 +330,7 @@ Outgoing messages are sent to a single connection chosen at random from the shar
|
|
|
332
330
|
underlying connection or experience channel [back pressure](#flow-control) on a corresponding channel are removed from
|
|
333
331
|
the pool until the issue is resolved. Pending messages meeting these conditions are immediately routed among the
|
|
334
332
|
remaining shards in the pool. If no shards are available, `send` / `publish` / `request` wait until a shard's
|
|
335
|
-
connection is re-established
|
|
336
|
-
`ETIMEDOUT` instead of hanging while the pool is empty or a Reply never arrives.
|
|
333
|
+
connection is re-established.
|
|
337
334
|
|
|
338
335
|
Incoming messages are consumed from all shards.
|
|
339
336
|
|
package/source/io.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const stream = require('node:stream')
|
|
4
4
|
const { EventEmitter } = require('node:events')
|
|
5
5
|
const { randomBytes } = require('node:crypto')
|
|
6
|
-
const { setTimeout
|
|
6
|
+
const { setTimeout } = require('node:timers/promises')
|
|
7
7
|
const { Promex } = require('promex')
|
|
8
8
|
const { memo, failsafe, lazy, track } = require('./attributes')
|
|
9
9
|
|
|
@@ -75,45 +75,24 @@ class IO {
|
|
|
75
75
|
/**
|
|
76
76
|
* @param {string} queue
|
|
77
77
|
* @param {any | Readable} payload
|
|
78
|
-
* @param {comq.Encoding
|
|
79
|
-
* @param {number} [timeout]
|
|
78
|
+
* @param {comq.Encoding} [encoding]
|
|
80
79
|
* @returns {Promise<any | Readable>}
|
|
81
80
|
*/
|
|
82
|
-
async (queue, payload, encoding
|
|
83
|
-
if (typeof encoding === 'number') {
|
|
84
|
-
timeout = encoding
|
|
85
|
-
encoding = undefined
|
|
86
|
-
}
|
|
87
|
-
|
|
81
|
+
async (queue, payload, encoding) => {
|
|
88
82
|
if (payload instanceof stream.Readable) {
|
|
89
83
|
return pipeline(
|
|
90
84
|
payload,
|
|
91
|
-
(payload) => this.request(queue, payload, encoding
|
|
85
|
+
(payload) => this.request(queue, payload, encoding),
|
|
92
86
|
this.#requests
|
|
93
87
|
)
|
|
94
88
|
}
|
|
95
89
|
|
|
96
|
-
const request = this.#createRequest(queue, payload,
|
|
90
|
+
const request = this.#createRequest(queue, payload, encoding)
|
|
97
91
|
const reply = this.#createReply(request)
|
|
98
92
|
|
|
99
|
-
|
|
100
|
-
.then(() => reply)
|
|
101
|
-
|
|
102
|
-
if (typeof timeout !== 'number') return done
|
|
93
|
+
await this.#requests.send(queue, request.buffer, request.properties)
|
|
103
94
|
|
|
104
|
-
|
|
105
|
-
return await Promise.race([
|
|
106
|
-
done,
|
|
107
|
-
delay(timeout).then(() => Promise.reject(timeoutError(timeout)))
|
|
108
|
-
])
|
|
109
|
-
} catch (exception) {
|
|
110
|
-
if (exception.code === 'ETIMEDOUT') {
|
|
111
|
-
request.emitter.removeAllListeners(request.properties.correlationId)
|
|
112
|
-
reply.reject(exception)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
throw exception
|
|
116
|
-
}
|
|
95
|
+
return reply
|
|
117
96
|
}))
|
|
118
97
|
|
|
119
98
|
consume = lazy(this, this.#createEventChannel,
|
|
@@ -428,7 +407,7 @@ class IO {
|
|
|
428
407
|
Even if these messages are lost, the reply stream will be closed anyway,
|
|
429
408
|
either due to missing heartbeat or the deletion of the stream queue.
|
|
430
409
|
*/
|
|
431
|
-
await
|
|
410
|
+
await setTimeout(50)
|
|
432
411
|
}
|
|
433
412
|
|
|
434
413
|
/**
|
|
@@ -495,19 +474,6 @@ const DEFAULT = 'application/json'
|
|
|
495
474
|
|
|
496
475
|
const RETRANSMISSION = /** @type {Error} */ Symbol('retransmission')
|
|
497
476
|
|
|
498
|
-
/**
|
|
499
|
-
* @param {number} timeout
|
|
500
|
-
* @returns {Error}
|
|
501
|
-
*/
|
|
502
|
-
function timeoutError (timeout) {
|
|
503
|
-
const error = /** @type {Error & { code: string }} */ new Error(
|
|
504
|
-
`Request timed out after ${timeout}ms`)
|
|
505
|
-
|
|
506
|
-
error.code = 'ETIMEDOUT'
|
|
507
|
-
|
|
508
|
-
return error
|
|
509
|
-
}
|
|
510
|
-
|
|
511
477
|
function noop () {}
|
|
512
478
|
|
|
513
479
|
exports.IO = IO
|
package/types/io.d.ts
CHANGED
|
@@ -27,11 +27,9 @@ declare namespace comq {
|
|
|
27
27
|
interface IO extends _diagnostics.Diagnosable {
|
|
28
28
|
reply(queue: string, produce: Producer): Promise<void>
|
|
29
29
|
|
|
30
|
-
request<Reply = any, Request = any>(queue: string, payload: Request, encoding?: _encoding.Encoding
|
|
30
|
+
request<Reply = any, Request = any>(queue: string, payload: Request, encoding?: _encoding.Encoding): Promise<Reply> | Promise<Readable>
|
|
31
31
|
|
|
32
|
-
request
|
|
33
|
-
|
|
34
|
-
request(queue: string, stream: Readable, encoding?: _encoding.Encoding, timeout?: number): Promise<Readable>
|
|
32
|
+
request(queue: string, stream: Readable, encoding?: _encoding.Encoding): Promise<Readable>
|
|
35
33
|
|
|
36
34
|
consume<T = any>(exchange: string, group: string, consumer: Consumer<T>): Promise<void>
|
|
37
35
|
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
4
|
-
|
|
5
|
-
const mock = require('./connection.mock')
|
|
6
|
-
const { IO } = require('../source/io')
|
|
7
|
-
|
|
8
|
-
/** @type {comq.IO} */
|
|
9
|
-
let io
|
|
10
|
-
|
|
11
|
-
/** @type {jest.MockedObject<comq.Connection>} */
|
|
12
|
-
let connection
|
|
13
|
-
|
|
14
|
-
const queue = generate()
|
|
15
|
-
const payload = { [generate()]: generate() }
|
|
16
|
-
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
jest.clearAllMocks()
|
|
19
|
-
|
|
20
|
-
connection = mock.connection()
|
|
21
|
-
io = new IO(connection)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('should reject when request times out', async () => {
|
|
25
|
-
await expect(io.request(queue, payload, 50)).rejects.toMatchObject({
|
|
26
|
-
message: expect.stringContaining('timed out'),
|
|
27
|
-
code: 'ETIMEDOUT'
|
|
28
|
-
})
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should reject when request times out with encoding', async () => {
|
|
32
|
-
await expect(io.request(queue, payload, 'application/json', 50)).rejects.toMatchObject({
|
|
33
|
-
code: 'ETIMEDOUT'
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('should time out while send is still pending', async () => {
|
|
38
|
-
connection.createChannel.mockImplementation(async () => hangingChannel())
|
|
39
|
-
|
|
40
|
-
await expect(io.request(queue, payload, 50)).rejects.toMatchObject({
|
|
41
|
-
code: 'ETIMEDOUT'
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @returns {jest.MockedObject<comq.Channel>}
|
|
47
|
-
*/
|
|
48
|
-
function hangingChannel () {
|
|
49
|
-
return /** @type {jest.MockedObject<comq.Channel>} */ ({
|
|
50
|
-
sharded: false,
|
|
51
|
-
consume: jest.fn(async () => undefined),
|
|
52
|
-
send: jest.fn(() => new Promise(() => {})),
|
|
53
|
-
deliver: jest.fn(async () => undefined),
|
|
54
|
-
fire: jest.fn(async () => undefined),
|
|
55
|
-
subscribe: jest.fn(async () => undefined),
|
|
56
|
-
publish: jest.fn(async () => undefined),
|
|
57
|
-
diagnose: jest.fn(),
|
|
58
|
-
seal: jest.fn(async () => undefined),
|
|
59
|
-
recover: jest.fn(async () => undefined)
|
|
60
|
-
})
|
|
61
|
-
}
|