Repository navigation
Conversation
On MySQL 9.1 and later (Oracle MySQL and Percona Server) the atomic cut-over never found its RENAME, and every attempt ended with Error 1205, as soon as another session ran a statement not representable in utf8mb3: an emoji in a string literal, or a binary parameter that is not valid UTF-8. Under write load the table lock of the cut-over queues exactly such statements. ExpectProcess read information_schema.processlist. Filling that table converts the statement text of every session into its utf8mb3 INFO column, and since 9.1 character set conversions into temporary tables are strict (sql_tmp_table.cc), so the long-standing MySQL Bug #87579 turned from a warning into Error 3854 that fails the whole read. The error arrives after the result set has started; sqlutils.QueryRowsMap does not check rows.Err(), so the read looked empty and the log only showed "cannot find process. Hints: metadata lock, rename". ExpectProcess now reads performance_schema.processlist (MySQL 8.0.22+) with the same conditions; it holds the statement text in the session's own character set and is not affected. Whether that table is usable is decided once when the applier initialises; information_schema.processlist remains the fallback when it is absent (MySQL 5.7, 8.0 before 8.0.22, MariaDB), empty because performance_schema is disabled, or not readable. On MySQL 9.1+ the fallback logs a warning naming the risk and the remedy. ExpectProcess also reads through QueryRow().Scan(), which reports the error of a failed read, and logs it, instead of passing it for a missing process. Fixes github#1780
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A Pull Request should be associated with an Issue.
Related issue: #1780
Description
This PR fixes the atomic cut-over on MySQL 9.1 and later (Oracle MySQL and Percona Server). Today it fails on every attempt with
Error 1205while any other session runs a statement that cannot be represented in utf8mb3, for example an emoji in a string literal or a binary parameter that is not valid UTF-8. Under write load, the cut-over's table lock queues exactly such statements.Cause.
ExpectProcessreadsinformation_schema.processlist. To fill that table, the server converts every session's statement text into a utf8mb3INFOcolumn. Since 9.1, character set conversions into temporary tables are strict (sql_tmp_table.cc), so the long-standing MySQL Bug #87579 now raisesError 3854for the whole read instead of a warning. The error arrives after the result set has started, andsqlutils.QueryRowsMapdoes not checkrows.Err(). As a result, the failed read looked like an empty result, and the log said onlycannot find process. Hints: metadata lock, rename. (In #1780 I wrote thatretryOperationswallows the error. It is actually dropped earlier, inQueryRowsMap.)Change (
go/logic/applier.go):ExpectProcessreadsperformance_schema.processlist(MySQL 8.0.22+) with the same three conditions. That table keeps the statement text in the session's own character set, so it is not affected.Whether that table is usable is decided once, in
Applier.InitDBConnections(): the applier must find its own session in it. Otherwiseinformation_schema.processliststays the fallback. This happens when the table is absent (MySQL 5.7, 8.0 before 8.0.22, MariaDB), when it is empty becauseperformance_schemais off (which, since Before the successful renaming, a session accessed the ghost table, w… #1536, requires--skip-metadata-lock-check), or when it is not readable. The log says which table is used. On MySQL 9.1+ the fallback logs a warning that names the risk and the remedy.ExpectProcessreads throughQueryRow().Scan(), which returns the error of a failed read. The error is logged at ERROR level, as the other retried cut-over checks are (ExpectMetadataLock, theRENAME), instead of being reported as a missing process.contributed code is using same conventions as original code
script/cibuildreturns with no formatting errors, build errors or unit test errors.How it was tested
New
TestApplierMySQL9suite (testcontainers,mysql:9.7.2). AnINSERTcarrying an emoji and then aRENAMEwait behind a table lock. As a control, the oldinformation_schema.processlistquery fails with3854.ExpectProcessfinds the waitingRENAME. Forced to the fallback table,ExpectProcessreturns the3854error instead ofcannot find process.ExpectProcessbody put back, the test fails withcannot find process. Hints: metadata lock, rename, which is the production symptom.failed reading information_schema.processlist: Error 3854 (HY000): Cannot convert string '\xF0\x9F\x98\x80')' from utf8mb4 to utf8mb3.sqlutils.QueryRowsMap, the fallback assertion fails because the error is again reported ascannot find process.ApplierTestSuite.TestExpectProcess(mysql:8.0.42): both tables find aRENAMEwaiting on a metadata lock, and a non-matching hint givescannot find process.TestInitDBConnectionsnow also asserts thatperformance_schema.processlistis chosen.TestHasStrictTmpTableCharsetConversion: the version gate for the warning, covering Oracle, Percona,-commercial, MariaDB and unparsable versions.script/cibuildwith Go 1.25.12 (macOS arm64, Docker): exit 0. All packagesok, 0 failures. The new 9.7.2 suite adds about 20 s togo/logic.golangci-lintv2.11.4 with.golangci.yml: 0 issues.script/docker-gh-ost-replica-testslocally, with host ports remapped:mysql:8.4.3takes theperformance_schema.processlistpath: 90 passed, 0 failed.mariadb:11.4.12takes the fallback: 90 passed, 0 failed.sysbenchwas skipped because it is not installed locally.End to end with the built binary on
mysql:9.7.2. Setup: a 60k-row table, 4 connections inserting continuously,--allow-on-master --cut-over=atomic --cut-over-lock-timeout-seconds=2 --default-retries=3.1205,cannot find process, no3854in the log[]bytethat is not valid UTF-8RENAMEfound on the first attempt[]bytethat is not valid UTF-8RENAMEfound on the first attemptperformance_schema=OFF(--skip-metadata-lock-check)information_schema.processlistperformance_schema=OFF(--skip-metadata-lock-check)ERROR failed reading information_schema.processlist: Error 3854 ...on every attemptOn MariaDB 11.4.12 the applier logs
will use information_schema.processlist on applier: performance_schema.processlist is not usable (Error 1146 ...)and proceeds as before.Notes
replica-tests.ymlmatrix. I'd be happy to add a 9.x image in a separate PR. Two caveats:script/docker/mysql-9.xconfig, and its replica terminology check matches only8.4.--test-on-replicaafter stopping replication. That means no foreign statements are in flight during the cut-over, so they would not catch this bug without a dedicated load on the replica.mysql:9.7.2container withinscript/cibuild. If you'd rather keep the unit run to one image, I can gate that suite differently.my.cnf.testis not used for the 9.x container, because MySQL 9.7 rejectsinnodb_log_file_size.