I am writing some transaction begin/commit/rollback functions, and I want to pair the block to prevent forgot commit
I write like this:
func (foo *Foo) Bar() (err error) {
foo.Begin()
defer foo.End(&err)
//some business code
return
}
func (foo *Foo) End(eptr *error) {
// if recover
if r := recover(); r != nil {
debug.PrintStack()
*eptr = r.(error)
}
var err = *eptr
if err != nil {
foo.Rollback()
} else {
foo.Commit()
}
}
It works, but it uses the "pointer to interface", I cannot find the specifications about pointer to interface.
So, I am not sure this code is OK enough. Could you give some suggestions?
thanks to teyzer and Corey Ogburn, this is my fixed solution:
func (foo *Foo) Bar() (err error) {
foo.Begin()
defer func() { foo.End(err) }()
defer func() {
if r := recover(); r != nil {
debug.PrintStack()
err = makeError(r)
}
} ()
//some business code
return
}
func (foo *Foo) End(err error) {
if err != nil {
foo.Rollback()
} else {
foo.Commit()
}
}
The usual approach is to use recover()
to catch any panic
s. What you have is very similar to that approach. Check out the Go wiki about Panic and Recover
func (foo *Foo) Bar() (err error) {
foo.Begin()
defer foo.End()
//some business code
err = fmt.Errorf("oh no, an error!") // set error so that Bar returns it even though it's caught
panic(err)
return
}
func (foo *Foo) End() {
err := recover()
if err != nil {
foo.Rollback()
} else {
foo.Commit()
}
}
The parameters to a deferred function are immediately evaluated when defer
is reached. Thats why you have to use a pointer if you defer End
itself.
Instead you can use a closure. Your Bar()
function would look like this:
func (foo *Foo) Bar() (err error) {
foo.Begin()
defer func() { foo.End(err) }()
//some business code
return
}
Here the value of err
is evaluated once the deferred closure is executed.
If you're not stuck on using error returns, using panic and recover is the more idiomatic way in Go to handle such errors that require a rollback (like Corey Ogburn recommended).