pingcap / pingcap/tidb

net_write_timeout=1 is accepted but TiDB keeps sending a result to a client that stopped reading

Open
#70,993 0 comments 0 reactions 0 assignees View on GitHub
affects-9.0 component/server found-by-ai may-affects-25.10 may-affects-26.3 may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/major sig/sql-infra type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

Using pymysql:

```python
conn = pymysql.connect(host="127.0.0.1", port=4002, user="root", password="", ssl_disabled=True)
cur = conn.cursor()
cur.execute("SET SESSION net_write_timeout=1")
# Send a large query packet on the raw socket, then stop reading for 3.5 s.
query = b"SELECT REPEAT('x', 10000000)"
payload = b"\x03" + query
packet = len(payload).to_bytes(3, "little") + b"\x00" + payload
conn._sock.sendall(packet)
time.sleep(3.5)
# Drain available bytes with a bounded recv loop and check for EOF.
```

Probe output:

```text
MYSQL_DEFAULT_NET_WRITE_TIMEOUT_OPEN
MYSQL_DEFAULT_AFTER_STALL_BYTES_READ=10000078 CLOSED=False

MYSQL_NET_WRITE_TIMEOUT_ENFORCED
MYSQL_NWT1_AFTER_STALL_BYTES_READ=1685472 CLOSED=True

TIDB_NET_WRITE_TIMEOUT_REPORTED=1

TIDB_NET_WRITE_TIMEOUT_IGNORED
TIDB_NWT1_AFTER_STALL_BYTES_READ=10000078 CLOSED=False
```

MySQL with the default 30 stays open and delivers all 10 MB, showing that a stalled client can legitimately remain connected when the timeout is long. MySQL with `net_write_timeout=1` closes after ~1.7 MB. TiDB with `net_write_timeout=1` behaves like MySQL's long-timeout case: full result delivered, connection still open.

### 2. What did you expect to see? (Required)

When a client stops reading for more than `net_write_timeout` seconds, the server must abort the write and close the connection, as in MySQL. If TiDB does not support the timeout, `SET net_write_timeout` must fail explicitly instead of reporting a value the write path ignores.

### 3. What did you see instead (Required)

MySQL 8.3 net_write_timeout=1: after a 3.5 s stall, drain reads ~1.7 MB then EOF/closed. MySQL default 30: drain reads all 10,000,078 bytes and the connection remains open. TiDB a514 net_write_timeout=1: drain reads all 10,000,078 bytes and the connection remains open.

### 4. What is your TiDB version? (Required)

```text
Release Version: v8.4.0-this-is-a-placeholder
Edition: Community
Git Commit Hash: None
Git Branch: None
UTC Build Time: None
GoVersion: go1.25.12
Race Enabled: false
Check Table Before Drop: false
Store: unistore
Kernel Type: Classic
```

Built from source commit `a514a92784c9654502686e6ee6efc9e0aeda8afa` (pingcap/tidb master, 2026-09-07).

### 5. Root cause (optional)

- pkg/sessionctx/variable/noop.go:379 registers net_write_timeout as a noop variable
- pkg/sessionctx/vardef/sysvar.go:283-284 defines NetWriteTimeout
- no pkg/server write path sets a write deadline or reads net_write_timeout

Contributor guide

Open the contributing guide

Research direction

Start with pkg/sessionctx/variable/noop.go around the net_write_timeout registration and pkg/sessionctx/vardef/sysvar.go around its definition, then inspect the pkg/server write path. Run the provided PyMySQL reproduction with a stalled client and verify that net_write_timeout is enforced during result writes, or that SET net_write_timeout fails explicitly if unsupported.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, mysql
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.