MySQL – How to install and use clone plugin?

For ages MySQL was lacking an ability to provision new instances with data, at least for the community builds. Well, technically there always was mysqldump but let’s forget about using it on any kind of scale. It’s too slow to be used in most of the real world scenarios. Finally, Oracle added a ‘clone’ plugin – available in both MySQL 5.7 and MySQL 8.0. Let’s take a look at it and see how we can use it for a quite standard use case like MySQL replication. No InnoDB Cluster nor MySQL Replica Set yet, just plain, old asynchronous replication.

As for the data, we are going to use SysBench and we are going to generate the data using following command:

root@vagrant:~# sysbench /root/sysbench/src/lua/oltp_read_write.lua --threads=4 --mysql-host=192.168.10.111 --mysql-user=sbtest --mysql-password=sbtest --mysql-port=6033 --tables=32 --table-size=1000000 --db-ps-mode=disable prepare

The connection is executed through one of ProxySQL instances, using Virtual IP provided by Keepalived. ProxySQL is configured to perform the read/write split, sending writes to the source node and reads to replica. Once the data is populated, we will be using sysbench to generate load on the cluster:

root@vagrant:~# while true ;do sysbench /root/sysbench/src/lua/oltp_read_write.lua --threads=4 --events=0 --time=$(shuf -i 60-300 -n 1) --mysql-host=192.168.10.111 --mysql-user=sbtest --mysql-password=sbtest --mysql-port=6033 --tables=32 --report-interval=1 --skip-trx=on --table-size=1000000 --db-ps-mode=disable --rate=$(shuf -i 1-20 -n 1) run ; done

Initial setup

There are a couple of prerequisites that ‘clone’ plugin requires to operate. First, we have to install the plugin itself. The whole process is described in detail on MySQL documentation website but here are the steps that we performed.

First, we have to set up the plugin in my.cnf. We did that by adding following lines to [mysqld] section of my.cnf:

plugin-load-add=mysql_clone.so
clone=FORCE_PLUS_PERMANENT

Then we performed the rolling restart of our cluster. It is also possible to enable the plugin on the fly using:

INSTALL PLUGIN clone SONAME 'mysql_clone.so';

Finally, we verified that the plugin is active:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS
    ->        FROM INFORMATION_SCHEMA.PLUGINS
    ->        WHERE PLUGIN_NAME = 'clone';
+-------------+---------------+
| PLUGIN_NAME | PLUGIN_STATUS |
+-------------+---------------+
| clone       | ACTIVE        |
+-------------+---------------+
1 row in set (0.00 sec)

All is good. We can then proceed to create required users and complete the configuration. The ‘clone’ plugin can be used for local and remote cloning. Obviously, we are interested in the latter. Using it for backups is a great thing but we want to see how it is going to work for provisioning of remote servers.

Clone plugin requires a user to connect to the remote server. Such user should have ‘BACKUP_ADMIN’ privilege. Ideally, you will create such user for every host in the cluster but we, for the sake of simplicity, will just create one user for all hosts:

 MySQL  localhost  SQL > CREATE USER 'donor'@'%' IDENTIFIED BY 'somepass';
Query OK, 0 rows affected (0.0118 sec)
 MySQL  localhost  SQL > GRANT BACKUP_ADMIN ON *.* to 'donor'@'%';
Query OK, 0 rows affected (0.0105 sec)

The same thing is for the user required on the recipient side:

 MySQL  localhost  SQL > CREATE USER 'receipient'@'%' IDENTIFIED BY 'somepass';
Query OK, 0 rows affected (0.0588 sec)
 MySQL  localhost  SQL > GRANT CLONE_ADMIN ON *.* TO 'receipient'@'%';
Query OK, 0 rows affected (0.0041 sec)

Of course, from the security standpoint it is not ideal, we should minimize the access granted and limit the hosts such users can connect from.

As the next step we should populate the ‘clone_valid_donor_list’ variable on all instances:

 MySQL  localhost  SQL > SET GLOBAL clone_valid_donor_list='192.168.10.157:3306,192.168.10.158:3306,192.168.10.159:3306';
Query OK, 0 rows affected (0.0044 sec)

We have also verified that the ‘innodb_page_size’ and ‘innodb_data_file_path’ are the same on all nodes, and we have checked that ‘max_allowed_packet’ is set to more than 2M. We have also checked that the undo files have unique names. All was good and we could proceed to test the ‘clone’ plugin.

Testing the clone plugin

Let’s start with a common scenario. We want to add a replica to the cluster. In our case we will stop one of them, wipe out the replication configuration and then re-add it using the clone plugin.

mysql> STOP REPLICA;
Query OK, 0 rows affected (0.01 sec)

mysql> RESET REPLICA ALL;
Query OK, 0 rows affected (0.04 sec)

mysql> RESET MASTER;
Query OK, 0 rows affected (0.05 sec)

mysql> DROP SCHEMA  sbtest;
Query OK, 32 rows affected (0.25 sec)

Let’s verify that everything is clean:

mysql> SHOW MASTER STATUS\G
*************************** 1. row ***************************
             File: binlog.000001
         Position: 156
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)

mysql> SHOW REPLICA STATUS\G
Empty set (0.00 sec)

mysql> SHOW GLOBAL VARIABLES LIKE '%gtid%';
+----------------------------------+-------+
| Variable_name                    | Value |
+----------------------------------+-------+
| binlog_gtid_simple_recovery      | ON    |
| enforce_gtid_consistency         | ON    |
| gtid_executed                    |       |
| gtid_executed_compression_period | 0     |
| gtid_mode                        | ON    |
| gtid_owned                       |       |
| gtid_purged                      |       |
| session_track_gtids              | OFF   |
+----------------------------------+-------+
8 rows in set (0.00 sec)

mysql> SHOW TABLES FROM sbtest;
ERROR 1049 (42000): Unknown database 'sbtest'

As you can see, we have no replication configuration nor any mention about executed or pruned GTID’s. Let’s proceed with cloning the data.

 MySQL  localhost:3306 ssl  SQL > CLONE INSTANCE FROM 'donor'@192.168.10.157:3306 IDENTIFIED BY 'somepass';
ERROR: 3869 (HY000): Clone system configuration: 192.168.10.157:3306 is not found in clone_valid_donor_list:
 MySQL  localhost:3306 ssl  SQL > SHOW GLOBAL VARIABLES LIKE 'clone_valid_donor_list';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| clone_valid_donor_list |       |
+------------------------+-------+
1 row in set (0.0064 sec)

Ok, it seems like at some point the donor list has been cleared (after a restart maybe?) or we forgot to set it up. Let’s recreate it.

 MySQL  localhost:3306 ssl  SQL > SET GLOBAL clone_valid_donor_list='192.168.10.157:3306,192.168.10.158:3306,192.168.10.159:3306';
Query OK, 0 rows affected (0.0003 sec)

Now we can run the clone command again:

 MySQL  localhost:3306 ssl  SQL > CLONE INSTANCE FROM 'donor'@192.168.10.157:3306 IDENTIFIED BY 'somepass';
Query OK, 0 rows affected (58.3180 sec)

Ok, let’s see where we are at?

 MySQL  localhost:3306 ssl  SQL > SHOW GLOBAL VARIABLES LIKE '%gtid%';
ERROR: 2013 (HY000): Lost connection to MySQL server during query
The global session got disconnected..
Attempting to reconnect to 'mysql://root@localhost:3306'..
The global session was successfully reconnected.
 MySQL  localhost:3306 ssl  SQL > SHOW GLOBAL VARIABLES LIKE '%gtid%';
+----------------------------------+------------------------------------------------+
| Variable_name                    | Value                                          |
+----------------------------------+------------------------------------------------+
| binlog_gtid_simple_recovery      | ON                                             |
| enforce_gtid_consistency         | ON                                             |
| gtid_executed                    | e8e57a44-4ded-11ec-ab00-0800277360cf:1-4025941 |
| gtid_executed_compression_period | 0                                              |
| gtid_mode                        | ON                                             |
| gtid_owned                       |                                                |
| gtid_purged                      | e8e57a44-4ded-11ec-ab00-0800277360cf:1-4025941 |
| session_track_gtids              | OFF                                            |
+----------------------------------+------------------------------------------------+
8 rows in set (0.0030 sec)

It looks like we are in good shape. The first error is expected: clone process requires MySQL to be restarted so reconnect is normal. Now we can set up the replication:

 MySQL  localhost:3306 ssl  SQL > CHANGE REPLICATION SOURCE TO SOURCE_HOST='192.168.10.157', SOURCE_USER='rpl_user', SOURCE_PASSWORD='3Lr2JM@Un.I}1cBwuUGji91hW,H7i84i', SOURCE_AUTO_POSITION=1;
Query OK, 0 rows affected, 1 warning (0.0306 sec)

 MySQL  localhost:3306 ssl  SQL > START REPLICA;
Query OK, 0 rows affected (0.0049 sec)

Let’s see how it goes:

 MySQL  localhost:3306 ssl  SQL > SHOW REPLICA STATUS\G
*************************** 1. row ***************************
             Replica_IO_State: Waiting for source to send event
                  Source_Host: 192.168.10.157
                  Source_User: rpl_user
                  Source_Port: 3306
                Connect_Retry: 60
              Source_Log_File: binlog.000012
          Read_Source_Log_Pos: 892387029
               Relay_Log_File: relay-bin.000002
                Relay_Log_Pos: 9616743
        Relay_Source_Log_File: binlog.000012
           Replica_IO_Running: Yes
          Replica_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Source_Log_Pos: 886019368
              Relay_Log_Space: 15984607
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Source_SSL_Allowed: No
           Source_SSL_CA_File:
           Source_SSL_CA_Path:
              Source_SSL_Cert:
            Source_SSL_Cipher:
               Source_SSL_Key:
        Seconds_Behind_Source: 78
Source_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Source_Server_Id: 3001
                  Source_UUID: e8e57a44-4ded-11ec-ab00-0800277360cf
             Source_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
    Replica_SQL_Running_State: Waiting for dependent transaction to commit
           Source_Retry_Count: 86400
                  Source_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Source_SSL_Crl:
           Source_SSL_Crlpath:
           Retrieved_Gtid_Set: e8e57a44-4ded-11ec-ab00-0800277360cf:4050605-4078176
            Executed_Gtid_Set: e8e57a44-4ded-11ec-ab00-0800277360cf:1-4067229
                Auto_Position: 1
         Replicate_Rewrite_DB:
                 Channel_Name:
           Source_TLS_Version:
       Source_public_key_path:
        Get_Source_public_key: 0
            Network_Namespace:
1 row in set (0.0290 sec)

All is looking good, replication is proceeding and we can clearly see the GTID’s being replicated and executed.

As you can see, using a clone plugin makes provisioning of new replicas quite an easy and convenient process compared to what we had to use in the past. As it is fully executable from the MySQL CLI (be it MySQL Shell or standard client), it is easier to implement cloning than xtrabackup, which provides pretty much the same functionality. In the future blog posts we will take a look at the SSL setup for clone plugin to see how we can further improve the security of the process by applying encryption of data in transfer.