rascal 13.1.3 → 14.2.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/.prettierrc.json +4 -1
- package/CHANGELOG.md +23 -0
- package/README.md +200 -209
- package/examples/advanced/cluster.js +4 -4
- package/examples/advanced/config.js +45 -50
- package/examples/advanced/handlers/deleteUser.js +9 -16
- package/examples/advanced/handlers/saveUser.js +11 -18
- package/examples/advanced/index.js +81 -103
- package/examples/busy-publisher/config.json +40 -0
- package/examples/busy-publisher/index.js +14 -20
- package/examples/default-exchange/config.json +25 -0
- package/examples/default-exchange/index.js +10 -15
- package/examples/mocha/config.json +21 -0
- package/examples/mocha/test.js +16 -24
- package/examples/promises/config.json +27 -0
- package/examples/promises/index.js +9 -14
- package/examples/simple/config.json +37 -0
- package/examples/simple/index.js +11 -15
- package/index.js +6 -6
- package/lib/amqp/Broker.js +65 -95
- package/lib/amqp/BrokerAsPromised.js +7 -16
- package/lib/amqp/Publication.js +72 -212
- package/lib/amqp/PublicationSession.js +8 -8
- package/lib/amqp/SubscriberError.js +107 -233
- package/lib/amqp/SubscriberSession.js +56 -76
- package/lib/amqp/SubscriberSessionAsPromised.js +3 -3
- package/lib/amqp/Subscription.js +96 -313
- package/lib/amqp/Vhost.js +120 -265
- package/lib/amqp/tasks/applyBindings.js +12 -42
- package/lib/amqp/tasks/assertExchanges.js +6 -11
- package/lib/amqp/tasks/assertQueues.js +4 -4
- package/lib/amqp/tasks/assertVhost.js +6 -4
- package/lib/amqp/tasks/checkExchanges.js +4 -4
- package/lib/amqp/tasks/checkQueues.js +4 -4
- package/lib/amqp/tasks/checkVhost.js +6 -4
- package/lib/amqp/tasks/closeChannel.js +3 -3
- package/lib/amqp/tasks/closeConnection.js +3 -3
- package/lib/amqp/tasks/createChannel.js +3 -3
- package/lib/amqp/tasks/createConnection.js +38 -53
- package/lib/amqp/tasks/deleteExchanges.js +5 -5
- package/lib/amqp/tasks/deleteQueues.js +4 -4
- package/lib/amqp/tasks/deleteVhost.js +12 -16
- package/lib/amqp/tasks/index.js +25 -25
- package/lib/amqp/tasks/initCounters.js +5 -6
- package/lib/amqp/tasks/initPublications.js +4 -4
- package/lib/amqp/tasks/initShovels.js +15 -20
- package/lib/amqp/tasks/initSubscriptions.js +5 -11
- package/lib/amqp/tasks/initVhosts.js +8 -8
- package/lib/amqp/tasks/purgeQueues.js +4 -4
- package/lib/backoff/exponential.js +6 -8
- package/lib/backoff/index.js +2 -2
- package/lib/backoff/linear.js +3 -3
- package/lib/config/baseline.js +15 -16
- package/lib/config/configure.js +68 -193
- package/lib/config/fqn.js +3 -3
- package/lib/config/schema.json +686 -0
- package/lib/config/tests.js +3 -3
- package/lib/config/validate.js +87 -458
- package/lib/counters/inMemory.js +3 -3
- package/lib/counters/inMemoryCluster.js +16 -23
- package/lib/counters/index.js +3 -3
- package/lib/management/Client.js +55 -0
- package/package.json +2 -1
- package/examples/busy-publisher/config.js +0 -39
- package/examples/default-exchange/config.js +0 -24
- package/examples/mocha/config.js +0 -20
- package/examples/promises/config.js +0 -26
- package/examples/simple/config.js +0 -36
- package/lib/management/client.js +0 -90
|
@@ -1,55 +1,34 @@
|
|
|
1
|
-
const debug = require(
|
|
2
|
-
const format = require(
|
|
3
|
-
const _ = require(
|
|
4
|
-
const async = require(
|
|
5
|
-
const setTimeoutUnref = require(
|
|
1
|
+
const debug = require('debug')('rascal:SubscriberError');
|
|
2
|
+
const format = require('util').format;
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const async = require('async');
|
|
5
|
+
const setTimeoutUnref = require('../utils/setTimeoutUnref');
|
|
6
6
|
|
|
7
7
|
module.exports = function SubscriptionRecovery(broker, vhost) {
|
|
8
8
|
this.handle = function (session, message, err, recoveryProcess, next) {
|
|
9
|
-
debug(
|
|
10
|
-
"Handling subscriber error for message: %s",
|
|
11
|
-
message.properties.messageId
|
|
12
|
-
);
|
|
9
|
+
debug('Handling subscriber error for message: %s', message.properties.messageId);
|
|
13
10
|
|
|
14
11
|
async.eachSeries(
|
|
15
|
-
[].concat(recoveryProcess || []).concat({ strategy:
|
|
12
|
+
[].concat(recoveryProcess || []).concat({ strategy: 'fallback-nack' }),
|
|
16
13
|
(recoveryConfig, cb) => {
|
|
17
|
-
debug(
|
|
18
|
-
"Attempting to recover message: %s using strategy: %s",
|
|
19
|
-
message.properties.messageId,
|
|
20
|
-
recoveryConfig.strategy
|
|
21
|
-
);
|
|
14
|
+
debug('Attempting to recover message: %s using strategy: %s', message.properties.messageId, recoveryConfig.strategy);
|
|
22
15
|
|
|
23
16
|
const once = _.once(cb);
|
|
24
17
|
|
|
25
18
|
setTimeoutUnref(() => {
|
|
26
|
-
getStrategy(recoveryConfig).execute(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
recoveryConfig.strategy
|
|
37
|
-
);
|
|
38
|
-
setImmediate(() => next(err));
|
|
39
|
-
return once(false);
|
|
40
|
-
}
|
|
41
|
-
if (handled) {
|
|
42
|
-
debug(
|
|
43
|
-
"Message: %s was recovered using stragegy: %s",
|
|
44
|
-
message.properties.messageId,
|
|
45
|
-
recoveryConfig.strategy
|
|
46
|
-
);
|
|
47
|
-
setImmediate(next);
|
|
48
|
-
return once(false);
|
|
49
|
-
}
|
|
50
|
-
once();
|
|
19
|
+
getStrategy(recoveryConfig).execute(session, message, err, _.omit(recoveryConfig, 'defer'), (err, handled) => {
|
|
20
|
+
if (err) {
|
|
21
|
+
debug('Message: %s failed to be recovered using stragegy: %s', message.properties.messageId, recoveryConfig.strategy);
|
|
22
|
+
setImmediate(() => next(err));
|
|
23
|
+
return once(false);
|
|
24
|
+
}
|
|
25
|
+
if (handled) {
|
|
26
|
+
debug('Message: %s was recovered using stragegy: %s', message.properties.messageId, recoveryConfig.strategy);
|
|
27
|
+
setImmediate(next);
|
|
28
|
+
return once(false);
|
|
51
29
|
}
|
|
52
|
-
|
|
30
|
+
once();
|
|
31
|
+
});
|
|
53
32
|
}, recoveryConfig.defer);
|
|
54
33
|
},
|
|
55
34
|
next
|
|
@@ -59,7 +38,7 @@ module.exports = function SubscriptionRecovery(broker, vhost) {
|
|
|
59
38
|
const recoveryStrategies = _.keyBy(
|
|
60
39
|
[
|
|
61
40
|
{
|
|
62
|
-
name:
|
|
41
|
+
name: 'ack',
|
|
63
42
|
execute(session, message, err, strategyConfig, next) {
|
|
64
43
|
session._ack(message, (err) => {
|
|
65
44
|
next(err, true);
|
|
@@ -67,7 +46,7 @@ module.exports = function SubscriptionRecovery(broker, vhost) {
|
|
|
67
46
|
},
|
|
68
47
|
},
|
|
69
48
|
{
|
|
70
|
-
name:
|
|
49
|
+
name: 'nack',
|
|
71
50
|
execute(session, message, err, strategyConfig, next) {
|
|
72
51
|
session._nack(message, { requeue: strategyConfig.requeue }, (err) => {
|
|
73
52
|
next(err, true);
|
|
@@ -75,7 +54,7 @@ module.exports = function SubscriptionRecovery(broker, vhost) {
|
|
|
75
54
|
},
|
|
76
55
|
},
|
|
77
56
|
{
|
|
78
|
-
name:
|
|
57
|
+
name: 'fallback-nack',
|
|
79
58
|
execute(session, message, err, strategyConfig, next) {
|
|
80
59
|
session._nack(message, { requeue: strategyConfig.requeue }, (err) => {
|
|
81
60
|
next(err, true);
|
|
@@ -83,229 +62,124 @@ module.exports = function SubscriptionRecovery(broker, vhost) {
|
|
|
83
62
|
},
|
|
84
63
|
},
|
|
85
64
|
{
|
|
86
|
-
name:
|
|
65
|
+
name: 'republish',
|
|
87
66
|
execute(session, message, err, strategyConfig, next) {
|
|
88
|
-
debug(
|
|
67
|
+
debug('Republishing message: %s', message.properties.messageId);
|
|
89
68
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
const republished = _.get(
|
|
95
|
-
message,
|
|
96
|
-
[
|
|
97
|
-
"properties",
|
|
98
|
-
"headers",
|
|
99
|
-
"rascal",
|
|
100
|
-
"recovery",
|
|
101
|
-
originalQueue,
|
|
102
|
-
"republished",
|
|
103
|
-
],
|
|
104
|
-
0
|
|
105
|
-
);
|
|
69
|
+
const once = _.once(next);
|
|
70
|
+
const originalQueue = _.get(message, 'properties.headers.rascal.originalQueue');
|
|
71
|
+
const republished = _.get(message, ['properties', 'headers', 'rascal', 'recovery', originalQueue, 'republished'], 0);
|
|
106
72
|
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
) {
|
|
111
|
-
debug(
|
|
112
|
-
"Skipping recovery - message: %s has already been republished %d times.",
|
|
113
|
-
message.properties.messageId,
|
|
114
|
-
republished
|
|
115
|
-
);
|
|
116
|
-
return next(null, false);
|
|
73
|
+
if (strategyConfig.attempts && strategyConfig.attempts <= republished) {
|
|
74
|
+
debug('Skipping recovery - message: %s has already been republished %d times.', message.properties.messageId, republished);
|
|
75
|
+
return once(null, false);
|
|
117
76
|
}
|
|
118
77
|
|
|
119
78
|
const publishOptions = _.cloneDeep(message.properties);
|
|
120
|
-
_.set(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
_.set(
|
|
126
|
-
publishOptions,
|
|
127
|
-
"headers.rascal.originalExchange",
|
|
128
|
-
message.fields.exchange
|
|
129
|
-
);
|
|
130
|
-
_.set(
|
|
131
|
-
publishOptions,
|
|
132
|
-
"headers.rascal.originalRoutingKey",
|
|
133
|
-
message.fields.routingKey
|
|
134
|
-
);
|
|
135
|
-
_.set(
|
|
136
|
-
publishOptions,
|
|
137
|
-
"headers.rascal.error.message",
|
|
138
|
-
_.truncate(err.message, { length: 1024 })
|
|
139
|
-
);
|
|
140
|
-
_.set(publishOptions, "headers.rascal.error.code", err.code);
|
|
141
|
-
_.set(
|
|
142
|
-
publishOptions,
|
|
143
|
-
"headers.rascal.restoreRoutingHeaders",
|
|
144
|
-
_.has(strategyConfig, "restoreRoutingHeaders")
|
|
145
|
-
? strategyConfig.restoreRoutingHeaders
|
|
146
|
-
: true
|
|
147
|
-
);
|
|
79
|
+
_.set(publishOptions, ['headers', 'rascal', 'recovery', originalQueue, 'republished'], republished + 1);
|
|
80
|
+
_.set(publishOptions, 'headers.rascal.originalExchange', message.fields.exchange);
|
|
81
|
+
_.set(publishOptions, 'headers.rascal.originalRoutingKey', message.fields.routingKey);
|
|
82
|
+
_.set(publishOptions, 'headers.rascal.error.message', _.truncate(err.message, { length: 1024 }));
|
|
83
|
+
_.set(publishOptions, 'headers.rascal.error.code', err.code);
|
|
84
|
+
_.set(publishOptions, 'headers.rascal.restoreRoutingHeaders', _.has(strategyConfig, 'restoreRoutingHeaders') ? strategyConfig.restoreRoutingHeaders : true);
|
|
148
85
|
|
|
149
|
-
if (strategyConfig.immediateNack)
|
|
150
|
-
_.set(
|
|
151
|
-
publishOptions,
|
|
152
|
-
["headers", "rascal", "recovery", originalQueue, "immediateNack"],
|
|
153
|
-
true
|
|
154
|
-
);
|
|
86
|
+
if (strategyConfig.immediateNack) _.set(publishOptions, ['headers', 'rascal', 'recovery', originalQueue, 'immediateNack'], true);
|
|
155
87
|
|
|
156
88
|
vhost.getConfirmChannel((err, publisherChannel) => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
);
|
|
89
|
+
const nackMessage = (err) => {
|
|
90
|
+
session._nack(message, (nackErr) => {
|
|
91
|
+
// nackError just means the channel was already closed meaning the original message would have been rolled back
|
|
92
|
+
once(err);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
164
95
|
|
|
165
|
-
|
|
96
|
+
if (err) return nackMessage(err);
|
|
166
97
|
|
|
167
|
-
publisherChannel.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
);
|
|
98
|
+
if (!publisherChannel) return nackMessage(new Error('Unable to handle subscriber error by republishing. The VHost is shutting down'));
|
|
99
|
+
|
|
100
|
+
publisherChannel.on('error', (err) => {
|
|
101
|
+
nackMessage(err);
|
|
102
|
+
});
|
|
103
|
+
publisherChannel.on('return', () => {
|
|
104
|
+
nackMessage(new Error(format('Message: %s was republished to queue: %s, but was returned', message.properties.messageId, originalQueue)));
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
publisherChannel.publish(undefined, originalQueue, message.content, publishOptions, (err) => {
|
|
108
|
+
if (err) return nackMessage(err); // Channel will already be closed, reclosing will trigger an error
|
|
109
|
+
|
|
110
|
+
publisherChannel.close();
|
|
111
|
+
debug('Message: %s was republished to queue: %s %d times', message.properties.messageId, originalQueue, republished + 1);
|
|
112
|
+
session._ack(message, (err) => {
|
|
113
|
+
once(err, true);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
186
116
|
});
|
|
187
117
|
},
|
|
188
118
|
},
|
|
189
119
|
{
|
|
190
|
-
name:
|
|
120
|
+
name: 'forward',
|
|
191
121
|
execute(session, message, err, strategyConfig, next) {
|
|
192
|
-
debug(
|
|
122
|
+
debug('Forwarding message: %s to publication: %s', message.properties.messageId, strategyConfig.publication);
|
|
193
123
|
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
);
|
|
198
|
-
const forwarded = _.get(
|
|
199
|
-
message,
|
|
200
|
-
[
|
|
201
|
-
"properties",
|
|
202
|
-
"headers",
|
|
203
|
-
"rascal",
|
|
204
|
-
"recovery",
|
|
205
|
-
originalQueue,
|
|
206
|
-
"forwarded",
|
|
207
|
-
],
|
|
208
|
-
0
|
|
209
|
-
);
|
|
124
|
+
const once = _.once(next);
|
|
125
|
+
const originalQueue = _.get(message, 'properties.headers.rascal.originalQueue');
|
|
126
|
+
const forwarded = _.get(message, ['properties', 'headers', 'rascal', 'recovery', originalQueue, 'forwarded'], 0);
|
|
210
127
|
|
|
211
128
|
if (strategyConfig.attempts && strategyConfig.attempts <= forwarded) {
|
|
212
|
-
debug(
|
|
213
|
-
|
|
214
|
-
message.properties.messageId,
|
|
215
|
-
forwarded
|
|
216
|
-
);
|
|
217
|
-
return next(null, false);
|
|
129
|
+
debug('Skipping recovery - message: %s has already been forwarded %d times.', message.properties.messageId, forwarded);
|
|
130
|
+
return once(null, false);
|
|
218
131
|
}
|
|
219
132
|
|
|
220
133
|
// See https://github.com/rabbitmq/rabbitmq-server/issues/161
|
|
221
|
-
if (strategyConfig.xDeathFix)
|
|
222
|
-
delete message.properties.headers["x-death"];
|
|
134
|
+
if (strategyConfig.xDeathFix) delete message.properties.headers['x-death'];
|
|
223
135
|
|
|
224
136
|
const forwardOverrides = _.cloneDeep(strategyConfig.options) || {};
|
|
225
|
-
_.set(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
_.set(
|
|
245
|
-
forwardOverrides,
|
|
246
|
-
"options.headers.rascal.error.message",
|
|
247
|
-
_.truncate(err.message, { length: 1024 })
|
|
248
|
-
);
|
|
249
|
-
_.set(
|
|
250
|
-
forwardOverrides,
|
|
251
|
-
"options.headers.rascal.error.code",
|
|
252
|
-
err.code
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
broker.forward(
|
|
256
|
-
strategyConfig.publication,
|
|
257
|
-
message,
|
|
258
|
-
forwardOverrides,
|
|
259
|
-
(err, publication) => {
|
|
260
|
-
if (err) return next(err);
|
|
261
|
-
publication.on("success", () => {
|
|
262
|
-
debug(
|
|
263
|
-
"Message: %s was forwarded to publication: %s %d times",
|
|
264
|
-
message.properties.messageId,
|
|
265
|
-
strategyConfig.publication,
|
|
266
|
-
forwarded + 1
|
|
267
|
-
);
|
|
268
|
-
session._ack(message, (err) => {
|
|
269
|
-
next(err, true);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
publication.on("error", next);
|
|
273
|
-
publication.on("return", () => {
|
|
274
|
-
next(
|
|
275
|
-
new Error(
|
|
276
|
-
format(
|
|
277
|
-
"Message: %s was forwared to publication: %s, but was returned",
|
|
278
|
-
message.properties.messageId,
|
|
279
|
-
strategyConfig.publication
|
|
280
|
-
)
|
|
281
|
-
)
|
|
282
|
-
);
|
|
137
|
+
_.set(forwardOverrides, 'restoreRoutingHeaders', _.has(strategyConfig, 'restoreRoutingHeaders') ? strategyConfig.restoreRoutingHeaders : true);
|
|
138
|
+
_.set(forwardOverrides, ['options', 'headers', 'rascal', 'recovery', originalQueue, 'forwarded'], forwarded + 1);
|
|
139
|
+
_.set(forwardOverrides, 'options.headers.rascal.error.message', _.truncate(err.message, { length: 1024 }));
|
|
140
|
+
_.set(forwardOverrides, 'options.headers.rascal.error.code', err.code);
|
|
141
|
+
|
|
142
|
+
const nackMessage = (err) => {
|
|
143
|
+
session._nack(message, (nackErr) => {
|
|
144
|
+
// nackError just means the channel was already closed meaning the original message would have been rolled back
|
|
145
|
+
once(err);
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
broker.forward(strategyConfig.publication, message, forwardOverrides, (err, publication) => {
|
|
150
|
+
if (err) return nackMessage(err);
|
|
151
|
+
|
|
152
|
+
publication.on('success', () => {
|
|
153
|
+
debug('Message: %s was forwarded to publication: %s %d times', message.properties.messageId, strategyConfig.publication, forwarded + 1);
|
|
154
|
+
session._ack(message, (ackErr) => {
|
|
155
|
+
once(ackErr, true);
|
|
283
156
|
});
|
|
284
|
-
}
|
|
285
|
-
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
publication.on('error', (err) => {
|
|
160
|
+
nackMessage(err);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
publication.on('return', () => {
|
|
164
|
+
publication.removeAllListeners('success');
|
|
165
|
+
nackMessage(new Error(format('Message: %s was forwarded to publication: %s, but was returned', message.properties.messageId, strategyConfig.publication)));
|
|
166
|
+
});
|
|
167
|
+
});
|
|
286
168
|
},
|
|
287
169
|
},
|
|
288
170
|
{
|
|
289
|
-
name:
|
|
171
|
+
name: 'unknown',
|
|
290
172
|
execute(session, message, err, strategyConfig, next) {
|
|
291
|
-
|
|
292
|
-
new Error(
|
|
293
|
-
|
|
294
|
-
"Error recovering message: %s. No such strategy: %s.",
|
|
295
|
-
message.properties.messageId,
|
|
296
|
-
strategyConfig.strategy
|
|
297
|
-
)
|
|
298
|
-
)
|
|
299
|
-
);
|
|
173
|
+
session._nack(message, () => {
|
|
174
|
+
next(new Error(format('Error recovering message: %s. No such strategy: %s.', message.properties.messageId, strategyConfig.strategy)));
|
|
175
|
+
});
|
|
300
176
|
},
|
|
301
177
|
},
|
|
302
178
|
],
|
|
303
|
-
|
|
179
|
+
'name'
|
|
304
180
|
);
|
|
305
181
|
|
|
306
182
|
function getStrategy(recoveryConfig) {
|
|
307
|
-
return
|
|
308
|
-
recoveryStrategies[recoveryConfig.strategy] || recoveryStrategies.unknown
|
|
309
|
-
);
|
|
183
|
+
return recoveryStrategies[recoveryConfig.strategy] || recoveryStrategies.unknown;
|
|
310
184
|
}
|
|
311
185
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
const debug = require(
|
|
2
|
-
const EventEmitter = require(
|
|
3
|
-
const inherits = require(
|
|
4
|
-
const _ = require(
|
|
5
|
-
const
|
|
1
|
+
const debug = require('debug')('rascal:SubscriberSession');
|
|
2
|
+
const EventEmitter = require('events').EventEmitter;
|
|
3
|
+
const inherits = require('util').inherits;
|
|
4
|
+
const _ = require('lodash');
|
|
5
|
+
const async = require('async');
|
|
6
|
+
const setTimeoutUnref = require('../utils/setTimeoutUnref');
|
|
6
7
|
|
|
7
8
|
module.exports = SubscriberSession;
|
|
8
9
|
|
|
@@ -23,15 +24,11 @@ function SubscriberSession(sequentialChannelOperations, config) {
|
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
this._open = function (channel, consumerTag, next) {
|
|
26
|
-
if (cancelled) return next(new Error(
|
|
27
|
-
debug(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
32
|
-
channels[consumerTag] = { index: index++, channel, consumerTag };
|
|
33
|
-
channel.once("close", unref.bind(null, consumerTag));
|
|
34
|
-
channel.once("error", unref.bind(null, consumerTag));
|
|
27
|
+
if (cancelled) return next(new Error('Subscriber has been cancelled'));
|
|
28
|
+
debug('Opening subscriber session: %s on channel: %s', consumerTag, channel._rascal_id);
|
|
29
|
+
channels[consumerTag] = { index: index++, channel, consumerTag, unacknowledgedMessages: 0 };
|
|
30
|
+
channel.once('close', unref.bind(null, consumerTag));
|
|
31
|
+
channel.once('error', unref.bind(null, consumerTag));
|
|
35
32
|
next();
|
|
36
33
|
};
|
|
37
34
|
|
|
@@ -51,20 +48,22 @@ function SubscriberSession(sequentialChannelOperations, config) {
|
|
|
51
48
|
|
|
52
49
|
this._unsafeClose = function (next) {
|
|
53
50
|
withCurrentChannel(
|
|
54
|
-
(channel, consumerTag) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
consumerTag,
|
|
58
|
-
channel._rascal_id
|
|
59
|
-
);
|
|
51
|
+
(channel, consumerTag, entry) => {
|
|
52
|
+
entry.doomed = true;
|
|
53
|
+
debug('Cancelling subscriber session: %s on channel: %s', consumerTag, channel._rascal_id);
|
|
60
54
|
channel.cancel(consumerTag, (err) => {
|
|
61
55
|
if (err) return next(err);
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
const waitOrTimeout = config.closeTimeout ? async.timeout(waitForUnacknowledgedMessages, config.closeTimeout) : waitForUnacknowledgedMessages;
|
|
57
|
+
waitOrTimeout(entry, null, (err) => {
|
|
58
|
+
channel.close(() => {
|
|
59
|
+
debug('Channel: %s was closed', entry.channel._rascal_id);
|
|
60
|
+
next(err);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
64
63
|
});
|
|
65
64
|
},
|
|
66
65
|
() => {
|
|
67
|
-
debug(
|
|
66
|
+
debug('No current subscriber session');
|
|
68
67
|
next();
|
|
69
68
|
}
|
|
70
69
|
);
|
|
@@ -74,10 +73,6 @@ function SubscriberSession(sequentialChannelOperations, config) {
|
|
|
74
73
|
timeout = setTimeoutUnref(fn, delay);
|
|
75
74
|
};
|
|
76
75
|
|
|
77
|
-
this._maxDeferCloseChannel = function (other) {
|
|
78
|
-
return Math.max(config.deferCloseChannel, other);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
76
|
this._getRascalChannelId = function () {
|
|
82
77
|
let rascalChannelId = null;
|
|
83
78
|
withCurrentChannel((channel) => {
|
|
@@ -86,46 +81,50 @@ function SubscriberSession(sequentialChannelOperations, config) {
|
|
|
86
81
|
return rascalChannelId;
|
|
87
82
|
};
|
|
88
83
|
|
|
84
|
+
this._incrementUnacknowledgeMessageCount = function (consumerTag) {
|
|
85
|
+
if (config.options.noAck) return;
|
|
86
|
+
withConsumerChannel(consumerTag, (channel, __, entry) => {
|
|
87
|
+
debug('Channel: %s has %s unacknowledged messages', channel._rascal_id, ++entry.unacknowledgedMessages);
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
this._decrementUnacknowledgeMessageCount = function (consumerTag) {
|
|
92
|
+
if (config.options.noAck) return;
|
|
93
|
+
withConsumerChannel(consumerTag, (channel, __, entry) => {
|
|
94
|
+
debug('Channel: %s has %s unacknowledged messages', channel._rascal_id, --entry.unacknowledgedMessages);
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
89
98
|
this._ack = function (message, next) {
|
|
90
99
|
withConsumerChannel(
|
|
91
100
|
message.fields.consumerTag,
|
|
92
101
|
(channel) => {
|
|
93
|
-
debug(
|
|
94
|
-
"Acknowledging message: %s on channel: %s",
|
|
95
|
-
message.properties.messageId,
|
|
96
|
-
channel._rascal_id
|
|
97
|
-
);
|
|
102
|
+
debug('Acknowledging message: %s on channel: %s', message.properties.messageId, channel._rascal_id);
|
|
98
103
|
channel.ack(message);
|
|
104
|
+
self._decrementUnacknowledgeMessageCount(message.fields.consumerTag);
|
|
99
105
|
setImmediate(next);
|
|
100
106
|
},
|
|
101
107
|
() => {
|
|
102
108
|
setImmediate(() => {
|
|
103
|
-
next(new Error(
|
|
109
|
+
next(new Error('The channel has been closed. Unable to ack message'));
|
|
104
110
|
});
|
|
105
111
|
}
|
|
106
112
|
);
|
|
107
113
|
};
|
|
108
114
|
|
|
109
115
|
this._nack = function (message, options, next) {
|
|
110
|
-
if (arguments.length === 2)
|
|
111
|
-
return self._nack(arguments[0], {}, arguments[1]);
|
|
116
|
+
if (arguments.length === 2) return self._nack(arguments[0], {}, arguments[1]);
|
|
112
117
|
withConsumerChannel(
|
|
113
118
|
message.fields.consumerTag,
|
|
114
119
|
(channel) => {
|
|
115
|
-
debug(
|
|
116
|
-
"Not acknowledging message: %s with requeue: %s on channel: %s",
|
|
117
|
-
message.properties.messageId,
|
|
118
|
-
!!options.requeue,
|
|
119
|
-
channel._rascal_id
|
|
120
|
-
);
|
|
120
|
+
debug('Not acknowledging message: %s with requeue: %s on channel: %s', message.properties.messageId, !!options.requeue, channel._rascal_id);
|
|
121
121
|
channel.nack(message, false, !!options.requeue);
|
|
122
|
+
self._decrementUnacknowledgeMessageCount(message.fields.consumerTag);
|
|
122
123
|
setImmediate(next);
|
|
123
124
|
},
|
|
124
125
|
() => {
|
|
125
126
|
setImmediate(() => {
|
|
126
|
-
next(
|
|
127
|
-
new Error("The channel has been closed. Unable to nack message")
|
|
128
|
-
);
|
|
127
|
+
next(new Error('The channel has been closed. Unable to nack message'));
|
|
129
128
|
});
|
|
130
129
|
}
|
|
131
130
|
);
|
|
@@ -134,10 +133,8 @@ function SubscriberSession(sequentialChannelOperations, config) {
|
|
|
134
133
|
function withCurrentChannel(fn, altFn) {
|
|
135
134
|
const entry = _.chain(channels)
|
|
136
135
|
.values()
|
|
137
|
-
.filter((
|
|
138
|
-
|
|
139
|
-
})
|
|
140
|
-
.sortBy("index")
|
|
136
|
+
.filter((entry) => !entry.doomed)
|
|
137
|
+
.sortBy('index')
|
|
141
138
|
.last()
|
|
142
139
|
.value();
|
|
143
140
|
if (entry) return fn(entry.channel, entry.consumerTag, entry);
|
|
@@ -152,37 +149,20 @@ function SubscriberSession(sequentialChannelOperations, config) {
|
|
|
152
149
|
|
|
153
150
|
function unref(consumerTag) {
|
|
154
151
|
withConsumerChannel(consumerTag, (channel) => {
|
|
155
|
-
debug(
|
|
152
|
+
debug('Removing channel: %s from session', channel._rascal_id);
|
|
156
153
|
delete channels[consumerTag];
|
|
157
154
|
});
|
|
158
155
|
}
|
|
159
156
|
|
|
160
|
-
function
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
but no way of telling how many are outstanding since due to potentially
|
|
171
|
-
complicated recovery strategies, with timeouts etc.
|
|
172
|
-
Keeping channels around for a minute shouldn't hurt
|
|
173
|
-
*/
|
|
174
|
-
function scheduleClose(entry) {
|
|
175
|
-
debug(
|
|
176
|
-
"Deferring close channel: %s by %dms",
|
|
177
|
-
entry.channel._rascal_id,
|
|
178
|
-
config.deferCloseChannel
|
|
179
|
-
);
|
|
180
|
-
setTimeoutUnref(() => {
|
|
181
|
-
withConsumerChannel(entry.consumerTag, (channel) => {
|
|
182
|
-
channel.close(() => {
|
|
183
|
-
debug("Channel: %s was closed", channel._rascal_id);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
}, config.deferCloseChannel);
|
|
157
|
+
function waitForUnacknowledgedMessages(entry, previousCount, next) {
|
|
158
|
+
const currentCount = entry.unacknowledgedMessages;
|
|
159
|
+
if (currentCount > 0) {
|
|
160
|
+
if (currentCount !== previousCount) {
|
|
161
|
+
debug('Waiting for %d unacknowledged messages from channel: %s', currentCount, entry.channel._rascal_id);
|
|
162
|
+
}
|
|
163
|
+
setTimeoutUnref(() => waitForUnacknowledgedMessages(entry, currentCount, next), 100);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
next();
|
|
187
167
|
}
|
|
188
168
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const EventEmitter = require(
|
|
2
|
-
const inherits = require(
|
|
3
|
-
const forwardEvents = require(
|
|
1
|
+
const EventEmitter = require('events').EventEmitter;
|
|
2
|
+
const inherits = require('util').inherits;
|
|
3
|
+
const forwardEvents = require('forward-emitter');
|
|
4
4
|
|
|
5
5
|
module.exports = SubscriberSessionAsPromised;
|
|
6
6
|
|