GA

2022/08/22

replica_parallel_workers > 1 && replica_preserve_commit_order=ON && slave_parallel_type = LOGICAL_CLOCK && FLUSH PRIVILEGES + アカウント権限周りのステートメントをほぼ同時に打つとMySQLがデッドロックすることがある

TL;DR


再現方法。

8.0.19でレプリケーション設定をしたMySQLを用意する(8.0.28では再現しなかった。5.7.39でも今のところ再現していない)


$ make_replication_sandbox --how_many_slaves=1 8.0.19

$ cd sandboxes/rsandbox_8_0_19/

$ vim master/my.sandbox.cnf

..

binlog_group_commit_sync_delay = 1000000  ### binlogのgroup_commitを起こしやすくするため

$ vim node1/my.sandbox.cnf

..

slave_parallel_workers = 2

slave_preserve_commit_order = ON

slave_parallel_type = LOGICAL_CLOCK

$ ./restart_all

俺は未だにMySQL::Sandboxを使っているのだ、すまんな…。

2つのターミナルを用意して、↓の2ステートメントを1秒以内( binlog_group_commit_sync_delay を100万マイクロ秒に設定しているので、1秒なら同じグループコミットに入れる)に実行する

### Terminal1
$ ./m -e "FLUSH PRIVILEGES"

### Terminal2
$ ./m -e "CREATE USER yoku0825"

体感、50%くらいで再現する。
再現すると、ソースは無事だけどレプリカに一切合切ログインできなくなる。

$ ./s1  ### 刺さって返ってこなくなる

新規接続は全部デッドロックに巻き込まれて動けなくなるけれど、既にログインしているアカウントなら動ける(ただし USE mysql とかすると噛まれて動けなくなる)

SHOW PROCESSLISTで見ると、 FLUSH PRIVILEGESWaiting for table metadata lock で止まり、そのMDLをもってそうなDROP USERは Waiting for preceding transaction to commit で待っている。

あとから追いかけてきたコネクションは Waiting for acl cache lock で待たされている。

slave1 [localhost] {msandbox} ((none)) > SHOW PROCESSLIST;
+----+----------------------+-----------------+------+---------+------+--------------------------------------------------------+--------------------+
| Id | User                 | Host            | db   | Command | Time | State                                                  | Info               |
+----+----------------------+-----------------+------+---------+------+--------------------------------------------------------+--------------------+
|  4 | system user          |                 | NULL | Query   |   95 | Slave has read all relay log; waiting for more updates | NULL               |
|  5 | event_scheduler      | localhost       | NULL | Daemon  |  186 | Waiting on empty queue                                 | NULL               |
|  8 | system user          |                 |      | Connect |   96 | Waiting for table metadata lock                        | FLUSH PRIVILEGES   |
|  9 | system user          |                 |      | Connect |   95 | Waiting for preceding transaction to commit            | DROP USER yoku0825 |
| 10 | system user          | connecting host | NULL | Connect |  186 | Waiting for master to send event                       | NULL               |
| 15 | msandbox             | localhost       | NULL | Query   |    0 | starting                                               | SHOW PROCESSLIST   |
| 16 | unauthenticated user | localhost       | NULL | Connect |   92 | Waiting for acl cache lock                             | NULL               |
+----+----------------------+-----------------+------+---------+------+--------------------------------------------------------+--------------------+
7 rows in set (0.03 sec)
  1. FLUSH PRIVILEGES, DROP USER の順番でソースのbinlogに入る
  2. FLUSH PRIVILEGES, DROP USER の順番でレプリカのrelaylogに入る
  3. last_committed=6 (同じLOGICAL_CLOCK)の枠の中にあるので、コイツらはパラレルで動かされる
  4. DROP USER が先に終わってしまうと、 slave_preserve_commit_order のために FLUSH PRIVILEGES が終わるのを( mysql.user とかの排他メタデータロックを持ったまま) 待つ
  5. FLUSH PRIVILEGESACL_Cache (= mysql.user とかの情報をメモリ上にロードしているアレ) の排他ロックをかけたまま、 mysql.user とかの共有メタデータロックが欲しいので待つ (デッドロック!)
  6. それ以降の接続や権限の読みなおし要求は全て ACL_Cache の排他ロックに阻まれてブロックされる
  7. 死!
slave1 [localhost] {msandbox} (mysql) > SELECT * FROM performance_schema.metadata_locks;
+-------------------+--------------------+------------------+-----------------------+-----------------------+----------------------+---------------+-------------+------------------------+-----------------+----------------+
| OBJECT_TYPE       | OBJECT_SCHEMA      | OBJECT_NAME      | COLUMN_NAME           | OBJECT_INSTANCE_BEGIN | LOCK_TYPE            | LOCK_DURATION | LOCK_STATUS | SOURCE                 | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------------+--------------------+------------------+-----------------------+-----------------------+----------------------+---------------+-------------+------------------------+-----------------+----------------+
| GLOBAL            | NULL               | NULL             | NULL                  |       140468113535472 | INTENTION_EXCLUSIVE  | STATEMENT     | GRANTED     | sql_base.cc:5398       |              48 |             15 |
| BACKUP LOCK       | NULL               | NULL             | NULL                  |       140468113913600 | INTENTION_EXCLUSIVE  | TRANSACTION   | GRANTED     | sql_base.cc:5405       |              48 |             15 |
| SCHEMA            | mysql              | NULL             | NULL                  |       140468113563696 | INTENTION_EXCLUSIVE  | TRANSACTION   | GRANTED     | sql_base.cc:5385       |              48 |             15 |
| TABLE             | mysql              | columns_priv     | NULL                  |       140468113536896 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | db               | NULL                  |       140468113982576 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | columns_priv     | NULL                  |       140468181071040 | SHARED_READ_ONLY     | TRANSACTION   | PENDING     | table.h:2553           |              47 |             39 |
| TABLE             | mysql              | default_roles    | NULL                  |       140468113772864 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | global_grants    | NULL                  |       140468113772960 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | password_history | NULL                  |       140468113540192 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | procs_priv       | NULL                  |       140468113540288 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | proxies_priv     | NULL                  |       140468113540384 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | role_edges       | NULL                  |       140468113534816 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | tables_priv      | NULL                  |       140468113534912 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLE             | mysql              | user             | NULL                  |       140468113535008 | SHARED_NO_READ_WRITE | TRANSACTION   | GRANTED     | table.h:2553           |              48 |             15 |
| TABLESPACE        | NULL               | mysql            | NULL                  |       140468113897264 | INTENTION_EXCLUSIVE  | TRANSACTION   | GRANTED     | lock.cc:795            |              48 |             15 |
| ACL CACHE         | NULL               | NULL             | NULL                  |       140468113911328 | EXCLUSIVE            | EXPLICIT      | GRANTED     | sql_auth_cache.cc:3393 |              48 |             16 |
| COMMIT            | NULL               | NULL             | NULL                  |       140468113899824 | INTENTION_EXCLUSIVE  | EXPLICIT      | GRANTED     | handler.cc:1719        |              48 |             16 |
| ACL CACHE         | NULL               | NULL             | NULL                  |       140468448997264 | SHARED               | EXPLICIT      | PENDING     | sql_auth_cache.cc:3393 |              57 |              1 |

バイナリログを開くと、 last_committed が同じになっている。
ただ、これでも先に DROP USER が処理されないと再現はしないので、100%再現とまではいかなかった。

#220822 14:39:48 server id 1  end_log_pos 1411 CRC32 0x3c26f86e         Anonymous_GTID  last_committed=6        sequence_number=7       rbr_only=no     original_committed_timestamp=1661146788679412   immediate_commit_timestamp=1661146788679412     transaction_length=167
# original_commit_timestamp=1661146788679412 (2022-08-22 14:39:48.679412 JST)
# immediate_commit_timestamp=1661146788679412 (2022-08-22 14:39:48.679412 JST)
/*!80001 SET @@session.original_commit_timestamp=1661146788679412*//*!*/;
/*!80014 SET @@session.original_server_version=80019*//*!*/;
/*!80014 SET @@session.immediate_server_version=80019*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1411
#220822 14:39:48 server id 1  end_log_pos 1501 CRC32 0xd17997a8         Query   thread_id=15    exec_time=0     error_code=0
SET TIMESTAMP=1661146788/*!*/;
FLUSH PRIVILEGES
/*!*/;

# at 1501
#220822 14:39:49 server id 1  end_log_pos 1578 CRC32 0x91c06a81         Anonymous_GTID  last_committed=6        sequence_number=8       rbr_only=no     original_committed_timestamp=1661146789250989   immediate_commit_timestamp=1661146789250989     transaction_length=178
# original_commit_timestamp=1661146789250989 (2022-08-22 14:39:49.250989 JST)
# immediate_commit_timestamp=1661146789250989 (2022-08-22 14:39:49.250989 JST)
/*!80001 SET @@session.original_commit_timestamp=1661146789250989*//*!*/;
/*!80014 SET @@session.original_server_version=80019*//*!*/;
/*!80014 SET @@session.immediate_server_version=80019*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1578
#220822 14:39:49 server id 1  end_log_pos 1679 CRC32 0xf4243118         Query   thread_id=16    exec_time=0     error_code=0    Xid = 34
SET TIMESTAMP=1661146789/*!*/;
DROP USER yoku0825
/*!*/;

InnoDBとかで刺さる時と違って、CPU使用率も大して上がらずに緩やかに刺さった。
対処方法は kill -9 での再起動(もう kill -15mysqladmin shutdown では落ちてくれない)

gtid_mode=ONになってないとMTS中の異常終了で起動した後もSQL_Threadが止まったり(これはもうgtid_mode=ONの時以外MTSは危険…)、そもそも起動の時にまた同じ組み合わせをパラレル適用しようとしてまた刺さったり(replica_parallel_workers = 1にして起動して回避する)とか、なかなかエグい。

いつかぶち当たる人がいたら、参考にしてください…。
(バージョンアップすれば当たらなくなりそうだからバージョン上げましょう…)

0 件のコメント :

コメントを投稿