Skip to content

RabbitMQ Coordinator Dynamic Queue Binding#36

Merged
brwyatt merged 9 commits into
mainfrom
35-rabbitmq-connection-muxing
Jul 2, 2026
Merged

RabbitMQ Coordinator Dynamic Queue Binding#36
brwyatt merged 9 commits into
mainfrom
35-rabbitmq-connection-muxing

Conversation

@brwyatt

@brwyatt brwyatt commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Description

This PR implements connection reuse/multiplexing for RabbitMQ-backed HTTP Polling/Streaming on the Coordinator, fixing the issue where every transient HTTP poll request established a completely new TCP/TLS connection to the RabbitMQ broker.

Instead of standard socket pooling, this PR resolves the connection churn by introducing Dynamic Queue Binding on the Coordinator's single, persistent robust server connection:

  1. Persistent Coordinator Queue: The RabbitMQServerTransport declares exactly one persistent coordinator-wide queue on startup (dffmpeg.coordinator.{uuid}).
  2. Dynamic Binding/Unbinding on Poll: When a worker or job client polls the coordinator over HTTP, the coordinator dynamically binds (bind) its single persistent queue to the workers/jobs exchanges using the client's routing key (e.g. worker.{worker_id}). On HTTP disconnect, the coordinator instantly unbinds (unbind) the routing key to prevent blackholing of messages.
  3. In-Memory Proxy Clients (RabbitMQProxyClientTransport): Refactored the multiplexed client transport into a 100% in-memory, lightweight transport local to the coordinator. It has zero TCP connection handles, zero AMQP channels, and mitigates handle leaks.
  4. Auto-Healing on Reconnects: Since all bindings are registered on the single robust coordinator queue, if the broker connection drops and reconnects, aio_pika's RobustQueue automatically restores all active client bindings natively!
  5. Boot-Race Resilience: If a worker polls before the Coordinator's background connection is up, it registers in-memory. Once up, _connection_task automatically restores and binds all registered clients.

Fixes #35 (issue)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Documentation update
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Breaking Changes

None. This is completely backward-compatible. A migration note has been added to docs/transports.md advising administrators to safely delete legacy durable worker-specific queues from the RabbitMQ broker once.

Checklist:

Code Quality

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have checked that my changes do not introduce new linting errors

Testing

  • I have added tests for critical code paths and functionality
  • New and existing unit tests pass locally with my changes

Architecture & Philosophy

  • Async/Await: Verified all I/O is non-blocking.
  • Database: Verified proper handling of database types across engines.
  • Statelessness: Ensured changes align with stateless/path-blind philosophy.
  • Plugins: Confirmed everything belongs in core transports.
  • Models: Used Pydantic V2 for models/config.

brwyatt added 9 commits June 21, 2026 18:15
First pass at shared connections and using channels to avoid unnecessary
connections and re-use existing connections. Should be more efficient
and kinder to the RabbitMQ infra.

But this doesn't quite work yet. Or, rather, it does... except channels
don't properly/correctly close... yet.
We weren't properly cleaning up and closing channels on the muxed
connections, which had some pretty negative impact. This makes sure they
get cleaned up properly, and just generally makes the RabbitMQ
disconnect more robust.
So when the RabbitMQ connection drops and reconnects... all channels
that had ever existed on the connection were getting re-connected when
the server reconnected, even if they were closed forever ago. This stops
that.

... this also has the side-effect of not reconnecting channels that
SHOULD have been reconnected, and doesn't reconnect properly
until/unless the worker/client reconnects, triggering a new channel to
be created.

This is next to be fixed here...
We can avoid creating and listening on queues if we just bind to the
exchanges and manage the bindings. This seems to actually be a LOT more
resilliant and functional than trying to leverage channels.
Also removes the config to enable/disable muxing (as the need for that
option doesn't really apply anymore)
This doesn't really do anything, we should be able to create the proxy
client and it'll work when the main connection boots back up (and create
the subscription then), AND this was really only happening when the dev
server reloaded and hit raceconditons that don't normally happen, which
results in the old (durable) queues getting created. So not only was it
not necessary (the proxy client version handles a late connection), but
it was actively annoying (re-creating durable queues).
... and the actual config option we added previously.
@brwyatt brwyatt linked an issue Jul 2, 2026 that may be closed by this pull request
5 tasks
@brwyatt brwyatt self-assigned this Jul 2, 2026
@brwyatt brwyatt added this to the 0.5.0 milestone Jul 2, 2026
@brwyatt brwyatt merged commit 3005db4 into main Jul 2, 2026
4 checks passed
@brwyatt brwyatt deleted the 35-rabbitmq-connection-muxing branch July 2, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RabbitMQ Connection Multiplexing for HTTP Polling Backend Proxy

1 participant