func fetchFoo(ctx context.Context) (_ Foo, mErr error) {
conn := pool.Acquire(ctx)
defer errs.Capture(&mErr, conn.Close, "close conn for fetch foo")
}
I have a couple of useful variants: defer errs.CaptureLog(logger, conn.Close, "close conn")
defer errs.CaptureT(t, conn.Close, "close conn") defer rows.Close()
/* do things */
err = rows.Close()
if err != nil {
}
1. If the explicit Close() is called first, the error from the deferred Close() is ignored.2. If the explicit Close() is never called because of a panic, the deferred Close() will kick in and cleanup the resources.
Like you said, the performance gains (if any) are probably not going to matter when a database call is involved.
I think if there’s a panic somewhere that you’re not explicitly expecting you can say goodbye to any notion of safety you might have had.
Most panics are small stupid things localized to a specific piece of code, like nil dereference or slice out of bounds, and not indicative of some global process state being messed up.
In my deployments, a server is just one in a pool of servers, so losing one is not a problem. The whole system is designed such that any server can die at any time and the overall service will continue to be available.
If you have a different architecture and design philosophy to me then I can see why you would want to recover in your handlers. It just doesn’t suit me at all.
Cases where it panics and leaves an incorrect state seem very rare; panics due to an incorrect state are slightly less rare, but still fairly rare.