🦾
GoのTransactionでラッパー関数
毎回の処理で都度Transactionを書く
こんな感じ↓
func (r *Repository) Do() error
tx, err := r.db.Begin()
if err != nil {
return err
}
defer func() {
// panic
if p := recover(); p != nil {
if err := tx.Rollback(); err != nil {
log.Printf("failed to MySQL Rollback: %v", e)
}
// re-throw panic after Rollback
panic(p)
}
// error
if err != nil {
if err := tx.Rollback(); err != nil {
log.Printf("failed to MySQL Rollback: %v", e)
}
return
}
// 正常
if err := tx.Commit(); err != nil {
log.Printf("failed to MySQL Commit: %v", e)
}
}()
if _, err = tx.Exec(...); err != nil {
return err
}
return nil
}
コード量も多くなって、毎回同じ処理を書くのであまりイケてない。
ラッパー関数にする
func (t *Transaction) Transaction(ctx context.Context, f func(ctx context.Context, tx *sql.Tx) error) error {
// trasaction start
tx, err := t.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
// panic
if p := recover(); p != nil {
if err := tx.Rollback(); err != nil {
log.Printf("failed to MySQL Rollback: %v", e)
}
// re-throw panic after Rollback
panic(p)
}
// error
if err != nil {
if err := tx.Rollback(); e != nil {
log.Printf("failed to MySQL Rollback: %v", e)
}
return
}
// 正常
if err := tx.Commit();err != nil {
log.Printf("failed to MySQL Commit: %v", e)
}
}()
// 主処理実行
if err := f(ctx, tx); err != nil {
return err
}
return nil
}
実際に使う時はこんな感じ。
トランザクションは色々な関数で使われるので、「開発する人によってrecover漏れてた」など発生しないメリットがある。
ラッパーにするのは必須になってくる。
func (r *Repository) Do(ctx context.Context) error {
return r.transaction.Transaction(ctx, func(ctx context.Context, tx *sql.Tx) error {
if _, err := tx.Exec(...); err != nil {
return err
}
return nil
})
}
Discussion