| Bug #109829 | Bug of function minAck in semisync plugin lead to unexpected behavior | ||
|---|---|---|---|
| Submitted: | 29 Jan 2023 9:52 | Modified: | 30 Jan 2023 14:46 |
| Reporter: | Ken Lau (OCA) | Email Updates: | |
| Status: | Verified | Impact on me: | |
| Category: | MySQL Server: Replication | Severity: | S3 (Non-critical) |
| Version: | all | OS: | Any |
| Assigned to: | CPU Architecture: | Any | |
| Tags: | semi-sync | ||
[30 Jan 2023 14:46]
MySQL Verification Team
Thank you very much for your patch contribution, we appreciate it! In order for us to continue the process of reviewing your contribution to MySQL, please send us a signed copy of the Oracle Contributor Agreement (OCA) as outlined in https://oca.opensource.oracle.com Signing an OCA needs to be done only once and it's valid for all other Oracle governed Open Source projects as well. Getting a signed/approved OCA on file will help us facilitate your contribution - this one, and others in the future. Please let me know, if you have any questions. Thank you for your interest in MySQL.

Description: There is a bug in function minAck in semisync_source.h in semisync plugin. The function doesn't find the minimum ack but the maximum index ack which is smaller than given position. And this will lead to unexpected behavior when rpl_semi_sync_master_wait_slaves is set to more than 3. ``` /** Find the minimum ack which is smaller than given position. When more than one slots are minimum acks, it returns the one has smallest index. @param[in] log_file_name binlog file name @param[in] log_file_pos binlog file position @return NULL if no ack is smaller than given position, otherwise return its pointer. */ AckInfo *minAck(const char *log_file_name, my_off_t log_file_pos) { unsigned int i; AckInfo *ackinfo = nullptr; for (i = 0; i < m_size; i++) { if (m_ack_array[i].less_than(log_file_name, log_file_pos)) ackinfo = m_ack_array + i; } return ackinfo; } ``` How to repeat: Suppose rpl_semi_sync_master_wait_slaves = 4. coming acks slot 1 slot 2 slot 3 =========== ========== ========== ========== 1:log1:120 1:log1:120 2:log1:120 1:log1:120 2:log1:120 1:log1:150 1:log1:150 2:log1:120 1:log1:170 1:log1:170 2:log1:120 3:log1:150 1:log1:170 2:log1:120 3:log1:150 4:log1:170 The minAck will return '3:log1:150' and m_greatest_ack will asign to '3:log1:150' too. Then the array will look like: coming acks slot 1 slot 2 slot 3 =========== ========== ========== ========== 1:log1:170 2:log1:120 log1:170 Slot 2 in this case will never become empty again. Suggested fix: Just fix the bug in minAck. Like: ``` AckInfo *minAck(const char *log_file_name, my_off_t log_file_pos) { unsigned int i; AckInfo *ackinfo = nullptr; const char *min_log_file_name = log_file_name; my_off_t min_log_file_pos = log_file_pos; for (i = 0; i < m_size; i++) { if (m_ack_array[i].less_than(min_log_file_name, min_log_file_pos)) { ackinfo = m_ack_array + i; min_log_file_name = m_ack_array[i].binlog_name; min_log_file_pos = m_ack_array[i].binlog_pos; } } ``` return ackinfo; }