Skip to content

MDEV-38741 Abort the running statement when its client disconnects - #6035

Draft
vaintroub wants to merge 3 commits into
mainfrom
main-MDEV-38741
Draft

vaintroub wants to merge 3 commits into
mainfrom
main-MDEV-38741

Conversation

@vaintroub

@vaintroub vaintroub commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

MDEV-38741 Abort the running statement when its client disconnects

A watcher thread subscribes to the OS notification that a client has gone (for example TCP FIN or RST for a TCP socket, a gone client process for a named pipe) and aborts the running statement as KILL CONNECTION would. The problem, the behavior, the limits and the disconnect_watcher variable are described in MDEV-38741. This page is for the review: how it works, what it costs, what was tested.

Implementation

Flags and the check at command start

THD::disconnect_flags is an Atomic_relaxed<uint8> with two bits, reset when the THD is reused. The watcher sets DISCONNECT_MARKED. DISCONNECT_UNINTERRUPTIBLE is set by THD::set_disconnect_uninterruptible() when the last reply of a command is sent, from Protocol::end_statement(), and from COM_STATISTICS and COM_STMT_PREPARE (send_prep_stmt()), which write their replies themselves. THD::check_disconnect() is called from dispatch_command() after reset_kill_query(). It clears DISCONNECT_UNINTERRUPTIBLE, and kills the connection if it is marked and the command expects an answer (command_expects_answer(): not COM_SLEEP, COM_QUIT, COM_STMT_CLOSE).

Ordering without a lock

The connection thread stores the command and then clears DISCONNECT_UNINTERRUPTIBLE with a release fetch_and. The watcher sets DISCONNECT_MARKED with an acquire fetch_or and then loads the command. Both are read-modify-writes of one atomic, so they have a single order. If the watcher's comes second, it sees the command and aborts it. If it comes first, the thread's fetch_and returns the mark and the thread aborts the command itself. A statement cannot run for a gone client without one of them noticing. LOCK_thd_kill is taken only when the mark is set. m_command is still a plain enum that is read without a lock, as it is by the processlist.

Finding and killing the connection

The watcher finds the THD by thread id like KILL does, and may act on a stale event: the id of a gone connection is not found, and a reused id takes 2^32 new connections. It marks the connections of a round in one scan of the thread list under the read lock, and kills outside of it, with the THD found again by its id, because aborting writes the error log, calls the storage engines and can wait for the victim. The kill takes LOCK_thd_kill and LOCK_thd_data, checks the command again and calls THD::kill_on_disconnect(), which uses awake_no_mutex(KILL_CONNECTION).

A connection that is already being killed is left alone. KILL CONNECTION shuts the socket of its victim, which the watcher reports, and a second awake of the dying thread would wait for it in abort_current_cond_wait() for up to 2 seconds. That left a killed SLEEP() of main.processlist visible to main.processlist_notembedded.

The reason "Client disconnected" is logged once, by the abort itself, and only if nothing else is killing the connection yet. The socket is not shut down for it, the client is gone already and the thread must still read what the client sent before it left. For COM_BINLOG_DUMP the reason is "BINLOG DUMP client disconnected", at the level of log_warnings=4.

Galera (wsrep) connections are never marked.

Platforms

  • Linux: one epoll set, waited for by a thread of its own, because aborting a statement takes locks and can wait for the victim, which must not delay accepting connections. EPOLLRDHUP is requested, as a close and a crashed client can be only a FIN. Sockets are in the oneshot mode.
  • Windows sockets: an I/O completion port fed by a wait completion packet on the event of WSAEventSelect(FD_CLOSE). The API is from ntdll, sparsely documented, and loaded with GetProcAddress, with no SDK header. A thread of its own takes the completions. The completion key is the connection id by value. A connection that ends ends the selection and cancels its packet, which drops a queued completion.
  • Windows named pipes: a packet for the exit of the local client process, per connection. Connections of one process share a refcounted process handle, so a process is opened once. If OpenProcess is denied, the client is impersonated for it, which needs SeImpersonatePrivilege; a service has it enabled (verified). Otherwise a warning is logged once and the connections of such a client are not watched.
  • BSD, macOS: kqueue and the same thread. The BSDs use EV_EOF of a read filter with EV_CLEAR and the low water mark out of reach. macOS uses the socket filter EVFILT_SOCK (NOTE_CONNRESET | NOTE_READCLOSED), which reports a reset and a shutdown of the read side, never data, because XNU clamps the low water mark to the receive buffer. It is private to XNU; libdispatch uses it too.

Cost

  • A thread: the watcher thread, and an epoll or kqueue descriptor (and a pipe to stop the thread) on Linux and the BSDs.
  • Per connection: registering and removing the watch, an epoll_ctl pair on Linux, a kevent pair on the BSDs, and on Windows an event handle for FD_CLOSE (WSAEventSelect) and a wait completion packet, created at login and closed when the connection ends.
  • Per command: atomic read-modify-writes on THD::disconnect_flags, at command start and when the last reply is sent, and no mutex.
  • Thread list: the watcher looks connections up by id under the read lock of the thread list, which every connect and disconnect takes for writing.

Optimizations

Throttling delay and the FIN and COM_QUIT race

After one round of events the watcher thread waits 50 ms (a poll() of its stop pipe on Linux and the BSDs, a Sleep() on Windows, rounded up to a timer tick). A statement that is aborted by the watcher takes up to that long longer to be aborted, which does not matter for a statement that runs for seconds. Many clients close their socket right after COM_QUIT, so the FIN can already be there before the server has read it, which is a race. The wait lets that connection remove its socket from the set first, which drops the pending event, so the watcher does not look up the connection for every normal disconnect. It does not remove all of them: with a thread pool that falls behind, a connection can wait longer than the delay before its COM_QUIT is read, and then the event is handled. The events of a round are served by one scan (below).

A client that follows the graceful disconnect sequence (COM_QUIT, then wait for the server to close, then close) hardly races, as the server closes first, and the client has no TIME_WAIT socket that holds a port. MariaDB Connector/J does this, reading until the server closes for up to about 10 ms. MariaDB Connector/Node.js does not: it destroys the socket right after it sends COM_QUIT.

One scan for a round

A lookup by thread id scans all connections, so the events of a round are served by one scan, with the ids sorted and found by binary search, instead of one scan per event. The scan only notes the disconnects and takes no mutex of the THD.

Benchmarks

Connections per second with disconnect_watcher ON compared with OFF, switched at runtime. sysbench connects and disconnects only ("connect only"), or connects, runs DO 1 or SELECT 1 and disconnects ("DO 1", "SELECT 1"). Three repetitions per mode unless stated, the run to run noise is about 5%. Linux numbers are from a RelWithDebInfo build with the thread pool and a unix socket unless stated. The delay in the code is 50 ms; the delay used for each measurement is stated, the 5, 15 and 100 ms measurements were made with a temporary setting.

Delay, Linux thread pool, unix socket

Clients Workload 5 ms 15 ms 50 ms 100 ms
256 connect only −11.2% −8.2% (5 runs) −3.4% (5 runs) −0.9% (5 runs)
256 DO 1 −2.8% (5 runs) −3.2% (5 runs)
1024 connect only −15.6% −9.4% −5.3%
1024 DO 1 −5.4% −2.5% −6.1%

At 1024 clients the server limit on connections was raised: sysbench does not wait for the server to close, and the thread pool cannot keep up, so closed connections pile up, with the watcher OFF too. The pile-up is part of what these runs measure. The OFF rate varies by up to 10% between configurations, so single percentages are not exact; at 256 clients the ON and OFF ranges overlap for 50 and 100 ms, and at 1024 for 50 and 100 ms.

Events that reach the scan (256 clients, connect only, 3 x 10 s): 72% of the disconnects with 5 ms (1.9 s of scan time in 30 s), 32% with 15 ms (0.61 s), 11% with 50 ms (0.18 s). One scan serves 430 to 630 events. The watcher thread itself uses 0.26% of the CPU samples of the server (perf, 15 ms); the cost is the writers of the thread list waiting for the scan.

A delay of 100 ms (not used), Linux unix socket

3 repetitions per mode, thread_cache_size=1024 for one thread per connection:

Thread handling Workload 256 clients 1024 clients
thread pool connect only −2.4% −6.9%
thread pool DO 1 −3.7% −4.6%
thread pool SELECT 1 +1.2% −4.5%
one thread per connection connect only −5.0%
one thread per connection DO 1 −5.4%
one thread per connection SELECT 1 −4.3%

Compared with 50 ms the picture is the same within the noise of about 5% to 10%: the thread pool storm at 1024 clients improved (−9.4% to −6.9%), the one thread per connection rows at 1024 clients are a little worse (for example connect only −0.6% to −5.0%), and 100 ms is not clearly better than 50 ms across the board. Most ON and OFF ranges overlap; the separated ones are the thread pool at 1024 clients for connect only and SELECT 1, and for one thread per connection DO 1.

thread_pool_size, Linux unix socket, 50 ms

Thread pool, ON vs OFF, 3 repetitions per mode (the default size is the number of CPUs, 16 here; the default column is from the earlier 50 ms runs):

Workload Clients 16 (default) 32 64
connect only 256 −3.4% −4.2% −3.0%
connect only 1024 −9.4% −13.8% −5.9%
DO 1 256 −3.2% −2.9% −3.0%
DO 1 1024 −2.5% −3.6% −2.7%
SELECT 1 256 −7.9% −1.8% −2.9%
SELECT 1 1024 −6.4% −3.1% −2.5%

With 64 threads the cost is about 3% in every row but connect only at 1024 clients (−5.9%), and the OFF rate of that row is higher (connect only, 1024 clients: 82,842 with 16, 96,846 with 32 and 99,116 connections per second with 64 threads, taken from different runs). The 32 thread run is mixed (connect only at 1024 clients −13.8%, with separated ranges). A larger pool probably keeps up with the clients better, so fewer disconnects are pending when the watcher looks; I did not test that.

Thread handling, thread cache and SELECT 1, Linux unix socket, 50 ms

Thread handling Workload 64 clients 256 clients 1024 clients
thread pool SELECT 1 −1.7% −7.9% −6.4%
one thread per connection, default cache connect only −3.9% (15 ms) −4.3%
one thread per connection, default cache DO 1 −3.2%
one thread per connection, thread_cache_size=1024 connect only −0.6%
one thread per connection, thread_cache_size=1024 DO 1 −4.8%
one thread per connection, thread_cache_size=1024 SELECT 1 −2.4% −1.9%

The runs scatter by about 10% (with the default cache, 1024 clients, connect only: OFF from 93,691 to 119,637 connections per second), and most ON and OFF ranges overlap; the thread pool with SELECT 1 at 1024 clients (ON 74,803 to 82,347, OFF 84,567 to 85,488) does not. The thread cache removes the creation of a thread for each connection: the OFF rate of DO 1 rose from 88,000 to 104,700 per second with 1024 clients.

Other setups, 15 ms

  • Unix socket, one thread per connection, connect only: 32 clients −3.5%, 64 clients −4.8%, 256 clients −3.9%, all within the noise.
  • TCP over 127.0.0.1, thread pool, server closes with SO_LINGER {1, 0} so that the client has no TIME_WAIT: connect only 64 clients −2.8%, 256 clients −1.6%; DO 1 64 clients −2.6%, 256 clients −2.5%.
  • Windows, TCP over 127.0.0.1, thread pool, clients close with SO_LINGER 0 (no TIME_WAIT), connect only, 64 clients: −1.6% with 5 ms, −1.3% with 15 ms, −1.5% with 50 ms, within the noise. The rate is 22,000 to 24,000 connections per second with the watcher ON or OFF. With 256 clients, sysbench's opening burst of connects is refused by Windows about half of the time (error 10061, with the watcher OFF too), because the listen backlog is capped (a test listening with SOMAXCONN_HINT(back_log) had no refused connects in 10 runs; that is MDEV-42258, merged in 10.11 and not yet in this branch), so the script retries a refused connect: −0.9% (5 runs per mode, 21,898 OFF and 21,705 ON, 50 ms).
  • Windows named pipe, connect only: a named pipe has no FIN, the watcher reacts only to the exit of the client process, and sysbench does not exit during the run, so no event ever arrives and the delay does not apply. This measures only registering and removing a watch for each connection: 8, 32, 64 clients −2.6%, −1.0%, −2.2% (thread pool) and −1.0%, −0.5%, −2.1% (one thread per connection).

Earlier measurements

  • Delay, Linux: connect, SELECT 1 and disconnect with 32 clients over a unix socket was 37% slower with the watcher without the delay, and 3% slower with 5 ms (8 clients: 7%, and less than 1%).
  • Delay, Windows: with 64 to 512 clients over TCP the lookups were 22% of the disconnects without the delay, and below 1% with it. The throughput was within the noise.
  • One scan per round, Linux (one thread per connection): with a scan per event, 256 connecting and disconnecting clients caused about 1300 scans per second, 7.8 events per round, 9% of the time in the lookup, and the connection rate was 17% below the server without the watcher at 192 and 256 clients. With one scan per round it was about 5% below.
  • Per command: LOCK_thd_kill acquisitions per 1000 statements: 1001 with the feature, 1001 with the watcher OFF.

Not measured: kqueue, Windows sockets with more than 256 clients, TCP without the SO_LINGER change.

Commits

  1. mariabackup: the debug thread that emulates a DDL sent several statements in one query and closed its connection after the first. It now sends them as one BEGIN NOT ATOMIC block. Without it mariabackup.innodb_ddl_on_intermediate_table hangs with the feature.
  2. The feature, with its tests.
  3. mtr: suppressions and tests that accept the reason.

The memory leak of a local statement that fails after its metadata (found with the SQL service while testing this) is MDEV-42087, in its own PR.

Tests

New tests are in main (disconnect_*), sys_vars.disconnect_watcher_basic, rpl.rpl_dump_thread_disconnect and mysql_client_test. They cover a client that leaves during a lock wait, a CPU-bound statement, a table lock wait, a stored procedure handler, an idle client, a killed client process (TCP, unix socket, named pipe), a reused thread, pipelined commands, and a command that already sent its reply.

innodb.xa_recovery no longer needs the work-around for this issue (the shortened innodb_lock_wait_timeout) when the watcher is ON: the disconnect aborts the lock wait, and the test checks that the lock is released.

The tests need the watcher ON. The global mtr suppressions of Aborted connection no longer match a line that ends in (Client disconnected), because they hid the aborts of ordinary clients. Tests that drop a client on purpose accept the reason with mtr.add_suppression, among them main.mysqldump (mariadb-import can leave a LOAD DATA running when it fails, fixed in MDEV-42164) and federated.federatedx_pushdown_upd_del (the master gives up after net_read_timeout while the slave is still running). rpl_init.inc accepts it on the servers of the replication tests, because a stopped replica closes its connection on purpose.

There is no deterministic test that COM_STMT_CLOSE and COM_QUIT sent in one write followed by a half-close are not aborted. It needs a sync point in the connection thread.

Verification and gaps

  • Windows (Debug, RelWithDebInfo) and Linux (debug, epoll): main 1st, the disconnect tests, both mysql_client_test variants and related main tests, three runs each, all pass. Run on the final design on Windows Debug before the last rebases.
  • macOS (buildbot aarch64-macos, release build): the watcher tests pass, including main.disconnect_*, dirty_close, aborted_clients, mysql_client_test and sys_vars.disconnect_watcher_basic. disconnect_lock_wait_idle needs a debug build and is skipped. The failures of that builder (sys_vars.path_basic, main.sp-security, perfschema.socket_summary_by_instance_func) also happen on main.
  • Other systems: the embedded server, illumos and other Solaris-like systems, AIX and other Unixes have no watcher (the stubs in disconnect_watcher.h are used). A watcher for them is not implemented, as it cannot be tested yet.
  • FreeBSD family: to be run on GhostBSD.
  • FreeBSD (buildbot amd64-freebsd-14): does not compile at the moment, on this PR and on main, because the columnstore configure step requires gettid(). The EV_EOF branch has not run.
  • Not run: the Galera exemption.
  • Deferred tests: pipelined requests on a named pipe, two pipes of one process, the race with KILL.

Follow-ups

  • The epoll and kqueue halves of disconnect_watcher.cc are similar; a shared poll layer could remove the duplication.
  • m_command could be made Atomic_relaxed; it is the other half of the race that the flags order.
  • The SQL service tests of this feature need the fix of MDEV-42087 and are added when that is merged up.

🤖 Generated with Claude Code

@vaintroub
vaintroub force-pushed the main-MDEV-38741 branch 2 times, most recently from c31cfc0 to 70e9cd4 Compare October 30, 2026 02:49
@vaintroub vaintroub changed the title MDEV-38741 Client disconnect fails to abort a lock wait MDEV-38741 Abort the running statement when its client disconnects Nov 2, 2026
@vaintroub
vaintroub force-pushed the main-MDEV-38741 branch 13 times, most recently from 3092ff2 to e7164fb Compare November 9, 2026 22:19
@vaintroub
vaintroub force-pushed the main-MDEV-38741 branch 6 times, most recently from 7667edd to 4c41cfb Compare November 13, 2026 14:19
@vaintroub
vaintroub requested a balanced review from Copilot November 13, 2026 20:39

Copilot AI 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.

🟡 Changes recommended

Unsynchronized command-state access can cause a data race, and disconnect handling can prevent an explicit KILL CONNECTION from unblocking a stalled socket write.

5 open issues
What changed in this PR

MDEV-38741 adds a client-disconnect watcher to MariaDB Server so abandoned statements can be aborted promptly, releasing connection resources and locks.

Changes:

  • Adds platform-specific disconnect monitoring, a global control variable, and server lifecycle integration.
  • Coordinates disconnect handling with command execution, final replies, and connection cleanup.
  • Adds regression coverage and updates existing tests for disconnect logging and asynchronous cleanup.
File Description
tests/​mysql_client_test.c Tests half-closed, pipelined client commands.
storage/​spider/​mysql-test/​spider/​bugfix/​t/​mdev_35959.test Accepts intentional disconnect logging.
storage/​spider/​mysql-test/​spider/​bugfix/​r/​mdev_35959.result Updates expected suppression output.
sql/​sys_vars.cc Defines the disconnect watcher variable.
sql/​sql_show.cc Displays marked disconnects in process state.
sql/​sql_prepare.cc Marks prepared-statement replies uninterruptible.
sql/​sql_parse.cc Checks disconnects at command start and handles graceful quit.
sql/​sql_connect.cc Registers and removes connection watches.
sql/​sql_class.h Declares disconnect state and handling methods.
sql/​sql_class.cc Implements disconnect-triggered cancellation and state reset.
sql/​protocol.cc Marks final replies uninterruptible.
sql/​mysqld.h Exposes watcher configuration and instrumentation.
sql/​mysqld.cc Integrates watcher startup, shutdown, and instrumentation.
sql/​disconnect_watcher.h Defines watcher interfaces and platform stubs.
sql/​disconnect_watcher.cc Implements platform-specific monitoring and batched cancellation.
sql/​CMakeLists.txt Includes the watcher implementation in the build.
mysql-test/​suite/​unit/​suite.pm Adds disconnect suppression for unit tests.
mysql-test/​suite/​sysschema/​t/​pr_ps_setup_show_enabled.test Excludes watcher instrumentation from enabled-thread output.
mysql-test/​suite/​sysschema/​r/​pr_ps_setup_show_enabled.result Updates expected thread filtering.
mysql-test/​suite/​sys_vars/​t/​disconnect_watcher_basic.test Tests variable scope and accepted values.
mysql-test/​suite/​sys_vars/​r/​sysvars_server_notembedded.result Records watcher variable metadata.
mysql-test/​suite/​sys_vars/​r/​sysvars_server_notembedded,aix.rdiff Omits the unsupported variable on AIX.
mysql-test/​suite/​sys_vars/​r/​disconnect_watcher_basic.result Records variable-test expectations.
mysql-test/​suite/​rpl/​t/​rpl_ssl1.test Ends replication-user sessions before cleanup.
mysql-test/​suite/​rpl/​t/​rpl_semi_sync_shutdown_await_ack.test Accepts intentional disconnect logging.
mysql-test/​suite/​rpl/​t/​rpl_password_boundaries.test Ends replication-user sessions before cleanup.
mysql-test/​suite/​rpl/​t/​rpl_get_lock.test Accepts Windows connection-reset errors.
mysql-test/​suite/​rpl/​t/​rpl_fail_register.test Tolerates dump threads ending before explicit kill.
mysql-test/​suite/​rpl/​t/​rpl_dump_thread_disconnect.test Tests dump-thread cancellation and logging levels.
mysql-test/​suite/​rpl/​t/​max_binlog_total_size.test Tolerates asynchronous dump-thread termination.
mysql-test/​suite/​rpl/​r/​rpl_ssl1.result Removes warnings after session cleanup.
mysql-test/​suite/​rpl/​r/​rpl_semi_sync_shutdown_await_ack.result Updates suppression output.
mysql-test/​suite/​rpl/​r/​rpl_password_boundaries.result Removes warnings after session cleanup.
mysql-test/​suite/​rpl/​r/​rpl_fail_register.result Removes nondeterministic kill output.
mysql-test/​suite/​rpl/​r/​rpl_dump_thread_disconnect.result Records cancellation and logging expectations.
mysql-test/​suite/​rpl/​r/​max_binlog_total_size.result Removes nondeterministic kill output.
mysql-test/​suite/​perfschema/​t/​threads_mysql.test Filters the platform-dependent watcher thread.
mysql-test/​suite/​perfschema/​r/​threads_mysql.result Updates expected thread filtering.
mysql-test/​suite/​mariabackup/​nolock_ddl_during_backup_end.test Accepts intentional disconnect logging.
mysql-test/​suite/​mariabackup/​nolock_ddl_during_backup_end.result Updates suppression output.
mysql-test/​suite/​innodb/​t/​xa_recovery.test Checks disconnect cancellation with a timeout fallback.
mysql-test/​suite/​innodb/​t/​innodb_bug38231.test Accepts disconnects during lock waits.
mysql-test/​suite/​innodb/​r/​xa_recovery.result Updates conditional-query output.
mysql-test/​suite/​innodb/​r/​innodb_bug38231.result Updates suppression output.
mysql-test/​suite/​federated/​federatedx_pushdown_upd_del.test Accepts disconnects caused by remote timeouts.
mysql-test/​suite/​federated/​federatedx_pushdown_upd_del.result Updates suppression output.
mysql-test/​mariadb-test-run.pl Stops globally suppressing watcher disconnect warnings.
mysql-test/​main/​ssl_timeout.test Accepts intentional timeout-related disconnects.
mysql-test/​main/​ssl_timeout.result Updates suppression output.
mysql-test/​main/​sp_notembedded.test Accepts disconnects during stored-procedure waits.
mysql-test/​main/​sp_notembedded.result Updates suppression output.
mysql-test/​main/​mysqltest.test Accepts exit with a statement still running.
mysql-test/​main/​mysqltest.result Updates suppression output.
mysql-test/​main/​mysqldump.test Accepts disconnects after import failures.
mysql-test/​main/​mysqldump.result Updates suppression output.
mysql-test/​main/​mysqld--help.result Documents the watcher option and default.
mysql-test/​main/​mysqld--help,aix.rdiff Adjusts AIX-specific help expectations.
mysql-test/​main/​mysql_client_test.test Accepts intentional client-test disconnects.
mysql-test/​main/​mysql_client_test.result Updates suppression output.
mysql-test/​main/​mysql_client_test_nonblock.test Accepts nonblocking client-test disconnects.
mysql-test/​main/​mysql_client_test_nonblock.result Updates suppression output.
mysql-test/​main/​mysql_client_test_comp.test Accepts compressed client-test disconnects.
mysql-test/​main/​mysql_client_test_comp.result Updates suppression output.
mysql-test/​main/​loaddata.test Accepts disconnects during local data loading.
mysql-test/​main/​loaddata.result Updates suppression output.
mysql-test/​main/​disconnect_wait_kinds.test Tests CPU-bound work, table waits, and idle disconnects.
mysql-test/​main/​disconnect_wait_kinds.result Records cancellation and idle-client expectations.
mysql-test/​main/​disconnect_thread_reuse.test Tests disconnect-state isolation across cached-thread reuse.
mysql-test/​main/​disconnect_thread_reuse.result Records thread-reuse expectations.
mysql-test/​main/​disconnect_thread_reuse.opt Selects one thread per connection.
mysql-test/​main/​disconnect_sp.test Tests cancellation despite stored-procedure error handlers.
mysql-test/​main/​disconnect_sp.result Records procedure cancellation and logging expectations.
mysql-test/​main/​disconnect_lock_wait.test Tests lock-wait cancellation and runtime disabling.
mysql-test/​main/​disconnect_lock_wait.result Records lock-wait and logging expectations.
mysql-test/​main/​disconnect_lock_wait_idle.test Tests command-start and final-reply timing.
mysql-test/​main/​disconnect_lock_wait_idle.result Records synchronized disconnect expectations.
mysql-test/​main/​disconnect_lock_owner.test Tests rollback and release of owned locks.
mysql-test/​main/​disconnect_lock_owner.result Records lock-release expectations.
mysql-test/​main/​disconnect_client_killed.test Tests cancellation after client-process termination.
mysql-test/​main/​disconnect_client_killed.result Records process-termination expectations.
mysql-test/​main/​disconnect_client_killed.opt Enables named-pipe testing where supported.
mysql-test/​main/​disconnect_client_killed.combinations Defines TCP, socket, and pipe variants.
mysql-test/​main/​dirty_close.test Accepts intentional abrupt disconnects.
mysql-test/​main/​dirty_close.result Updates suppression output.
mysql-test/​include/​rpl_init.inc Adds per-server replication disconnect suppression.
mysql-test/​include/​mtr_warnings.sql Keeps watcher warnings outside global suppression.
mysql-test/​include/​have_disconnect_watcher.inc Gates tests on an enabled watcher.
mysql-test/​include/​end_user_sessions.inc Adds user-session termination and waiting helper.
mysql-test/​include/​disconnect_watcher_value.inc Detects watcher availability and enabled state.
include/​my_net.h Defines Windows write-side socket shutdown constant.
extra/​mariabackup/​xtrabackup.cc Keeps debug DDL connected through a compound statement.

Review effort: Balanced

Comment thread sql/disconnect_watcher.cc Resolved
Comment thread sql/sql_class.cc Resolved
Comment thread sql/disconnect_watcher.h Resolved
Comment thread sql/protocol.cc Resolved
Comment thread sql/sql_class.cc Resolved

Copilot AI 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.

Comment thread sql/sql_parse.cc Resolved
Comment thread sql/disconnect_watcher.cc Resolved
Comment thread sql/protocol.cc Resolved
Comment thread tests/mysql_client_test.c Resolved
The debug thread of mariabackup that emulates a DDL sends several
statements in one query, and closes its connection as soon as the first
of them returned. The server used to run the rest. A server that aborts
the statement of a client that is gone does not, and
mariabackup.innodb_ddl_on_intermediate_table hangs, as the debug sync
signals the main thread waits for do not come. Run the statements in
one BEGIN NOT ATOMIC block, so that the client waits for all of them.
The ALTER of that test fails with a lock wait timeout on purpose, which
the handler of the block ignores, as the thread did before.

Assisted-by: Claude:claude-sonnet-5-5
A watcher thread subscribes to the OS notification that a client has
gone (e.g. TCP FIN or RST for a TCP socket, a gone client process for a
named pipe) and aborts the running statement like KILL CONNECTION does.
Controlled by the global variable disconnect_watcher. Details in
PR#6035.

Assisted-by: Claude:claude-sonnet-5-5
The global suppressions of "Aborted connection", in mariadb-test-run.pl
and mtr_warnings.sql, hid the line that the client disconnect watcher
writes when it aborts a command: "Aborted connection N to db: 'x' user:
'u' host: 'h' (Client disconnected)". Nothing showed when the watcher
aborted a connection that it should have left alone. main.locale found
one, because its server log is in German and the English entry did not
match.

Make the two entries skip a line that ends in "(Client disconnected)".
Other aborted connections are suppressed as before. The tests that drop
a client on purpose accept the reason with mtr.add_suppression(),
details in PR#6035.

Assisted-by: Claude:claude-sonnet-5-5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants