Overview
Try-Confirm-Cancel, or TCC, is a compensation-based distributed transaction protocol that works at the business level. Instead of locking rows, each service reserves capacity in the Try step and finalizes or releases it later. It is popular in payment and e-commerce platforms.
How it works
Order flow: reserve stock + hold funds
Try: stock.hold(item, qty, ttl=10m) wallet.hold(user, amt)
all ok? -> Confirm: stock.deduct(hold) wallet.capture(hold)
any fail -> Cancel: stock.release(hold) wallet.release(hold)
The coordinator persists a transaction record with a state per participant before issuing each phase. Every operation carries a global transaction id, which participants use as an idempotency key and to record whether Try, Confirm or Cancel has already run. Holds carry a time-to-live so abandoned reservations expire.
Pitfalls
- Empty rollback: Cancel may arrive when Try never executed. Record the cancel so a late Try is rejected.
- Out-of-order delivery: a delayed Try arriving after Cancel must be ignored.
- Confirm and Cancel must never fail for business reasons; they should only be retried.
- Reservation expiry races with a slow Confirm; choose TTLs generously and reconcile.
- Reserved resources reduce availability for other customers while held.
Key takeaways
- TCC trades extra service code for better isolation than plain sagas.
- Idempotency, empty rollback handling and fencing late messages are mandatory.
- Use durable coordinator state and automatic retries until every participant confirms or cancels.
- Monitor reservation age and stuck transactions, and run a reconciliation job that cancels expired holds so capacity is not leaked.