# How go-sql-driver/mysql Manages Connection Timeout, readTimeout, and writeTimeout Settings

> Learn how go-sql-driver/mysql manages connection timeout, readTimeout, and writeTimeout. Understand context deadlines and per-operation socket settings for robust MySQL connections.

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

---

**The go-sql-driver/mysql driver applies `timeout` as a context deadline during TCP dial operations, while `readTimeout` and `writeTimeout` are enforced as per-operation socket deadlines via `SetReadDeadline` and `SetWriteDeadline` on the established connection.**

The go-sql-driver/mysql package provides granular control over network timeouts through three distinct DSN parameters. Understanding how the driver differentiates between connection establishment, read operations, and write operations helps prevent hanging goroutines and ensures resilient database interactions in production environments.

## Parsing Timeout Parameters from the DSN

The DSN parser in [`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go) extracts timeout values and populates the corresponding fields in the `Config` struct. Each parameter maps to a specific phase of the connection lifecycle.

### Connection Timeout (timeout)

The `timeout` parameter controls how long the driver waits for the TCP handshake to complete. In [`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go), the parser assigns this value to `cfg.Timeout`:

```go
case "timeout":
    cfg.Timeout, err = time.ParseDuration(value)

```

*Source:* [[`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go)](https://github.com/go-sql-driver/mysql/blob/master/dsn.go)

### Read Timeout (readTimeout)

The `readTimeout` parameter sets a deadline for every read operation from the server. The parser stores this in `cfg.ReadTimeout`:

```go
case "readTimeout":
    cfg.ReadTimeout, err = time.ParseDuration(value)

```

When serializing the DSN for logging, non-zero values are formatted back into the connection string:

```go
if cfg.ReadTimeout > 0 {
    writeDSNParam(&buf, &hasParam, "readTimeout", cfg.ReadTimeout.String())
}

```

*Source:* [[`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go)](https://github.com/go-sql-driver/mysql/blob/master/dsn.go)

### Write Timeout (writeTimeout)

Similarly, `writeTimeout` governs how long the driver waits to send data to the server. The parser handles it identically to the read timeout:

```go
case "writeTimeout":
    cfg.WriteTimeout, err = time.ParseDuration(value)

```

*Source:* [[`dsn.go`](https://github.com/go-sql-driver/mysql/blob/main/dsn.go)](https://github.com/go-sql-driver/mysql/blob/master/dsn.go)

## Applying Connection Timeout During Dial

When the driver establishes a new connection, [`connector.go`](https://github.com/go-sql-driver/mysql/blob/main/connector.go) applies the `Config.Timeout` value by wrapping the parent context with `context.WithTimeout`. This creates a hard deadline for the TCP dial operation:

```go
dctx := ctx
if mc.cfg.Timeout > 0 {
    var cancel context.CancelFunc
    dctx, cancel = context.WithTimeout(ctx, mc.cfg.Timeout)
    defer cancel()
}

```

The resulting `dctx` is passed to the `net.Dialer` or a custom `DialFunc`. If the TCP handshake exceeds the configured duration, the context cancels and the driver returns an error immediately rather than hanging indefinitely.

*Source:* [[`connector.go`](https://github.com/go-sql-driver/mysql/blob/main/connector.go)](https://github.com/go-sql-driver/mysql/blob/master/connector.go)

## Enforcing Read and Write Timeouts on Established Connections

Once the TCP connection is established, the driver no longer uses context cancellation for I/O. Instead, it applies per-operation deadlines directly to the socket using the standard library's `net.Conn` methods.

### Per-Operation Read Deadlines

The `mysqlConn` struct provides `readWithTimeout`, which sets a read deadline before every socket read:

```go
func (mc *mysqlConn) readWithTimeout(b []byte) (int, error) {
    to := mc.cfg.ReadTimeout
    if to > 0 {
        if err := mc.netConn.SetReadDeadline(time.Now().Add(to)); err != nil {
            return 0, err
        }
    }
    return mc.netConn.Read(b)
}

```

This helper is invoked throughout [`packets.go`](https://github.com/go-sql-driver/mysql/blob/main/packets.go) whenever the driver reads server responses. If the server fails to send data within the `readTimeout` window, the operation returns a timeout error.

*Source:* [[`connection.go`](https://github.com/go-sql-driver/mysql/blob/main/connection.go)](https://github.com/go-sql-driver/mysql/blob/master/connection.go)

### Per-Operation Write Deadlines

Similarly, `writeWithTimeout` applies a write deadline before transmitting data:

```go
func (mc *mysqlConn) writeWithTimeout(b []byte) (int, error) {
    to := mc.cfg.WriteTimeout
    if to > 0 {
        if err := mc.netConn.SetWriteDeadline(time.Now().Add(to)); err != nil {
            return 0, err
        }
    }
    return mc.netConn.Write(b)
}

```

This ensures that slow network conditions or backpressure from the server cannot cause the client to block indefinitely while sending queries or prepared statements.

*Source:* [[`connection.go`](https://github.com/go-sql-driver/mysql/blob/main/connection.go)](https://github.com/go-sql-driver/mysql/blob/master/connection.go)

## Complete Working Example

The following example demonstrates how to configure all three timeout parameters in a DSN and how they behave during actual operations:

```go
package main

import (
    "database/sql"
    "fmt"
    "time"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // DSN with explicit timeout settings
    dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?" +
        "timeout=5s&readTimeout=2s&writeTimeout=3s"

    db, err := sql.Open("mysql", dsn)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    // The dial timeout (5s) is applied when the first connection
    // is opened (e.g. on Ping or first Exec/Query).
    if err := db.Ping(); err != nil {
        fmt.Printf("dial timeout or connectivity problem: %v\n", err)
        return
    }

    // Subsequent queries use the read/write deadlines.
    // The driver will abort a read if the server does not send data
    // within 2 seconds, and abort a write if the client cannot
    // flush data within 3 seconds.
    rows, err := db.Query("SELECT SLEEP(4)") // exceeds readTimeout → error
    if err != nil {
        fmt.Printf("query failed (likely read timeout): %v\n", err)
        return
    }
    rows.Close()
}

```

In this scenario:
- `db.Ping()` fails if the TCP handshake exceeds 5 seconds.
- `SELECT SLEEP(4)` fails because the server waits 4 seconds before responding, exceeding the 2-second `readTimeout`.

## Summary

- **`timeout`** controls the TCP dial phase via `context.WithTimeout` in [`connector.go`](https://github.com/go-sql-driver/mysql/blob/main/connector.go), limiting how long the driver waits to establish a connection.
- **`readTimeout`** is enforced as a per-operation deadline via `SetReadDeadline` in [`connection.go`](https://github.com/go-sql-driver/mysql/blob/main/connection.go), applied before every socket read in [`packets.go`](https://github.com/go-sql-driver/mysql/blob/main/packets.go).
- **`writeTimeout`** is enforced as a per-operation deadline via `SetWriteDeadline` in [`connection.go`](https://github.com/go-sql-driver/mysql/blob/main/connection.go), applied before every socket write.
- Zero values disable the respective timeouts, allowing operations to block indefinitely until the OS intervenes.

## Frequently Asked Questions

### What happens if I only set the connection timeout but not read or write timeouts?

If you only configure `timeout` in the DSN, the driver will enforce a deadline during the initial TCP handshake, but once the connection is established, all subsequent read and write operations can block indefinitely. This means a stalled server or broken network path could cause your application goroutines to hang forever waiting for I/O.

### How does readTimeout differ from the connection timeout?

The `timeout` parameter applies only to the dial phase when the driver establishes the TCP connection via `context.WithTimeout` in [`connector.go`](https://github.com/go-sql-driver/mysql/blob/main/connector.go). In contrast, `readTimeout` applies to every individual read operation on the already-established socket, setting a deadline via `SetReadDeadline` before each call to `netConn.Read` in [`connection.go`](https://github.com/go-sql-driver/mysql/blob/main/connection.go).

### Can I change these timeouts dynamically after the connection is established?

No, the driver does not support dynamic reconfiguration of timeouts on existing connections. The `Config` values are read once during connection initialization in [`connector.go`](https://github.com/go-sql-driver/mysql/blob/main/connector.go) and stored in the `mysqlConn` struct. To use different timeout values, you must create a new `sql.DB` instance with a modified DSN string.

### Where are the deadlines actually enforced in the MySQL protocol implementation?

The read and write deadlines are enforced in [`connection.go`](https://github.com/go-sql-driver/mysql/blob/main/connection.go) through the `readWithTimeout` and `writeWithTimeout` helper methods. These methods are invoked throughout [`packets.go`](https://github.com/go-sql-driver/mysql/blob/main/packets.go) whenever the driver needs to read server responses or send client commands, ensuring that every low-level I/O operation respects the configured `readTimeout` and `writeTimeout` values.