Documentation
¶
Overview ¶
Package saga is an implementation of saga design pattern for error handling. It aims to solve distributed (business) transactions without two-phase-commit as this does not scale in distributed systems. The saga has responsibility to gain eventual consistency by calling compensation steps in reverse order.
This library uses orchestration approach by a centralized state machine. The implementation is inspired by this great talk https://www.youtube.com/watch?v=xDuwrtwYHu8. More documents could be found at https://www.reactivedesignpatterns.com/patterns/saga.html or https://msdn.microsoft.com/en-us/library/jj591569.aspx
If you have any suggestion or comment, please feel free to open an issue on this github
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Activity ¶
type Activity struct {
SuccessState State
FailureState State
RolledBackState State
Aggregator Aggregator
Compensation Aggregator
}
Activity includes details of an activity in a saga - Aggregator - SuccessState when it is executed successfully - FailureState when it is failed to be executed. - RolledBackState when it is successfulled executed but rolled back - Compensataion aggregator to rollback
type Aggregator ¶
type Aggregator interface {
Execute(ctx context.Context, tx Transaction) (Transaction, error)
}
Aggregator is an interface of a agregator which execute a single step in a list of steps in a saga transaction.
Execute process the current step of a transaction and returns another object of the transaction in next state. It returns an error when fails to process.
type Config ¶
Config includes details of a saga - InitState is the initial state - Activities list of activities in this saga in order - Logger is a logger to log state of a saga transaction
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor to execute a saga transaction
Example ¶
package main
import (
"context"
"fmt"
"github.com/bongnv/saga"
)
const (
stateInit saga.State = iota
stateBookedHotel
stateCanceledHotel
stateBookHotelFailed
stateBookedFlight
stateCanceledFlight
stateBookFlightFailed
)
type exampleTransaction struct {
state saga.State
}
func (t *exampleTransaction) State() saga.State {
return t.state
}
type aggBookHotel struct{}
func (a *aggBookHotel) Execute(ctx context.Context, tx saga.Transaction) (saga.Transaction, error) {
fmt.Println("Book hotel")
return &exampleTransaction{
state: stateBookedHotel,
}, nil
}
type aggBookFlight struct {
nextState saga.State
}
func (a *aggBookFlight) Execute(ctx context.Context, tx saga.Transaction) (saga.Transaction, error) {
fmt.Println("Book flight")
return &exampleTransaction{
state: a.nextState,
}, nil
}
func main() {
exampleConfig := saga.Config{
InitState: stateInit,
Activities: []saga.Activity{
{
Aggregator: &aggBookHotel{},
SuccessState: stateBookedHotel,
FailureState: stateBookHotelFailed,
RolledBackState: stateCanceledHotel,
},
{
Aggregator: &aggBookFlight{
nextState: stateBookedFlight,
},
SuccessState: stateBookedFlight,
FailureState: stateBookFlightFailed,
RolledBackState: stateCanceledFlight,
},
},
}
executor, err := saga.NewExecutor(exampleConfig)
if err != nil {
fmt.Printf("Failed to create saga executor, err: %v.\n", err)
return
}
finalTx, err := executor.Execute(context.Background(), &exampleTransaction{
state: stateInit,
})
if err != nil {
fmt.Printf("Failed to execute saga transaction, err: %v.\n", err)
return
}
if finalTx.State() == stateBookedFlight {
fmt.Println("OK")
}
}
Output: Book hotel Book flight OK
Example (Rollback) ¶
package main
import (
"context"
"fmt"
"github.com/bongnv/saga"
)
const (
stateInit saga.State = iota
stateBookedHotel
stateCanceledHotel
stateBookHotelFailed
stateBookedFlight
stateCanceledFlight
stateBookFlightFailed
)
type exampleTransaction struct {
state saga.State
}
func (t *exampleTransaction) State() saga.State {
return t.state
}
type aggBookHotel struct{}
func (a *aggBookHotel) Execute(ctx context.Context, tx saga.Transaction) (saga.Transaction, error) {
fmt.Println("Book hotel")
return &exampleTransaction{
state: stateBookedHotel,
}, nil
}
type aggBookFlight struct {
nextState saga.State
}
func (a *aggBookFlight) Execute(ctx context.Context, tx saga.Transaction) (saga.Transaction, error) {
fmt.Println("Book flight")
return &exampleTransaction{
state: a.nextState,
}, nil
}
type aggCancelHotel struct{}
func (a *aggCancelHotel) Execute(ctx context.Context, tx saga.Transaction) (saga.Transaction, error) {
fmt.Println("Cancel hotel")
return &exampleTransaction{
state: stateCanceledHotel,
}, nil
}
func main() {
exampleConfig := saga.Config{
InitState: stateInit,
Activities: []saga.Activity{
{
Aggregator: &aggBookHotel{},
SuccessState: stateBookedHotel,
FailureState: stateBookHotelFailed,
Compensation: &aggCancelHotel{},
RolledBackState: stateCanceledHotel,
},
{
Aggregator: &aggBookFlight{
nextState: stateBookFlightFailed,
},
SuccessState: stateBookedFlight,
FailureState: stateBookFlightFailed,
RolledBackState: stateCanceledFlight,
},
},
}
executor, err := saga.NewExecutor(exampleConfig)
if err != nil {
fmt.Printf("Failed to create saga executor, err: %v.\n", err)
return
}
finalTx, err := executor.Execute(context.Background(), &exampleTransaction{
state: stateInit,
})
if err != nil {
fmt.Printf("Failed to execute saga transaction, err: %v.\n", err)
return
}
if finalTx.State() == stateCanceledHotel {
fmt.Println("OK")
}
}
Output: Book hotel Book flight Cancel hotel OK
func NewExecutor ¶
NewExecutor creates a new Executor for a saga
func (*Executor) Execute ¶
func (e *Executor) Execute(ctx context.Context, tx Transaction) (Transaction, error)
Execute continue to process a saga transaction based on current state It returns a transaction object after it's executed successfully It returns error when failed to execute it
type Logger ¶
type Logger interface {
Log(ctx context.Context, tx Transaction) error
}
Logger is an interface that wraps Log function.
Log persists a transaction normally for auditing or recovering It returns an error while fails to store.
type MockAggregator ¶
MockAggregator is an autogenerated mock type for the Aggregator type
func (*MockAggregator) Execute ¶
func (_m *MockAggregator) Execute(ctx context.Context, tx Transaction) (Transaction, error)
Execute provides a mock function with given fields: ctx, tx
type MockTransaction ¶
MockTransaction is an autogenerated mock type for the Transaction type
func (*MockTransaction) State ¶
func (_m *MockTransaction) State() State
State provides a mock function with given fields:
type Transaction ¶
type Transaction interface {
State() State
}
Transaction is an interface of a saga transaction.
State returns current state of a transaction.