-
Notifications
You must be signed in to change notification settings - Fork 18k
database/sql: Exec() never returns when IP is not reachable and maxOpenConns is reached #10886
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
Comments
The fix looks reasonable to me. |
Hmm.. Though on further though I think that might open up a race condition. Consider:
Now you have three connections open, even though maxOpenConns is two. I'm not sure whether a similar race condition already exists in the code or what the best way to fix it is. The most simple fix appears to be to change this code in if db.maxOpen > 0 && db.numOpen >= db.maxOpen to also take db.pendingOpens into account. But that might break some code path which already works today. |
I have encountered this problem. Can we add it to the Go1.6 milestone? I can help work on the fix if that adds weight to my request. |
I have a potential fix for this sitting in my local checkout. It's a little ugly, but I'd be happy to mail a CL if someone wants to review and propose edits |
Sure, the easiest way to make sure an issue is fixed in the
next release is to propose CL for it (after discussion of the
design, of course).
|
Of course - the changes proposed above cover the case of func (db *DB) connectionOpener() {
for range db.openerCh {
db.openNewConnection()
+
+ db.mu.Lock()
+ if db.numOpen == 0 {
+ // If there are no open connections,
+ // check to make sure we don't have any connection requests
+ // waiting for this one to be freed.
+ // This is done in a separate goroutine so that there is no chance
+ // for us to cause a deadlock.
+ go func() {
+ db.mu.Lock()
+ db.maybeOpenNewConnections()
+ db.mu.Unlock()
+ }()
+ }
+ db.mu.Unlock()
}
} As I said this is kind of ugly, but as far as I can see we need to have some sort of check here if we want to preserve the ability to open connections in parallel. |
I have written a test that reproduces this issue. The issue still exists in 1.5.1. The proposed fix in db.numOpen++ // optimistically
db.mu.Unlock()
ci, err := db.driver.Open(db.dsn)
if err != nil {
db.mu.Lock()
db.numOpen-- // correct for earlier optimism
db.maybeOpenNewConnections() // <--- fix
db.mu.Unlock()
return nil, err
} @johto Although the scenario you describe does allow a third connection to be opened, the change made in cce127a causes the extra connection to be closed before it is ever added to the pool or services any queries. Your suggestion to change if db.maxOpen > 0 && db.numOpen >= db.maxOpen to if db.maxOpen > 0 && (db.numOpen+db.pendingOpens) >= db.maxOpen seems correct to me. All database/sql tests still pass when I make that change. @potocnyj I don't understand how the proposed fix doesn't handle I will submit a CL with the new test and the above changes if there are no objections. |
@ChrisHines I appear to have misspoken - without the additional change I proposed For reference, the full code I am using to test the failure is here: https://gist.github.com/potocnyj/fe05406010ad73660889 |
@potocnyj OK, thanks for the correction. Yes, I understand now. Opening I have additions to the proposed fix that work in this scenario, and it actually simplifies the logic. I propose we consolidate the During testing I also uncovered a panic that occurred when With these changes all |
CL https://golang.org/cl/14547 mentions this issue. |
With go1.4.2, maxOpenConn set to 2, and an unreachable IP:
- two Exec() returns an error
- but the third Exec() never returns
Adding a call to db.maybeOpenNewConnections() after database/sql/sql.go:667 seems to fix this issue. But I'm not sure whether this is the correct fix or not. Please help. Thanks.
The text was updated successfully, but these errors were encountered: