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 Done signal and invokes mc.cancel(ctx.Err())
  • The cancel method stores the error in mc.canceled and 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.ErrBadConn or the original context error
  • The database/sql package recognizes ErrBadConn and 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 watchCancel in connection.go and monitors them through a background startWatcher goroutine
  • Pre-flight checks in QueryContext and ExecContext return ctx.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.canceled and return driver.ErrBadConn, prompting the pool to discard them
  • The cleanup mechanism in watchCancel ensures 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →