Sitemap

Amazon SQS Fair Queues for Fairness in Multi-Tenant Environments

4 min readJul 30, 2025

--

Press enter or click to view image in full size
Multi-tenant SQS diagram | taken from aws-samples example

Introduction

Multi tenancy is where multiple applications or tenants share resources in order to gain agility and cost efficiency. In modern cloud architectures, it’s common to have multiple tenants (or customers) sharing the same SQS queue to decouple and scale processing. However, a single “noisy” tenant can flood the queue, starving quieter tenants of processing capacity. Amazon SQS Fair Queues is a new feature that solves this noisy neighbor problem by dynamically reordering messages so that tenants with fewer in-flight messages get priority.

Enabling Fair Queues

Enabling Fair Queues requires no change to consumer applications. Producers should only include MessageGroupId when sending messages, as shown in the example Java code below.

// Producer only needs to add MessageGroupId
SendMessageRequest req = SendMessageRequest.builder()
.queueUrl(queueUrl)
.messageBody(payload)
.messageGroupId(tenantId) // ← Enables Fair Queues
.build();
sqs.sendMessage(req);

How Fair Queues Work

  1. Tenant Identification
    Fair Queues leverages the existing MessageGroupId property on SQS messages to tag each message with a tenant identifier called “tenant key.” When you send a message to a standard queue and include a MessageGroupId, SQS treats that value as the tenant key. Internally, every message with the same group ID is counted toward that tenant’s in-flight total. The mere presence of MessageGroupId in a standard queue automatically enables the Fair Queue capability for those messages.
  2. Detection of Noisy Tenants
    As messages are delivered (but not yet deleted), SQS maintains real-time counts of in-flight messages per MessageGroupId. When one tenant’s in-flight count grows disproportionately large, because it’s sending faster or its consumers are slower, SQS flags that group as a noisy neighbor. This detection happens continuously behind the scenes, with no consumer-side logic required.
  3. Dynamic Reordering
    On each ReceiveMessage call, Fair Queues biases which messages it returns based on current in-flight counts. Messages from quieter tenants (those with fewer in-flight messages) are surfaced first, while messages from the noisy group are temporarily deprioritized. If there’s spare consumer capacity or no other messages waiting, SQS will still return noisy-group messages, so overall throughput remains unlimited, but it always prefers to pull from non-noisy tenants until the imbalance resolves.

Supported Metrics for Monitoring Fair Queues

Amazon SQS publishes both existing and new Fair Queue–specific metrics to CloudWatch:

  • ApproximateNumberOfNoisyGroups: Count of groups currently considered noisy.
  • ApproximateNumberOfMessagesVisibleInQuietGroups: Messages visible from non-noisy (quiet) groups.
  • ApproximateNumberOfMessagesNotVisibleInQuietGroups: In-flight messages from quiet groups.
  • ApproximateNumberOfMessagesDelayedInQuietGroups: Delayed messages originating from quiet groups.
  • ApproximateAgeOfOldestMessageInQuietGroups: Age of the oldest message in quiet groups.

Validating Metrics

aws-samples on GitHub also contains a helpful repository that includes the consumer, producer and the infrastructure code. Testing the behavior of fair queues can easily be done using this repository on GitHub. I have also created a sample Java application to observe the same results, but in order to see the detailed metrics provided within the aws-samples example, you can verify the metrics easily by yourself following the steps on the repository README.

Note that deploying this infrastucture and running the load test may incur charges.

Press enter or click to view image in full size
Infrastructure for the sample application | taken from aws-samples example

Example Codes

Producer (per tenant):

Example producer code in Java looks like the following, note that the only difference is that the messageGroupId is provided when sending the message to SQS.

SendMessageResponse resp = sqs.sendMessage(
SendMessageRequest.builder()
.queueUrl(QUEUE_URL)
.messageBody("Msg from " + tenantId + " @ " + Instant.now())
.messageGroupId(tenantId) // ← enables Fair Queues on a standard queue
.build()
);

Consumer (shared):

Example consumer code in Java looks like the following, note that there is no change compared to a regular consumer code.

ReceiveMessageResponse resp = sqs.receiveMessage(
ReceiveMessageRequest.builder()
.queueUrl(QUEUE_URL)
.maxNumberOfMessages(10)
.waitTimeSeconds(5)
.build()
);
for (Message m : resp.messages()) {
sqs.deleteMessage(DeleteMessageRequest.builder()
.queueUrl(QUEUE_URL)
.receiptHandle(m.receiptHandle())
.build());
}

Test Yourself

  1. Clone the repository from this link.
  2. Configure AWS credentials which will have the required SQS, CloudWatch, Lambda, API Gateway and CloudWatch permissions.
  3. Follows the steps on the README.md to deploy the infrastructure and perform the load test.

Results

Press enter or click to view image in full size
Queue backlog for quiet and noisy neighbors
  1. ApproximateNumberOfNoisyGroups
    This metric showed that tenantA was frequently identified as noisy, which aligns with its 100× higher message rate. As Fair Queues kicked in, tenantA was deprioritized during high in-flight periods, giving tenantB and tenantC fair access to processing.
    Observation: Noisy group detection works instantly and updates every minute.
  2. ApproximateNumberOfMessagesVisibleInQuietGroups
    This number remained stable throughout the run. Even with tenantA sending more aggressively, Fair Queues ensured that visible messages from tenantB and tenantC were consistently surfaced.
    Conclusion: Quiet tenants are not blocked by bursty senders.
  3. ApproximateAgeOfOldestMessageInQuietGroups
    These are in-flight messages from quiet tenants. The count fluctuated slightly but stayed within expected bounds, confirming that quiet tenants’ messages were picked up quickly by the consumer.
    Takeaway: Quiet group messages are getting processed without delay.

Conclusion

This test shows that enabling Fair Queues is extremely simple, setting MessageGroupId on the producer application enables gaining fair message distribution for multi-tenant scenarios. Without writing any extra logic, the consumer automatically benefits from dynamic message reordering based on real-time load.

One caveat worth mentioning: while Fair Queues optimize delivery across tenants, they still rely on well-behaved consumers. If your consumer logic introduces significant processing delays or uneven scaling, fairness may degrade despite SQS’s best efforts. Monitoring both message metrics and consumer performance is key to getting the full benefit.

Useful Links

--

--

akkurtfurkan
akkurtfurkan

Written by akkurtfurkan

Software Engineer | Cloud & Software Architect