database/sql および driver パッケージと Tx を使用すると、トランザクションがコミットまたはロールバックされたかどうかを検出するには、別のトランザクションを試行して結果としてエラーを受け取り、エラーを調べてそのタイプを判断する必要があるようです。エラー。コミットされているかどうかを Tx オブジェクトから判断できるようにしたいと考えています。確かに、Tx を使用する関数で別の変数を定義して設定することはできますが、かなりの数の変数があり、毎回 2 倍になります (変数と代入)。必要に応じてロールバックを実行する遅延関数もあり、bool 変数を渡す必要があります。
コミットまたはロールバック後に Tx 変数を nil に設定することは許容されますか?GC はメモリを回復しますか?
1 に答える
Begin()
、Commit()
、およびRollback()
が同じ関数内にあることを確認します。これにより、トランザクションの追跡が容易になり、defer
.
これは、エラーが返されるかどうかに応じてコミットまたはロールバックを行う例です。
func (s Service) DoSomething() (err error) {
tx, err := s.db.Begin()
if err != nil {
return
}
defer func() {
if err != nil {
tx.Rollback()
return
}
err = tx.Commit()
}()
if _, err = tx.Exec(...); err != nil {
return
}
if _, err = tx.Exec(...); err != nil {
return
}
// ...
return
}
これは少し繰り返しになる可能性があります。これを行う別の方法は、トランザクション ハンドラを使用してトランザクションをラップすることです。
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() // err is non-nil; don't change it
} else {
err = tx.Commit() // err is nil; if Commit returns error update err
}
}()
err = txFunc(tx)
return err
}
上記のトランザクション ハンドラを使用すると、次のことができます。
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
}
return nil
})
}
これにより、トランザクションが簡潔になり、by トランザクションが適切に処理されるようになります。
私のトランザクション ハンドラーではrecover()
、パニックをキャッチして、ロールバックがすぐに発生するようにしています。パニックが予想される場合にコードがキャッチできるように、パニックを再スローします。通常の状況では、パニックは発生しません。代わりにエラーが返されます。
パニックを処理しなければ、トランザクションは最終的にロールバックされます。コミットされていないトランザクションは、クライアントが切断されたとき、またはトランザクションがガベージ コレクションを取得したときに、データベースによってロールバックされます。ただし、トランザクションが自然に解決されるのを待つと、他の (未定義の) 問題が発生する可能性があります。そのため、できるだけ早く解決することをお勧めします。
すぐに明確にならない可能性があることの 1 つはdefer
、戻り変数がキャプチャされた場合にクロージャー内で戻り値を変更できることです。トランザクション ハンドラでerr
(戻り値) が nil の場合、トランザクションはコミットされます。への呼び出しCommit
もエラーを返す可能性があるため、戻り値を err に設定しerr = tx.Commit()
ます。is が非 nil であり、既存のエラーを上書きしたくないので、同じRollback
ことはしません。err