database/sql.DB.Close documentation is misleading

224 views
Skip to first unread message

Toby Allsopp

unread,
Nov 5, 2025, 6:03:47 PM (6 days ago) Nov 5
to golang-nuts
The doc comment for database/sql/(*DB).Close says:

// Close closes the database and prevents new queries from starting.
// Close then waits for all queries that have started processing on the server
// to finish.
//
// It is rare to Close a [DB], as the [DB] handle is meant to be
// long-lived and shared between many goroutines.
func (db *DB) Close() error {

I was led astray for quite some time by the "Close then waits" part. I thought that meant that the Close method would block (or "wait") until all connections that had been issued from the pool had been returned to the pool and then closed. It turns out that this is not the case, and I think what the comment means is that in-use connections will be closed later, after Close returns, once they have completed their work.

I'm considering creating an issue for this and am wondering if anyone has an opinion on whether this is actually a problem with the documentation and whether it is worth raising.

Thanks,
Toby.

Sean Liao

unread,
Nov 5, 2025, 7:46:18 PM (6 days ago) Nov 5
to golang-nuts
Why do you think it doesn't wait for all connections to be closed and freed?

- sean

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/5d75008b-fcab-4422-b493-a57531a4daddn%40googlegroups.com.

Toby Allsopp

unread,
Nov 5, 2025, 10:15:15 PM (6 days ago) Nov 5
to golang-nuts
Hi! Response inline below.

On Thursday, 6 November 2025 at 13:46:18 UTC+13 Sean Liao wrote:
Why do you think it doesn't wait for all connections to be closed and freed?

I have three pieces of evidence:

1. Experimentally, in our application, we observe that connections are still open when DB.Close returns.

We're using SQLite, and sometimes we want to replace the SQLite database file with a new version received from another machine. In order to do this, we require that there are no connections open to the database. On Windows, we experienced errors when replacing the sqlite database file after calling DB.Close. When we added a polling loop waiting for the number of in-use connections (using DB.DBStats) to go to zero after calling DB.Close, the errors stopped happening.

2. Inspecting the source for the Close method, there is code that deals with idle connections and connection requests, but nothing about in-use connections.


3. The unit tests for the database/sql package use a helper function to wait for connections to be closed after DB.Close is called.


Thanks,
Toby.

Sean Liao

unread,
Nov 5, 2025, 11:32:02 PM (6 days ago) Nov 5
to golang-nuts
I agree then that the docs don't match the current behavior, open an issue. 
Less clear to me is whether the fix is to change the docs or change the behavior to match the current docs. 
But the current design certainly doesn't hold references to conns that have been handed out.

- sean

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Robert Engels

unread,
Nov 6, 2025, 12:14:06 AM (6 days ago) Nov 6
to Sean Liao, golang-nuts

Isn’t the design supposed to be that the driver maintains the connections so when you close the db, it closes the driver connection, and that should wait until the current statements complete or terminate them. Terminating a live connection is going to be db specific.

I think it is driver issue. 

On Nov 5, 2025, at 10:31 PM, 'Sean Liao' via golang-nuts <golan...@googlegroups.com> wrote:



Robert Engels

unread,
Nov 6, 2025, 12:14:10 AM (6 days ago) Nov 6
to Sean Liao, golang-nuts


Toby Allsopp

unread,
Nov 6, 2025, 4:50:39 PM (5 days ago) Nov 6
to golang-nuts
On Thursday, 6 November 2025 at 18:14:06 UTC+13 Robert Engels wrote:

Isn’t the design supposed to be that the driver maintains the connections so when you close the db, it closes the driver connection, and that should wait until the current statements complete or terminate them. Terminating a live connection is going to be db specific.

I think it is driver issue. 

As far as I can tell, the only indication the driver gets that the DB has been closed is the following code:

if c, ok := db.connector.(io.Closer); ok {
    err1 := c.Close()
    if err1 != nil {
        err = err1
    }
}

Are you saying that the connector (which implements database/sql/driver.Connector) is expected to keep track of open connections and block in its Close method for them to be closed? Or to actively terminate them?

The doc for the Connector interface does mention DB.Close, but doesn't really say what it's expected to do:

// A Connector represents a driver in a fixed configuration
// and can create any number of equivalent Conns for use
// by multiple goroutines.
//
// A Connector can be passed to [database/sql.OpenDB], to allow drivers
// to implement their own [database/sql.DB] constructors, or returned by
// [DriverContext]'s OpenConnector method, to allow drivers
// access to context and to avoid repeated parsing of driver
// configuration.
//
// If a Connector implements [io.Closer], the [database/sql.DB.Close]
// method will call the Close method and return error (if any).

A very quick look at mysql and postgresql drivers doesn't turn up anything like that in their Connector implementations.

Cheers,
Toby.

Toby Allsopp

unread,
Nov 6, 2025, 4:54:24 PM (5 days ago) Nov 6
to golang-nuts
On Thursday, 6 November 2025 at 17:32:02 UTC+13 Sean Liao wrote:
I agree then that the docs don't match the current behavior, open an issue. 
Less clear to me is whether the fix is to change the docs or change the behavior to match the current docs. 
But the current design certainly doesn't hold references to conns that have been handed out.

Thanks. I think it's pretty unlikely that the behavior can be changed at this point. It could be useful to add an additional method like CloseAndWait(context.Context) error, but I'd be happy if the documentation for the existing behavior was made clearer.

Cheers,
Toby.

robert engels

unread,
Nov 6, 2025, 8:01:50 PM (5 days ago) Nov 6
to Toby Allsopp, golang-nuts
I reviewed the stdlib a bit more, and the driver connection is the “db connection” (upon which statements are run - these are handed out) and these are tracked:

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Toby Allsopp

unread,
Nov 6, 2025, 8:19:06 PM (5 days ago) Nov 6
to golang-nuts
On Friday, 7 November 2025 at 14:01:50 UTC+13 robert engels wrote:
I reviewed the stdlib a bit more, and the driver connection is the “db connection” (upon which statements are run - these are handed out) and these are tracked:


Yes, that's true, the DB does track the connections that have been handed out. So it could, if it wanted, do something with them during DB.Close. But it doesn't, unless I'm missing something.

Is it your contention that there is an issue with the particular driver I'm using in my experiments? I'm a little confused about how the dependency tracking that you linked to is connected to your earlier statement "I think it is driver issue".

Can you connect the dots for me please?

Thanks,
Toby.

robert engels

unread,
Nov 7, 2025, 4:18:12 PM (4 days ago) Nov 7
to Toby Allsopp, golang-nuts
I looked through the code some more, and I think these lines are where the connector should close/wait for outstanding connections to be finished.


I would assume this is the connector’s responsibility as long as it implements io.Closer.

I have’t run anything through we I could break point and see what’s going on.

I look through the other structures and it certainly seems like closing and waiting (for statements on each connection to be done) is a responsibility.

But the stblib test case would fail if there were open connections are the close, based on these:



--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Toby Allsopp

unread,
2:43 AM (12 hours ago) 2:43 AM
to golang-nuts
Hi, thanks for your response. My reply is inline below.

On Saturday, 8 November 2025 at 10:18:12 UTC+13 robert engels wrote:
I looked through the code some more, and I think these lines are where the connector should close/wait for outstanding connections to be finished.


I would assume this is the connector’s responsibility as long as it implements io.Closer.

I have’t run anything through we I could break point and see what’s going on.

I look through the other structures and it certainly seems like closing and waiting (for statements on each connection to be done) is a responsibility.

I agree that the connector _could_ keep track of connections and then block waiting for them to be closed in its Close method, and that this would cause the DB.Close method to block and wait for all the connections to be returned to the pool and thus be closed.

But, I can't find a single example of a connector that actually does that.

In the standard library, we have database/sql.dsnConnector, which is used for drivers that don't implement DriverContext. This doesn't have a Close method.

The very popular pgx library also doesn't have a Close method on its connector type: https://github.com/jackc/pgx/blob/f2d5d4447821540092446e81246c0d9c27acf5cf/stdlib/sql.go#L256


I think it's more likely that the documentation for DB.Close is misleading, and it is _not_ expected to block waiting for all connections to be closed, than every implementation of driver.Connector, including in the standard library, is missing an undocumented requirement to track and wait for connection in their optional Close method.

Or have I misunderstood your point?

Cheers,
Toby.

robert engels

unread,
3:46 AM (11 hours ago) 3:46 AM
to Toby Allsopp, golang-nuts
Yea, I going to have to back track, I didn’t read deep enough on the test to see that it was polling and waiting.

At the same time, I am not sure how this would would if the driver isn’t signaling to all open connections that you should close… and then coordinating it.

Like a lot in the Go stdlib it seems underspecified. I am going to agree with you that the documentation is incorrect. Maybe there is a path to a FullyCloseWithTimeout to be added to the stdlib which doesn’t essentially what the test case does.

BUT, I still don’t see where in the api what closing the db means to a connection that I already have that I am making calls on. How will I even know that the database is closing and I should close my connection and /or do an orderly shutdown?

That’s why it seems underspecified.

I think this is why there is no “DB” concept in Java. You just have a connection to a database (which may be pooled/cached behind the scenes). If the db goes down, you won’t know until you try and use the connection - and its up to you to coordinate outside lifecycle activities.





-- 
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages