Skip to content

server: detect client disconnects in explicit transactions - #70343

Open
YangKeao wants to merge 1 commit into
pingcap:masterfrom
YangKeao:fix-68682-explicit-txn-disconnect
Open

server: detect client disconnects in explicit transactions#70343
YangKeao wants to merge 1 commit into
pingcap:masterfrom
YangKeao:fix-68682-explicit-txn-disconnect

Conversation

@YangKeao

@YangKeao YangKeao commented Aug 4, 2026

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: close #68682

Problem Summary:

When a client disconnects while a statement in an explicit transaction is blocked in TiKV, TiDB cannot read the next command to notice the closed connection. The statement can keep retrying and retain transaction locks long after the client has gone away.

What changed and how does it work?

  • Register SQLKiller as client-go's kill-signal handler so TiKV retry checkpoints can cooperatively run the existing signal and connection-liveness checks.
  • Install the connection-liveness probe during ExecuteStmt for DML, and for DO and locking reads in explicit transactions. Ordinary reads continue to install it while writing their result sets, and DDL and transaction-control statements remain excluded from early cancellation.
  • Resolve both SQL EXECUTE and binary COM_STMT_EXECUTE to the underlying prepared AST before deciding whether the probe is needed.
  • Cover BEGIN and autocommit=0 transactions with both COM_QUERY and binary prepared-statement protocols.

Depends on tikv/client-go#2042. The client-go fork replacement is temporary until that PR is merged.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No need to test
    • I checked and no code files have been changed.

Unit and integration tests:

./tools/check/failpoint-go-test.sh pkg/server -run '^TestShouldInstallConnectionAliveDuringExecute$' -count=1
./tools/check/failpoint-go-test.sh pkg/server/tests/commontest -run '^TestClientDisconnectKillsExplicitTxn$' -count=1
make lint

Manual test with a real TiKV cluster:

tiup playground nightly \
  --db.binpath "$PWD/bin/tidb-server" \
  --db 1 --pd 1 --kv 1 \
  --without-monitor --port-offset 10000

The manual matrix closed the client TCP connection while statements were blocked and covered COM_QUERY, SQL PREPARE/EXECUTE, binary COM_STMT_PREPARE/COM_STMT_EXECUTE with parameters, BEGIN, autocommit=0, autocommit DML, INSERT/UPDATE/DELETE, SELECT ... FOR UPDATE, FOR UPDATE WAIT, FOR UPDATE NOWAIT, LOCK IN SHARE MODE, DO SLEEP(), ordinary SELECT SLEEP(), and DDL waiting for metadata locks. All 20 cases passed. Explicit transactions were rolled back and released their earlier locks in about one second. Disconnected DDL sessions were intentionally not killed and completed after their metadata-lock blocker was released.

make bazel_prepare was also attempted, but this machine does not have the bazel executable installed (make: bazel: No such file or directory).

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

Please refer to Release Notes Language Style Guide to write a quality release note.

Fix the issue that TiDB does not promptly interrupt a running statement or release transaction locks when the client disconnects during an explicit transaction

Summary by CodeRabbit

  • Bug Fixes

    • Improved connection-liveness handling for updates, locking queries, and procedural statements within transactions.
    • Fixed cleanup when clients disconnect during row-lock waits.
    • Ensured interrupted operations terminate correctly without affecting subsequent transaction work.
    • Improved consistency across regular and prepared statements, including disabled autocommit mode.
    • Improved connection cleanup and cancellation during transaction processing.
  • Tests

    • Added coverage for client disconnects, blocked transactional operations, and statement execution scenarios.

@ti-chi-bot ti-chi-bot Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change replaces the TiKV client dependency, connects SQL kill signals to compatible KV variables, and extends connection-liveness handling to explicit transactions and prepared statements. Tests cover statement classification, interruption, disconnects, lock release, and transaction cleanup.

Changes

Connection liveness

Layer / File(s) Summary
Dependency and kill-signal wiring
DEPS.bzl, go.mod, pkg/sessionctx/variable/session.go
The TiKV client uses the YangKeao fork. NewSessionVars registers SQLKiller with compatible KVVars.
Statement probe execution
pkg/server/conn.go, pkg/server/conn_stmt.go, pkg/server/conn_stmt_test.go, tests/realtikvtest/pessimistictest/pessimistic_test.go
Connection-liveness probing classifies prepared statements, DML, DO, and locking reads. Tests cover transaction modes and query interruption errors.
Explicit transaction disconnect validation
pkg/server/tests/commontest/tidb_test.go
Integration tests verify that disconnecting during a blocked update interrupts execution, removes the process, releases locks, and allows a later update to commit.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

Suggested reviewers: ekexium, d3hunter, bb7133

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant TiDBConnection
  participant SQLKiller
  participant TiKVTransaction
  Client->>TiDBConnection: Disconnect during blocked update
  TiDBConnection->>SQLKiller: Trigger connection kill signal
  SQLKiller->>TiKVTransaction: Interrupt blocked statement
  TiKVTransaction-->>TiDBConnection: Return query interruption
  TiDBConnection-->>Client: Terminate disconnected process
Loading

Poem

A rabbit wires the signal bright,
A blocked transaction ends its fight.
The SQL killer stops the wait,
Released locks restore the state.
A later update commits right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address issue #68682 by interrupting blocked explicit-transaction statements on disconnect and releasing transaction locks promptly.
Out of Scope Changes check ✅ Passed The dependency replacement, implementation changes, and tests directly support client-disconnect handling in explicit transactions.
Title check ✅ Passed The title clearly and concisely describes the main change: detecting client disconnects during explicit transactions.
Description check ✅ Passed The description includes the required issue reference, problem, implementation details, tests, side effects, documentation impact, and release note.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread go.mod Outdated
cloud.google.com/go/storage => cloud.google.com/go/storage v1.39.1
github.com/go-ldap/ldap/v3 => github.com/YangKeao/ldap/v3 v3.4.5-0.20230421065457-369a3bab1117
github.com/pingcap/tidb/pkg/parser => ./pkg/parser
github.com/tikv/client-go/v2 => github.com/YangKeao/client-go/v2 v2.0.1-0.20260804124005-da5feb949c36

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will replace it after tikv/client-go#2042 is merged.

@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch from 84be5cf to d34988c Compare August 4, 2026 17:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/server/tests/commontest/tidb_test.go`:
- Around line 3703-3712: Update the transaction verification in the test around
the existing ExecContext call to increment v by 2 rather than overwrite it with
2, so a prior incorrect commit of v = 1 yields 3. Keep the subsequent
QueryRowContext assertions aligned with the intended rollback result of row1 = 2
and row2 = 0.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 88f7655d-e2d7-4d73-ae60-13d0343b30f9

📥 Commits

Reviewing files that changed from the base of the PR and between c48a991 and 84be5cf.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • DEPS.bzl
  • go.mod
  • pkg/server/conn.go
  • pkg/server/conn_stmt.go
  • pkg/server/conn_stmt_test.go
  • pkg/server/tests/commontest/tidb_test.go
  • pkg/sessionctx/variable/session.go

Comment on lines +3703 to +3712
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err = dbt.GetDB().ExecContext(ctx, "update "+tableName+" set v = 2 where id = 1")
require.NoError(t, err)

var row1, row2 int
err = dbt.GetDB().QueryRowContext(context.Background(), "select sum(if(id = 1, v, 0)), sum(if(id = 2, v, 0)) from "+tableName).Scan(&row1, &row2)
require.NoError(t, err)
require.Equal(t, 2, row1)
require.Equal(t, 0, row2)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not mask a failed rollback.

Line 3705 overwrites v with 2. If the disconnected transaction incorrectly commits its earlier v = 1, the final value is still 2 and this test passes. Use an increment so an incorrect commit produces 3.

Proposed test fix
-	_, err = dbt.GetDB().ExecContext(ctx, "update "+tableName+" set v = 2 where id = 1")
+	_, err = dbt.GetDB().ExecContext(ctx, "update "+tableName+" set v = v + 2 where id = 1")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err = dbt.GetDB().ExecContext(ctx, "update "+tableName+" set v = 2 where id = 1")
require.NoError(t, err)
var row1, row2 int
err = dbt.GetDB().QueryRowContext(context.Background(), "select sum(if(id = 1, v, 0)), sum(if(id = 2, v, 0)) from "+tableName).Scan(&row1, &row2)
require.NoError(t, err)
require.Equal(t, 2, row1)
require.Equal(t, 0, row2)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err = dbt.GetDB().ExecContext(ctx, "update "+tableName+" set v = v + 2 where id = 1")
require.NoError(t, err)
var row1, row2 int
err = dbt.GetDB().QueryRowContext(context.Background(), "select sum(if(id = 1, v, 0)), sum(if(id = 2, v, 0)) from "+tableName).Scan(&row1, &row2)
require.NoError(t, err)
require.Equal(t, 2, row1)
require.Equal(t, 0, row2)
🧰 Tools
🪛 ast-grep (0.45.0)

[error] 3704-3704: SQL query is built by concatenating a string literal with a variable and passed to a database/sql call (Query, Exec, QueryRow, Prepare, or their Context variants). String concatenation lets attacker-controlled input alter the query structure, enabling SQL injection. Use parameterized queries with placeholders ('?' or '') and pass the values as separate arguments instead of concatenating them into the query string.
Context: dbt.GetDB().ExecContext(ctx, "update "+tableName+" set v = 2 where id = 1")
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').

(sql-injection-query-string-concat-go)


[error] 3708-3708: SQL query is built by concatenating a string literal with a variable and passed to a database/sql call (Query, Exec, QueryRow, Prepare, or their Context variants). String concatenation lets attacker-controlled input alter the query structure, enabling SQL injection. Use parameterized queries with placeholders ('?' or '') and pass the values as separate arguments instead of concatenating them into the query string.
Context: dbt.GetDB().QueryRowContext(context.Background(), "select sum(if(id = 1, v, 0)), sum(if(id = 2, v, 0)) from "+tableName)
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').

(sql-injection-query-string-concat-go)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/server/tests/commontest/tidb_test.go` around lines 3703 - 3712, Update
the transaction verification in the test around the existing ExecContext call to
increment v by 2 rather than overwrite it with 2, so a prior incorrect commit of
v = 1 yields 3. Keep the subsequent QueryRowContext assertions aligned with the
intended rollback result of row1 = 2 and row2 = 0.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/server/tests/commontest/tidb_test.go`:
- Around line 3658-3661: Update the blocker cleanup defer in the test to capture
and report errors from both blocker.ExecContext rollback and blocker.Close
instead of discarding them. Ensure both cleanup operations always run, and
include actionable context for each reported failure while preserving the
existing cleanup order.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 65666cdf-0f9f-427b-92e9-5f491497b60d

📥 Commits

Reviewing files that changed from the base of the PR and between 84be5cf and d34988c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (7)
  • DEPS.bzl
  • go.mod
  • pkg/server/conn.go
  • pkg/server/conn_stmt.go
  • pkg/server/conn_stmt_test.go
  • pkg/server/tests/commontest/tidb_test.go
  • pkg/sessionctx/variable/session.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • pkg/server/conn.go
  • pkg/server/conn_stmt.go
  • go.mod
  • pkg/server/conn_stmt_test.go
  • DEPS.bzl
  • pkg/sessionctx/variable/session.go

Comment on lines +3658 to +3661
defer func() {
_, _ = blocker.ExecContext(context.Background(), "rollback")
_ = blocker.Close()
}()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Report blocker cleanup failures.

Line 3659 discards the rollback error. Line 3660 discards the close error. If either operation fails, the test can leave the row-2 lock holder active and conceal the cleanup failure. Run both cleanup operations, then report both errors.

Proposed fix
 defer func() {
-	_, _ = blocker.ExecContext(context.Background(), "rollback")
-	_ = blocker.Close()
+	_, rollbackErr := blocker.ExecContext(context.Background(), "rollback")
+	closeErr := blocker.Close()
+	if rollbackErr != nil {
+		t.Errorf("rollback blocker: %v", rollbackErr)
+	}
+	if closeErr != nil {
+		t.Errorf("close blocker: %v", closeErr)
+	}
 }()

As per coding guidelines, “Keep error handling actionable and contextual; do not silently swallow errors.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
defer func() {
_, _ = blocker.ExecContext(context.Background(), "rollback")
_ = blocker.Close()
}()
defer func() {
_, rollbackErr := blocker.ExecContext(context.Background(), "rollback")
closeErr := blocker.Close()
if rollbackErr != nil {
t.Errorf("rollback blocker: %v", rollbackErr)
}
if closeErr != nil {
t.Errorf("close blocker: %v", closeErr)
}
}()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/server/tests/commontest/tidb_test.go` around lines 3658 - 3661, Update
the blocker cleanup defer in the test to capture and report errors from both
blocker.ExecContext rollback and blocker.Close instead of discarding them.
Ensure both cleanup operations always run, and include actionable context for
each reported failure while preserving the existing cleanup order.

Source: Coding guidelines

@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch from d34988c to 131001e Compare August 4, 2026 18:10
@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 56.00000% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.4044%. Comparing base (4b2a5bb) to head (6516fba).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #70343        +/-   ##
================================================
- Coverage   76.3277%   73.4044%   -2.9234%     
================================================
  Files          2041       2080        +39     
  Lines        558363     583797     +25434     
================================================
+ Hits         426186     428533      +2347     
- Misses       131277     154759     +23482     
+ Partials        900        505       -395     
Flag Coverage Δ
integration 40.7398% <56.0000%> (+1.0711%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
dumpling 59.8974% <ø> (ø)
parser ∅ <ø> (∅)
br 46.6167% <ø> (-16.0923%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/server/tests/commontest/tidb_test.go`:
- Around line 3699-3701: Update the disconnect verification around
processlistCountByInfo to query the captured processID and assert that no
processlist row remains for that session before executing fallback cleanup. Keep
the existing statement-info check only as a fallback, ensuring the assertion
specifically detects removal of the disconnected process ID rather than merely
completion of its statement.
- Around line 3631-3642: Update the deferred cleanup around conn.Close and
stmt.Close in the prepared-statement test path to inspect and propagate
unexpected close errors with resource-specific context. Only suppress the
explicitly expected error that occurs after the raw net.Conn is closed; do not
discard other failures, while preserving cleanup ordering and existing test
assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa6fbe22-6b47-4210-818b-32ade334e440

📥 Commits

Reviewing files that changed from the base of the PR and between d34988c and 131001e.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • DEPS.bzl
  • go.mod
  • pkg/server/conn.go
  • pkg/server/conn_stmt.go
  • pkg/server/conn_stmt_test.go
  • pkg/server/tests/commontest/tidb_test.go
  • pkg/sessionctx/variable/session.go
  • tests/realtikvtest/pessimistictest/pessimistic_test.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • pkg/server/conn.go
  • pkg/sessionctx/variable/session.go
  • go.mod
  • pkg/server/conn_stmt_test.go
  • pkg/server/conn_stmt.go
  • DEPS.bzl

Comment on lines +3631 to +3642
defer func() {
_ = conn.Close()
}()

blockedSQL := fmt.Sprintf("update %s set v = 2 where id = 2", tableName)
var stmt *sql.Stmt
if prepared {
stmt, err = conn.PrepareContext(context.Background(), blockedSQL)
require.NoError(t, err)
defer func() {
_ = stmt.Close()
}()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the relevant helper and nearby tests.
file="pkg/server/tests/commontest/tidb_test.go"
if [ -f "$file" ]; then
  wc -l "$file"
  sed -n '3590,3675p' "$file" | nl -ba -v3590
  echo '--- usages/helper definition snippets ---'
  rg -n "func .*Prepare|PrepareContext|Close.*conn|conn\.Close|stmt\.Close|blockedSQL|TestClientDisconnectKills" "$file"
else
  echo "file missing"
fi

echo '--- go files under commontest (if needed) ---'
find pkg/server/tests/commontest -maxdepth 1 -type f | sort

Repository: pingcap/tidb

Length of output: 236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="pkg/server/tests/commontest/tidb_test.go"

echo '--- relevant lines ---'
awk 'NR>=3590 && NR<=3675 { printf "%8d\t%s\n", NR, $0 }' "$file"

echo '--- related search results ---'
grep -nE "conn\.Close|stmt\.Close|PrepareContext|blockedSQL|TestClientDisconnectKills|defer func\(\)" "$file" | sed -n '1,120p'

echo '--- surrounding function outline around TestClientDisconnectKillsExplicitTxn ---'
grep -n "func TestClientDisconnectKillsExplicitTxn\|func .*connectAndHandleBlocked" "$file" | sed -n '1,40p'

Repository: pingcap/tidb

Length of output: 6160


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="pkg/server/tests/commontest/tidb_test.go"

echo '--- continuation after raw connection extraction ---'
awk 'NR>=3665 && NR<=3725 { printf "%8d\t%s\n", NR, $0 }' "$file"

echo '--- getRawNetConn definition ---'
grep -n -A40 "func getRawNetConn" "$file"

echo '--- other conn/stmt close error handling patterns in file ---'
grep -nE "(require\.NoError\(t, conn\.Close\(\)|terror\.Call\(conn\.Close\)|_ = conn\.Close\(\)|stmt\.Close\(\))" "$file" | sed -n '1,120p'

echo '--- deterministic close-error context probe for discarded errors in source ---'
python3 - <<'PY'
from pathlib import Path
text = Path("pkg/server/tests/commontest/tidb_test.go").read_text()
for needle in ["_ = conn.Close()", "_ = stmt.Close()", "conn.Close()", "stmt.Close()"]:
    print(needle, "=" , text.count(needle))
PY

Repository: pingcap/tidb

Length of output: 4636


🌐 Web query:

Go database/sql Conn Close after net.Conn Close expected error documentation

💡 Result:

In Go's database/sql package, calling Conn.Close after the underlying net.Conn has already been closed is generally handled gracefully by the driver, though it may return an error depending on the driver's implementation. 1. Expected Behavior: When you call Conn.Close, the database/sql package invokes the driver's Close method [1][2]. If the underlying network connection is already closed, the driver's attempt to perform a final network-level close or cleanup may encounter an error (e.g., "connection already closed" or similar) [2]. 2. Error Handling: The Go standard library documentation for database/sql/driver does not strictly forbid returning an error from Close if the connection is already invalid [1]. Many production drivers, such as those for PostgreSQL (like pgx), include network operations within their Close implementation; if the socket is unreachable or closed, that operation may return an error to the caller [2]. 3. Connection Pool Management: If you are using a pool via *sql.DB, the pool manages the lifecycle of these connections. The database/sql package is designed to be resilient to stale connections, often surfacing them as "bad connection" or "connection already closed" errors when they are pulled from the pool and used [3][4]. 4. Best Practices: You do not typically need to manually call Close on an individual connection unless you are using sql.Conn for a dedicated, continuous session [5][6]. For standard query patterns, the sql.DB handle manages connection recycling automatically [7][8]. If you do manage a dedicated sql.Conn, you should always ensure Conn.Close is called, but be prepared for it to return an error if the underlying state has changed or if the connection was already terminated by the server or a network event [5][6][2]. In summary, seeing an error from Conn.Close after a network failure is common and expected behavior, not necessarily an indication of a bug in your code, provided you are handling the potential error returned by that Close call.

Citations:


Handle client-resource cleanup errors instead of discarding them.

The deferred conn.Close and stmt.Close ignore failure cases. Return unexpected close errors with resource context; only swallow errors that are explicitly known to be expected after the raw net.Conn is closed.

Do this in pkg/server/tests/commontest/tidb_test.go:3631-3641.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/server/tests/commontest/tidb_test.go` around lines 3631 - 3642, Update
the deferred cleanup around conn.Close and stmt.Close in the prepared-statement
test path to inspect and propagate unexpected close errors with
resource-specific context. Only suppress the explicitly expected error that
occurs after the raw net.Conn is closed; do not discard other failures, while
preserving cleanup ordering and existing test assertions.

Source: Coding guidelines

Comment on lines +3699 to +3701
require.Eventually(t, func() bool {
return processlistCountByInfo(t, dbt, pattern) == 0
}, 5*time.Second, 50*time.Millisecond)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map relevant helper declarations before inspecting implementations.
ast-grep outline pkg/server/tests/commontest/tidb_test.go \
  --items all --type function --match 'processlist|cleanupProcess'

# Expect either a process-ID absence check or helper semantics that prove it.
rg -n -C 12 --type go \
  'func\s+(processlist(?:CountByInfo|IDByInfo)|cleanupProcessByID)\b|(?:processlist(?:CountByInfo|IDByInfo)|cleanupProcessByID)\(' \
  pkg/server

Repository: pingcap/tidb

Length of output: 10654


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the disconnected statement test helper and the helper bodies around cleanupProcessByID.
sed -n '3548,3775p' pkg/server/tests/commontest/tidb_test.go | cat -n

Repository: pingcap/tidb

Length of output: 8217


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the remaining cleanupProcessByID implementation and nearby tests for the expected cleanup path.
sed -n '3768,3802p' pkg/server/tests/commontest/tidb_test.go | cat -n
rg -n -C 8 'cleanupProcessByID\(t, dbt\.GetDB\(\), processID\)' pkg/server/tests/commontest/tidb_test.go

Repository: pingcap/tidb

Length of output: 2377


Assert that the captured process ID is absent after disconnect.

processlistCountByInfo only checks information_schema.processlist.info, so it can pass after statement completion while the disconnected session remains. Use the captured processID and assert no processlist row exists for that ID before the fallback cleanup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/server/tests/commontest/tidb_test.go` around lines 3699 - 3701, Update
the disconnect verification around processlistCountByInfo to query the captured
processID and assert that no processlist row remains for that session before
executing fallback cleanup. Keep the existing statement-info check only as a
fallback, ensuring the assertion specifically detects removal of the
disconnected process ID rather than merely completion of its statement.

@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch 2 times, most recently from 3409ddb to c55f7ed Compare August 4, 2026 18:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/server/tests/commontest/tidb_test.go`:
- Around line 3683-3685: Update the fallback cleanup in RunTests after processID
is captured so cleanupProcessByID’s kill-query work executes while db remains
open, rather than being deferred through t.Cleanup until after db closes. Use a
local cleanup helper and defer or invoke it before the test callback returns,
preserving the existing process lookup and cleanup behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 900b7757-0731-4fd8-afbb-1ec8196f4e69

📥 Commits

Reviewing files that changed from the base of the PR and between 3409ddb and c55f7ed.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (8)
  • DEPS.bzl
  • go.mod
  • pkg/server/conn.go
  • pkg/server/conn_stmt.go
  • pkg/server/conn_stmt_test.go
  • pkg/server/tests/commontest/tidb_test.go
  • pkg/sessionctx/variable/session.go
  • tests/realtikvtest/pessimistictest/pessimistic_test.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/realtikvtest/pessimistictest/pessimistic_test.go
  • go.mod
  • pkg/sessionctx/variable/session.go
  • pkg/server/conn_stmt.go
  • pkg/server/conn_stmt_test.go
  • pkg/server/conn.go

Comment thread pkg/server/tests/commontest/tidb_test.go
@YangKeao

YangKeao commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

/test check-dev

@YangKeao

YangKeao commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

/hold

@ti-chi-bot ti-chi-bot Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 5, 2026
@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch 2 times, most recently from 9e96bb9 to 4fe9a0b Compare August 6, 2026 04:24
Comment thread pkg/server/conn.go Outdated
}
switch stmt.(type) {
explicitTxn := !sessVars.IsAutocommit() || sessVars.InTxn()
switch stmt := stmt.(type) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other statements like DDL are still ignored here to keep existing behavior.

If the DDL is canceled because of the misconfiguration of keepalive, it might be annoying.

@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch from 4fe9a0b to 3bd2c05 Compare August 6, 2026 05:44
@ti-chi-bot

ti-chi-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign cfzjywxk, terry1purcell for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@YangKeao YangKeao changed the title server: detect client disconnects in explicit transactions PIN-93: server: detect client disconnects in explicit transactions Aug 10, 2026
@YangKeao YangKeao changed the title PIN-93: server: detect client disconnects in explicit transactions server: detect client disconnects in explicit transactions [PIN-93] Aug 10, 2026
@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch 2 times, most recently from ddfd3d6 to b3899a0 Compare August 10, 2026 08:54
@YangKeao YangKeao changed the title server: detect client disconnects in explicit transactions [PIN-93] server: detect client disconnects in explicit transactions Aug 10, 2026
@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch from b3899a0 to e4f46d3 Compare August 10, 2026 09:01
Signed-off-by: Yang Keao <yangkeao@chunibyo.icu>
@YangKeao
YangKeao force-pushed the fix-68682-explicit-txn-disconnect branch from e4f46d3 to 6516fba Compare August 10, 2026 09:52
@ti-chi-bot

ti-chi-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

@YangKeao: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
pull-build-next-gen 6516fba link true /test pull-build-next-gen
idc-jenkins-ci-tidb/build 6516fba link true /test build

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interrupt running statement in explicit transaction (autocommit=0) on client disconnect

1 participant