The culprit is innodb_change_buffering, which is set to all both in this report and in MDEV-31713. inserts is sufficient for duplicate keys to show up.
Most likely the problem was introduced, or at least became visible, in 10.6.12. On some reason it is very poorly reproducible on 10.6.12, so I couldn't bisect, but I did see it happen on 10.6.12, while never on 10.6.11 (or 10.5). And there were indeed a bunch of seemingly related changes in 10.6.12, so it fits.
On 10.6.14 and the current tree it is reproducible somewhat better, although maybe it's coincidental.
Not reproducible on 11.0, not surprisingly since innodb_change_buffering was removed there.
The basic test flow is such:
- do a lot of inserts in concurrent threads with potentially duplicate keys;
- restart the server;
- do some more inserts (sometimes even a few non-concurrent ones are sufficient);
- observe duplicate keys.
The test below is for reproducing only, don't put it into the regression suite!.
I'm not even sure it is suitable for debugging, but hopefully InnoDB experts will be able to create a better one with this as a starting point.
I've made the test as tunable as possible, because different builds seem to demand different tweaks – some want more concurrency but are okay with less queries, some vice versa, and so on.
For some builds the parameters set in the tests would be excessive.
The test case doesn't have to be run with --repeat=N, instead max_trials value can be increased.
The failure happens both on optimized and debug builds, although of course the test runs much faster on optimized ones.
I doubt it is rr-able, but I only tried a few times (without success).
--source include/have_innodb.inc
|
|
let $restart_parameters= --innodb-change-buffering=insert --innodb-buffer-pool-size=1G --innodb-log-file-size=128M;
|
let $threads= 16;
|
let $queries= 640000; # Total amount, across all threads
|
let $max_trials= 20;
|
|
let $my_slap_command= $MYSQL_SLAP -uroot --create-schema=test --port=$MASTER_MYPORT --protocol=tcp --concurrency=$threads --query="INSERT INTO test.woocommerce_sessions (session_key, session_value, session_expiry) VALUES (ROUND(RAND()*5000), REPEAT(CHR(ROUND(@@timestamp)%26 + 97),2141), ROUND(@@timestamp)) ON DUPLICATE KEY UPDATE session_value = VALUES(session_value), session_expiry = VALUES(session_expiry)";
|
|
CREATE TABLE woocommerce_sessions (
|
session_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
session_key char(32) NOT NULL,
|
session_value longtext NOT NULL,
|
session_expiry bigint(20) unsigned NOT NULL,
|
PRIMARY KEY (session_id),
|
UNIQUE KEY session_key (session_key)
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
|
# This restart is to pick up innodb-change-buffering,
|
# it seems to work (better? or only?) than dynamic setting
|
echo #;
|
source include/restart_mysqld.inc;
|
|
let $trial= 0;
|
|
while ($trial < $max_trials)
|
{
|
inc $trial;
|
echo ##################;
|
echo # Trial $trial/$max_trials: running ~$queries queries via $threads parallel threads;
|
|
--disable_result_log
|
exec $my_slap_command --number-of-queries=$queries;
|
|
echo #;
|
source include/restart_mysqld.inc;
|
|
echo #;
|
echo # Sending some more queries;
|
|
--disable_result_log
|
exec $my_slap_command --number-of-queries=100;
|
|
if (`SELECT COUNT(*) FROM (SELECT session_key, COUNT(*) cnt FROM test.woocommerce_sessions GROUP BY session_key HAVING cnt > 1) sq`)
|
{
|
enable_query_log;
|
enable_result_log;
|
echo #######################;
|
echo # Found duplicate keys:;
|
echo #######################;
|
SELECT session_key, COUNT(*) cnt FROM woocommerce_sessions GROUP BY session_key HAVING cnt > 1;
|
CHECK TABLE woocommerce_sessions EXTENDED;
|
die # Found duplicate keys, see above;
|
}
|
echo #;
|
echo # No duplicate keys yet;
|
}
|
|
# Cleanup
|
connection default;
|
DROP TABLE woocommerce_sessions;
|
Example of the output:
10.6 a03ce7b9 non-debug
|
# Trial 4/20: running ~640000 queries via 16 parallel threads
|
#
|
# restart: --innodb-change-buffering=insert --innodb-buffer-pool-size=1G --innodb-log-file-size=128M
|
#
|
# Sending some more queries
|
#######################
|
# Found duplicate keys:
|
#######################
|
SELECT session_key, COUNT(*) cnt FROM woocommerce_sessions GROUP BY session_key HAVING cnt > 1;
|
session_key cnt
|
1374 2
|
1405 2
|
<...cut...>
|
603 2
|
67 2
|
CHECK TABLE woocommerce_sessions EXTENDED;
|
Table Op Msg_type Msg_text
|
test.woocommerce_sessions check status OK
|
bug.t 'innodb' [ fail ]
|
Test ended at 2023-07-19 04:02:48
|
|
CURRENT_TEST: bug.t
|
mysqltest: At line 57: # Found duplicate keys, see above
|
Retested 10.11, 11.0, 11.1 with InnoDB gtid_slave_pos running CHECK TABLES EXTENDED in the loop and didn't generate a clustered index warning (or any other errors) in 50k rows.
Leaving gtid_slave_pos duplicate as a problem for
MDEV-27849.