Skip to content

fix(sqs): scope queue cache, noack set and predefined clients to Connection - #2676

Merged
auvipy merged 5 commits into
celery:mainfrom
kratos0718:fix-sqs-scope-state-to-connection
Sep 27, 2026
Merged

auvipy merged 5 commits into
celery:mainfrom
kratos0718:fix-sqs-scope-state-to-connection

Conversation

@kratos0718

Copy link
Copy Markdown
Contributor

The SQS Channel declares _queue_cache, _noack_queues, _predefined_queue_clients and _predefined_queue_async_clients as class attributes, so every SQS channel in the process shares one dict/set per name regardless of which Connection it belongs to. This is the same problem #2543 fixed for Azure Service Bus.

What that breaks on main:

  1. Queue URL cache poisoning across Connections. Two Connections to different regions or AWS accounts that both use a queue called orders resolve to whichever URL was cached first, so one of them sends to and polls the other's queue.
  2. Predefined-queue clients reused across credentials. Those boto3 clients are built from the owning Connection's credentials (or the per-queue ones in its predefined_queues) but cached by queue name only, so a second Connection with different credentials gets the first one's client.
  3. no_ack leaks across Connections. _noack_queues decides whether a received message is deleted immediately. A no_ack=True consumer on one Connection makes consumers of the same queue name on every other Connection delete messages on receipt, so a worker crash there loses the message instead of it being redelivered.

The fix moves the four containers onto Transport (one per Connection) and exposes them on Channel through properties, following #2543. Channels of the same Connection still share them, so caching behaves the same within a Connection.

Tests: added test_SQS_per_connection_state, which fails on main (a channel on a second Connection sees the first one's cached URL) and passes with the change, plus a check that channels of one Connection still share. setup_method no longer needs to clear SQS.Channel._queue_cache between tests, and three tests that reassigned the client maps now reset them in place. Full t/unit run: 1536 passed with the change, 1534 without (the two new tests), same two pre-existing Azure-deps collection errors either way. pre-commit hooks pass on both files.

…ection

Channel._queue_cache, _noack_queues, _predefined_queue_clients and
_predefined_queue_async_clients were class attributes, so every SQS
Channel in the process shared them regardless of which Connection it
belonged to. Two Connections to different regions or accounts that use
the same queue name resolve to whichever queue URL was cached first, a
Connection with different credentials reuses another Connection's boto3
client for a predefined queue, and a no_ack consumer on one Connection
makes consumers of the same queue name on every other Connection delete
messages on receipt.

Move them onto the Transport, one per Connection, and expose them on
Channel through properties, the same approach celery#2543 took for Azure
Service Bus. Channels of one Connection still share them.
@auvipy
auvipy requested a review from ask September 26, 2026 07:23
@auvipy

auvipy commented Sep 26, 2026

Copy link
Copy Markdown
Member

@MehrazRumman

@auvipy
auvipy requested review from auvipy and removed request for ask September 26, 2026 07:24

@auvipy auvipy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we also add integration tests for this aswell?

@auvipy auvipy added this to the 5.7.0 milestone Sep 26, 2026
@codecov

codecov Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.97%. Comparing base (3081017) to head (1b7a51f).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2676      +/-   ##
==========================================
+ Coverage   83.95%   83.97%   +0.02%     
==========================================
  Files          80       80              
  Lines       11217    11231      +14     
  Branches     1345     1345              
==========================================
+ Hits         9417     9431      +14     
  Misses       1583     1583              
  Partials      217      217              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Two Connections whose predefined_queues map the same queue name to
different SQS queues, run against GoAws. Publishing through the first
Connection after the second has opened a channel must still land in the
first Connection's own queue. Fails on main, where the second channel
overwrites the shared cache entry.
@kratos0718

Copy link
Copy Markdown
Contributor Author

Added an integration test in t/integration/test_sqs.py that runs against GoAws. It creates two real queues and two Connections whose predefined_queues map orders to different ones, then publishes through the first Connection after the second has opened a channel. On main the message never reaches the first Connection's queue, and with this change it does. The full SQS integration suite passes locally (22 tests) along with the pre-commit hooks.

@auvipy

auvipy commented Sep 27, 2026

Copy link
Copy Markdown
Member

@MehrazRumman

@auvipy

auvipy commented Sep 27, 2026

Copy link
Copy Markdown
Member

tried to fix a newly emerged merge conflict due to a new merge in main branch

Comment thread t/integration/test_sqs.py Resolved
@auvipy
auvipy merged commit 665e181 into celery:main Sep 27, 2026
40 checks passed
auvipy added a commit that referenced this pull request Sep 30, 2026
Channel._queue_name_cache was a class attribute, so every Azure Storage
Queues channel in the process shared one dict of queue names, filled
from each storage account's list_queues(). A Connection to a second
storage account then trusts the first account's listing: _ensure_queue
finds the name in the cache, skips create_queue, and the send fails
with ResourceNotFoundError because the queue only exists in the first
account.

Move the cache onto the Transport, one per Connection, and expose it on
Channel through a property, the same approach as #2543 and #2676.

Co-authored-by: Asif Saif Uddin {"Auvi":"অভি"} <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants