Identified problem
Because of this commit the reindex that happens immediately after upgrade to Magento 2.3.3 causes Galera clusters to exit abruptly. The problem has been flagged to Magento development team, but their answer was simply to follow my own advice.
Problem description
Magento uses a number of temporary table during its reindex procedure:
mysql> SELECT TABLE_NAME,ENGINE FROM information_schema.TABLES WHERE ENGINE='MEMORY'; +---------------------------------------------+--------+ | TABLE_NAME | ENGINE | +---------------------------------------------+--------+ | catalog_category_product_index_tmp | MEMORY | | catalog_product_index_eav_decimal_tmp | MEMORY | | catalog_product_index_eav_tmp | MEMORY | | catalog_product_index_price_bundle_opt_tmp | MEMORY | | catalog_product_index_price_bundle_sel_tmp | MEMORY | | catalog_product_index_price_bundle_tmp | MEMORY | | catalog_product_index_price_cfg_opt_agr_tmp | MEMORY | | catalog_product_index_price_cfg_opt_tmp | MEMORY | | catalog_product_index_price_downlod_tmp | MEMORY | | catalog_product_index_price_final_tmp | MEMORY | | catalog_product_index_price_opt_agr_tmp | MEMORY | | catalog_product_index_price_opt_tmp | MEMORY | | catalog_product_index_price_tmp | MEMORY | | cataloginventory_stock_status_tmp | MEMORY | +---------------------------------------------+--------+ 14 rows in set (0,00 sec)
prior to Magento 2.3.3 these tables were using the MEMORY engine.
Per Galera’s design there are some known limitations, importantly - the replication is supported only for the InnoDB engine. That means, specifically the content of tables based on MEMORY engine are not replicated.
During Magento upgrade the index tables are altered and the engine is changed from MEMORY to InnoDB. At this point the content of these tables becomes out-of-sync between the nodes of the Galera cluster and one of these nodes may have data in the table, while the other nodes will not.
First node:
mysql> select * from catalog_product_index_price_bundle_tmp; +-----------+-------------------+------------+--------------+------------+---------------+--------------+------------+------------+------------+------------+------------+-----------+ | entity_id | customer_group_id | website_id | tax_class_id | price_type | special_price | tier_percent | orig_price | price | min_price | max_price | tier_price | base_tier | +-----------+-------------------+------------+--------------+------------+---------------+--------------+------------+------------+------------+------------+------------+-----------+ | 337 | 0 | 1 | 2 | 1 | NULL | NULL | 37.950000 | 37.950000 | 37.950000 | 37.950000 | NULL | NULL | | 337 | 1 | 1 | 2 | 1 | NULL | NULL | 37.950000 | 37.950000 | 37.950000 | 37.950000 | NULL | NULL | // ... REDACTED | 456 | 4 | 1 | 2 | 1 | NULL | NULL | 20.990000 | 20.990000 | 20.990000 | 20.990000 | NULL | NULL | | 456 | 6 | 1 | 2 | 1 | NULL | NULL | 20.990000 | 20.990000 | 20.990000 | 20.990000 | NULL | NULL | +-----------+-------------------+------------+--------------+------------+---------------+--------------+------------+------------+------------+------------+------------+-----------+ 60 rows in set (0,00 sec)
other nodes:
mysql> select * from catalog_product_index_price_bundle_tmp; Empty set (0,00 sec)
at the same time, the engine has been upgraded to InnoDB which means any further changes to these tables will be replicated over the cluster.
mysql> select TABLE_NAME,ENGINE from information_schema.TABLES where TABLE_SCHEMA='ploom_hdls_test' and TABLE_NAME like 'catalog_%_tmp'; +---------------------------------------------+--------+ | TABLE_NAME | ENGINE | +---------------------------------------------+--------+ | catalog_category_product_index_tmp | InnoDB | | catalog_product_index_eav_decimal_tmp | InnoDB | | catalog_product_index_eav_tmp | InnoDB | | catalog_product_index_price_bundle_opt_tmp | InnoDB | | catalog_product_index_price_bundle_sel_tmp | InnoDB | | catalog_product_index_price_bundle_tmp | InnoDB | | catalog_product_index_price_cfg_opt_agr_tmp | InnoDB | | catalog_product_index_price_cfg_opt_tmp | InnoDB | | catalog_product_index_price_downlod_tmp | InnoDB | | catalog_product_index_price_final_tmp | InnoDB | | catalog_product_index_price_opt_agr_tmp | InnoDB | | catalog_product_index_price_opt_tmp | InnoDB | | catalog_product_index_price_tmp | InnoDB | | cataloginventory_stock_status_tmp | InnoDB | +---------------------------------------------+--------+ 14 rows in set (0,01 sec)
In such a case, part of the standard reindex procedure, which sends a DELETE DML is propagated over the cluster and results in termination of all-but-one of the Galera nodes:
The command issued during reindex:
mysql> DELETE FROM catalog_product_index_price_bundle_tmp; Query OK, 60 rows affected (0,01 sec)
Result on remaining Galera nodes:
2019-10-28 10:04:17 41267 [ERROR] Slave SQL: Could not execute Delete_rows event on table catalog_product_index_price_bundle_tmp; Can't find record in 'catalog_product_index_price_bundle_tmp', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log FIRST, end_log_pos 3577, Error_code: 1032 2019-10-28 10:04:17 41267 [Warning] WSREP: RBR event 3 Delete_rows apply warning: 120, 498863560 2019-10-28 10:04:17 41267 [ERROR] WSREP: Failed to apply trx: source: 9f112bd7-f7c1-11e9-a173-f2aa3d9f6709 version: 4 local: 0 state: APPLYING flags: 1 conn_id: 166495 trx_id: 8030458603 seqnos (l: 613988, g: 498863560, s: 498863559, d: 498863504, ts: 56153475565059237) 2019-10-28 10:04:17 41267 [ERROR] WSREP: Failed to apply trx 498863560 4 times 2019-10-28 10:04:17 41267 [ERROR] WSREP: Node consistency compromised, aborting... 2019-10-28 10:04:17 41267 [Note] WSREP: Closing send monitor... 2019-10-28 10:04:17 41267 [Note] WSREP: Closed send monitor. 2019-10-28 10:04:17 41267 [Note] WSREP: gcomm: terminating thread 2019-10-28 10:04:17 41267 [Note] WSREP: gcomm: joining thread 2019-10-28 10:04:17 41267 [Note] WSREP: gcomm: closing backend 2019-10-28 10:04:18 41267 [Note] WSREP: view(view_id(NON_PRIM,67dbaea5,16) memb { cb60a6a6,0 } joined { } left { } partitioned { 67dbaea5,0 9f112bd7,0 })
Summary
The problem is partly inside Galera (at least Server version: 5.6.45 MySQL Community Server (GPL), wsrep_25.27), because it allows to end-up in a situation like this and partly inside Magento upgrade, because it does not empty the temp tables, which would prevent this from happening as well.
Percona XDB is already preventing such thing from happening:
mysql> INSERT INTO fail_test values (1); ERROR 1105 (HY000): Percona-XtraDB-Cluster prohibits use of DML command on a table (fail_test.fail_test) that resides in non-transactional storage engine with pxc_strict_mode = ENFORCING or MASTER mysql>
tested in version Server version: 5.7.27-30-57 Percona XtraDB Cluster (GPL), Release rel30, Revision 64987d4, WSREP version 31.39, wsrep_31.39
Minimal reproducible case
- Create MEMORY based table inside cluster
- Insert data into the table
- Convert table to InnoDB
- Delete data in the table
CREATE TABLE `mem_based_table` (field1 int) Engine='MEMORY'; INSERT INTO `mem_based_table` values (1); ALTER TABLE `mem_based_table` Engine='InnoDB' DELETE FROM `mem_based_table`;
This has been successfuly replicated on:
- MySQL 5.6.45 wsrep_25.27
- MariaDB 10.1.23-MariaDB-1~jessie
For example with the following environment
node1: image: hauptmedia/mariadb:10.1 hostname: node1 ports: - 13306:3306 environment: - MYSQL_ROOT_PASSWORD=test - REPLICATION_PASSWORD=test - MYSQL_DATABASE=maria - MYSQL_USER=maria - MYSQL_PASSWORD=test - GALERA=On - NODE_NAME=node1 - CLUSTER_NAME=maria_cluster - CLUSTER_ADDRESS=gcomm:// command: --wsrep-new-cluster node2: image: hauptmedia/mariadb:10.1 hostname: node2 links: - node1 ports: - 23306:3306 environment: - REPLICATION_PASSWORD=test - GALERA=On - NODE_NAME=node2 - CLUSTER_NAME=maria_cluster - CLUSTER_ADDRESS=gcomm://node1 node3: image: hauptmedia/mariadb:10.1 hostname: node3 links: - node1 ports: - 33306:3306 environment: - REPLICATION_PASSWORD=test - GALERA=On - NODE_NAME=node3 - CLUSTER_NAME=maria_cluster - CLUSTER_ADDRESS=gcomm://node1
after executing the set of commands mentioned above, we get:
$ docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------------------------------------- galera-cluster_node1_1 /entrypoint.sh --wsrep-new ... Up 0.0.0.0:13306->3306/tcp, 4444/tcp, 4567/tcp, 4568/tcp galera-cluster_node2_1 /entrypoint.sh mysqld Exit 139 galera-cluster_node3_1 /entrypoint.sh mysqld Exit 139
Possible workaround
Empty the temporary tables before the upgrade to Magento 2.3.3, while still using the MEMORY engine.