Getting Started

MySQL Group Replication is released as a plugin to the MySQL server and, quite obviously, one can load it into the MySQL servers as any other plugin. This Chapter will present a detailed walkthrough to get a replication group with three servers up and running.

Deploying a Group with Three Servers in Multi-Master Mode

Three servers deployed in a group.

Three servers deployed in a group.

This chapter exemplifies how to deploy a replication group with three MySQL servers. This means that three data directories will be needed, one per server. Each server will also have to be configured independently.

This guide will start by letting the user know where to get the MySQL server with the plugin. Then it will deploy the binaries and initialize the data directories. Then configure each instance separately and bring all three servers up. And finally verify that everything is setup correctly and that replication is working properly.

Downloading

You can get the server with the Group Replication plugin from the MySQL labs web site. Select the build named “MySQL Group Replication for MySQL Server 5.7.15”. This bundle is a standard server bundle that contains the replication plugin together with it.

Once you download that server package, you will have to unpack it and install the binaries. Lets assume that the server was downloaded and unpacked into the current directory, under the directory named mysql-5.7.

Deploying

Before anything else, one needs to deploy three instances of the MySQL server. Thence lets start by creating three directories, which store the data for the three different instances. Data directories will be created under a directory named data.

1
2
3
4
mkdir data
mysql-5.7/bin/mysqld --initialize-insecure --basedir=$PWD/mysql-5.7 --datadir=$PWD/data/s1
mysql-5.7/bin/mysqld --initialize-insecure --basedir=$PWD/mysql-5.7 --datadir=$PWD/data/s2
mysql-5.7/bin/mysqld --initialize-insecure --basedir=$PWD/mysql-5.7 --datadir=$PWD/data/s3

After this is done, inside data/s1, data/s2, data/s3, one will find an initialized data directory, containing the mysql system database and related tables and much more. If you want to learn more about what the initialization procedure consists of, you can find more information in the MySQL reference manual.

Warning

--initialize-insecure is not to be used on production environments and is only used here since deploying a secure server is out of this tutorial scope.

Configuring

Before addressing the configuration options that one must set up to be able to install, load and use the MySQL Group Replication plugin, the reader is invited to become acquainted with the list of existing requirements for using Group Replication. Section Requirements and Limitations details each and every requirement. You do not need to go through that section right now, but the information in it motivates clearly part of the configuration options.

Server

Lets start by highlighting the classic server configuration. Unless stated otherwise, what follows is the configuration for the first instance in the group, i.e., server named s1.

1
2
3
4
5
6
7
8
[mysqld]

# server configuration
datadir=<full_path_to_data>/data/s1
basedir=<full_path_to_bin>/mysql-5.7/

port=24801
socket=<full_path_to_sock_dir>/s1.sock

This piece of configuration tells the server some important things, such as where the data files are located and also which port should the server open and start listening for incoming connections. Now lets configure the MySQL replication framework. We must do it in such a way that we meet the MySQL Group Replication requirements.

Replication Framework

1
2
3
4
5
6
7
8
9
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW

This piece of information tells the server that its replication unique identifier is number 1. It also tells it that it needs to operate with global transaction identifiers turned on and to store replication metadata in system tables instead of files. Moreover, it instructs the server to turn on the binary log, use row based logging and disable binary log event checksums. Some of these are required by group replication. For more details on requirements and limitations, please, check out the Requirements and Limitations section.

Group Replication

At this point the server is configured and is instructed to instantiate the replication infrastructure under a given configuration. Next, one needs to tell the server how group replication will be configured.

1
2
3
4
5
6
7
8
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
loose-group_replication_start_on_boot=off
loose-group_replication_local_address= "127.0.0.1:24901"
loose-group_replication_group_seeds= "127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903"
loose-group_replication_bootstrap_group= off
loose-group_replication_single_primary_mode=FALSE
loose-group_replication_enforce_update_everywhere_checks= TRUE

Note

The loose- prefix for the group_replication variables instruct the server to continue to start if the plugin has not been loaded at the time the server is started (i.e., it does not recognize those options.)

  • Line 1 instructs the server that for each transaction it has to collect the write set and encode it as a hash using the XXHASH64 hashing algorithm.
  • Line 2 tells the plugin that the group that one is joining/creating is named “aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa”.
  • Line 3 instructs the plugin to not start operations automatically when the server starts.
  • Line 4 tells the plugin that it should use the port 24901 to accept incoming connections from other members in the group.

Note

The server will listen on this port for member-to-member connections. This port is not to be used by the user or user applications at all, it is just to be used for inter-communication between different members of the group while running the group replication protocol.

  • Line 5 tells the plugin that the following members on those hosts and ports should be contacted in case it needs to join the group. These are seed members, in the sense that when this member wants to connect to the group, it contacts one of them first (seed) and then the it will ask the group to reconfigure to allow the joiner to be accepted in the group. Note that this option does not list all members in the group, but rather a list of servers that should be contacted in case this server wishes to join the group.

Note

Note that the server that starts the group does not make use of this option, since it is the initial server and as such, it is in charge of bootstrapping the group. The second server joining, will ask the one and only member in the group to join and then the group will expand. The third one joining can ask any of the two to join, and then the group will expand again. The fourth, fifth and so on, will repeat this procedure when joining.

Warning

When joining multiple servers at the same time, make sure that they point to seed members that are already in the group. Do not set as as seeds, members that are also joining the group since they may not yet be in the group when contacted.

Warning

It is a good practice to start the bootstrap member first, and let it create the group. Then make it the seed member for the rest of the members that are joining. That way one is sure that there will already be a group formed when joining the rest of the members.

Creating a group and joining multiple members at the same time is not supported. It may work, but chances are that the operations will race and then the act of joining the group ends up in an error or a time out.

  • Line 6 instructs the plugin whether to boostrap the group or not.

Note

Note that this option should be used only once, the first time one bootstraps the group (or in case the entire group is brought down and back up again). If one bootstraps the group multiple times, i.e., if different servers have this option set, then they can effectively create an artificial split brain scenario, in which two distinct groups with the same name exist. Preferably one should disable after the first server becomes online.

  • Line 7 instructs the plugin to disable single_primary_mode.
  • Line 8 instructs the plugin to enable multi-master update everywhere extra checks.

Configuration for all three servers is quite similar. One needs to change the specifics about each server (server-id, datadir, group_replication_local_address), but for the most part, it is pretty much the same.

User Credentials

Group Replication relies on the asynchronous replication protocol to be able to run distributed recovery. As such, one needs to setup a replication user to be able to establish direct member-to-member recovery channels.

As such, one needs to start the server:

1
mysql-5.7/bin/mysqld --defaults-file=data/s1/s1.cnf

And then create a user and save the credentials for the recovery channel.

1
2
3
4
5
6
SET SQL_LOG_BIN=0;
CREATE USER rpl_user@'%';
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%' IDENTIFIED BY 'rpl_pass';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='rpl_pass' FOR CHANNEL 'group_replication_recovery';
  • Lines 1-5 instruct the server to create the recovery user.
  • Line 6 configures this server to use the given credentials next time it needs to recovery state from another member.
    • Distributed recovery is the first step of any server that joins the group, thus, if credentials are not set properly, the server will not be able to run the recovery protocol, and ultimately it will not be able to join the group.

Launching

Assuming that server s1 has already been configured and started as instructed above, one shall then install the plugin. By opening a session to the server, one issues the following command:

1
INSTALL PLUGIN group_replication SONAME 'group_replication.so';

This command must have succeeded. You can check that the plugin was installed successfully by issuing SHOW PLUGINS; and look at the output. It should show something like this:

mysql> SHOW PLUGINS;
+----------------------------+----------+--------------------+----------------------+-------------+
| Name                       | Status   | Type               | Library              | License     |
+----------------------------+----------+--------------------+----------------------+-------------+
| binlog                     | ACTIVE   | STORAGE ENGINE     | NULL                 | PROPRIETARY |

(...)

| group_replication          | ACTIVE   | GROUP REPLICATION  | group_replication.so | PROPRIETARY |
+----------------------------+----------+--------------------+----------------------+-------------+

To start the group, the only thing left to do is to instruct server s1 to bootstrap the group and then start group replication. Mind you that bootstrap should only be done by a single server, the one that starts the group and only once. Note also the value of the bootstrap configuration option was not persisted in the configuration file. Had it been persisted, the server could crash and restart and automatically bootstrap a second group with the same name. This would result in two distinct groups with the same name. The same reasoning applies to stopping and restarting the plugin with this option set to ON.

1
2
3
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

Once the START GROUP_REPLICATION command returns, the group must have been started. You can see that the group is now created and that there is one member in it:

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE  |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| group_replication_applier | ce9be252-2b71-11e6-b8f4-00212844f856 | myhost       |       24800 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
1 row in set (0,00 sec)

The information in this table tells the user that there is a member in the group with the unique identifier ce9be252-2b71-11e6-b8f4-00212844f856, that it is ONLINE and is at myhost listening for mysql client connections on port 24800.

For the purpose of demonstrating that the server is indeed in a group and that it is able to handle load, lets create a table and add some content to it.

mysql> CREATE DATABASE test;
Query OK, 1 row affected (0,00 sec)

mysql> use test
Database changed
mysql> CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL);
Query OK, 0 rows affected (0,00 sec)

mysql> INSERT INTO t1 VALUES (1, 'Luis');
Query OK, 1 row affected (0,01 sec)

Now lets check the content of the table and the binary log.

mysql> SELECT * FROM t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
1 row in set (0,00 sec)

mysql> SHOW BINLOG EVENTS;
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name      | Pos | Event_type     | Server_id | End_log_pos | Info                                                               |
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
| binlog.000001 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.14-labs-gr080-log, Binlog ver: 4                   |
| binlog.000001 | 123 | Previous_gtids |         1 |         150 |                                                                    |
| binlog.000001 | 150 | Gtid           |         1 |         211 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'  |
| binlog.000001 | 211 | Query          |         1 |         270 | BEGIN                                                              |
| binlog.000001 | 270 | View_change    |         1 |         369 | view_id=14724817264259180:1                                        |
| binlog.000001 | 369 | Query          |         1 |         434 | COMMIT                                                             |
| binlog.000001 | 434 | Gtid           |         1 |         495 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2'  |
| binlog.000001 | 495 | Query          |         1 |         585 | CREATE DATABASE test                                               |
| binlog.000001 | 585 | Gtid           |         1 |         646 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3'  |
| binlog.000001 | 646 | Query          |         1 |         770 | use `test`; CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL) |
| binlog.000001 | 770 | Gtid           |         1 |         831 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4'  |
| binlog.000001 | 831 | Query          |         1 |         899 | BEGIN                                                              |
| binlog.000001 | 899 | Table_map      |         1 |         942 | table_id: 108 (test.t1)                                            |
| binlog.000001 | 942 | Write_rows     |         1 |         984 | table_id: 108 flags: STMT_END_F                                    |
| binlog.000001 | 984 | Xid            |         1 |        1011 | COMMIT /* xid=38 */                                                |
+---------------+-----+----------------+-----------+-------------+--------------------------------------------------------------------+
15 rows in set (0,00 sec)

As one can see, the database and the table objects were created and their corresponding DDL statements were written to the binary log. Also, the data was inserted into the table and written to the binary log. The importance of the binary log entries will become clearer in the next section as one grows the group and distributed recovery is executed as new members try to catch up and become online.

Growing the Group

At this point, one has a group with one member in it, server s1, which has some data in it. It is now time to expand the group by adding the other two servers configured.

Adding a Second Server

In order to add a second server, server s2, one needs to first create the configuration file for it. Note that the configuration is pretty much similar to the one used for server s1, except for some obvious things, like the location of the data directory or the ports that s2 is going to be listening on as well as its server-id. These lines are highlighted on the listing below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[mysqld]

# server configuration
datadir=<full_path_to_data>/data/s2
basedir=<full_path_to_bin>/mysql-5.7/

port=24802
socket=<full_path_to_sock_dir>/s2.sock

#
# Replication configuration parameters
#
server_id=2
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW

#
# Group Replication configuration
#
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
loose-group_replication_start_on_boot=off
loose-group_replication_local_address= "127.0.0.1:24902"
loose-group_replication_group_seeds= "127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903"
loose-group_replication_bootstrap_group= off
loose-group_replication_single_primary_mode=FALSE
loose-group_replication_enforce_update_everywhere_checks= TRUE

Just like one did for server s1, now that the configuration file is written down, lets launch the server.

1
mysql-5.7/bin/mysqld --defaults-file=data/s2/s2.cnf

And configure the recovery credentials. The next illustrates how an actual execution would look like. The commands are the same used when setting up server s1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
mysql> SET SQL_LOG_BIN=0;
Query OK, 0 rows affected (0,00 sec)

mysql> CREATE USER rpl_user@'%';
Query OK, 0 rows affected (0,00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%' IDENTIFIED BY 'rpl_pass';
Query OK, 0 rows affected, 1 warning (0,00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0,00 sec)

mysql> SET SQL_LOG_BIN=1;
Query OK, 0 rows affected (0,00 sec)

mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='rpl_pass' FOR CHANNEL 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0,01 sec)

Great! Now all that remains is to install the group replication plugin and start join the server to the group. The following installs the plugin. Again, nothing new by now, the command is the same used while deploying server s1.

1
2
mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';
Query OK, 0 rows affected (0,01 sec)

Finally, lets add server s2 to the group.

1
2
mysql> START GROUP_REPLICATION;
Query OK, 0 rows affected (44,88 sec)

Now, here one may realize that there is something different when comparing this step to when one started server s1. The only difference is that one did not issue SET GLOBAL group_replication_bootstrap_group=ON; before starting the group, because the group had already been created and bootstrapped by server s1. At this point in server s2, one only needs to add it to the group.

Checking again the table performance_schema.replication_group_members one can find that there are now two ONLINE servers in the group.

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE  |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| group_replication_applier | 395409e1-6dfa-11e6-970b-00212844f856 | myhost       |       24801 | ONLINE       |
| group_replication_applier | ac39f1e6-6dfa-11e6-a69d-00212844f856 | myhost       |       24802 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
2 rows in set (0,00 sec)

Moreover, since server s2 is marked as ONLINE, it must have already caught up with server s1 automatically. Lets verify that it has indeed synchronized with server s1.

mysql> SHOW DATABASES LIKE 'test';
+-----------------+
| Database (test) |
+-----------------+
| test            |
+-----------------+
1 row in set (0,00 sec)

mysql> SELECT * FROM test.t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
1 row in set (0,00 sec)

mysql> SHOW BINLOG EVENTS;
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name      | Pos  | Event_type     | Server_id | End_log_pos | Info                                                               |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| binlog.000001 |    4 | Format_desc    |         2 |         123 | Server ver: 5.7.14-labs-gr080-log, Binlog ver: 4                   |
| binlog.000001 |  123 | Previous_gtids |         2 |         150 |                                                                    |
| binlog.000001 |  150 | Gtid           |         1 |         211 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'  |
| binlog.000001 |  211 | Query          |         1 |         270 | BEGIN                                                              |
| binlog.000001 |  270 | View_change    |         1 |         369 | view_id=14724832985483517:1                                        |
| binlog.000001 |  369 | Query          |         1 |         434 | COMMIT                                                             |
| binlog.000001 |  434 | Gtid           |         1 |         495 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2'  |
| binlog.000001 |  495 | Query          |         1 |         585 | CREATE DATABASE test                                               |
| binlog.000001 |  585 | Gtid           |         1 |         646 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3'  |
| binlog.000001 |  646 | Query          |         1 |         770 | use `test`; CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL) |
| binlog.000001 |  770 | Gtid           |         1 |         831 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4'  |
| binlog.000001 |  831 | Query          |         1 |         890 | BEGIN                                                              |
| binlog.000001 |  890 | Table_map      |         1 |         933 | table_id: 108 (test.t1)                                            |
| binlog.000001 |  933 | Write_rows     |         1 |         975 | table_id: 108 flags: STMT_END_F                                    |
| binlog.000001 |  975 | Xid            |         1 |        1002 | COMMIT /* xid=30 */                                                |
| binlog.000001 | 1002 | Gtid           |         1 |        1063 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5'  |
| binlog.000001 | 1063 | Query          |         1 |        1122 | BEGIN                                                              |
| binlog.000001 | 1122 | View_change    |         1 |        1261 | view_id=14724832985483517:2                                        |
| binlog.000001 | 1261 | Query          |         1 |        1326 | COMMIT                                                             |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
19 rows in set (0,00 sec)

As one can see, the second server has been added to the group and it has replicated the changes from server s1 automatically. According to the distributed recovery procedure, this means that just after joining the group and right before being declared online, servers s2 has connected to server s1 automatically and fetched the missing data from it. In other words, it copied transactions from the binary log of s1, those that it was missing, up to the point in time that it had joined the group.

Adding a Third Server

Adding a third server is pretty much the same sequence of steps as to add the second server except that the configuration has to be changed as it had to be for server s2. Lets do it quickly and just by listing the commands this time.

1. Create the configuration file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[mysqld]

# server configuration
datadir=<full_path_to_data>/data/s3
basedir=<full_path_to_bin>/mysql-5.7/

port=24802
socket=<full_path_to_sock_dir>/s3.sock

#
# Replication configuration parameters
#
server_id=3
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW

#
# Group Replication configuration
#
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
loose-group_replication_start_on_boot=off
loose-group_replication_local_address= "127.0.0.1:24903"
loose-group_replication_group_seeds= "127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903"
loose-group_replication_bootstrap_group= off
loose-group_replication_single_primary_mode=FALSE
loose-group_replication_enforce_update_everywhere_checks= TRUE

2. Start the server

1
mysql-5.7/bin/mysqld --defaults-file=data/s3/s3.cnf

3. Configure the recovery credentials for the group_replication_recovery channel.

1
2
3
4
5
6
SET SQL_LOG_BIN=0;
CREATE USER rpl_user@'%';
GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%' IDENTIFIED BY 'rpl_pass';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='rpl_pass' FOR CHANNEL 'group_replication_recovery';

4. Install the group replication plugin and start it.

1
2
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
START GROUP_REPLICATION;

By now, server s3 is up and running, joined the group and has caught up. Consulting again the table performance_schema.replication_group_members one will find:

mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE  |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
| group_replication_applier | 395409e1-6dfa-11e6-970b-00212844f856 | myhost       |       24801 | ONLINE       |
| group_replication_applier | 7eb217ff-6df3-11e6-966c-00212844f856 | myhost       |       24803 | ONLINE       |
| group_replication_applier | ac39f1e6-6dfa-11e6-a69d-00212844f856 | myhost       |       24802 | ONLINE       |
+---------------------------+--------------------------------------+-------------+-------------+---------------+
3 rows in set (0,00 sec)

Issuing this same query on server s2 or server s1 will yield the same result. Also, you can verify that server s3 has also caught up:

mysql> SHOW DATABASES LIKE 'test';
+-----------------+
| Database (test) |
+-----------------+
| test            |
+-----------------+
1 row in set (0,00 sec)

mysql> SELECT * FROM test.t1;
+----+------+
| c1 | c2   |
+----+------+
|  1 | Luis |
+----+------+
1 row in set (0,00 sec)

mysql>  SHOW BINLOG EVENTS;
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| Log_name      | Pos  | Event_type     | Server_id | End_log_pos | Info                                                               |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
| binlog.000001 |    4 | Format_desc    |         3 |         123 | Server ver: 5.7.14-labs-gr080-log, Binlog ver: 4                   |
| binlog.000001 |  123 | Previous_gtids |         3 |         150 |                                                                    |
| binlog.000001 |  150 | Gtid           |         1 |         211 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:1'  |
| binlog.000001 |  211 | Query          |         1 |         270 | BEGIN                                                              |
| binlog.000001 |  270 | View_change    |         1 |         369 | view_id=14724832985483517:1                                        |
| binlog.000001 |  369 | Query          |         1 |         434 | COMMIT                                                             |
| binlog.000001 |  434 | Gtid           |         1 |         495 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:2'  |
| binlog.000001 |  495 | Query          |         1 |         585 | CREATE DATABASE test                                               |
| binlog.000001 |  585 | Gtid           |         1 |         646 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:3'  |
| binlog.000001 |  646 | Query          |         1 |         770 | use `test`; CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 TEXT NOT NULL) |
| binlog.000001 |  770 | Gtid           |         1 |         831 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:4'  |
| binlog.000001 |  831 | Query          |         1 |         890 | BEGIN                                                              |
| binlog.000001 |  890 | Table_map      |         1 |         933 | table_id: 108 (test.t1)                                            |
| binlog.000001 |  933 | Write_rows     |         1 |         975 | table_id: 108 flags: STMT_END_F                                    |
| binlog.000001 |  975 | Xid            |         1 |        1002 | COMMIT /* xid=29 */                                                |
| binlog.000001 | 1002 | Gtid           |         1 |        1063 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:5'  |
| binlog.000001 | 1063 | Query          |         1 |        1122 | BEGIN                                                              |
| binlog.000001 | 1122 | View_change    |         1 |        1261 | view_id=14724832985483517:2                                        |
| binlog.000001 | 1261 | Query          |         1 |        1326 | COMMIT                                                             |
| binlog.000001 | 1326 | Gtid           |         1 |        1387 | SET @@SESSION.GTID_NEXT= 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:6'  |
| binlog.000001 | 1387 | Query          |         1 |        1446 | BEGIN                                                              |
| binlog.000001 | 1446 | View_change    |         1 |        1585 | view_id=14724832985483517:3                                        |
| binlog.000001 | 1585 | Query          |         1 |        1650 | COMMIT                                                             |
+---------------+------+----------------+-----------+-------------+--------------------------------------------------------------------+
23 rows in set (0,00 sec)

Now that the reader has learned how to setup a replication group with three servers in it, it is a good time to learn how to monitor the group more closely. The next Chapter explains the group replication related performance schema tables.