net_write_timeout=1 is accepted but TiDB keeps sending a result to a client that stopped reading
- 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
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