sqs-consumer 8.1.5 → 8.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/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ ## [8.2.0](https://github.com/bbc/sqs-consumer/compare/v8.1.5...v8.2.0) (2024-02-06)
2
+
3
+
4
+ ### Features
5
+
6
+ * Wait for msgs to be processed before emitting stopped ([#454](https://github.com/bbc/sqs-consumer/issues/454)) ([0fa5305](https://github.com/bbc/sqs-consumer/commit/0fa53055a4957dba7381307cdeb65aa99128c315))
7
+
8
+
9
+ ### Chores
10
+
11
+ * removing semantic git ([b834205](https://github.com/bbc/sqs-consumer/commit/b834205bfe9183bc69141c4016831cdbb6e33d7a))
12
+ * updating release config ([6b95edb](https://github.com/bbc/sqs-consumer/commit/6b95edbf894864e25a61a748ab0ac9d6ebf2a2f7))
@@ -23,7 +23,10 @@ export declare class Consumer extends TypedEventEmitter {
23
23
  private waitTimeSeconds;
24
24
  private authenticationErrorTimeout;
25
25
  private pollingWaitTimeMs;
26
+ private pollingCompleteWaitTimeMs;
26
27
  private heartbeatInterval;
28
+ private isPolling;
29
+ private stopRequestedAtTimestamp;
27
30
  abortController: AbortController;
28
31
  constructor(options: ConsumerOptions);
29
32
  /**
@@ -42,6 +45,11 @@ export declare class Consumer extends TypedEventEmitter {
42
45
  * Stop polling the queue for messages (pre existing requests will still be made until concluded).
43
46
  */
44
47
  stop(options?: StopOptions): void;
48
+ /**
49
+ * Wait for final poll and in flight messages to complete.
50
+ * @private
51
+ */
52
+ private waitForPollingToComplete;
45
53
  /**
46
54
  * Returns the current polling state of the consumer: `true` if it is actively polling, `false` if it is not.
47
55
  */
package/dist/consumer.js CHANGED
@@ -12,7 +12,7 @@ const logger_1 = require("./logger");
12
12
  */
13
13
  class Consumer extends emitter_1.TypedEventEmitter {
14
14
  constructor(options) {
15
- var _a, _b, _c, _d, _e;
15
+ var _a, _b, _c, _d, _e, _f;
16
16
  super();
17
17
  this.pollingTimeoutId = undefined;
18
18
  this.stopped = true;
@@ -34,8 +34,9 @@ class Consumer extends emitter_1.TypedEventEmitter {
34
34
  this.authenticationErrorTimeout =
35
35
  (_b = options.authenticationErrorTimeout) !== null && _b !== void 0 ? _b : 10000;
36
36
  this.pollingWaitTimeMs = (_c = options.pollingWaitTimeMs) !== null && _c !== void 0 ? _c : 0;
37
- this.shouldDeleteMessages = (_d = options.shouldDeleteMessages) !== null && _d !== void 0 ? _d : true;
38
- this.alwaysAcknowledge = (_e = options.alwaysAcknowledge) !== null && _e !== void 0 ? _e : false;
37
+ this.pollingCompleteWaitTimeMs = (_d = options.pollingCompleteWaitTimeMs) !== null && _d !== void 0 ? _d : 0;
38
+ this.shouldDeleteMessages = (_e = options.shouldDeleteMessages) !== null && _e !== void 0 ? _e : true;
39
+ this.alwaysAcknowledge = (_f = options.alwaysAcknowledge) !== null && _f !== void 0 ? _f : false;
39
40
  this.sqs =
40
41
  options.sqs ||
41
42
  new client_sqs_1.SQSClient({
@@ -92,7 +93,27 @@ class Consumer extends emitter_1.TypedEventEmitter {
92
93
  this.abortController.abort();
93
94
  this.emit('aborted');
94
95
  }
95
- this.emit('stopped');
96
+ this.stopRequestedAtTimestamp = Date.now();
97
+ this.waitForPollingToComplete();
98
+ }
99
+ /**
100
+ * Wait for final poll and in flight messages to complete.
101
+ * @private
102
+ */
103
+ waitForPollingToComplete() {
104
+ if (!this.isPolling || !(this.pollingCompleteWaitTimeMs > 0)) {
105
+ this.emit('stopped');
106
+ return;
107
+ }
108
+ const exceededTimeout = Date.now() - this.stopRequestedAtTimestamp >
109
+ this.pollingCompleteWaitTimeMs;
110
+ if (exceededTimeout) {
111
+ logger_1.logger.debug('waiting_for_polling_to_complete_timeout_exceeded');
112
+ this.emit('stopped');
113
+ return;
114
+ }
115
+ logger_1.logger.debug('waiting_for_polling_to_complete');
116
+ setTimeout(this.waitForPollingToComplete, 1000);
96
117
  }
97
118
  /**
98
119
  * Returns the current polling state of the consumer: `true` if it is actively polling, `false` if it is not.
@@ -140,6 +161,7 @@ class Consumer extends emitter_1.TypedEventEmitter {
140
161
  return;
141
162
  }
142
163
  logger_1.logger.debug('polling');
164
+ this.isPolling = true;
143
165
  let currentPollingTimeout = this.pollingWaitTimeMs;
144
166
  this.receiveMessage({
145
167
  QueueUrl: this.queueUrl,
@@ -168,6 +190,9 @@ class Consumer extends emitter_1.TypedEventEmitter {
168
190
  })
169
191
  .catch((err) => {
170
192
  this.emitError(err);
193
+ })
194
+ .finally(() => {
195
+ this.isPolling = false;
171
196
  });
172
197
  }
173
198
  /**
package/dist/types.d.ts CHANGED
@@ -45,6 +45,12 @@ export interface ConsumerOptions {
45
45
  * @defaultvalue `0`
46
46
  */
47
47
  pollingWaitTimeMs?: number;
48
+ /**
49
+ * If you want the stop action to wait for the final poll to complete and in-flight messages
50
+ * to be processed before emitting 'stopped' set this to the max amount of time to wait.
51
+ * @defaultvalue `0`
52
+ */
53
+ pollingCompleteWaitTimeMs?: number;
48
54
  /**
49
55
  * If true, sets the message visibility timeout to 0 after a `processing_error`.
50
56
  * @defaultvalue `false`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqs-consumer",
3
- "version": "8.1.5",
3
+ "version": "8.2.0",
4
4
  "description": "Build SQS-based Node applications without the boilerplate",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -118,13 +118,14 @@
118
118
  }
119
119
  }
120
120
  ],
121
- "@semantic-release/git",
121
+ "@semantic-release/changelog",
122
122
  "@semantic-release/github",
123
123
  "@semantic-release/npm"
124
124
  ]
125
125
  },
126
126
  "devDependencies": {
127
127
  "@cucumber/cucumber": "10.0.1",
128
+ "@semantic-release/changelog": "^6.0.3",
128
129
  "@semantic-release/commit-analyzer": "^11.1.0",
129
130
  "@semantic-release/git": "^10.0.1",
130
131
  "@semantic-release/github": "^9.2.6",
package/src/consumer.ts CHANGED
@@ -54,7 +54,10 @@ export class Consumer extends TypedEventEmitter {
54
54
  private waitTimeSeconds: number;
55
55
  private authenticationErrorTimeout: number;
56
56
  private pollingWaitTimeMs: number;
57
+ private pollingCompleteWaitTimeMs: number;
57
58
  private heartbeatInterval: number;
59
+ private isPolling: boolean;
60
+ private stopRequestedAtTimestamp: number;
58
61
  public abortController: AbortController;
59
62
 
60
63
  constructor(options: ConsumerOptions) {
@@ -77,6 +80,7 @@ export class Consumer extends TypedEventEmitter {
77
80
  this.authenticationErrorTimeout =
78
81
  options.authenticationErrorTimeout ?? 10000;
79
82
  this.pollingWaitTimeMs = options.pollingWaitTimeMs ?? 0;
83
+ this.pollingCompleteWaitTimeMs = options.pollingCompleteWaitTimeMs ?? 0;
80
84
  this.shouldDeleteMessages = options.shouldDeleteMessages ?? true;
81
85
  this.alwaysAcknowledge = options.alwaysAcknowledge ?? false;
82
86
  this.sqs =
@@ -142,7 +146,31 @@ export class Consumer extends TypedEventEmitter {
142
146
  this.emit('aborted');
143
147
  }
144
148
 
145
- this.emit('stopped');
149
+ this.stopRequestedAtTimestamp = Date.now();
150
+ this.waitForPollingToComplete();
151
+ }
152
+
153
+ /**
154
+ * Wait for final poll and in flight messages to complete.
155
+ * @private
156
+ */
157
+ private waitForPollingToComplete(): void {
158
+ if (!this.isPolling || !(this.pollingCompleteWaitTimeMs > 0)) {
159
+ this.emit('stopped');
160
+ return;
161
+ }
162
+
163
+ const exceededTimeout =
164
+ Date.now() - this.stopRequestedAtTimestamp >
165
+ this.pollingCompleteWaitTimeMs;
166
+ if (exceededTimeout) {
167
+ logger.debug('waiting_for_polling_to_complete_timeout_exceeded');
168
+ this.emit('stopped');
169
+ return;
170
+ }
171
+
172
+ logger.debug('waiting_for_polling_to_complete');
173
+ setTimeout(this.waitForPollingToComplete, 1000);
146
174
  }
147
175
 
148
176
  /**
@@ -198,6 +226,8 @@ export class Consumer extends TypedEventEmitter {
198
226
 
199
227
  logger.debug('polling');
200
228
 
229
+ this.isPolling = true;
230
+
201
231
  let currentPollingTimeout = this.pollingWaitTimeMs;
202
232
  this.receiveMessage({
203
233
  QueueUrl: this.queueUrl,
@@ -227,6 +257,9 @@ export class Consumer extends TypedEventEmitter {
227
257
  })
228
258
  .catch((err) => {
229
259
  this.emitError(err);
260
+ })
261
+ .finally(() => {
262
+ this.isPolling = false;
230
263
  });
231
264
  }
232
265
 
package/src/types.ts CHANGED
@@ -46,6 +46,12 @@ export interface ConsumerOptions {
46
46
  * @defaultvalue `0`
47
47
  */
48
48
  pollingWaitTimeMs?: number;
49
+ /**
50
+ * If you want the stop action to wait for the final poll to complete and in-flight messages
51
+ * to be processed before emitting 'stopped' set this to the max amount of time to wait.
52
+ * @defaultvalue `0`
53
+ */
54
+ pollingCompleteWaitTimeMs?: number;
49
55
  /**
50
56
  * If true, sets the message visibility timeout to 0 after a `processing_error`.
51
57
  * @defaultvalue `false`