Skip to content

Conversation

@DrisDary
Copy link
Contributor

Motivation

The following PR addresses the bug: #11774

Currently, localstacks sqs list_queues method does not implement pagination and cannot display results outside of a given 'MaxResults'. No 'NextToken' value is generated.

Expected behaviour: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ListQueues.html

Changes

The localstack sqs list_queues method now implements pagination by returning and accepting a NextToken value. If you do not set MaxResults, then the response only returns a maximum of 1000 results and all other values are retrieved by using the NextToken value from the previous response.

Testing

create a list of 20 queues:

for i in $(seq 1 20); do
  awslocal sqs create-queue --queue-name "Queue$i"
  echo "Created Queue$i"
done

verify creation of queues:

awslocal sqs list-queues

Apply a MaxResult value.This should result in only the first 4 queues and a NextToken value:

awslocal sqs list-queues --max-results 4

Pass the value of the above NextToken in the response above to see the next batch of queues:

awslocal sqs list-queues --max-results 4 --next-token xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

TODO

Implement error handling and error testing as described by https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ListQueues.html

Copy link
Contributor

@localstack-bot localstack-bot left a comment

Choose a reason for hiding this comment

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

Welcome to LocalStack! Thanks for raising your first Pull Request and landing in your contributions. Our team will reach out with any reviews or feedbacks that we have shortly. We recommend joining our Slack Community and share your PR on the #community channel to share your contributions with us. Please make sure you are following our contributing guidelines and our Code of Conduct.

@localstack-bot
Copy link
Contributor

localstack-bot commented Mar 17, 2025

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@DrisDary
Copy link
Contributor Author

I have read the CLA Document and I hereby sign the CLA

localstack-bot added a commit that referenced this pull request Mar 17, 2025
@alexrashed alexrashed self-requested a review March 17, 2025 13:58
@alexrashed alexrashed self-assigned this Mar 17, 2025
@alexrashed alexrashed added the semver: patch Non-breaking changes which can be included in patch releases label Mar 17, 2025
@alexrashed alexrashed added this to the Playground milestone Mar 17, 2025
Copy link
Member

@alexrashed alexrashed left a comment

Choose a reason for hiding this comment

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

Thanks a lot for your contribution, @DrisDary!
The changes are looking really good! We just need to address some comments / discuss some changes, afterwards this is good to get merged into LocalStack! 🚀

page_size = self.DEFAULT_PAGE_SIZE

if len(result_list) <= page_size:
if len(result_list) <= page_size and next_token is None:
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: ‏Could you maybe add a comment here to explain what these two checks are doing?

Copy link
Contributor Author

@DrisDary DrisDary Mar 19, 2025

Choose a reason for hiding this comment

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

Sure. Just FYI, if length of list is less than the permitted max default size and next_token is not found, then return the last N remaining elements of page where N is length of list. Without the next_token condition, there may be cases where elements from previous pages could be returned.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, interesting! So basically, what you are saying is that if I use next_token on an endpoint where there are less entries than the defined max, the next_token won't be considered.
What is the handling of AWS in this case? Have you verified that AWS respects the next token in this case?
Very good catch!

Copy link
Contributor Author

@DrisDary DrisDary Mar 19, 2025

Choose a reason for hiding this comment

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

suppose you have awslocal sqs list-queues

{
    "QueueUrls": [
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-0",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-1",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-2",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-3",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-4",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-5",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-6",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-7",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-8",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-9"
    ]
}

and you executed awslocal sqs list-queues --max-results 2 then you would get

{
    "QueueUrls": [
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-0",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-1"
    ],
"NextToken": "aHR0cDovL3Nxcy48cmVnaW9uPi5sb2NhbGhvc3QubG9jYWxzdGFjay5jbG91ZDo0NTY2LzExMTExMTExMTExMS83ZjdkZjBmNS10ZXN0LXF1ZXVlLTE="
}

if after you execute list-queues --max-results 10 --next-token "aHR0cDovL3Nxcy48cmVnaW9uPi5sb2NhbGhvc3QubG9jYWxzdGFjay5jbG91ZDo0NTY2LzExMTExMTExMTExMS83ZjdkZjBmNS10ZXN0LXF1ZXVlLTE=" then you now get

{
    "QueueUrls": [
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-2",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-3",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-4",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-5",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-6",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-7",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-8",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-9"
    ]
}

whereas before you would get

{
    "QueueUrls": [
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-0",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-1",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-2",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-3",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-4",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-5",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-6",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-7",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-8",
      "http://sqs.<region>.localhost.localstack.cloud:4566/111111111111/7f7df0f5-test-queue-9"
    ]
}

which of course is incorrect.

The addition of 'and next_token is None:' corrects this.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks a lot for the detailed explanation! This is a really great catch and will actually affect quite a few other services! 🤩

Copy link
Member

@alexrashed alexrashed left a comment

Choose a reason for hiding this comment

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

Thanks a lot for addressing all the comments! I added another round, I think you already fixed the rebase issues, but the test still needs to be adjusted. Afterwards, we should be good to go! 🚀

page_size = self.DEFAULT_PAGE_SIZE

if len(result_list) <= page_size:
if len(result_list) <= page_size and next_token is None:
Copy link
Member

Choose a reason for hiding this comment

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

Oh, interesting! So basically, what you are saying is that if I use next_token on an endpoint where there are less entries than the defined max, the next_token won't be considered.
What is the handling of AWS in this case? Have you verified that AWS respects the next token in this case?
Very good catch!

Copy link
Member

@alexrashed alexrashed left a comment

Choose a reason for hiding this comment

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

Thanks so much for the great contribution! Really thorough testing and you even caught an issue in the pagination implementation shared with a lot of other services! 🚀

page_size = self.DEFAULT_PAGE_SIZE

if len(result_list) <= page_size:
if len(result_list) <= page_size and next_token is None:
Copy link
Member

Choose a reason for hiding this comment

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

Thanks a lot for the detailed explanation! This is a really great catch and will actually affect quite a few other services! 🤩

@thrau thrau merged commit 68df8dc into localstack:master Mar 21, 2025
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver: patch Non-breaking changes which can be included in patch releases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants