-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: cmd/vet: detect defer rows.Close() #34544
Comments
/cc @robpike @kardianos but this does seem OK given #17780. |
I have some people on my team at work new to Go and we had two outages due to rows.Close() missing. What makes this worse is with the mysql driver this results in leaked goroutines and fails in random ways and it's not very clear from the failures or the goroutine leak that the code was missing Now some of this could be fixed by some changes in the way packets and errors are handled in the mysql driver but having a clear vet would make it easier to discover the problem. We ended up just manually scanning the changed code looking for the missing calls which is a lot more error prone. |
Seems OK to me based on the story here, but I am not a user of that package. |
Any helper function that wraps Query and returns Rows would probably be a false positive. I don't know how common those are. QueryContext will close Rows when ctx is canceled. I typically buffer the entire result set and close the Query by default in part to avoid this type of issue, so it can be a problem. In short, it would probably be okay, but I'm guessing for ~1% of users it will display a false positive, though that may be high. (defering rows.Close wouldn't work in a helper function that calls Query and returns Rows.) |
How does this work now if one returns a |
Vet checks aim to have zero false positives, or else false positives with an easy rewrite to silence the error. I don't see a rewrite here for wrappers other than something like At the least we need more data here. But we also need a story for why there won't be unfixable (or only fixable with ugly code) false positives. Maybe the check would have to look for rows being returned and understand that close need not be called. But then that would apply to rows being put in data structures too. Or sent on channels. It's unclear where it ends. Maybe one could define a clear "rows does not escape" signal, like only methods on rows are invoked and it is never copied anywhere (not into other functions, channels, data structures, etc). We'd then need to check and see if that catches useful bugs. (I guess the http.Response Body doesn't get wrapped or copied around as much?) |
@rsc I agree this needs more information. I'll see what I can dig up this week for data. I picked a random "used by" on pkg.go.dev and the first one I got was this:
Which I think would fail the vet check proposed. One possibility is to add this check ONLY if rows.Scan is also called in the same method. I think this should be safe 100% of the time. But I should probably do a corpus analysis before making that judgement. |
We can wait a little longer for data but it's really starting to sound like this is a no-go. The false positive with no clear workaround really sinks this from a vet perspective. We'd need not just data but also a design without that problem. |
The discussion above has identified that there are common patterns that will cause false positives in this check that will be quite difficult (or at least ugly and non-obvious) to work around. Based on that, this seems like a likely decline. |
No change in consensus, so declined. |
I'd like to suggest, based on the prior art of #17780, that we also add a check to make sure that
rows.Close()
is always deferred for any SQL call that returns rows). I struggle to think of a false positive especially due to the fact thatrows.Close()
can be safely deferred in every case.Consider this code:
Let's say I've done this in some test setup, I'd like to clean up the DB for another set of tests, I am now unable to truncate the database due to the fact that I have left this connection open. Production code won't run into this truncation issue (I hope) but it could potentially put pressure on the database due to the number of open connections instead.
A False positive could potentially look like this:
My for loop will have consumed all the rows and I therefore get a close call automatically. We still show a deferred close here for users just in case they decide to break out of their for loop early thus turning this back into a problem.
The text was updated successfully, but these errors were encountered: