6.7 KiB
RabbitMQ Queue Bindings - Quick Reference
Last Updated: 2026-02-03
Related Fix: Queue Separation for InquiryHandler, CompletionListener, and ExecutionManager
Overview
This document provides a quick reference for understanding RabbitMQ queue bindings in Attune after the inquiry queue separation fix.
Exchange Topology
Attune uses three main exchanges:
attune.events(Topic) - Event messages from sensorsattune.executions(Topic) - Execution and enforcement messagesattune.notifications(Fanout) - System notifications
Queue → Routing Key Bindings
Events Exchange (attune.events)
| Queue | Routing Key | Message Type | Consumer |
|---|---|---|---|
attune.events.queue |
# (all) |
EventCreatedPayload |
EventProcessor (executor) |
Executions Exchange (attune.executions)
| Queue | Routing Key | Message Type | Consumer |
|---|---|---|---|
attune.enforcements.queue |
enforcement.# |
EnforcementCreatedPayload |
EnforcementProcessor (executor) |
attune.execution.requests.queue |
execution.requested |
ExecutionRequestedPayload |
ExecutionScheduler (executor) |
attune.execution.status.queue |
execution.status.changed |
ExecutionStatusChangedPayload |
ExecutionManager (executor) |
attune.execution.completed.queue |
execution.completed |
ExecutionCompletedPayload |
CompletionListener (executor) |
attune.inquiry.responses.queue |
inquiry.responded |
InquiryRespondedPayload |
InquiryHandler (executor) |
Notifications Exchange (attune.notifications)
| Queue | Routing Key | Message Type | Consumer |
|---|---|---|---|
attune.notifications.queue |
(none - fanout) | NotificationCreatedPayload |
Various |
Message Publishers
Worker Service
execution.status.changed→ Published during execution with status updatesexecution.completed→ Published when execution finishes (success/failure)
API Service
inquiry.responded→ Published when user responds to an inquiryinquiry.created→ Published when inquiry is created (executor publishes this)
Executor Service
enforcement.created→ Published when rule triggersexecution.requested→ Published to schedule executioninquiry.created→ Published when action requests human input
Sensor Service
event.created→ Published when trigger condition is met
Fixed Issues
✅ Fixed: inquiry_id Deserialization Error
Problem: InquiryHandler was consuming from execution.status.queue, receiving wrong message types (ExecutionCompletedPayload instead of InquiryRespondedPayload).
Solution: Created dedicated attune.inquiry.responses.queue bound to inquiry.responded.
Status: Fixed in 2026-02-03.
✅ Fixed: action_id Deserialization Error
Problem: ExecutionManager was consuming from execution.status.queue along with CompletionListener, receiving wrong message types (ExecutionCompletedPayload with action_id instead of ExecutionStatusChangedPayload).
Solution: Created dedicated attune.execution.completed.queue bound to execution.completed for CompletionListener. ExecutionManager now exclusively consumes from attune.execution.status.queue.
Status: Fixed in 2026-02-03.
Message Flow Examples
Execution Completion Flow
Worker → ExecutionCompletedPayload
→ routing key: "execution.completed"
→ exchange: "attune.executions"
→ queue: "attune.execution.completed.queue"
→ consumer: CompletionListener (executor)
→ action: Release queue slot, notify waiting executions
Inquiry Response Flow
API → InquiryRespondedPayload
→ routing key: "inquiry.responded"
→ exchange: "attune.executions"
→ queue: "attune.inquiry.responses.queue"
→ consumer: InquiryHandler (executor)
→ action: Resume paused execution with inquiry response
Execution Status Update Flow
Worker → ExecutionStatusChangedPayload
→ routing key: "execution.status.changed"
→ exchange: "attune.executions"
→ queue: "attune.execution.status.queue"
→ consumer: ExecutionManager (executor)
→ action: Update execution record, trigger child executions
Consumer Tags
Each consumer has a unique tag for identification:
executor.event- EventProcessorexecutor.enforcement- EnforcementProcessorexecutor.scheduler- ExecutionSchedulerexecutor.completion- CompletionListenerexecutor.manager- ExecutionManagerexecutor.inquiry- InquiryHandler
Debugging Tips
Check Queue Bindings
# Via RabbitMQ Management CLI
rabbitmqadmin list bindings
# Via Management UI
http://localhost:15672 → Exchanges → attune.executions → Bindings
# Check specific queue
rabbitmqadmin list bindings source=attune.executions
Monitor Message Routing
# View messages in queue
rabbitmqadmin get queue=attune.inquiry.responses.queue count=10
# Check consumer connections
rabbitmqadmin list consumers
Check for Deserialization Errors
# Grep executor logs
grep "Failed to deserialize message" logs/executor.log
# Look for specific field errors
grep "missing field.*inquiry_id" logs/executor.log
grep "missing field.*action_id" logs/executor.log
Configuration
Queue configuration is defined in:
- File:
attune/crates/common/src/mq/config.rs - Struct:
QueuesConfig
Bindings are established in:
- File:
attune/crates/common/src/mq/connection.rs - Function:
Connection::setup_infrastructure()
Best Practices
- One queue, one message type - Avoid multiple message schemas per queue
- One queue, one consumer - Avoid competing consumers on the same queue
- Use specific routing keys - Prefer
execution.completedoverexecution.# - Canonical message types - Use structs from
attune_common::mq::messages - Monitor dead letter queues - Check DLQ for routing/deserialization failures
Summary of Queue Architecture
Before Fix: Multiple consumers competing on same queue
attune.execution.status.queuehad 3 consumers with 3 different message types ❌
After Fix: One queue, one consumer, one message type
attune.execution.status.queue→ ExecutionManager only ✅attune.execution.completed.queue→ CompletionListener only ✅attune.inquiry.responses.queue→ InquiryHandler only ✅
Related Documentation
attune/work-summary/2026-02-03-inquiry-queue-separation.md- Complete fix detailsattune/docs/architecture/queue-architecture.md- Overall architectureattune/crates/common/src/mq/messages.rs- Message type definitions