Distributed Consistency and Transactional Messaging Solutions
This article explains the challenges of achieving consistency in distributed systems and presents practical solutions such as two‑phase commit, asynchronous assurance, compensating transactions, message retry mechanisms, idempotent designs, and a custom Redis‑based delayed queue (DelayQ) with a transactional proxy (TMQProxy) to provide reliable transactional messaging.
In modern internet applications, distributed systems and micro‑service architectures are prevalent, making a single operation often involve multiple services and database instances. High‑consistency scenarios like internet finance face difficult consistency problems, leading many to abandon strong consistency in favor of eventual consistency based on the CAP theorem.
The article discusses several eventual‑consistency solutions, starting with the classic two‑phase commit (2PC) which requires a coordinator and participants that lock resources during the pre‑commit phase and either commit or roll back, but suffers from complexity and failure handling.
It then introduces an "asynchronous assurance" approach without transactional messages, relying on a message table and a queue (often an MQ) that is assumed not to lose messages. Producers record message status in the same transaction as business data, while consumers process messages and send confirm or retry signals, with periodic scans to resend pending messages.
When MQs support transactional messages, the workflow resembles 2PC: a prepared message is sent, the local transaction executes, and a confirm or cancel is issued. The article uses RocketMQ as an example, describing how a check interface allows the MQ to poll the producer for transaction status.
Compensating transactions are presented as an alternative to 2PC, where each operation registers a compensating action that can be executed in reverse order if a later step fails, reducing lock contention but requiring careful definition of compensation logic.
Message retry mechanisms are essential for ensuring consumer‑MQ consistency. Various retry strategies (fixed delay, exponential backoff, DB‑based delay queues, Redis zset‑based delay queues) are described, highlighting the need for idempotent processing and handling of dead‑letter queues.
Idempotency techniques include using unique keys for inserts, version control for updates, and log tables with unique constraints to prevent duplicate processing, emphasizing simplicity and database‑level conflict handling.
The article then details a custom Redis‑based delayed queue named DelayQ, which stores messages in a sorted set (zset) with execution timestamps, supports addMsg and deleteMsg interfaces, and drives message delivery via timed workers.
Building on DelayQ, a transactional proxy (TMQProxy) is proposed to provide transactional messaging semantics. Producers supply checkUrl and optional confirmUrl; the proxy coordinates message delivery, checks transaction readiness, and notifies the producer upon success, abstracting the underlying MQ implementation.
Additional operational concerns such as rate limiting, monitoring, degradation, deployment, and performance testing are briefly mentioned, underscoring the complexity of moving from design diagrams to production‑ready systems.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.