sqs-consumer 6.1.0 → 6.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/src/consumer.ts CHANGED
@@ -16,102 +16,42 @@ import {
16
16
  ReceiveMessageCommandOutput
17
17
  } from '@aws-sdk/client-sqs';
18
18
  import Debug from 'debug';
19
- import { EventEmitter } from 'events';
20
19
 
21
- import { AWSError, ConsumerOptions, Events } from './types';
20
+ import { ConsumerOptions, TypedEventEmitter } from './types';
22
21
  import { autoBind } from './bind';
23
- import { SQSError, TimeoutError } from './errors';
22
+ import {
23
+ SQSError,
24
+ TimeoutError,
25
+ toSQSError,
26
+ isConnectionError
27
+ } from './errors';
28
+ import { assertOptions, hasMessages } from './validation';
24
29
 
25
30
  const debug = Debug('sqs-consumer');
26
31
 
27
- const requiredOptions = [
28
- 'queueUrl',
29
- // only one of handleMessage / handleMessagesBatch is required
30
- 'handleMessage|handleMessageBatch'
31
- ];
32
-
33
- interface TimeoutResponse {
34
- timeout: NodeJS.Timeout;
35
- pending: Promise<void>;
36
- }
37
-
38
- function createTimeout(duration: number): TimeoutResponse[] {
39
- let timeout;
40
- const pending = new Promise((_, reject) => {
41
- timeout = setTimeout((): void => {
42
- reject(new TimeoutError());
43
- }, duration);
44
- });
45
- return [timeout, pending];
46
- }
47
-
48
- function assertOptions(options: ConsumerOptions): void {
49
- requiredOptions.forEach((option) => {
50
- const possibilities = option.split('|');
51
- if (!possibilities.find((p) => options[p])) {
52
- throw new Error(
53
- `Missing SQS consumer option [ ${possibilities.join(' or ')} ].`
54
- );
55
- }
56
- });
57
-
58
- if (options.batchSize > 10 || options.batchSize < 1) {
59
- throw new Error('SQS batchSize option must be between 1 and 10.');
60
- }
61
-
62
- if (
63
- options.heartbeatInterval &&
64
- !(options.heartbeatInterval < options.visibilityTimeout)
65
- ) {
66
- throw new Error('heartbeatInterval must be less than visibilityTimeout.');
67
- }
68
- }
69
-
70
- function isConnectionError(err: Error): boolean {
71
- if (err instanceof SQSError) {
72
- return (
73
- err.statusCode === 403 ||
74
- err.code === 'CredentialsError' ||
75
- err.code === 'UnknownEndpoint' ||
76
- err.code === 'AWS.SimpleQueueService.NonExistentQueue'
77
- );
78
- }
79
- return false;
80
- }
81
-
82
- function toSQSError(err: AWSError, message: string): SQSError {
83
- const sqsError = new SQSError(message);
84
- sqsError.code = err.name;
85
- sqsError.statusCode = err.$metadata?.httpStatusCode;
86
- sqsError.retryable = err.$retryable?.throttling;
87
- sqsError.service = err.$service;
88
- sqsError.fault = err.$fault;
89
- sqsError.time = new Date();
90
-
91
- return sqsError;
92
- }
93
-
94
- function hasMessages(response: ReceiveMessageCommandOutput): boolean {
95
- return response.Messages && response.Messages.length > 0;
96
- }
97
-
98
- export class Consumer extends EventEmitter {
32
+ /**
33
+ * [Usage](https://bbc.github.io/sqs-consumer/index.html#usage)
34
+ */
35
+ export class Consumer extends TypedEventEmitter {
36
+ private pollingTimeoutId: NodeJS.Timeout | undefined = undefined;
37
+ private heartbeatTimeoutId: NodeJS.Timeout | undefined = undefined;
38
+ private handleMessageTimeoutId: NodeJS.Timeout | undefined = undefined;
39
+ private stopped = true;
99
40
  private queueUrl: string;
100
- private handleMessage: (message: Message) => Promise<void>;
41
+ private handleMessage: (message: Message) => Promise<Message | void>;
101
42
  private handleMessageBatch: (message: Message[]) => Promise<Message[] | void>;
43
+ private sqs: SQSClient;
102
44
  private handleMessageTimeout: number;
103
45
  private attributeNames: string[];
104
46
  private messageAttributeNames: string[];
105
- private stopped: boolean;
47
+ private shouldDeleteMessages: boolean;
106
48
  private batchSize: number;
107
49
  private visibilityTimeout: number;
50
+ private terminateVisibilityTimeout: boolean;
108
51
  private waitTimeSeconds: number;
109
52
  private authenticationErrorTimeout: number;
110
53
  private pollingWaitTimeMs: number;
111
- private terminateVisibilityTimeout: boolean;
112
54
  private heartbeatInterval: number;
113
- private sqs: SQSClient;
114
- private shouldDeleteMessages: boolean;
115
55
 
116
56
  constructor(options: ConsumerOptions) {
117
57
  super();
@@ -122,7 +62,6 @@ export class Consumer extends EventEmitter {
122
62
  this.handleMessageTimeout = options.handleMessageTimeout;
123
63
  this.attributeNames = options.attributeNames || [];
124
64
  this.messageAttributeNames = options.messageAttributeNames || [];
125
- this.stopped = true;
126
65
  this.batchSize = options.batchSize || 1;
127
66
  this.visibilityTimeout = options.visibilityTimeout;
128
67
  this.terminateVisibilityTimeout =
@@ -133,89 +72,171 @@ export class Consumer extends EventEmitter {
133
72
  options.authenticationErrorTimeout ?? 10000;
134
73
  this.pollingWaitTimeMs = options.pollingWaitTimeMs ?? 0;
135
74
  this.shouldDeleteMessages = options.shouldDeleteMessages ?? true;
136
-
137
75
  this.sqs =
138
76
  options.sqs ||
139
77
  new SQSClient({
140
78
  region: options.region || process.env.AWS_REGION || 'eu-west-1'
141
79
  });
142
-
143
80
  autoBind(this);
144
81
  }
145
82
 
146
- emit<T extends keyof Events>(event: T, ...args: Events[T]) {
147
- return super.emit(event, ...args);
83
+ /**
84
+ * Creates a new SQS consumer.
85
+ */
86
+ public static create(options: ConsumerOptions): Consumer {
87
+ return new Consumer(options);
148
88
  }
149
89
 
150
- on<T extends keyof Events>(
151
- event: T,
152
- listener: (...args: Events[T]) => void
153
- ): this {
154
- return super.on(event, listener);
90
+ /**
91
+ * Start polling the queue for messages.
92
+ */
93
+ public start(): void {
94
+ if (this.stopped) {
95
+ debug('Starting consumer');
96
+ this.stopped = false;
97
+ this.poll();
98
+ }
155
99
  }
156
100
 
157
- once<T extends keyof Events>(
158
- event: T,
159
- listener: (...args: Events[T]) => void
160
- ): this {
161
- return super.once(event, listener);
101
+ /**
102
+ * Stop polling the queue for messages (pre existing requests will still be made until concluded).
103
+ */
104
+ public stop(): void {
105
+ if (this.stopped) {
106
+ debug('Consumer was already stopped');
107
+ return;
108
+ }
109
+
110
+ debug('Stopping consumer');
111
+ this.stopped = true;
112
+
113
+ if (this.pollingTimeoutId) {
114
+ clearTimeout(this.pollingTimeoutId);
115
+ this.pollingTimeoutId = undefined;
116
+ }
117
+
118
+ this.emit('stopped');
162
119
  }
163
120
 
121
+ /**
122
+ * Returns the current polling state of the consumer: `true` if it is actively polling, `false` if it is not.
123
+ */
164
124
  public get isRunning(): boolean {
165
125
  return !this.stopped;
166
126
  }
167
127
 
168
- public static create(options: ConsumerOptions): Consumer {
169
- return new Consumer(options);
128
+ /**
129
+ * Emit one of the consumer's error events depending on the error received.
130
+ * @param err The error object to forward on
131
+ * @param message The message that the error occurred on
132
+ */
133
+ private emitError(err: Error, message?: Message): void {
134
+ if (!message) {
135
+ this.emit('error', err);
136
+ } else if (err.name === SQSError.name) {
137
+ this.emit('error', err, message);
138
+ } else if (err instanceof TimeoutError) {
139
+ this.emit('timeout_error', err, message);
140
+ } else {
141
+ this.emit('processing_error', err, message);
142
+ }
170
143
  }
171
144
 
172
- public start(): void {
145
+ /**
146
+ * Poll for new messages from SQS
147
+ */
148
+ private poll(): void {
173
149
  if (this.stopped) {
174
- debug('Starting consumer');
175
- this.stopped = false;
176
- this.poll();
150
+ debug('Poll was called while consumer was stopped, cancelling poll...');
151
+ return;
177
152
  }
153
+
154
+ debug('Polling for messages');
155
+
156
+ let currentPollingTimeout = this.pollingWaitTimeMs;
157
+ this.receiveMessage({
158
+ QueueUrl: this.queueUrl,
159
+ AttributeNames: this.attributeNames,
160
+ MessageAttributeNames: this.messageAttributeNames,
161
+ MaxNumberOfMessages: this.batchSize,
162
+ WaitTimeSeconds: this.waitTimeSeconds,
163
+ VisibilityTimeout: this.visibilityTimeout
164
+ })
165
+ .then(this.handleSqsResponse)
166
+ .catch((err) => {
167
+ this.emitError(err);
168
+ if (isConnectionError(err)) {
169
+ debug('There was an authentication error. Pausing before retrying.');
170
+ currentPollingTimeout = this.authenticationErrorTimeout;
171
+ }
172
+ return;
173
+ })
174
+ .then(() => {
175
+ if (this.pollingTimeoutId) {
176
+ clearTimeout(this.pollingTimeoutId);
177
+ }
178
+ this.pollingTimeoutId = setTimeout(this.poll, currentPollingTimeout);
179
+ })
180
+ .catch((err) => {
181
+ this.emitError(err);
182
+ });
178
183
  }
179
184
 
180
- public stop(): void {
181
- debug('Stopping consumer');
182
- this.stopped = true;
185
+ /**
186
+ * Send a request to SQS to retrieve messages
187
+ * @param params The required params to receive messages from SQS
188
+ */
189
+ private async receiveMessage(
190
+ params: ReceiveMessageCommandInput
191
+ ): Promise<ReceiveMessageCommandOutput> {
192
+ try {
193
+ return await this.sqs.send(new ReceiveMessageCommand(params));
194
+ } catch (err) {
195
+ throw toSQSError(err, `SQS receive message failed: ${err.message}`);
196
+ }
183
197
  }
184
198
 
199
+ /**
200
+ * Handles the response from AWS SQS, determining if we should proceed to
201
+ * the message handler.
202
+ * @param response The output from AWS SQS
203
+ */
185
204
  private async handleSqsResponse(
186
205
  response: ReceiveMessageCommandOutput
187
206
  ): Promise<void> {
188
- debug('Received SQS response');
189
- debug(response);
190
-
191
- if (response) {
192
- if (hasMessages(response)) {
193
- if (this.handleMessageBatch) {
194
- // prefer handling messages in batch when available
195
- await this.processMessageBatch(response.Messages);
196
- } else {
197
- await Promise.all(response.Messages.map(this.processMessage));
198
- }
199
- this.emit('response_processed');
207
+ if (hasMessages(response)) {
208
+ if (this.handleMessageBatch) {
209
+ await this.processMessageBatch(response.Messages);
200
210
  } else {
201
- this.emit('empty');
211
+ await Promise.all(response.Messages.map(this.processMessage));
202
212
  }
213
+
214
+ this.emit('response_processed');
215
+ } else if (response) {
216
+ this.emit('empty');
203
217
  }
204
218
  }
205
219
 
220
+ /**
221
+ * Process a message that has been received from SQS. This will execute the message
222
+ * handler and delete the message once complete.
223
+ * @param message The message that was delivered from SQS
224
+ */
206
225
  private async processMessage(message: Message): Promise<void> {
207
- this.emit('message_received', message);
208
-
209
- let heartbeat;
210
226
  try {
227
+ this.emit('message_received', message);
228
+
211
229
  if (this.heartbeatInterval) {
212
- heartbeat = this.startHeartbeat(async () => {
213
- return this.changeVisibilityTimeout(message, this.visibilityTimeout);
214
- });
230
+ this.heartbeatTimeoutId = this.startHeartbeat(message);
231
+ }
232
+
233
+ const ackedMessage = await this.executeHandler(message);
234
+
235
+ if (ackedMessage?.MessageId === message.MessageId) {
236
+ await this.deleteMessage(message);
237
+
238
+ this.emit('message_processed', message);
215
239
  }
216
- await this.executeHandler(message);
217
- await this.deleteMessage(message);
218
- this.emit('message_processed', message);
219
240
  } catch (err) {
220
241
  this.emitError(err, message);
221
242
 
@@ -223,63 +244,71 @@ export class Consumer extends EventEmitter {
223
244
  await this.changeVisibilityTimeout(message, 0);
224
245
  }
225
246
  } finally {
226
- clearInterval(heartbeat);
247
+ clearInterval(this.heartbeatTimeoutId);
248
+ this.heartbeatTimeoutId = undefined;
227
249
  }
228
250
  }
229
251
 
230
- private async receiveMessage(
231
- params: ReceiveMessageCommandInput
232
- ): Promise<ReceiveMessageCommandOutput> {
252
+ /**
253
+ * Process a batch of messages from the SQS queue.
254
+ * @param messages The messages that were delivered from SQS
255
+ */
256
+ private async processMessageBatch(messages: Message[]): Promise<void> {
233
257
  try {
234
- return await this.sqs.send(new ReceiveMessageCommand(params));
235
- } catch (err) {
236
- throw toSQSError(err, `SQS receive message failed: ${err.message}`);
237
- }
238
- }
258
+ messages.forEach((message) => {
259
+ this.emit('message_received', message);
260
+ });
239
261
 
240
- private async deleteMessage(message: Message): Promise<void> {
241
- if (!this.shouldDeleteMessages) {
242
- debug(
243
- 'Skipping message delete since shouldDeleteMessages is set to false'
244
- );
245
- return;
246
- }
247
- debug('Deleting message %s', message.MessageId);
262
+ if (this.heartbeatInterval) {
263
+ this.heartbeatTimeoutId = this.startHeartbeat(null, messages);
264
+ }
248
265
 
249
- const deleteParams: DeleteMessageCommandInput = {
250
- QueueUrl: this.queueUrl,
251
- ReceiptHandle: message.ReceiptHandle
252
- };
266
+ const ackedMessages = await this.executeBatchHandler(messages);
253
267
 
254
- try {
255
- await this.sqs.send(new DeleteMessageCommand(deleteParams));
256
- } catch (err) {
257
- throw toSQSError(err, `SQS delete message failed: ${err.message}`);
258
- }
259
- }
268
+ if (ackedMessages?.length > 0) {
269
+ await this.deleteMessageBatch(ackedMessages);
260
270
 
261
- private async executeHandler(message: Message): Promise<void> {
262
- let timeout;
263
- let pending;
264
- try {
265
- if (this.handleMessageTimeout) {
266
- [timeout, pending] = createTimeout(this.handleMessageTimeout);
267
- await Promise.race([this.handleMessage(message), pending]);
268
- } else {
269
- await this.handleMessage(message);
271
+ ackedMessages.forEach((message) => {
272
+ this.emit('message_processed', message);
273
+ });
270
274
  }
271
275
  } catch (err) {
272
- if (err instanceof TimeoutError) {
273
- err.message = `Message handler timed out after ${this.handleMessageTimeout}ms: Operation timed out.`;
274
- } else if (err instanceof Error) {
275
- err.message = `Unexpected message handler failure: ${err.message}`;
276
+ this.emit('error', err, messages);
277
+
278
+ if (this.terminateVisibilityTimeout) {
279
+ await this.changeVisibilityTimeoutBatch(messages, 0);
276
280
  }
277
- throw err;
278
281
  } finally {
279
- clearTimeout(timeout);
282
+ clearInterval(this.heartbeatTimeoutId);
283
+ this.heartbeatTimeoutId = undefined;
280
284
  }
281
285
  }
282
286
 
287
+ /**
288
+ * Trigger a function on a set interval
289
+ * @param heartbeatFn The function that should be triggered
290
+ */
291
+ private startHeartbeat(
292
+ message?: Message,
293
+ messages?: Message[]
294
+ ): NodeJS.Timeout {
295
+ return setInterval(() => {
296
+ if (this.handleMessageBatch) {
297
+ return this.changeVisibilityTimeoutBatch(
298
+ messages,
299
+ this.visibilityTimeout
300
+ );
301
+ } else {
302
+ return this.changeVisibilityTimeout(message, this.visibilityTimeout);
303
+ }
304
+ }, this.heartbeatInterval * 1000);
305
+ }
306
+
307
+ /**
308
+ * Change the visibility timeout on a message
309
+ * @param message The message to change the value of
310
+ * @param timeout The new timeout that should be set
311
+ */
283
312
  private async changeVisibilityTimeout(
284
313
  message: Message,
285
314
  timeout: number
@@ -300,156 +329,137 @@ export class Consumer extends EventEmitter {
300
329
  }
301
330
  }
302
331
 
303
- private emitError(err: Error, message: Message): void {
304
- if (err.name === SQSError.name) {
305
- this.emit('error', err, message);
306
- } else if (err instanceof TimeoutError) {
307
- this.emit('timeout_error', err, message);
308
- } else {
309
- this.emit('processing_error', err, message);
310
- }
311
- }
312
-
313
- private poll(): void {
314
- if (this.stopped) {
315
- this.emit('stopped');
316
- return;
317
- }
318
-
319
- debug('Polling for messages');
320
- const receiveParams: ReceiveMessageCommandInput = {
332
+ /**
333
+ * Change the visibility timeout on a batch of messages
334
+ * @param messages The messages to change the value of
335
+ * @param timeout The new timeout that should be set
336
+ */
337
+ private async changeVisibilityTimeoutBatch(
338
+ messages: Message[],
339
+ timeout: number
340
+ ): Promise<ChangeMessageVisibilityBatchCommandOutput> {
341
+ const params: ChangeMessageVisibilityBatchCommandInput = {
321
342
  QueueUrl: this.queueUrl,
322
- AttributeNames: this.attributeNames,
323
- MessageAttributeNames: this.messageAttributeNames,
324
- MaxNumberOfMessages: this.batchSize,
325
- WaitTimeSeconds: this.waitTimeSeconds,
326
- VisibilityTimeout: this.visibilityTimeout
343
+ Entries: messages.map((message) => ({
344
+ Id: message.MessageId,
345
+ ReceiptHandle: message.ReceiptHandle,
346
+ VisibilityTimeout: timeout
347
+ }))
327
348
  };
328
-
329
- let currentPollingTimeout = this.pollingWaitTimeMs;
330
- this.receiveMessage(receiveParams)
331
- .then(this.handleSqsResponse)
332
- .catch((err) => {
333
- this.emit('error', err);
334
- if (isConnectionError(err)) {
335
- debug('There was an authentication error. Pausing before retrying.');
336
- currentPollingTimeout = this.authenticationErrorTimeout;
337
- }
338
- return;
339
- })
340
- .then(() => {
341
- setTimeout(this.poll, currentPollingTimeout);
342
- })
343
- .catch((err) => {
344
- this.emit('error', err);
345
- });
349
+ try {
350
+ return await this.sqs.send(
351
+ new ChangeMessageVisibilityBatchCommand(params)
352
+ );
353
+ } catch (err) {
354
+ this.emit(
355
+ 'error',
356
+ toSQSError(err, `Error changing visibility timeout: ${err.message}`),
357
+ messages
358
+ );
359
+ }
346
360
  }
347
361
 
348
- private async processMessageBatch(messages: Message[]): Promise<void> {
349
- messages.forEach((message) => {
350
- this.emit('message_received', message);
351
- });
352
-
353
- let heartbeat;
362
+ /**
363
+ * Trigger the applications handleMessage function
364
+ * @param message The message that was received from SQS
365
+ */
366
+ private async executeHandler(message: Message): Promise<Message> {
354
367
  try {
355
- if (this.heartbeatInterval) {
356
- heartbeat = this.startHeartbeat(async () => {
357
- return this.changeVisibilityTimeoutBatch(
358
- messages,
359
- this.visibilityTimeout
360
- );
368
+ let result;
369
+
370
+ if (this.handleMessageTimeout) {
371
+ const pending = new Promise((_, reject) => {
372
+ this.handleMessageTimeoutId = setTimeout((): void => {
373
+ reject(new TimeoutError());
374
+ }, this.handleMessageTimeout);
361
375
  });
376
+ result = await Promise.race([this.handleMessage(message), pending]);
377
+ } else {
378
+ result = await this.handleMessage(message);
362
379
  }
363
- const ackedMessages = await this.executeBatchHandler(messages);
364
380
 
365
- if (ackedMessages.length > 0) {
366
- await this.deleteMessageBatch(ackedMessages);
381
+ return result instanceof Object ? result : message;
382
+ } catch (err) {
383
+ err.message =
384
+ err instanceof TimeoutError
385
+ ? `Message handler timed out after ${this.handleMessageTimeout}ms: Operation timed out.`
386
+ : `Unexpected message handler failure: ${err.message}`;
387
+ throw err;
388
+ } finally {
389
+ if (this.handleMessageTimeoutId) {
390
+ clearTimeout(this.handleMessageTimeoutId);
367
391
  }
392
+ }
393
+ }
368
394
 
369
- ackedMessages.forEach((message) => {
370
- this.emit('message_processed', message);
371
- });
372
- } catch (err) {
373
- this.emit('error', err, messages);
395
+ /**
396
+ * Execute the application's message batch handler
397
+ * @param messages The messages that should be forwarded from the SQS queue
398
+ */
399
+ private async executeBatchHandler(messages: Message[]): Promise<Message[]> {
400
+ try {
401
+ const result = await this.handleMessageBatch(messages);
374
402
 
375
- if (this.terminateVisibilityTimeout) {
376
- await this.changeVisibilityTimeoutBatch(messages, 0);
377
- }
378
- } finally {
379
- clearInterval(heartbeat);
403
+ return result instanceof Object ? result : messages;
404
+ } catch (err) {
405
+ err.message = `Unexpected message handler failure: ${err.message}`;
406
+ throw err;
380
407
  }
381
408
  }
382
409
 
383
- private async deleteMessageBatch(messages: Message[]): Promise<void> {
410
+ /**
411
+ * Delete a single message from SQS
412
+ * @param message The message to delete from the SQS queue
413
+ */
414
+ private async deleteMessage(message: Message): Promise<void> {
384
415
  if (!this.shouldDeleteMessages) {
385
416
  debug(
386
417
  'Skipping message delete since shouldDeleteMessages is set to false'
387
418
  );
388
419
  return;
389
420
  }
390
- debug(
391
- 'Deleting messages %s',
392
- messages.map((msg) => msg.MessageId).join(' ,')
393
- );
421
+ debug('Deleting message %s', message.MessageId);
394
422
 
395
- const deleteParams: DeleteMessageBatchCommandInput = {
423
+ const deleteParams: DeleteMessageCommandInput = {
396
424
  QueueUrl: this.queueUrl,
397
- Entries: messages.map((message) => ({
398
- Id: message.MessageId,
399
- ReceiptHandle: message.ReceiptHandle
400
- }))
425
+ ReceiptHandle: message.ReceiptHandle
401
426
  };
402
427
 
403
428
  try {
404
- await this.sqs.send(new DeleteMessageBatchCommand(deleteParams));
429
+ await this.sqs.send(new DeleteMessageCommand(deleteParams));
405
430
  } catch (err) {
406
431
  throw toSQSError(err, `SQS delete message failed: ${err.message}`);
407
432
  }
408
433
  }
409
434
 
410
- private async executeBatchHandler(messages: Message[]): Promise<Message[]> {
411
- try {
412
- const result = await this.handleMessageBatch(messages);
413
-
414
- if (result instanceof Object) {
415
- return result;
416
- }
417
-
418
- return messages;
419
- } catch (err) {
420
- err.message = `Unexpected message handler failure: ${err.message}`;
421
- throw err;
435
+ /**
436
+ * Delete a batch of messages from the SQS queue.
437
+ * @param messages The messages that should be deleted from SQS
438
+ */
439
+ private async deleteMessageBatch(messages: Message[]): Promise<void> {
440
+ if (!this.shouldDeleteMessages) {
441
+ debug(
442
+ 'Skipping message delete since shouldDeleteMessages is set to false'
443
+ );
444
+ return;
422
445
  }
423
- }
446
+ debug(
447
+ 'Deleting messages %s',
448
+ messages.map((msg) => msg.MessageId).join(' ,')
449
+ );
424
450
 
425
- private async changeVisibilityTimeoutBatch(
426
- messages: Message[],
427
- timeout: number
428
- ): Promise<ChangeMessageVisibilityBatchCommandOutput> {
429
- const params: ChangeMessageVisibilityBatchCommandInput = {
451
+ const deleteParams: DeleteMessageBatchCommandInput = {
430
452
  QueueUrl: this.queueUrl,
431
453
  Entries: messages.map((message) => ({
432
454
  Id: message.MessageId,
433
- ReceiptHandle: message.ReceiptHandle,
434
- VisibilityTimeout: timeout
455
+ ReceiptHandle: message.ReceiptHandle
435
456
  }))
436
457
  };
458
+
437
459
  try {
438
- return await this.sqs.send(
439
- new ChangeMessageVisibilityBatchCommand(params)
440
- );
460
+ await this.sqs.send(new DeleteMessageBatchCommand(deleteParams));
441
461
  } catch (err) {
442
- this.emit(
443
- 'error',
444
- toSQSError(err, `Error changing visibility timeout: ${err.message}`),
445
- messages
446
- );
462
+ throw toSQSError(err, `SQS delete message failed: ${err.message}`);
447
463
  }
448
464
  }
449
-
450
- private startHeartbeat(heartbeatFn: () => void): NodeJS.Timeout {
451
- return setInterval(() => {
452
- heartbeatFn();
453
- }, this.heartbeatInterval * 1000);
454
- }
455
465
  }