Bug #47200 | RBR: Absence of PK on slave leads to slave stop. | ||
---|---|---|---|
Submitted: | 8 Sep 2009 22:38 | Modified: | 24 Mar 2011 15:50 |
Reporter: | Luis Soares | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: Row Based Replication ( RBR ) | Severity: | S3 (Non-critical) |
Version: | 6.0 codebase | OS: | Any |
Assigned to: | Luis Soares | CPU Architecture: | Any |
Tags: | RBR, regression |
[8 Sep 2009 22:38]
Luis Soares
[9 Sep 2009 14:01]
Luis Soares
Analysis ======== - find_row When processing the before image (BI), from the row event, to fetch the correct record from the storage engine (SE), the slave first checks if there is a PK in its table. If there is, then the row is searched and fetched from the SE using the PK. find_row succeeds if the engine is able to find the record. However, when there is no PK, an index or table scan are performed. In such cases, there is the need to iterate over the records fetched from the SE and compare them against the one in the BI. If there is a match, the record is selected and the find_row succeeds. Record comparison is performed in record_compare function. - record_compare In this function, there are two optimizations, regarding the comparison of records: 1. We can compare both records using cmp_record if all bits are set in the read_set of the event (meaning that the records should be both complete); 2. Or we can compare using cmp_record if the engine provides partial column fetch - HA_PARTIAL_COLUMN_READ. This means that if the read_set on the event is honored by the SE while the fetching the row, both the BI and the row fetched should have the same fields. If none of the optimizations may be used (the read_set is not completely set or the SE does not provide partial row fetch), then columns from both rows are compared one by one against each other, provided that their bit is set in the read_set. If all columns signaled in the read_set match, then record_compare succeeds, otherwise it fails. Problem Statement ================= There can be two cases that put at risk the usage of optimization #2 in record_compare: 1. there can be the case, that the SE bypasses the read_set provided in the row event, returning the full row (despite the fact that it provides HA_PARTIAL_COLUMN_READ). This causes comparison to fail (partial BI will be compared against full row from SE); 2. If the SE does honor the read_set, there can be the case that when preparing and unpacking the row event, default values are assigned to missing columns on the BI row, therefore BI will be filled with extra data. This can cause comparison to fail (BI row, filled also with default values, will be compared against SE row containing only requested columns). In the case presented in the howto repeat section, it seems that the engine is returning the full row while the table scan is performed on the slave (in find_row). Checking the SE implementation, I find the following remark on storage/innobase/handler/ha_innodb.cc:build_template : /* We always retrieve the whole clustered index record if we use exclusive row level locks, for example, if the read is done in an UPDATE statement. */ Which seems to cause the SE to return the entire row for the test case presented, ultimately causing the test to fail.
[10 Sep 2009 9:38]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/82907 2813 Luis Soares 2009-09-10 BUG#47200: RBR: Absence of PK on slave leads to slave stop. If the same table is declared with a PK on the master but without the PK on the slave, row based replication may break. This is noticed for InnoDB engine, which provides HA_PARTIAL_COLUMN_READ, but it may also fail with other engines providing the same flag. The failure happens when searching the row on the slave to be updated. When the slave thread performs a scan (table or index), the search procedure is basically fetching rows from slave engine and comparing them against the row in the event before image. If the engine provides HA_PARTIAL_COLUMN_READ, then a memory comparison is performed for both rows, otherwise, a column by column (for only those marked in the read_set) comparison is done. There can be two problems with this: First, engine does not honor HA_PARTIAL_COLUMN_READ for some cases. When fetching the row from the slave engine, and although it provides HA_PARTIAL_COLUMN_READ, the table->read_set (which marks only the columns as in the master PK) is ignored. Thence, the engine returns the full row instead of only the columns needed for comparison. In this case, the comparison will fail. Second, when the RBR event row is unpacked it is filled with default values for the missing columns. So, it can be the case the event before image is filled with default values, on the slave at unpack time. Consequently, when searching the record, and even if the engine honors HA_PARTIAL_COLUMN_READ, the slave will fail comparison - it will do a memory comparison of row with PK + default values against a row with only the PK. This patch fixes this issue by removing the part of the comparison that takes into account the HA_PARTIAL_COLUMN_READ. In such cases it performs a column by column comparison (for those marked in the read_set).
[14 Sep 2009 10:17]
Sveta Smirnova
Verified as described. Not repeatable with version 5.1
[17 Sep 2009 14:36]
Lars Thalmann
See also BUG#47303
[1 Oct 2009 11:32]
Luis Soares
Using the test case in the howto repeat section, I checked against the following trees: mysql-5.1-bugteam => Test SUCCEEDS mysql-next-mr => Test SUCCEEDS mysql-6.0-codebase-bugfixing => Test FAILS So I confirm Sveta's comment ([14 Sep 12:17] Sveta Smirnova) and extend the verification to next-mr tree.
[24 Mar 2011 15:50]
Jon Stephens
Fixed in 5.6.2 by WL#5092, qv. for docs info. Closed.