In distributed system, transactions over network (distributed transaction) is a hard thing.
There are some patterns to achieve distributed transactions, the one of them is called TCC
.
This library enables to implement TCC easily.
Working sample is in _example directory, or you can try the code in Go Playground.
package main
import (
"log"
"github.com/dty1er/tcc"
)
var (
flightService = tcc.NewService(
"flight reservation",
db.tryReserveFlightSeat,
db.confirmFlightSeatReservation,
db.cancelFlightSeat,
)
hotelService = tcc.NewService(
"hotel reservation",
db.tryReserveHotelRoom,
db.confirmHotelRoomReservation,
db.cancelHotelRoom,
)
)
func main() {
doFirstReservation(db)
doSecondReservation(db)
}
func doFirstReservation(db *FakeDB) {
orchestrator := tcc.NewOrchestrator([]*tcc.Service{flightService, hotelService}, tcc.WithMaxRetries(1))
err := orchestrator.Orchestrate()
if err != nil {
log.Printf("error happened in 1st reservation: %s", err)
}
}
func doSecondReservation(db *FakeDB) {
// In second reservation, flight seat is not enough
// Please refer to working example
orchestrator := tcc.NewOrchestrator([]*tcc.Service{flightService, hotelService}, tcc.WithMaxRetries(1))
err := orchestrator.Orchestrate()
if err != nil {
log.Printf("error happened in 2nd reservation: %s", err)
}
// When error is returned, it can be casted into *tcc.Error
tccErr := err.(*tcc.Error)
log.Printf("tccErr.Error: %v", tccErr.Error())
log.Printf("tccErr.FailedPhase == ErrTryFailed: %v", tccErr.FailedPhase() == tcc.ErrTryFailed)
log.Printf("tccErr.ServiceName: %v", tccErr.ServiceName())
}
Described in GoDoc.
References for TCC pattern.
Eventual Data Consistency Solution in ServiceComb - part 3 Transactions for the REST of Us
MIT