GA

2024/03/07

replicate_wild_ignore_table="mysql.%" で CREATE USERやGRANTがignoreされる理由

TL;DR

  • 体感としては知ってたけどレプリケーションフィルタには対応するコードがなくてもんにょりしていた
  • sql/rpl_filter.cc じゃなくて sql/auth/sql_user_table.cc に埋まっていやがった

ドキュメントには、

ただし、通常はこの情報を間接的に更新するステートメント (GRANT、REVOKE、およびトリガー、ストアドルーチン、およびビューを操作するステートメント) は、ステートメントベースレプリケーションを使用してレプリカにレプリケートされます。

MySQL :: MySQL 8.0 リファレンスマニュアル :: 17.5.1.22 mysql システムスキーマのレプリケーション

Table-level replication filters are only applied to tables that are explicitly mentioned and operated on in the query. They do not apply to tables that are implicitly updated by the query. For example, a GRANT statement, which updates the mysql.user system table but does not mention that table, is not affected by a filter that specifies mysql.% as the wildcard pattern.

https://dev.mysql.com/doc/refman/8.0/en/replication-options-replica.html#option_mysqld_replicate-wild-ignore-table

(日本語版にはこの表記がなかった…)

しかもご丁寧に回避方法まで

If you need to filter out GRANT statements or other administrative statements, a possible workaround is to use the —replicate-ignore-db filter. This filter operates on the default database that is currently in effect, as determined by the USE statement.

https://dev.mysql.com/doc/refman/8.0/en/replication-options-replica.html#option_mysqld_replicate-wild-ignore-table

と、あたかもCREATE USERもGRANTもフィルターに引っかからずにステートメントベースのルールでだけ評価されそうなことを言っているのに、現実には replicate_wild_ignore_table=mysql.% でフィルタされてしまう。

実験。

### Sourceになる
mysqlsh -- dba deploySandboxInstance 3306 --password=testpass

### 比較のためにreplicate_ignore_db=mysqlを設定したMySQL
mysqlsh -- dba deploySandboxInstance 3307 --password=testpass --mysqldOptions="replicate-ignore-db=mysql"

### 本丸のreplicate_wild_ignore_table=mysql.%を設定したMySQL
mysqlsh -- dba deploySandboxInstance 3308 --password=testpass --mysqldOptions="replicate-wild-ignore-table=mysql.%"

ソースにCREATE USER/GRANTでレプリケーション用アカウントを作る。ここまではフツー。

mysqlsh root:testpass@localhost:3306 --sql

CREATE USER replicate IDENTIFIED BY 'replication';
GRANT REPLICATION SLAVE ON *.* TO replicate;

3307のインスタンスと3308のインスタンスでそれぞれレプリケーションを組む。 source_ssl=1 にしているのはcaching_sha2_passwordのエラー避け ( 日々の覚書: MySQL 8.0のcaching_sha2_password + 非SSL接続が転ける )
別に GET_SOURCE_PUBLIC_KEY=1 でもいい。

mysqlsh root:testpass@localhost:3307 --sql

CHANGE REPLICATION SOURCE TO source_host = '127.0.0.1', source_port = 3306, source_user = 'replicate', source_password = 'replication', source_ssl = 1;
START REPLICA;

---

mysqlsh root:testpass@localhost:3308 --sql

CHANGE REPLICATION SOURCE TO source_host = '127.0.0.1', source_port = 3306, source_user = 'replicate', source_password = 'replication', source_ssl = 1;
START REPLICA;

source_log_pos を指定していないのでソースに残っている先頭のバイナリログのpos=1からレプリケーションするので、本来 CREATE USERとGRANTはレプリカでも実行されてアカウントが作成されるはず。

それぞれのインスタンスでSHOW GRANTSしてみると、

for port in 3306 3307 3308 ; do
  mysqlsh root:testpass@localhost:$port --sql -e "SHOW GRANTS FOR replicate"
done

Grants for replicate@%
GRANT REPLICATION SLAVE ON *.* TO `replicate`@`%`    ### 3306, replication source

Grants for replicate@%
GRANT REPLICATION SLAVE ON *.* TO `replicate`@`%`    ### 3307, replicate_ignore_db=mysql だけどCREATE USERもGRANTも `USE` しない状態でやっているのでフィルターされない

ERROR: 1141 (42000) at line 1: There is no such grant defined for user 'replicate' on host '%'   ### 3308のreplicate_wild_ignore_tablesだとフィルターされてCREATEもGRANTも実行されない

コードを見る限りたぶんMySQLのバグじゃなくてドキュメントが間違ってるんだと思う。

https://github.com/mysql/mysql-server/blob/mysql-8.0.36/sql/auth/sql_user_table.cc#L1975-L1990

0 件のコメント :

コメントを投稿