# How Context Cancellation in Go Affects In-Flight MySQL Queries and Connection State

> Learn how context cancellation in Go aborts in-flight MySQL queries, closes network connections, and invalidates driver connections, preventing pool reuse.

- Repository: [Go SQL Drivers/mysql](https://github.com/go-sql-driver/mysql)
- Tags: internals
- Published: 2026-03-02

---

**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`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/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`](https://github.com/go-sql-driver/mysql/blob/main/connection.go), as well as `Stmt.QueryContext` and `Stmt.ExecContext` in [`statement.go`](https://github.com/go-sql-driver/mysql/blob/main/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:

```go
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:

```go
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:

```go
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`](https://github.com/go-sql-driver/mysql/blob/main/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.