Skip to content

Fix/enhance sqs partial acknowledgement handling#1562

Open
co2plant wants to merge 7 commits intoawspring:mainfrom
co2plant:fix/EnhanceSQSPartialAcknowledgementHandling
Open

Fix/enhance sqs partial acknowledgement handling#1562
co2plant wants to merge 7 commits intoawspring:mainfrom
co2plant:fix/EnhanceSQSPartialAcknowledgementHandling

Conversation

@co2plant
Copy link

@co2plant co2plant commented Jan 25, 2026

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

#1497
I first added logging for partial failures.
I checked the directory and found "SqsTemplate.deleteMessages," so I tried to refactor it to match the style as closely as possible.

💡 Motivation and Context

💚 How did you test it?

In the test code (sqs/listener/acknowledgement/SqsAcknowledgementExecutorTests.java), I changed null to DeleteMessageBatchResponse and added code for partial failures.

{53D7D246-5B6C-4C25-962C-6B3EC52FCF69}

📝 Checklist

  • I reviewed submitted code
  • I added tests to verify changes
  • I updated reference documentation to reflect the change
  • All tests passing
  • No breaking changes

🔮 Next steps

@github-actions github-actions bot added the component: sqs SQS integration related issue label Jan 25, 2026
Copy link
Contributor

@tomazfernandes tomazfernandes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @co2plant, on a first glance looks good.

Notice that SqsAcknowledgementException has a constructor that accepts separate lists for successfully acknowledged messages and failed acknowledgement messages - it would be useful to populate the lists according to AWS response.

public SqsAcknowledgementException(String errorMessage, Collection<Message<?>> successfullyAcknowledgedMessages,
Collection<Message<?>> failedAcknowledgementMessages, String queue, @Nullable Throwable cause) {
super(errorMessage, cause);
this.queue = queue;
this.failedAcknowledgementMessages = failedAcknowledgementMessages.stream().map(msg -> (Message<?>) msg)
.collect(Collectors.toList());
this.successfullyAcknowledgedMessages = successfullyAcknowledgedMessages.stream().map(msg -> (Message<?>) msg)
.collect(Collectors.toList());
}

Thanks.

DeleteMessageBatch response
- Correlate items using message IDs for accurate mapping
- Pass different lists to the SqsAcknowledgementException constructor to handle partial failures
- Extract partial error handling logic from the deleteMessages method
- Improved code readability and maintainability
- placed the calling method at the top and the helper method at the bottom
@co2plant
Copy link
Author

Thanks for the review @tomazfernandes!

I was wondering how to separate the failures from the full failures, and your review was very helpful.

I updated the code to map item IDs to message IDs based on the AWS responses and populate the success/failure lists.
I also separated the partial failure handling logic into a separate method to improve readability.

{D81CB6CC-D0D6-420A-B8E1-51F06E4455C1}

@co2plant co2plant marked this pull request as ready for review January 25, 2026 22:47
Copy link
Contributor

@tomazfernandes tomazfernandes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @co2plant, looking good.

Left a few suggestions. Also, can you please add a section in the docs explaining how partial Acknowledgement failures are handled?

We could also add to the docs that the resulting SqsAcknowledgementException will be available in AcknowledgementResultCallback, so failed exceptions can be inspected and e.g. retry acknowledging them.

.thenRun(() -> {}),
t -> CompletableFutures.failedFuture(createAcknowledgementException(messagesToAck, t)))
.thenCompose(response -> {
if (!response.failed().isEmpty()) {
Copy link
Contributor

@tomazfernandes tomazfernandes Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can we move this logic to a separate method?

given(queueAttributes.getQueueName()).willReturn(queueName);
given(queueAttributes.getQueueUrl()).willReturn(queueUrl);

BatchResultErrorEntry failedEntry = BatchResultErrorEntry.builder().id("test-id").code("ReceiptHandleIsInvalid")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this id match the one in the message?

executor.setQueueAttributes(queueAttributes);

assertThatThrownBy(() -> executor.execute(messages).join()).isInstanceOf(CompletionException.class)
.hasCauseInstanceOf(SqsAcknowledgementException.class);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add assertions to verify getFailedAcknowledgementMessages() contains the message. We could also add successful messages to verify the exception is being create properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: sqs SQS integration related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants