Dubbo TCC Pattern Explained in Detail

In distributed systems, the accuracy of transactions is paramount. However, as system scales expand, traditional relational database transaction mechanisms often struggle to meet high-concurrency, cross-database, or even cross-system business requirements. The Dubbo TCC (Try-Confirm-Cancel) pattern serves as an effective solution to this pain point by introducing three steps: "Attempt," "Confirm," and "Cancel." This approach minimizes the complexity of distributed transactions without relying on complex coordinators or two-phase commit protocols, instead leveraging local transaction atomicity to ensure data consistency. Consequently, it achieves the optimal balance between performance and reliability.

The core principle of the TCC pattern lies in decomposing business logic into three distinct phases: Try (Attempt), Confirm, and Cancel.

  • Try Phase: Executes pre-checks, locks critical resources, reserves business states, but does not actually deduct or alter data. This phase returns a unique TransactionId for subsequent steps to reference.
  • Confirm Phase: Upon successful completion of the Try phase, executes the actual business logic and completes final data modifications.
  • Cancel Phase: If Confirm fails or is cancelled by the user, it executes compensatory logic to release resources reserved during the Try phase and restore the system to its original state.

This design allows each phase to run independently; even if an intermediate step fails, it does not affect the execution of other steps, significantly enhancing fault tolerance capabilities.
To build a TCC service based on Dubbo, one must first define interfaces adhering to specific standards. Dubbo provides the TccService interface along with its corresponding implementation class TccServiceImpl. Developers simply need to follow specific method signatures to automatically have them recognized and registered as TCC services.

A standard TCC interface should include three core methods:

  1. try Method: Used for reserving resources. This method accepts business parameters and an optional TransactionId, returning a unique TransactionId. The caller attempts to retrieve this specific TransactionId upon successful completion of the Try phase.
  2. confirm Method: Used for submitting transactions. It receives business parameters and the aforementioned TransactionId. Only when the Confirm phase succeeds will core business logic be truly executed.
  3. cancel Method: Used for rolling back transactions. It accepts business parameters and a TransactionId. This method must be invoked if Confirm fails or is actively cancelled by the user, serving to clean up reserved resources.

In implementation classes, these three methods must maintain identical parameter signatures. Typically, Try and Cancel methods do not need to return specific status codes; instead, they directly throw exceptions to indicate failure or return a specific result indicating success. The Dubbo TCC protocol automatically handles the execution order of these methods and exception capture logic.

Code Implementation Examples in Typical Business Scenarios

To better understand the application of the TCC pattern, let us illustrate it using a typical "Flash Sale Ticket Booking" scenario. Suppose we need to purchase limited 100 tickets; inventory deduction must ensure atomicity to prevent overselling. Traditional optimistic locking or pessimistic locking performs poorly under high concurrency, whereas the TCC pattern can handle this situation gracefully.

First, define the TicketService interface:

public interface TicketService {
    // Attempt to reserve inventory and create order status
    TransactionId tryReserve(TicketParam param);
    
    // Confirm deduction of stock and update order details
    void confirm(TransactionId transactionId, TicketParam param);
    
    // Cancel reservation and release inventory
    void cancel(TransactionId transactionId, TicketParam param);
}

Next, implement TicketServiceImpl. During the Try phase, query remaining inventory and lock it while creating a pending order; during the Confirm phase, truly deduct stock; during the Cancel phase, roll back the order status and release locks:

public class TicketServiceImpl implements TicketService {
    private StockRepository stockRepo;
    private OrderRepository orderRepo;

    @Override
    public TransactionId tryReserve(TicketParam param) {
        // 1. Check if sufficient inventory is available
        int available = stockRepo.getAvailable(param.ticketId);
        if (available < 1) {
            throw new BusinessException("Insufficient Inventory");
        }
        
        // 2. Lock the inventory (simulating database optimistic lock or distributed lock)
        stockRepo.lockTicket(param.ticketId, param.userId);
        
        // 3. Create a pending order without actually deducting real-time inventory
        orderRepo.createPendingOrder(param);
        
        return TransactionId.generate();
    }

    @Override
    public void confirm(TransactionId transactionId, TicketParam param) {
        try {
            // 4. Truly deduct stock from the system
            stockRepo.decreaseStock(param.ticketId);
            
            // 5. Update order status to PAID
            orderRepo.updateStatus(transactionId, "PAID");
        } catch (Exception e) {
            throw new BusinessException("Confirm Failed: " + e.getMessage());
        }
    }

    @Override
    public void cancel(TransactionId transactionId, TicketParam param) {
        try {
            // 6. Roll back order status
            orderRepo.updateStatus(transactionId, "CANCELLED");
            
            // 7. Release the inventory lock
            stockRepo.unlockTicket(param.ticketId);
        } catch (Exception e) {
            throw new BusinessException("Cancel Failed: " + e.getMessage());
        }
    }
}

Exception Handling Mechanism and Transaction Consistency Assurance

The lifecycle of the TCC pattern relies on the ordered execution of three steps; therefore, exception handling is critical. The Dubbo TCC protocol includes robust fault tolerance strategies built-in:

  • Try Phase Failure: If Try throws an exception, the entire TCC process terminates without entering the Confirm phase. The caller needs to catch this exception and decide subsequent actions (e.g., give up directly or retry).
  • Confirm Phase Failure: This is the most common scenario. Dubbo automatically converts exceptions from Confirm into Cancel requests, or sets automatic execution policies for Cancel in configuration settings. This means that as long as Confirm fails, the Cancel logic will be forcibly triggered to ensure resources are released.
  • Cancel Phase Failure: If Cancel also fails, the system records detailed logs but does not block the main process. Callers can monitor TCC transaction status and perform secondary processing via scheduled tasks or compensation mechanisms.

In practical applications, it is recommended to enable Dubbo's tcc filter and configure timeout durations and retry counts reasonably. For instance, set the maximum execution time for the Confirm phase to 100ms; exceeding this duration will be treated as a failure with automatic triggering of Cancel. Additionally, for critical business scenarios, manual confirmation mode can be enabled, allowing users to manually decide whether to enter the Confirm phase after Try succeeds, thereby avoiding unnecessary resource waste.

Through these mechanisms, the Dubbo TCC pattern effectively encapsulates the complexity of distributed transactions internally, enabling developers to focus on core business logic while ensuring data consistency and system stability.