Bug #109829 Bug of function minAck in semisync plugin lead to unexpected behavior
Submitted: 29 Jan 9:52 Modified: 30 Jan 14:46
Reporter: Ken Lau (OCA) Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Replication Severity:S3 (Non-critical)
Version:all OS:Any
Assigned to: CPU Architecture:Any
Tags: semi-sync

[29 Jan 9:52] Ken Lau
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;
  }
[30 Jan 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.