Skip to content

High throughput: one DeleteMessage API call per consumed message #11

Description

@josuedeavila

Problem

route.Commit issues one DeleteMessage call per consumed message. Since ReceiveMessage already runs at MaxNumberOfMessages=10, a full receive cycle costs 1 receive call and up to 10 delete calls, so deletes dominate the API request count — and SQS bills per request regardless of how many messages a request carries.

At 1M messages/day that is ~1M delete requests where DeleteMessageBatch (up to 10 entries per call, the same ceiling as MaxNumberOfMessages) would need ~100k.

Why a plain time-based buffer is not enough

The obvious fix — accumulate deletes and flush on a timer — trades cost for latency, and on FIFO queues that trade is a bad one: SQS does not deliver the next message of a MessageGroupId while one is still in flight, so every millisecond a delete sits in a buffer throttles the whole group.

Any batching design here has to avoid paying that latency in the common case.

Proposal

Make the end of the receive cycle the primary flush trigger instead of a timer. A per-route in-flight counter is incremented in GetMessages and decremented as each message resolves (committed, backed off, or failed in the handler); reaching zero flushes immediately. A linger stays as the safety net for when a slow handler holds the batch back.

Measured against LocalStack:

scenario messages delete API calls wall time
standard, today 100 100 DeleteMessage
standard, batched 100 10 DeleteMessageBatch
FIFO, 4 groups, today 60 60 DeleteMessage 528ms
FIFO, 4 groups, batched 60 6 DeleteMessageBatch 322ms

FIFO gets faster rather than slower, because workers stop blocking on a delete round trip.

Notes

  • This is a breaking change to the SQSClient interface (DeleteMessageBatch is added). The AWS SDK client already satisfies it; only custom implementations are affected.
  • It should be opt-in, so existing consumers keep the current behaviour on upgrade.
  • ChangeMessageVisibility has the same batching opportunity, but it is a smaller win: every message goes through Commit, while only a fraction is ever extended. Worth a separate issue.

I have an implementation ready and will open a PR linked to this issue.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions