How Context Cancellation in Go Affects In-Flight MySQL Queries and Connection State
When using go-sql-driver/mysql, canceling a Context aborts in-flight queries by forcibly closing the underlying network connection and marks the connection as invalid to prevent reuse from the pool.
The go-sql-driver/mysql driver implements comprehensive context-aware support for Go's database/sql interface. When you pass a Context to methods like QueryContext or ExecContext, the driver actively monitors for cancellation signals throughout the operation lifecycle. This mechanism ensures that long-running MySQL commands respect deadlines while protecting the connection pool from contaminated sockets.
Context Registration and Background Monitoring
The driver establishes a monitoring infrastructure when connections are created to track context lifecycle events.
The watchCancel Mechanism
Before executing any MySQL command, the driver registers the context with the connection through the watchCancel function in connection.go (lines 534–564). This method stores the context in a per-connection channel called watcher and sets a watching flag on the connection state. If the context is already canceled when watchCancel is invoked, the function immediately calls mc.cleanup() to close the socket and returns nil, preventing the operation from proceeding on a dead context.
The startWatcher Goroutine
When connector.Connect creates a new connection, it invokes startWatcher (lines 659–682 in connection.go) to launch a background goroutine. This goroutine monitors the watcher channel for context cancellation signals. Upon detecting that the context is Done, the watcher calls mc.cancel(err), which records the cancellation error in the connection's canceled field and forces the underlying TCP connection to close.
Aborting In-Flight Queries
The driver handles cancellation differently depending on whether it occurs before or during network transmission.
Pre-Flight Cancellation Checks
Each high-level entry point—QueryContext, ExecContext, and PrepareContext in connection.go, as well as Stmt.QueryContext and Stmt.ExecContext in statement.go—invokes watchCancel before sending the MySQL command. If the context is already canceled, these methods return the context's error immediately without transmitting any bytes to the server.
Mid-Flight Cancellation Behavior
When a query is actively executing on the wire and the context becomes canceled:
- The background watcher goroutine receives the
Donesignal and invokesmc.cancel(ctx.Err()) - The
cancelmethod stores the error inmc.canceledand abruptly closes the network connection - Any pending read or write operation returns an error immediately
- Subsequent driver calls detect the stored error and translate it to
driver.ErrBadConnor the original context error - The
database/sqlpackage recognizesErrBadConnand discards the connection from the pool, ensuring no other goroutine reuses the half-closed socket
Post-Cancellation Connection State
After cancellation triggers, the connection enters a terminal state that prevents undefined behavior.
Connection Cleanup
In the cancellation path of watchCancel (lines 534–543), when the driver detects that mc.watching is already true, it invokes mc.cleanup(). This method closes the socket, clears internal buffers, and returns nil to indicate the connection has already been cleaned up. The connection remains in a "canceled" state where the canceled field holds the original context error.
Pool Safety Guarantees
Any later operation attempted on the same *sql.Conn will immediately fail with the stored context error. This prevents the application from reading partial results or writing to a corrupted stream. Because the driver returns driver.ErrBadConn for canceled connections, Go's connection pool automatically removes these connections from circulation, maintaining pool integrity without manual intervention.
Practical Context Cancellation Patterns
The following examples demonstrate how the driver behaves under different cancellation scenarios.
Cancel before the query is sent—the driver returns ctx.Err() immediately without network overhead:
ctx, cancel := context.WithCancel(context.Background())
cancel() // cancel immediately
_, err := db.QueryContext(ctx, "SELECT SLEEP(5)")
// err == context.Canceled
Cancel while the query is executing—the driver aborts the MySQL command by closing the connection:
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(100 * time.Millisecond)
cancel() // cancel mid-flight
}()
rows, err := db.QueryContext(ctx, "SELECT SLEEP(5)")
// err == context.Canceled, rows is nil
Reuse after cancellation—the underlying connection has been closed, so database/sql obtains a fresh connection from the pool:
ctx = context.Background()
_, err = db.ExecContext(ctx, "INSERT INTO t (v) VALUES (1)")
// err == nil (new healthy connection)
Summary
- The driver registers contexts via
watchCancelinconnection.goand monitors them through a backgroundstartWatchergoroutine - Pre-flight checks in
QueryContextandExecContextreturnctx.Err()immediately if the context is already canceled - Mid-flight cancellation forces the network socket closed via
mc.cancel(), causing pending I/O to fail - Canceled connections store the error in
mc.canceledand returndriver.ErrBadConn, prompting the pool to discard them - The cleanup mechanism in
watchCancelensures sockets are properly closed even when cancellation races with query execution
Frequently Asked Questions
Does canceling a context immediately kill the connection?
Yes. When the background watcher detects a cancellation, it calls mc.cancel(), which closes the underlying TCP connection immediately. This forces any in-flight read or write operation to return an error, effectively terminating the query execution on both the client and server sides.
Can I reuse a connection after canceling a query?
No. Once a connection experiences context cancellation, the driver marks it with the cancellation error in the canceled field and typically returns driver.ErrBadConn for subsequent operations. The database/sql pool automatically removes these connections from circulation, so the next operation receives a fresh, healthy connection from the pool.
What error does the driver return when canceling a mid-flight query?
The driver returns the original context error (such as context.Canceled or context.DeadlineExceeded). During cleanup, the connection may also return driver.ErrBadConn to signal to the database/sql package that the connection is dead and should not be reused.
How does the watcher goroutine know which context to monitor?
Each connection has a watcher channel field. When you call a context-aware method like QueryContext, the watchCancel function sends the context into this channel. The startWatcher goroutine launched during connection initialization reads from this channel and monitors the ctx.Done() channel until the context completes or the connection closes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →