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.
Files changed (69) hide show
  1. package/.prettierrc.json +4 -1
  2. package/CHANGELOG.md +23 -0
  3. package/README.md +200 -209
  4. package/examples/advanced/cluster.js +4 -4
  5. package/examples/advanced/config.js +45 -50
  6. package/examples/advanced/handlers/deleteUser.js +9 -16
  7. package/examples/advanced/handlers/saveUser.js +11 -18
  8. package/examples/advanced/index.js +81 -103
  9. package/examples/busy-publisher/config.json +40 -0
  10. package/examples/busy-publisher/index.js +14 -20
  11. package/examples/default-exchange/config.json +25 -0
  12. package/examples/default-exchange/index.js +10 -15
  13. package/examples/mocha/config.json +21 -0
  14. package/examples/mocha/test.js +16 -24
  15. package/examples/promises/config.json +27 -0
  16. package/examples/promises/index.js +9 -14
  17. package/examples/simple/config.json +37 -0
  18. package/examples/simple/index.js +11 -15
  19. package/index.js +6 -6
  20. package/lib/amqp/Broker.js +65 -95
  21. package/lib/amqp/BrokerAsPromised.js +7 -16
  22. package/lib/amqp/Publication.js +72 -212
  23. package/lib/amqp/PublicationSession.js +8 -8
  24. package/lib/amqp/SubscriberError.js +107 -233
  25. package/lib/amqp/SubscriberSession.js +56 -76
  26. package/lib/amqp/SubscriberSessionAsPromised.js +3 -3
  27. package/lib/amqp/Subscription.js +96 -313
  28. package/lib/amqp/Vhost.js +120 -265
  29. package/lib/amqp/tasks/applyBindings.js +12 -42
  30. package/lib/amqp/tasks/assertExchanges.js +6 -11
  31. package/lib/amqp/tasks/assertQueues.js +4 -4
  32. package/lib/amqp/tasks/assertVhost.js +6 -4
  33. package/lib/amqp/tasks/checkExchanges.js +4 -4
  34. package/lib/amqp/tasks/checkQueues.js +4 -4
  35. package/lib/amqp/tasks/checkVhost.js +6 -4
  36. package/lib/amqp/tasks/closeChannel.js +3 -3
  37. package/lib/amqp/tasks/closeConnection.js +3 -3
  38. package/lib/amqp/tasks/createChannel.js +3 -3
  39. package/lib/amqp/tasks/createConnection.js +38 -53
  40. package/lib/amqp/tasks/deleteExchanges.js +5 -5
  41. package/lib/amqp/tasks/deleteQueues.js +4 -4
  42. package/lib/amqp/tasks/deleteVhost.js +12 -16
  43. package/lib/amqp/tasks/index.js +25 -25
  44. package/lib/amqp/tasks/initCounters.js +5 -6
  45. package/lib/amqp/tasks/initPublications.js +4 -4
  46. package/lib/amqp/tasks/initShovels.js +15 -20
  47. package/lib/amqp/tasks/initSubscriptions.js +5 -11
  48. package/lib/amqp/tasks/initVhosts.js +8 -8
  49. package/lib/amqp/tasks/purgeQueues.js +4 -4
  50. package/lib/backoff/exponential.js +6 -8
  51. package/lib/backoff/index.js +2 -2
  52. package/lib/backoff/linear.js +3 -3
  53. package/lib/config/baseline.js +15 -16
  54. package/lib/config/configure.js +68 -193
  55. package/lib/config/fqn.js +3 -3
  56. package/lib/config/schema.json +686 -0
  57. package/lib/config/tests.js +3 -3
  58. package/lib/config/validate.js +87 -458
  59. package/lib/counters/inMemory.js +3 -3
  60. package/lib/counters/inMemoryCluster.js +16 -23
  61. package/lib/counters/index.js +3 -3
  62. package/lib/management/Client.js +55 -0
  63. package/package.json +2 -1
  64. package/examples/busy-publisher/config.js +0 -39
  65. package/examples/default-exchange/config.js +0 -24
  66. package/examples/mocha/config.js +0 -20
  67. package/examples/promises/config.js +0 -26
  68. package/examples/simple/config.js +0 -36
  69. package/lib/management/client.js +0 -90
package/README.md CHANGED
@@ -7,9 +7,7 @@ Rascal is a rich pub/sub wrapper around [amqplib](https://www.npmjs.com/package/
7
7
  [![Node.js CI](https://github.com/guidesmiths/rascal/workflows/Node.js%20CI/badge.svg)](https://github.com/guidesmiths/rascal/actions?query=workflow%3A%22Node.js+CI%22)
8
8
  [![Code Climate](https://codeclimate.com/github/guidesmiths/rascal/badges/gpa.svg)](https://codeclimate.com/github/guidesmiths/rascal)
9
9
  [![Test Coverage](https://codeclimate.com/github/guidesmiths/rascal/badges/coverage.svg)](https://codeclimate.com/github/guidesmiths/rascal/coverage)
10
- [![Code Style](https://img.shields.io/badge/code%20style-esnext-brightgreen.svg)](https://www.npmjs.com/package/eslint-config-esnext)
11
- [![Dependency Status](https://david-dm.org/guidesmiths/rascal.svg)](https://david-dm.org/guidesmiths/rascal)
12
- [![devDependencies Status](https://david-dm.org/guidesmiths/rascal/dev-status.svg)](https://david-dm.org/guidesmiths/rascal?type=dev)
10
+ [![Code Style](https://img.shields.io/badge/code%20style-prettier-brightgreen.svg)](https://github.com/prettier/prettier)
13
11
  [![rascal](https://snyk.io/advisor/npm-package/rascal/badge.svg)](https://snyk.io/advisor/npm-package/rascal)
14
12
  [![Discover zUnit](https://img.shields.io/badge/Discover-zUnit-brightgreen)](https://www.npmjs.com/package/zunit)
15
13
 
@@ -49,6 +47,10 @@ Rascal extends the existing [RabbitMQ Concepts](https://www.rabbitmq.com/tutoria
49
47
 
50
48
  A **publication** is a named configuration for publishing a message, including the destination queue or exchange, routing configuration, encryption profile and reliability guarantees, message options, etc. A **subscription** is a named configuration for consuming messages, including the source queue, encryption profile, content encoding, delivery options (e.g. acknowledgement handling and prefetch), etc. These must be [configured](#configuration) and supplied when creating the Rascal broker. After the broker has been created the subscriptions and publications can be retrivied from the broker and used to publish and consume messages.
51
49
 
50
+ ### Breaking Changes in Rascal@14
51
+
52
+ Rascal@15 waits for inflight messages to be acknowledged before closing subscriber channels. Prior to this version Rascal just waited an arbitary amount of time. If you application does not acknowledge a message for some reason (quite likely in tests) calling `subscription.cancel`, `broker.unsubscribeAll`, `broker.bounce`, `broker.shutdown` or `broker.nuke` will wait indefinitely. You can specify a `closeTimeout` in your subscription config, however if this is exceeded the `subscription.cancel` and `broker.unsubscribeAll` methods will yield an error, while the `broker.bounce`, `broker.shutdown` and `broker.nuke` methods will emit an error, but attempt to continue. In both cases the error will have a code of `ETIMEDOUT` and message stating `Callback function "waitForUnacknowledgedMessages" timed out`.
53
+
52
54
  ### Special Note
53
55
 
54
56
  RabbitMQ 3.8.0 introduced [quorum queues](https://www.rabbitmq.com/quorum-queues.html). Although quorum queues may not be suitable in all situations, they provide [poison message handling](https://www.rabbitmq.com/quorum-queues.html#poison-message-handling) without the need for an external [redelivery counter](https://github.com/guidesmiths/rascal#dealing-with-redeliveries) and offer better data safety in the event of a network partition. You can read more about them [here](https://www.cloudamqp.com/blog/reasons-you-should-switch-to-quorum-queues.html) and [here](https://blog.rabbitmq.com/posts/2020/06/quorum-queues-local-delivery).
@@ -58,29 +60,26 @@ RabbitMQ 3.8.0 introduced [quorum queues](https://www.rabbitmq.com/quorum-queues
58
60
  ### Async/Await
59
61
 
60
62
  ```js
61
- const Broker = require("rascal").BrokerAsPromised;
62
- const config = require("./config");
63
+ const Broker = require('rascal').BrokerAsPromised;
64
+ const config = require('./config');
63
65
 
64
66
  (async () => {
65
67
  try {
66
68
  const broker = await Broker.create(config);
67
- broker.on("error", console.error);
69
+ broker.on('error', console.error);
68
70
 
69
71
  // Publish a message
70
- const publication = await broker.publish(
71
- "demo_publication",
72
- "Hello World!"
73
- );
74
- publication.on("error", console.error);
72
+ const publication = await broker.publish('demo_publication', 'Hello World!');
73
+ publication.on('error', console.error);
75
74
 
76
75
  // Consume a message
77
- const subscription = await broker.subscribe("demo_subscription");
76
+ const subscription = await broker.subscribe('demo_subscription');
78
77
  subscription
79
- .on("message", (message, content, ackOrNack) => {
78
+ .on('message', (message, content, ackOrNack) => {
80
79
  console.log(content);
81
80
  ackOrNack();
82
81
  })
83
- .on("error", console.error);
82
+ .on('error', console.error);
84
83
  } catch (err) {
85
84
  console.error(err);
86
85
  }
@@ -90,29 +89,29 @@ const config = require("./config");
90
89
  ### Callbacks
91
90
 
92
91
  ```js
93
- const Broker = require("rascal").Broker;
94
- const config = require("./config");
92
+ const Broker = require('rascal').Broker;
93
+ const config = require('./config');
95
94
 
96
95
  Broker.create(config, (err, broker) => {
97
96
  if (err) throw err;
98
97
 
99
- broker.on("error", console.error);
98
+ broker.on('error', console.error);
100
99
 
101
100
  // Publish a message
102
- broker.publish("demo_publication", "Hello World!", (err, publication) => {
101
+ broker.publish('demo_publication', 'Hello World!', (err, publication) => {
103
102
  if (err) throw err;
104
- publication.on("error", console.error);
103
+ publication.on('error', console.error);
105
104
  });
106
105
 
107
106
  // Consume a message
108
- broker.subscribe("demo_subscription", (err, subscription) => {
107
+ broker.subscribe('demo_subscription', (err, subscription) => {
109
108
  if (err) throw err;
110
109
  subscription
111
- .on("message", (message, content, ackOrNack) => {
110
+ .on('message', (message, content, ackOrNack) => {
112
111
  console.log(content);
113
112
  ackOrNack();
114
113
  })
115
- .on("error", console.error);
114
+ .on('error', console.error);
116
115
  });
117
116
  });
118
117
  ```
@@ -121,12 +120,13 @@ See [here](https://github.com/guidesmiths/rascal/tree/master/examples) for more
121
120
 
122
121
  ## Avoiding Potential Message Loss
123
122
 
124
- There are two situations when Rascal will nack a message without requeue, leading to potential data loss.
123
+ There are three situations when Rascal will nack a message without requeue, leading to potential data loss.
125
124
 
126
125
  1. When it is unable to parse the message content and the subscriber has no 'invalid_content' listener
127
126
  1. When the subscriber's (optional) redelivery limit has been exceeded and the subscriber has neither a 'redeliveries_error' nor a 'redeliveries_exceeded' listener
127
+ 1. When attempting to recover by [republishing](#republishing), [forwarding](#forwarding), but the recovery operation fails.
128
128
 
129
- The reason Rascal nacks the message is because the alternative is to rollback and retry the message in an infinite tight loop. This can DDOS your application and cause problems for your infrastructure. Providing you have correctly configured dead letter queues and/or listen to the "invalid_content" and "redeliveries_exceeded" subscriber events, your messages should be safe.
129
+ The reason Rascal nacks the message is because the alternatives are to leave the message unacknowledged indefinitely, or to rollback and retry the message in an infinite tight loop. This can DDOS your application and cause problems for your infrastructure. Providing you have correctly configured dead letter queues and/or listen to the "invalid_content" and "redeliveries_exceeded" subscriber events, your messages should be safe.
130
130
 
131
131
  ## Very Important Section About Event Handling
132
132
 
@@ -135,8 +135,8 @@ The reason Rascal nacks the message is because the alternative is to rollback an
135
135
  1. Immediately after obtaining a broker instance
136
136
 
137
137
  ```js
138
- broker.on("error", (err, { vhost, connectionUrl }) => {
139
- console.error("Broker error", err, vhost, connectionUrl);
138
+ broker.on('error', (err, { vhost, connectionUrl }) => {
139
+ console.error('Broker error', err, vhost, connectionUrl);
140
140
  });
141
141
  ```
142
142
 
@@ -145,13 +145,13 @@ The reason Rascal nacks the message is because the alternative is to rollback an
145
145
  ```js
146
146
  // Async/Await
147
147
  try {
148
- const subscription = await broker.subscribe("s1");
148
+ const subscription = await broker.subscribe('s1');
149
149
  subscription
150
- .on("message", (message, content, ackOrNack) => {
150
+ .on('message', (message, content, ackOrNack) => {
151
151
  // Do stuff with message
152
152
  })
153
- .on("error", (err) => {
154
- console.error("Subscriber error", err);
153
+ .on('error', (err) => {
154
+ console.error('Subscriber error', err);
155
155
  });
156
156
  } catch (err) {
157
157
  throw new Error(`Rascal config error: ${err.message}`);
@@ -160,14 +160,14 @@ The reason Rascal nacks the message is because the alternative is to rollback an
160
160
 
161
161
  ```js
162
162
  // Callbacks
163
- broker.subscribe("s1", (err, subscription) => {
163
+ broker.subscribe('s1', (err, subscription) => {
164
164
  if (err) throw new Error(`Rascal config error: ${err.message}`);
165
165
  subscription
166
- .on("message", (message, content, ackOrNack) => {
166
+ .on('message', (message, content, ackOrNack) => {
167
167
  // Do stuff with message
168
168
  })
169
- .on("error", (err) => {
170
- console.error("Subscriber error", err);
169
+ .on('error', (err) => {
170
+ console.error('Subscriber error', err);
171
171
  });
172
172
  });
173
173
  ```
@@ -177,9 +177,9 @@ The reason Rascal nacks the message is because the alternative is to rollback an
177
177
  ```js
178
178
  // Async/Await
179
179
  try {
180
- const publication = await broker.publish("p1", "some text");
181
- publication.on("error", (err, messageId) => {
182
- console.error("Publisher error", err, messageId);
180
+ const publication = await broker.publish('p1', 'some text');
181
+ publication.on('error', (err, messageId) => {
182
+ console.error('Publisher error', err, messageId);
183
183
  });
184
184
  } catch (err) {
185
185
  throw new Error(`Rascal config error: ${err.message}`);
@@ -188,10 +188,10 @@ The reason Rascal nacks the message is because the alternative is to rollback an
188
188
 
189
189
  ```js
190
190
  // Callbacks
191
- broker.publish("p1", "some text", (err, publication) => {
191
+ broker.publish('p1', 'some text', (err, publication) => {
192
192
  if (err) throw new Error(`Rascal config error: ${err.message}`);
193
- publication.on("error", (err, messageId) => {
194
- console.error("Publisher error", err, messageId);
193
+ publication.on('error', (err, messageId) => {
194
+ console.error('Publisher error', err, messageId);
195
195
  });
196
196
  });
197
197
  ```
@@ -201,9 +201,9 @@ The reason Rascal nacks the message is because the alternative is to rollback an
201
201
  ```js
202
202
  // Async/Await
203
203
  try {
204
- const publication = await broker.forward("p1", message);
205
- publication.on("error", (err, messageId) => {
206
- console.error("Publisher error", err, messageId);
204
+ const publication = await broker.forward('p1', message);
205
+ publication.on('error', (err, messageId) => {
206
+ console.error('Publisher error', err, messageId);
207
207
  });
208
208
  } catch (err) {
209
209
  throw new Error(`Rascal config error: ${err.message}`);
@@ -212,10 +212,10 @@ The reason Rascal nacks the message is because the alternative is to rollback an
212
212
 
213
213
  ```js
214
214
  // Callbacks
215
- broker.forward("p1", message, (err, publication) => {
215
+ broker.forward('p1', message, (err, publication) => {
216
216
  if (err) throw new Error(`Rascal config error: ${err.message}`);
217
- publication.on("error", (err, messageId) => {
218
- console.error("Publisher error", err, messageId);
217
+ publication.on('error', (err, messageId) => {
218
+ console.error('Publisher error', err, messageId);
219
219
  });
220
220
  });
221
221
  ```
@@ -227,10 +227,8 @@ The reason Rascal nacks the message is because the alternative is to rollback an
227
227
  The broker emits the `vhost_initialised` event after recovering from a connection error. An object containing the vhost name and connection url (with obfuscated password) are passed to he event handler. e.g.
228
228
 
229
229
  ```js
230
- broker.on("vhost_initialised", ({ vhost, connectionUrl }) => {
231
- console.log(
232
- `Vhost: ${vhost} was initialised using connection: ${connectionUrl}`
233
- );
230
+ broker.on('vhost_initialised', ({ vhost, connectionUrl }) => {
231
+ console.log(`Vhost: ${vhost} was initialised using connection: ${connectionUrl}`);
234
232
  });
235
233
  ```
236
234
 
@@ -239,15 +237,11 @@ broker.on("vhost_initialised", ({ vhost, connectionUrl }) => {
239
237
  RabbitMQ notifies clients of [blocked and unblocked](https://www.rabbitmq.com/connection-blocked.html) connections, which rascal forwards from the connection to the broker. e.g.
240
238
 
241
239
  ```js
242
- broker.on("blocked", (reason, { vhost, connectionUrl }) => {
243
- console.log(
244
- `Vhost: ${vhost} was blocked using connection: ${connectionUrl}. Reason: ${reason}`
245
- );
240
+ broker.on('blocked', (reason, { vhost, connectionUrl }) => {
241
+ console.log(`Vhost: ${vhost} was blocked using connection: ${connectionUrl}. Reason: ${reason}`);
246
242
  });
247
- broker.on("unblocked", ({ vhost, connectionUrl }) => {
248
- console.log(
249
- `Vhost: ${vhost} was unblocked using connection: ${connectionUrl}.`
250
- );
243
+ broker.on('unblocked', ({ vhost, connectionUrl }) => {
244
+ console.log(`Vhost: ${vhost} was unblocked using connection: ${connectionUrl}.`);
251
245
  });
252
246
  ```
253
247
 
@@ -256,16 +250,16 @@ broker.on("unblocked", ({ vhost, connectionUrl }) => {
256
250
  Rascal is highly configurable, but ships with what we consider to be sensible defaults (optimised for reliability rather than speed) for production and test environments.
257
251
 
258
252
  ```js
259
- var rascal = require("rascal");
260
- var definitions = require("./your-config.json");
253
+ var rascal = require('rascal');
254
+ var definitions = require('./your-config.json');
261
255
  var config = rascal.withDefaultConfig(definitions);
262
256
  ```
263
257
 
264
258
  or
265
259
 
266
260
  ```js
267
- var rascal = require("rascal");
268
- var definitions = require("./your-test-config.json");
261
+ var rascal = require('rascal');
262
+ var definitions = require('./your-test-config.json');
269
263
  var config = rascal.withTestConfig(definitions);
270
264
  ```
271
265
 
@@ -279,10 +273,11 @@ The most common configuration options are
279
273
  - [publications](#publications)
280
274
  - [subscriptions](#subscriptions)
281
275
 
282
- A simple configuration is shown below.
276
+ A simple configuration is shown below. You can reference Rascal's JSON schema from the config to enable validation and suggestions in compatible IDEs.
283
277
 
284
278
  ```json
285
279
  {
280
+ "$schema": "./node_modules/rascal/lib/config/schema.json",
286
281
  "vhosts": {
287
282
  "/": {
288
283
  "connection": {
@@ -416,11 +411,7 @@ If you specify an array of connections instead of a single connection object Ras
416
411
  "vhosts": {
417
412
  "v1": {
418
413
  "connectionStrategy": "random",
419
- "connections": [
420
- "amqp://guest:guest@broker1.example.com:5672/v1?heartbeat=10",
421
- "amqp://guest:guest@broker2.example.com:5672/v1?heartbeat=10",
422
- "amqp://guest:guest@broker3.example.com:5672/v1?heartbeat=10"
423
- ]
414
+ "connections": ["amqp://guest:guest@broker1.example.com:5672/v1?heartbeat=10", "amqp://guest:guest@broker2.example.com:5672/v1?heartbeat=10", "amqp://guest:guest@broker3.example.com:5672/v1?heartbeat=10"]
424
415
  }
425
416
  }
426
417
  }
@@ -493,6 +484,15 @@ Rascal uses [superagent](https://github.com/visionmedia/superagent) under the ho
493
484
  }
494
485
  ```
495
486
 
487
+ You can also supply your own agent via the broker components. Use this when you need to set [TLS options](https://visionmedia.github.io/superagent/#tls-options).
488
+
489
+ ```js
490
+ const superagent = require('superagent-defaults');
491
+ const agent = superagent().on('request', (req) => console.log(req.url));
492
+ const components = { agent };
493
+ const broker = await Broker.create(config, components);
494
+ ```
495
+
496
496
  #### assert
497
497
 
498
498
  When set to true, Rascal will create the vhost if one doesn't exist using the RabbitMQ management API. This requires the [management plugin](https://www.rabbitmq.com/management.html) to be installed on the broker and for the management user to have necessary permissions.
@@ -557,21 +557,15 @@ Unfortunately there is a [bug](https://github.com/coopernurse/node-pool/issues/1
557
557
  [amqplib flow control](https://www.squaremobius.net/amqp.node/channel_api.html#flowcontrol) dictates channels act like stream.Writable when Rascal calls `channel.publish` or `channel.sendToQueue`, returning false when the channel is saturated and true if it is not. While it is possible to ignore this and keep publishing messages, it is preferable to apply back pressure to the message source. You can do this by listening to the broker `busy` and `ready` events. Busy events are emitted when the number of outstanding channel requests reach the pool max size, and ready events emitted when the outstanding channel requests falls back down to zero. The pool details are passed to both event handlers so you can take selective action.
558
558
 
559
559
  ```js
560
- broker.on(
561
- "busy",
562
- ({ vhost, mode, queue, size, available, borrowed, min, max }) => {
563
- if (vhost === "events") return eventStream.pause();
564
- console.warn(`vhost ${vhost} is busy`);
565
- }
566
- );
560
+ broker.on('busy', ({ vhost, mode, queue, size, available, borrowed, min, max }) => {
561
+ if (vhost === 'events') return eventStream.pause();
562
+ console.warn(`vhost ${vhost} is busy`);
563
+ });
567
564
 
568
- broker.on(
569
- "ready",
570
- ({ vhost, mode, queue, size, available, borrowed, min, max }) => {
571
- if (vhost === "events") return eventStream.resume();
572
- console.info(`vhost ${vhost} is ready`);
573
- }
574
- );
565
+ broker.on('ready', ({ vhost, mode, queue, size, available, borrowed, min, max }) => {
566
+ if (vhost === 'events') return eventStream.resume();
567
+ console.info(`vhost ${vhost} is ready`);
568
+ });
575
569
  ```
576
570
 
577
571
  #### namespace
@@ -822,7 +816,7 @@ Now that you've bound your queues and exchanges, you need to start sending them
822
816
  ```
823
817
 
824
818
  ```js
825
- broker.publish("p1", "some message");
819
+ broker.publish('p1', 'some message');
826
820
  ```
827
821
 
828
822
  If you prefer to send messages to a queue
@@ -862,20 +856,20 @@ Rascal supports text, buffers and anything it can JSON.stringify. Rascal will au
862
856
  The `broker.publish` method is overloaded to accept a runtime routing key or options.
863
857
 
864
858
  ```js
865
- broker.publish("p1", "some message", callback);
866
- broker.publish("p1", "some message", "some.routing.key", callback);
867
- broker.publish("p1", "some message", {
868
- routingKey: "some.routing.key",
869
- options: { messageId: "foo", expiration: 5000 },
859
+ broker.publish('p1', 'some message', callback);
860
+ broker.publish('p1', 'some message', 'some.routing.key', callback);
861
+ broker.publish('p1', 'some message', {
862
+ routingKey: 'some.routing.key',
863
+ options: { messageId: 'foo', expiration: 5000 },
870
864
  });
871
865
  ```
872
866
 
873
867
  ```js
874
- await broker.publish("p1", "some message");
875
- await broker.publish("p1", "some message", "some.routing.key");
876
- await broker.publish("p1", "some message", {
877
- routingKey: "some.routing.key",
878
- options: { messageId: "foo", expiration: 5000 },
868
+ await broker.publish('p1', 'some message');
869
+ await broker.publish('p1', 'some message', 'some.routing.key');
870
+ await broker.publish('p1', 'some message', {
871
+ routingKey: 'some.routing.key',
872
+ options: { messageId: 'foo', expiration: 5000 },
879
873
  });
880
874
  ```
881
875
 
@@ -884,33 +878,33 @@ The callback parameters are err (indicating the publication could not be found)
884
878
  If you specify the "mandatory" option (or use Rascal's defaults) you can also listen for returned messages (i.e. messages that were not delivered to any queues)
885
879
 
886
880
  ```js
887
- broker.publish("p1", "some message", (err, publication) => {
881
+ broker.publish('p1', 'some message', (err, publication) => {
888
882
  if (err) throw err; // publication didn't exist
889
883
  publication
890
- .on("success", (messageId) => {
891
- console.log("Message id was: ", messageId);
884
+ .on('success', (messageId) => {
885
+ console.log('Message id was: ', messageId);
892
886
  })
893
- .on("error", (err, messageId) => {
894
- console.error("Error was: ", err.message);
887
+ .on('error', (err, messageId) => {
888
+ console.error('Error was: ', err.message);
895
889
  })
896
- .on("return", (message) => {
897
- console.warn("Message was returned: ", message.properties.messageId);
890
+ .on('return', (message) => {
891
+ console.warn('Message was returned: ', message.properties.messageId);
898
892
  });
899
893
  });
900
894
  ```
901
895
 
902
896
  ```js
903
897
  try {
904
- const publication = await broker.publish("p1", "some message");
898
+ const publication = await broker.publish('p1', 'some message');
905
899
  publication
906
- .on("success", (messageId) => {
907
- console.log("Message id was: ", messageId);
900
+ .on('success', (messageId) => {
901
+ console.log('Message id was: ', messageId);
908
902
  })
909
- .on("error", (err, messageId) => {
910
- console.error("Error was: ", err.message);
903
+ .on('error', (err, messageId) => {
904
+ console.error('Error was: ', err.message);
911
905
  })
912
- .on("return", (message) => {
913
- console.warn("Message was returned: ", message.properties.messageId);
906
+ .on('return', (message) => {
907
+ console.warn('Message was returned: ', message.properties.messageId);
914
908
  });
915
909
  } catch (err) {
916
910
  // publication didn't exist
@@ -971,17 +965,17 @@ If you start experiencing publication timeouts you may find it useful to monitor
971
965
  Rascal uses a channel pool to publish messages. Access to the channel pool is synchronised via an in memory queue, which will be paused if the connection to the broker is temporarily lost. Consequently instead of erroring, publishes will be held until the connection is re-established. If you would rather abort under these circumstances, you can listen for the publication 'paused' event, and call `publication.abort()`. When the connection is re-established any aborted messages will be dropped instead of published.
972
966
 
973
967
  ```js
974
- broker.publish("p1", "some message", (err, publication) => {
968
+ broker.publish('p1', 'some message', (err, publication) => {
975
969
  if (err) throw err; // publication didn't exist
976
970
  publication
977
- .on("success", (messageId) => {
978
- console.log("Message id was: ", messageId);
971
+ .on('success', (messageId) => {
972
+ console.log('Message id was: ', messageId);
979
973
  })
980
- .on("error", (err, messageId) => {
981
- console.error("Error was: ", err.message);
974
+ .on('error', (err, messageId) => {
975
+ console.error('Error was: ', err.message);
982
976
  })
983
- .on("paused", (messageId) => {
984
- console.warn("Publication was paused. Aborting message: ", messageId);
977
+ .on('paused', (messageId) => {
978
+ console.warn('Publication was paused. Aborting message: ', messageId);
985
979
  publication.abort();
986
980
  });
987
981
  });
@@ -989,16 +983,16 @@ broker.publish("p1", "some message", (err, publication) => {
989
983
 
990
984
  ```js
991
985
  try {
992
- const publication = await broker.publish("p1", "some message");
986
+ const publication = await broker.publish('p1', 'some message');
993
987
  publication
994
- .on("success", (messageId) => {
995
- console.log("Message id was: ", messageId);
988
+ .on('success', (messageId) => {
989
+ console.log('Message id was: ', messageId);
996
990
  })
997
- .on("error", (err, messageId) => {
998
- console.error("Error was: ", err.message);
991
+ .on('error', (err, messageId) => {
992
+ console.error('Error was: ', err.message);
999
993
  })
1000
- .on("paused", (messageId) => {
1001
- console.warn("Publication was paused. Aborting message: ", messageId);
994
+ .on('paused', (messageId) => {
995
+ console.warn('Publication was paused. Aborting message: ', messageId);
1002
996
  publication.abort();
1003
997
  });
1004
998
  } catch (err) {
@@ -1042,33 +1036,33 @@ Rascal will set the content type for encrypted messages to 'application/octet-st
1042
1036
  Sometimes you want to forward a message to a publication. This may be part of a shovel program for transferring messages between vhosts, or because you want to ensure a sequence in some workflow, but do not need to modify the original message. Rascal supports this via `broker.forward`. The syntax is similar to `broker.publish` except from you pass in the original message you want to be forwarded instead of the message payload. If the publication or overrides don't specify a routing key, the original forwarding key will be maintained. The message will also be CC'd with an additional routingkey of `<queue>.<routingKey>` which can be useful for some retry scenarios.
1043
1037
 
1044
1038
  ```js
1045
- broker.forward("p1", message, overrides, (err, publication) => {
1039
+ broker.forward('p1', message, overrides, (err, publication) => {
1046
1040
  if (err) throw err; // publication didn't exist
1047
1041
  publication
1048
- .on("success", (messageId) => {
1049
- console.log("Message id was: ", messageId);
1042
+ .on('success', (messageId) => {
1043
+ console.log('Message id was: ', messageId);
1050
1044
  })
1051
- .on("error", (err, messageId) => {
1052
- console.error("Error was: ", err.message);
1045
+ .on('error', (err, messageId) => {
1046
+ console.error('Error was: ', err.message);
1053
1047
  })
1054
- .on("return", (message) => {
1055
- console.warn("Message was returned: ", message.properties.messageId);
1048
+ .on('return', (message) => {
1049
+ console.warn('Message was returned: ', message.properties.messageId);
1056
1050
  });
1057
1051
  });
1058
1052
  ```
1059
1053
 
1060
1054
  ```js
1061
1055
  try {
1062
- const publication = await broker.forward("p1", message, overrides);
1056
+ const publication = await broker.forward('p1', message, overrides);
1063
1057
  publication
1064
- .on("success", (messageId) => {
1065
- console.log("Message id was: ", messageId);
1058
+ .on('success', (messageId) => {
1059
+ console.log('Message id was: ', messageId);
1066
1060
  })
1067
- .on("error", (err, messageId) => {
1068
- console.error("Error was: ", err.message);
1061
+ .on('error', (err, messageId) => {
1062
+ console.error('Error was: ', err.message);
1069
1063
  })
1070
- .on("return", (message) => {
1071
- console.warn("Message was returned: ", message.properties.messageId);
1064
+ .on('return', (message) => {
1065
+ console.warn('Message was returned: ', message.properties.messageId);
1072
1066
  });
1073
1067
  } catch (err) {
1074
1068
  // publication didn't exist
@@ -1095,27 +1089,27 @@ The real fun begins with subscriptions
1095
1089
  ```
1096
1090
 
1097
1091
  ```js
1098
- broker.subscribe("s1", (err, subscription) => {
1092
+ broker.subscribe('s1', (err, subscription) => {
1099
1093
  if (err) throw err; // subscription didn't exist
1100
1094
  subscription
1101
- .on("message", (message, content, ackOrNack) => {
1095
+ .on('message', (message, content, ackOrNack) => {
1102
1096
  // Do stuff with message
1103
1097
  })
1104
- .on("error", (err) => {
1105
- console.error("Subscriber error", err);
1098
+ .on('error', (err) => {
1099
+ console.error('Subscriber error', err);
1106
1100
  });
1107
1101
  });
1108
1102
  ```
1109
1103
 
1110
1104
  ```js
1111
1105
  try {
1112
- const subscription = await broker.subscribe("s1");
1106
+ const subscription = await broker.subscribe('s1');
1113
1107
  subscription
1114
- .on("message", (message, content, ackOrNack) => {
1108
+ .on('message', (message, content, ackOrNack) => {
1115
1109
  // Do stuff with message
1116
1110
  })
1117
- .on("error", (err) => {
1118
- console.error("Subscriber error", err);
1111
+ .on('error', (err) => {
1112
+ console.error('Subscriber error', err);
1119
1113
  });
1120
1114
  } catch (err) {
1121
1115
  // subscription didn't exist
@@ -1143,7 +1137,7 @@ Rascal supports text, buffers and anything it can JSON.parse, providing the cont
1143
1137
  The `broker.subscribe` method also accepts an options parameter which will override options specified in config
1144
1138
 
1145
1139
  ```js
1146
- broker.subscribe("s1", { prefetch: 10, retry: false }, callback);
1140
+ broker.subscribe('s1', { prefetch: 10, retry: false }, callback);
1147
1141
  ```
1148
1142
 
1149
1143
  ```js
@@ -1163,11 +1157,11 @@ broker.subscribeAll((err, subscriptions) => {
1163
1157
  if (err) throw err; // one or more subscriptions didn't exist
1164
1158
  subscriptions.forEach((subscription) => {
1165
1159
  subscription
1166
- .on("message", (message, content, ackOrNack) => {
1160
+ .on('message', (message, content, ackOrNack) => {
1167
1161
  // Do stuff with message
1168
1162
  })
1169
- .on("error", (err) => {
1170
- console.error("Subscriber error", err);
1163
+ .on('error', (err) => {
1164
+ console.error('Subscriber error', err);
1171
1165
  });
1172
1166
  });
1173
1167
  });
@@ -1178,11 +1172,11 @@ try {
1178
1172
  const subscriptions = await broker.subscribeAll();
1179
1173
  subscriptions.forEach((subscription) => {
1180
1174
  subscription
1181
- .on("message", (message, content, ackOrNack) => {
1175
+ .on('message', (message, content, ackOrNack) => {
1182
1176
  // Do stuff with message
1183
1177
  })
1184
- .on("error", (err) => {
1185
- console.error("Subscriber error", err);
1178
+ .on('error', (err) => {
1179
+ console.error('Subscriber error', err);
1186
1180
  });
1187
1181
  });
1188
1182
  } catch (err) {
@@ -1199,11 +1193,11 @@ broker.subscribeAll(
1199
1193
  if (err) throw err; // one or more subscriptions didn't exist
1200
1194
  subscriptions.forEach((subscription) => {
1201
1195
  subscription
1202
- .on("message", (message, content, ackOrNack) => {
1196
+ .on('message', (message, content, ackOrNack) => {
1203
1197
  // Do stuff with message
1204
1198
  })
1205
- .on("error", (err) => {
1206
- console.error("Subscriber error", err);
1199
+ .on('error', (err) => {
1200
+ console.error('Subscriber error', err);
1207
1201
  });
1208
1202
  });
1209
1203
  }
@@ -1230,17 +1224,17 @@ try {
1230
1224
  If rascal can't parse the content (e.g. the message had a content type of 'application/json' but the content was not JSON), it will emit an 'invalid_content' event
1231
1225
 
1232
1226
  ```js
1233
- broker.subscribe("s1", (err, subscription) => {
1227
+ broker.subscribe('s1', (err, subscription) => {
1234
1228
  if (err) throw err; // subscription didn't exist
1235
1229
  subscription
1236
- .on("message", (message, content, ackOrNack) => {
1230
+ .on('message', (message, content, ackOrNack) => {
1237
1231
  // Do stuff with message
1238
1232
  })
1239
- .on("error", (err) => {
1240
- console.error("Subscriber error", err);
1233
+ .on('error', (err) => {
1234
+ console.error('Subscriber error', err);
1241
1235
  })
1242
- .on("invalid_content", (err, message, ackOrNack) => {
1243
- console.error("Invalid content", err);
1236
+ .on('invalid_content', (err, message, ackOrNack) => {
1237
+ console.error('Invalid content', err);
1244
1238
  ackOrNack(err);
1245
1239
  });
1246
1240
  });
@@ -1248,16 +1242,16 @@ broker.subscribe("s1", (err, subscription) => {
1248
1242
 
1249
1243
  ```js
1250
1244
  try {
1251
- const subscription = await broker.subscribe("s1");
1245
+ const subscription = await broker.subscribe('s1');
1252
1246
  subscription
1253
- .on("message", (message, content, ackOrNack) => {
1247
+ .on('message', (message, content, ackOrNack) => {
1254
1248
  // Do stuff with message
1255
1249
  })
1256
- .on("error", (err) => {
1257
- console.error("Subscriber error", err);
1250
+ .on('error', (err) => {
1251
+ console.error('Subscriber error', err);
1258
1252
  })
1259
- .on("invalid_content", (err, message, ackOrNack) => {
1260
- console.error("Invalid content", err);
1253
+ .on('invalid_content', (err, message, ackOrNack) => {
1254
+ console.error('Invalid content', err);
1261
1255
  ackOrNack(err);
1262
1256
  });
1263
1257
  } catch (err) {
@@ -1330,17 +1324,17 @@ If your app crashes before acknowledging a message, the message will be rolled b
1330
1324
  ```
1331
1325
 
1332
1326
  ```js
1333
- broker.subscribe("s1", (err, subscription) => {
1327
+ broker.subscribe('s1', (err, subscription) => {
1334
1328
  if (err) throw err; // subscription didn't exist
1335
1329
  subscription
1336
- .on("message", (message, content, ackOrNack) => {
1330
+ .on('message', (message, content, ackOrNack) => {
1337
1331
  // Do stuff with message
1338
1332
  })
1339
- .on("error", (err) => {
1340
- console.error("Subscriber error", err);
1333
+ .on('error', (err) => {
1334
+ console.error('Subscriber error', err);
1341
1335
  })
1342
- .on("redeliveries_exceeded", (err, message, ackOrNack) => {
1343
- console.error("Redeliveries exceeded", err);
1336
+ .on('redeliveries_exceeded', (err, message, ackOrNack) => {
1337
+ console.error('Redeliveries exceeded', err);
1344
1338
  ackOrNack(err);
1345
1339
  });
1346
1340
  });
@@ -1348,16 +1342,16 @@ broker.subscribe("s1", (err, subscription) => {
1348
1342
 
1349
1343
  ```js
1350
1344
  try {
1351
- const subscription = await broker.subscribe("s1");
1345
+ const subscription = await broker.subscribe('s1');
1352
1346
  subscription
1353
- .on("message", (message, content, ackOrNack) => {
1347
+ .on('message', (message, content, ackOrNack) => {
1354
1348
  // Do stuff with message
1355
1349
  })
1356
- .on("error", (err) => {
1357
- console.error("Subscriber error", err);
1350
+ .on('error', (err) => {
1351
+ console.error('Subscriber error', err);
1358
1352
  })
1359
- .on("redeliveries_exceeded", (err, message, ackOrNack) => {
1360
- console.error("Redeliveries exceeded", err);
1353
+ .on('redeliveries_exceeded', (err, message, ackOrNack) => {
1354
+ console.error('Redeliveries exceeded', err);
1361
1355
  ackOrNack(err);
1362
1356
  });
1363
1357
  } catch (err) {
@@ -1392,7 +1386,7 @@ When using the promises API, ackOrNack will work as for the callback API unless
1392
1386
  ##### Nack (Reject or Dead Letter)
1393
1387
 
1394
1388
  ```js
1395
- ackOrNack(err, { strategy: "nack" });
1389
+ ackOrNack(err, { strategy: 'nack' });
1396
1390
  ```
1397
1391
 
1398
1392
  Nack causes the message to be discarded or routed to a dead letter exchange if configured.
@@ -1400,7 +1394,7 @@ Nack causes the message to be discarded or routed to a dead letter exchange if c
1400
1394
  ##### Nack with Requeue
1401
1395
 
1402
1396
  ```js
1403
- ackOrNack(err, { strategy: "nack", defer: 1000, requeue: true });
1397
+ ackOrNack(err, { strategy: 'nack', defer: 1000, requeue: true });
1404
1398
  ```
1405
1399
 
1406
1400
  The defer option is not mandatory, but without it you are likely retry your message thousands of times a second. Even then requeueing is a inadequate strategy for error handling, since the message will be rolled back to the front of the queue and there is no simple way to detect how many times the message has been redelivered.
@@ -1410,7 +1404,7 @@ Dead lettering is a good option for invalid messages but with one major flaw - b
1410
1404
  ##### Republish
1411
1405
 
1412
1406
  ```js
1413
- ackOrNack(err, { strategy: "republish", defer: 1000 });
1407
+ ackOrNack(err, { strategy: 'republish', defer: 1000 });
1414
1408
  ```
1415
1409
 
1416
1410
  An alternative to nacking to republish the message back to the queue it came from. This has the advantage that the message will be resent to the back of the queue, allowing other messages to be processed and potentially fixing errors relating to ordering.
@@ -1418,10 +1412,7 @@ An alternative to nacking to republish the message back to the queue it came fro
1418
1412
  Rascal keeps track of the number of republishes so you can limit the number of attempts. **Whenever you specify a number of attempts you should always chain a fallback strategy**, otherwise if the attempts are exceeded your message will be neither acked or nacked.
1419
1413
 
1420
1414
  ```js
1421
- ackOrNack(err, [
1422
- { strategy: "republish", defer: 1000, attempts: 10 },
1423
- { strategy: "nack" },
1424
- ]);
1415
+ ackOrNack(err, [{ strategy: 'republish', defer: 1000, attempts: 10 }, { strategy: 'nack' }]);
1425
1416
  ```
1426
1417
 
1427
1418
  Rascal also annotates the message with detail of the error `message.properties.headers.rascal.<queue>.error` which can be useful if you eventually dead letter it.
@@ -1441,7 +1432,7 @@ Before using republish please consider the following:
1441
1432
  As mentioned previously, dead lettering invalid messages is a good strategy with one flaw - since there is no way to modify the message you cannot annotate it with failure details. A solution to this is to republish with attempts = 1 and then nacking it to a dead letter exchange. The problem with this approach is that invalid messages will always be processed twice. To workaround this set immediateNack to true in the recovery options. This will instruct Rascal to nack the message immediately instead of emitting the 'message' event.
1442
1433
 
1443
1434
  ```js
1444
- ackOrNack(err, { strategy: "republish", immediateNack: true });
1435
+ ackOrNack(err, { strategy: 'republish', immediateNack: true });
1445
1436
  ```
1446
1437
 
1447
1438
  If you ever want to resend the message to the same queue you will have to remove the `properties.headers.rascal.<queue>.immediateNack` header first.
@@ -1451,7 +1442,7 @@ If you ever want to resend the message to the same queue you will have to remove
1451
1442
  Instead of republishing the message to the same queue you can forward it to a Rascal publication. You should read the section entitled [Forwarding messages](#Forwarding-messages) to understand the risks of this.
1452
1443
 
1453
1444
  ```js
1454
- ackOrNack(err, { strategy: "forward", publication: "some_exchange" });
1445
+ ackOrNack(err, { strategy: 'forward', publication: 'some_exchange' });
1455
1446
  ```
1456
1447
 
1457
1448
  **Danger**
@@ -1462,12 +1453,12 @@ Furthermore if the message is forwarded but cannot be routed (e.g. due to an inc
1462
1453
  ```js
1463
1454
  ackOrNack(err, [
1464
1455
  {
1465
- strategy: "forward",
1466
- publication: "some_exchange",
1456
+ strategy: 'forward',
1457
+ publication: 'some_exchange',
1467
1458
  defer: 1000,
1468
1459
  attempts: 10,
1469
1460
  },
1470
- { strategy: "nack" },
1461
+ { strategy: 'nack' },
1471
1462
  ]);
1472
1463
  ```
1473
1464
 
@@ -1476,11 +1467,11 @@ You can also override the publication options
1476
1467
  ```js
1477
1468
  ackOrNack(err, [
1478
1469
  {
1479
- strategy: "forward",
1480
- publication: "some_exchange",
1481
- options: { routingKey: "custom.routing.key" },
1470
+ strategy: 'forward',
1471
+ publication: 'some_exchange',
1472
+ options: { routingKey: 'custom.routing.key' },
1482
1473
  },
1483
- { strategy: "nack" },
1474
+ { strategy: 'nack' },
1484
1475
  ]);
1485
1476
  ```
1486
1477
 
@@ -1491,7 +1482,7 @@ One use of the forward recovery strategy is to send messages to a wait queue whi
1491
1482
  Acknowledges the message, guaranteeing that it will be discarded in the event you also have a dead letter exchange configured. Sometimes useful in automated tests or when chaining a sequence of other recovery strategies.
1492
1483
 
1493
1484
  ```js
1494
- ackOrNack(err, { strategy: "ack" });
1485
+ ackOrNack(err, { strategy: 'ack' });
1495
1486
  ```
1496
1487
 
1497
1488
  #### Chaining Recovery Strategies
@@ -1501,18 +1492,18 @@ By chaining Rascal's recovery strategies and leveraging some of RabbitMQs lesser
1501
1492
  ```js
1502
1493
  ackOrNack(err, [
1503
1494
  {
1504
- strategy: "republish",
1495
+ strategy: 'republish',
1505
1496
  defer: 1000,
1506
1497
  attempts: 10,
1507
1498
  },
1508
1499
  {
1509
- strategy: "nack",
1500
+ strategy: 'nack',
1510
1501
  },
1511
1502
  ]);
1512
1503
  ```
1513
1504
 
1514
1505
  Far more sophisticated strategies are achievable...
1515
- ![Retry BackOff Fail](https://user-images.githubusercontent.com/229672/49589770-2359d080-f962-11e8-957e-8d5368561afd.png "Retry BackOff Fail")
1506
+ ![Retry BackOff Fail](https://user-images.githubusercontent.com/229672/49589770-2359d080-f962-11e8-957e-8d5368561afd.png 'Retry BackOff Fail')
1516
1507
 
1517
1508
  1. Producer publishes a message with the routing key "a.b.c" to the "jobs" topic exchange
1518
1509
  2. The message is routed to the "incoming" queue. The "incoming" queue is configured with a dead letter exchange.
@@ -1537,24 +1528,24 @@ If an error occurs on the channel (which will happen if you accidentally acknowl
1537
1528
 
1538
1529
  ```js
1539
1530
  // Does not retry. This will cause an error to be emitted which unhandled will crash your process. See [Subscriber Events](#subscriber-events)
1540
- broker.subscribe("s1", { prefetch: 10, retry: false }, callback);
1531
+ broker.subscribe('s1', { prefetch: 10, retry: false }, callback);
1541
1532
 
1542
1533
  // Retries without delay.
1543
- broker.subscribe("s1", { prefetch: 10, retry: true }, callback);
1534
+ broker.subscribe('s1', { prefetch: 10, retry: true }, callback);
1544
1535
 
1545
1536
  // Retries after a one second interval.
1546
- broker.subscribe("s1", { prefetch: 10, retry: { delay: 1000 } }, callback);
1537
+ broker.subscribe('s1', { prefetch: 10, retry: { delay: 1000 } }, callback);
1547
1538
  ```
1548
1539
 
1549
1540
  ```js
1550
1541
  // Does not retry. This will cause an error to be emitted which unhandled will crash your process. See [Subscriber Events](#subscriber-events)
1551
- await broker.subscribe("s1", { prefetch: 10, retry: false });
1542
+ await broker.subscribe('s1', { prefetch: 10, retry: false });
1552
1543
 
1553
1544
  // Retries without delay.
1554
- await broker.subscribe("s1", { prefetch: 10, retry: true });
1545
+ await broker.subscribe('s1', { prefetch: 10, retry: true });
1555
1546
 
1556
1547
  // Retries after a one second interval.
1557
- await broker.subscribe("s1", { prefetch: 10, retry: { delay: 1000 } });
1548
+ await broker.subscribe('s1', { prefetch: 10, retry: { delay: 1000 } });
1558
1549
  ```
1559
1550
 
1560
1551
  #### Subscriber Events
@@ -1610,7 +1601,7 @@ Configuring each vhost, exchange, queue, binding, publication and subscription e
1610
1601
  You can cancel subscriptions as follows
1611
1602
 
1612
1603
  ```js
1613
- broker.subscribe("s1", (err, subscription) => {
1604
+ broker.subscribe('s1', (err, subscription) => {
1614
1605
  if (err) throw err; // subscription didn't exist
1615
1606
  subscription.cancel((err) => {
1616
1607
  console.err(err);
@@ -1620,18 +1611,18 @@ broker.subscribe("s1", (err, subscription) => {
1620
1611
 
1621
1612
  ```js
1622
1613
  try {
1623
- const subscription = await broker.subscribe("s1");
1614
+ const subscription = await broker.subscribe('s1');
1624
1615
  await subscription.cancel();
1625
1616
  } catch (err) {
1626
1617
  // subscription didn't exist or could not be cancelled
1627
1618
  }
1628
1619
  ```
1629
1620
 
1630
- Cancelling a subscribion will stop consuming messages, but leave the channel open for a short while so your application can still ack/nack messages. By default the channel is left open for 10 seconds, but can be overridden through the `deferCloseChannel` subscription property.
1621
+ Cancelling a subscribion will stop consuming messages, but leave the channel open until any outstanding messages have been acknowledged, or the timeout specified by through the `closeTimeout` subscription property is exceeded.
1631
1622
 
1632
1623
  ## Shutdown
1633
1624
 
1634
- You can shutdown the broker by calling `await broker.shutdown()` or `broker.shutdown(cb)`. Shutting down the broker will cancel all subscriptions, then wait a short amount of time for inflight messages to be acknowledged (configurable via the `deferCloseChannel` subscription property), before closing channels and disconnecting.
1625
+ You can shutdown the broker by calling `await broker.shutdown()` or `broker.shutdown(cb)`.
1635
1626
 
1636
1627
  ## Bonus Features
1637
1628
 
@@ -1705,7 +1696,7 @@ is equivalent to...
1705
1696
  Rascal is a rich pub/sub wrapper and as such hides much of the amqplib [channel api](https://www.squaremobius.net/amqp.node/channel_api.html#channel). If you need to access this you can programmatically establish a connection to a vhost as follows.
1706
1697
 
1707
1698
  ```js
1708
- broker.connect("/", (err, connection) => {
1699
+ broker.connect('/', (err, connection) => {
1709
1700
  if (err) throw new Error(`Connection error: ${err.message}`);
1710
1701
  // profit
1711
1702
  });
@@ -1713,7 +1704,7 @@ broker.connect("/", (err, connection) => {
1713
1704
 
1714
1705
  ```js
1715
1706
  try {
1716
- const connection = broker.connect("/");
1707
+ const connection = broker.connect('/');
1717
1708
  // profit
1718
1709
  } catch (err) {
1719
1710
  throw new Error(`Connection error: ${err.message}`);