domain platform Commons: 4/5

Competing Consumers Pattern

Also known as: Competing Workers Pattern

1. Overview

The Competing Consumers pattern is a fundamental design pattern in distributed systems and messaging architectures. It enables multiple concurrent consumers to process messages received on the same messaging channel, effectively distributing the workload and improving the overall throughput and scalability of the system. This pattern is particularly significant in modern cloud-native applications and microservices architectures, where asynchronous communication and parallel processing are essential for achieving high performance and resilience. The origins of this pattern can be traced back to the principles of enterprise integration and messaging systems, where the need to decouple message producers from consumers and to balance the load of message processing was first identified [2].

2. Core Principles

The Competing Consumers pattern is defined by a set of core principles that govern its implementation and behavior:

  • Shared Message Channel: A single message queue or topic is used as the communication channel between message producers and consumers.
  • Multiple Concurrent Consumers: Two or more consumer instances run concurrently, each capable of processing messages from the shared channel.
  • Independent Processing: Each consumer independently competes to receive and process messages. When a message is sent to the channel, only one of the consumers will successfully receive and process it.
  • Atomic Message Operations: The act of receiving and processing a message should be atomic. Once a consumer receives a message, it should be locked or removed from the queue to prevent other consumers from processing the same message.

3. Key Practices

In many distributed applications, there is a need to process a high volume of tasks or messages asynchronously. A single consumer processing messages from a queue can easily become a bottleneck, leading to increased latency and reduced throughput. Furthermore, if this single consumer fails, the entire message processing pipeline comes to a halt, impacting the availability and reliability of the system. The challenge is to design a solution that can handle a variable load of messages efficiently, scale on demand, and remain resilient to consumer failures.

4. Implementation

The Competing Consumers pattern addresses this problem by introducing multiple consumers that compete to process messages from a single queue. When a message arrives in the queue, any of the available consumers can pick it up and process it. This parallel processing of messages significantly increases the overall message throughput. The number of consumers can be scaled up or down based on the message load, providing elasticity and cost-effectiveness. If one consumer fails, the other consumers can continue processing messages, ensuring high availability and fault tolerance.

5. 7 Pillars Assessment

Pillar Score (1-5) Rationale
Purpose 3 Serves a clear technical purpose in system design
Governance 3 Can be governed through standard engineering practices
Culture 3 Supports engineering culture of reliability and quality
Incentives 3 Aligns incentives toward system stability
Knowledge 4 Well-documented pattern with extensive community knowledge
Technology 4 Directly applicable to modern technology stacks
Resilience 4 Contributes to overall system resilience
Overall 3.4 A valuable technical pattern that supports commons infrastructure

While the Competing Consumers pattern offers significant benefits, it also introduces certain trade-offs and considerations that must be carefully managed:

Pros Cons
Improved Throughput: Parallel processing of messages by multiple consumers increases the overall message processing rate. Non-Guaranteed Message Ordering: Since messages are processed concurrently by different consumers, the order in which they are processed is not guaranteed.
Enhanced Scalability: The number of consumers can be dynamically adjusted to match the message load, allowing the system to scale horizontally. Potential for Message Duplication: If a consumer fails after receiving a message but before completing its processing, the message might be re-processed by another consumer, leading to potential data inconsistencies.
Increased Availability: The system remains operational even if some consumers fail, as other consumers can continue processing messages. Requires Coordination Mechanism: A mechanism is needed to coordinate the consumers and ensure that each message is processed only once. This often involves features like message locking or visibility timeouts provided by the messaging system.

6. When to Use

The Competing Consumers pattern is widely used in various real-world systems and platforms:

  • Azure Service Bus: Multiple listeners can be configured to receive messages from a single Service Bus queue, implementing the Competing Consumers pattern to scale out message processing [1].
  • RabbitMQ: This popular message broker supports the Competing Consumers pattern by allowing multiple consumers to subscribe to the same queue. RabbitMQ then distributes the incoming messages among the consumers.
  • Amazon SQS: Amazon Simple Queue Service (SQS) is a fully managed message queuing service that enables you to decouple and scale microservices, distributed systems, and serverless applications. Multiple consumers can poll an SQS queue to process messages in parallel.

7. Anti-Patterns & Gotchas

In the cognitive era, where AI and machine learning workloads are becoming increasingly prevalent, the Competing Consumers pattern remains highly relevant. It can be used to distribute and parallelize computationally intensive tasks, such as model training, inference, and data preprocessing, across a cluster of worker nodes. For example, a large dataset can be partitioned into smaller chunks, and each chunk can be placed as a message in a queue. Multiple consumers, each running on a powerful machine, can then compete to process these chunks in parallel, significantly reducing the overall processing time.

8. References

The Competing Consumers pattern aligns well with the principles of the Commons:

  • Shared Resource: The message queue acts as a shared resource that is accessible to all consumers. This promotes the efficient utilization of the messaging infrastructure.
  • Democratic Governance: The consumers are independent and autonomous, competing for messages in a decentralized and democratic manner. There is no central authority dictating which consumer should process which message.
  • Equitable Access: All consumers have equal access to the message queue and an equal opportunity to process messages. This ensures fairness and prevents any single consumer from monopolizing the resources.
  • Sustainability: The pattern promotes sustainability by enabling the system to scale its resource consumption based on the actual demand. This avoids over-provisioning and reduces operational costs.
  • Community Benefit: The Competing Consumers pattern is a well-established and widely adopted pattern that benefits the entire software engineering community by providing a standard and effective solution for building scalable and resilient systems.

8. References

[1] Microsoft. (n.d.). Competing Consumers pattern. Azure Architecture Center. Retrieved from https://learn.microsoft.com/en-us/azure/architecture/patterns/competing-consumers [2] Hohpe, G., & Woolf, B. (2003). Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley. [3] Microservices.io. (n.d.). Competing Consumers pattern. Retrieved from https://microservices.io/patterns/messaging/competing-consumers.html