エンジニアはこわくない

雑記とかエンジニアっぽい内容を好きなように書く場所

golangでトランザクション管理を少し楽にするラッパー関数


スポンサーリンク

f:id:tsujitaku50:20170224131252j:plain

僕はgolangmysqlを使っているのですが、いかんせんトランザクション管理に困っております。

go-sql-driver/mysqlにはbegincommitrollbackというメソッドがありますが

まさか一つ一つ手で書く・・・?

力技としてならそれもありかも・・・なんて思っていた時期がありました。

そしてトランザクション管理について調べていると、海外の方が良さげなラッパー関数を紹介してくれていました。

ラッパー関数

元記事はこちら↓

stackoverflow.com

記事で紹介されているラッパー関数は下記になります。

func Transact(db *sql.DB, txFunc func(*sql.Tx) error) (err error) {
    tx, err := db.Begin()
    if err != nil {
        return
    }
    defer func() {
        if p := recover(); p != nil {
            tx.Rollback()
            panic(p) // re-throw panic after Rollback
        } else if err != nil {
            tx.Rollback()
        } else {
            err = tx.Commit()
        }
    }()
    err = txFunc(tx)
    return err
}

これはすごい良いんじゃないでしょうか。

実際の処理は移譲していて、このラッパー関数の中ではbeginトランザクションを開始して

そのトランザクションを移譲する関数の引数に入れています。

そしてdeferの中ではエラーがあればRollback、無事に処理が終わればCommitしています。

実際にラッパー関数を使用するときは

func (s Service) DoSomething() error {
    return Transact(s.db, func (tx *sql.Tx) error {
        if _, err := tx.Exec(...); err != nil {
            return err
        }
        if _, err := tx.Exec(...); err != nil {
            return err
        }
    })
}

上記のように、ラッパー関数の中に処理を書くことで使用できます。

この方法ならトランザクションをはりたい処理はラッパー関数の中に全部書くことができますね。

僕の中で現状のベストプラクティスになりそうです。

まとめ

golangでのトランザクション管理ってみんなどうしているんでしょうか。。

もしもgolangでゲームなどを作る場合はトランザクション管理が必須だし。。

でもその割には検索しても有力な知見も出てこない感じがするし。。

おわり!!