Mastering TCC in Go Microservices: A Complete Practical Guide

This guide explains the TCC (Try‑Confirm‑Cancel) distributed transaction model for Go microservices, covering its principles, comparison with 2PC and Saga, architecture, implementation steps, code examples, real‑world e‑commerce case, failure scenarios, table design, and production‑grade best practices.

Ray's Galactic Tech
Ray's Galactic Tech
Ray's Galactic Tech
Mastering TCC in Go Microservices: A Complete Practical Guide

What is TCC?

TCC (Try‑Confirm‑Cancel) is a business‑compensating distributed‑transaction pattern. It splits a single business operation into three phases to achieve strong consistency of core resources (e.g., inventory, funds, quota) under high concurrency.

Try : perform business checks and reserve (freeze) the required resources.

Confirm : permanently commit the business result.

Cancel : compensate and roll back the effects of the Try phase.

Use a flexible business‑level transaction instead of a rigid database lock.

The pattern guarantees business‑level strong consistency, not database‑level atomicity.

TCC vs. 2PC

Key differences are summarized below:

Dimension          | TCC                     | Traditional 2PC
-------------------|------------------------|-------------------
Core Idea          | Business‑compensating   | Resource‑locking
Consistency        | Business‑level strong   | Strong (DB)
Concurrency        | High (no global lock)   | Low (blocking lock)
Business Intrusion | High                    | Low
Failure Recovery   | Business‑controlled     | Coordinator‑dependent
Typical Scenarios  | E‑commerce, inventory, fund freeze | Core financial accounting
TCC’s “strong consistency” refers to the business result layer, not linear database consistency.

System Roles and Execution Workflow

TM (Transaction Manager) : initiates the global transaction.

TC (Transaction Coordinator) : coordinates the transaction across resources.

RM (Resource Manager) : manages a specific resource such as inventory or account balance.

Execution Flow

TM creates a global transaction ID (XID).

TM invokes the Try interface of each RM.

If all Try calls succeed, TC triggers Confirm on every RM.

If any Try fails, TC triggers Cancel on every RM.

Both Confirm and Cancel must be idempotent and support retries.

Implementing TCC in Go Microservices

Option 1 – Use a Mature Framework (Seata‑go)

Pros: built‑in TM/TC/RM, automatic XID and branch management.

Cons: steep learning curve and still invasive to business code.

Option 2 – Build a Lightweight TCC Yourself

Persistence : MySQL or PostgreSQL for transaction logs and freeze tables.

Global State Store : etcd or ZooKeeper to hold transaction status.

Idempotency Helper : Redis for pre‑check and retry control.

Communication : gRPC or HTTP for Try/Confirm/Cancel RPC calls.

Real‑World E‑Commerce Order Example

Scenario

A user places an order that must simultaneously create the order, freeze inventory, and freeze account balance – a classic cross‑service strong‑consistency transaction.

Try Stage (Resource Reservation)

Service          | Action
----------------|--------------------------
Order Service    | Create "pending" order
Inventory Service| Freeze inventory
Account Service  | Freeze balance

Inventory Service Try Interface (Go)

type InventoryTCCService interface {
    TryFreeze(ctx context.Context, req *TryReq) error
    Confirm(ctx context.Context, req *ConfirmReq) error
    Cancel(ctx context.Context, req *CancelReq) error
}
func (s *InventoryService) TryFreeze(ctx context.Context, req *TryReq) error {
    xid := ctx.Value("xid").(string)
    // Prevent hanging: reject Try if transaction already canceled
    if s.txRepo.IsCanceled(xid, req.BranchID) {
        return errors.New("transaction already canceled")
    }
    // Freeze inventory
    return s.repo.Freeze(req.ProductID, req.Count, xid)
}

Confirm Stage (Final Commit – Idempotent)

func (s *InventoryService) Confirm(ctx context.Context, req *ConfirmReq) error {
    // Idempotent check – only proceed if status is TRY
    if s.txRepo.Status(req.XID, req.BranchID) != "TRY" {
        return nil
    }
    return s.repo.DeductFrozen(req.ProductID, req.Count)
}

Executed exactly once.

No additional business decisions are made.

Cancel Stage (Compensating Rollback)

func (s *InventoryService) Cancel(ctx context.Context, req *CancelReq) error {
    // Empty rollback handling – if no record, treat as success
    if !s.txRepo.Exists(req.XID, req.BranchID) {
        return nil
    }
    return s.repo.ReleaseFrozen(req.ProductID, req.Count)
}

Failure Scenarios and Mitigation

Scenario 1 – Try Succeeds, Confirm Lost

Cause: network glitch or TC restart.

Consequence: resource remains frozen.

Mitigation: store each branch in a transaction table, run periodic scans for timed‑out branches, and automatically issue Confirm or Cancel.

Scenario 2 – Cancel Arrives Before Try (Empty Rollback)

Mitigation: Cancel first checks the transaction record; if none exists, it returns success.

Scenario 3 – Cancel Arrives First, Then Try (Hanging)

Mitigation: Try checks the global transaction status; if it is already canceled, the Try is rejected.

In essence, TCC is a recoverable state machine.

Core Table Design

CREATE TABLE tcc_branch_transaction (
  xid VARCHAR(64),
  branch_id VARCHAR(64),
  resource VARCHAR(64),
  status VARCHAR(16),   -- TRY / CONFIRM / CANCEL
  create_time DATETIME,
  update_time DATETIME,
  UNIQUE KEY (xid, branch_id)
);
-- Example freeze function for inventory
inventory_freeze(product_id, freeze_count, xid);

TCC vs. Saga – When Not to Use TCC

Dimension          | TCC | Saga
-------------------|-----|------
Requires Freeze    | Yes | No
Concurrency        | High| Medium
Implementation Complexity | High | Medium
Third‑Party APIs   | Not suitable | Suitable
Business that cannot express a “freeze” semantics should never use TCC.

Practical Pitfalls Checklist

Confirm and Cancel must be idempotent.

Try phase must not perform irreversible actions.

Design a unified timeout for the whole call chain.

Persist transaction logs reliably.

Avoid overusing TCC in non‑critical business.

Production‑Grade Deployment Recommendations

Boundaries : wrap only core consistency resources (inventory, funds, quota).

Observability : expose metrics such as active_tcc_transaction, tcc_confirm_retry_total, tcc_cancel_retry_total to monitor active, hanging, and timed‑out transactions.

Governance : implement periodic scans for timed‑out transactions, provide manual Confirm/Cancel UI, and define a TTL for transactions.

Pilot Strategy : start with a single resource (e.g., inventory), then add account handling, and finally the full order flow.

Conclusion

TCC replaces heavyweight database locks with engineered business logic that precisely controls the consistency of critical resources under high load. It is appropriate when the domain has a clear “freeze” semantics and experiences strong concurrency pressure. Otherwise, simpler Saga or eventual‑consistency approaches are preferable.

Start with one core resource, run a small pilot, and expand gradually.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

TCCdistributed transactions
Ray's Galactic Tech
Written by

Ray's Galactic Tech

Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.