├── LICENSE ├── README.md ├── docker-replication ├── README.md ├── common.sh ├── deploy-nodes-restart.sh ├── deploy-nodes.sh ├── doc_store.sh ├── fake_uuid ├── group-replication │ ├── gr.sh │ └── gr_test.sh ├── home_my.cnf ├── my-gtid.cnf ├── my-minimal.cnf ├── my-template.cnf ├── remove-nodes.sh ├── safebox.sh ├── set-replication-multisource.sh ├── set-replication-simple.sh ├── simple │ ├── my-master.cnf │ ├── my-slave.cnf │ ├── mysql-2node-install.sh │ ├── remove-2node.sh │ └── set-2node-replication.sh ├── single_box.sh ├── start_percona.sh └── two_boxes.sh ├── general-replication ├── measuring-replication-speed.pl └── poor-man-monitoring.sh ├── group-replication ├── data │ ├── input_data.sql │ ├── user.sql │ └── user2.sql ├── gr.sh ├── gr_clean.sh ├── gr_start.sh ├── gr_test.sh ├── my-template.cnf └── start_gr.sql ├── images ├── circular_replication.jpg ├── fan-in.jpg ├── hybrid.png ├── master_slave.jpg ├── point-to-point-all_masters.jpg └── star.png ├── multi-master-comparison ├── README.md ├── mm_gr.sh ├── mm_ms.sh └── mm_test.sh ├── multi_source ├── multi_source.sh ├── sample_runs.md ├── test_all_masters_replication.sh └── test_multi_source_replication.sh ├── parallel_replication ├── checksum.sh ├── insert_many.sh ├── install_parallel.sh ├── multi_inserts.sh └── multi_inserts_one_db.sh ├── replication-skeptic-roundup.pdf ├── semi-synchronous └── make_semi_synchronous_replication.sh └── star-and-hybrid ├── common.sh ├── hybrid-data-flow.txt ├── set-hybrid.sh ├── set_star_topology.sh └── star-change-hub.sh /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## mysql-replication-samples 2 | A collection of tools for deploying and testing replication topologies with MySQL and MariaDB 3 | 4 | The files in this collection are here to help beginners who want to start using advanced features like multi-source replication and test how they work. 5 | They are based on [MySQL Sandbox](http://mysqlsandbox.net), with the goal that they will be eventually integrated in that project. 6 | 7 | ### FILES 8 | 9 | * multi_source.sh can create bith a FAN-IN or ALL-MASTERS topology using both MySQL 5.7 or MariaDB 10 10 | * test_multi_source_replication.sh tests the fan-in scenario 11 | * test_all_masters_replication.sh tests the all_masters scenario 12 | * common.sh a collection of useful routines 13 | * set_star_topology.sh Creates a star topology 14 | * star-change-hub.sh A proof-of-concept script that replaces the hub in a star topology 15 | * set-hybrid.sh A proof-of-concept script that installs a hybrid topology 16 | * parallel_replication/* Set of scripts to test parallel replication 17 | 18 | ### VARIABLES 19 | 20 | The following variables can change the installation for multi_source.sh 21 | * SKIP_INSTALLATION (Will not install the sandbox, but assume that it is already there) 22 | * DRYRUN or DRY_RUN (Show the replication commands, but does not execute anything) 23 | 24 | ### Articles 25 | 26 | The following articles cover the material in these samples 27 | * [MySQL replication monitoring 101](http://datacharmer.blogspot.com/2015/07/mysql-replication-monitoring-101.html) 28 | * [MySQL replication in action - Part 1: GTID & Co](http://datacharmer.blogspot.com/2015/08/mysql-replication-in-action-part-1-gtid.html) 29 | * [MySQL replication in action - Part 2 - Fan-in topology](http://datacharmer.blogspot.com/2015/08/mysql-replication-in-action-part-2-fan.html) 30 | * [MySQL replication in action - Part 3 - All-masters P2P topology](http://datacharmer.blogspot.com/2015/08/mysql-replication-in-action-part-3-all.html) 31 | * [MySQL replication in action - Part 4 - star and hybrid topologies](http://datacharmer.blogspot.com/2015/08/mysql-replication-in-action-part-4-star.html) 32 | * [MySQL replication in action - Part 5 - parallel appliers](http://datacharmer.blogspot.com.es/2015/08/mysql-replication-in-action-part-5.html) 33 | 34 | -------------------------------------------------------------------------------- /docker-replication/README.md: -------------------------------------------------------------------------------- 1 | ## Experiments with MySQL and Docker 2 | 3 | These examples implement MySQL replication with Docker. Unlike most of the scripts in other directories of this repository, they don't use MySQL-Sandbox, but [Docker](https://www.docker.com/). 4 | 5 | ### A simple master-slave deployment 6 | 7 | * *simple/mysql-2node-install.sh* Deploys two MySQL nodes, without dedicated storage 8 | * *simple/set-2node-replication.sh* (invoked by mysql-2node-install.sh) Sets replication between two nodes 9 | * *simple/remove-2node.sh* Removes the two nodes 10 | * *simple/my-master.cnf* options file for master node 11 | * *simple/my-slave.cnf* options file for slave node 12 | 13 | ### A multi-node deployment 14 | 15 | Requires Docker 1.7+. 16 | 17 | Installs N nodes of MySQL, with dedicated storage and customized options file for each one. 18 | 19 | * *deploy-nodes.sh* is the main command. Invoke with ./deploy-nodes.sh [NUM_NODES] 20 | * *common.sh* contains common routines 21 | * *my-template.cnf* is the basis for the MySQL server templates 22 | * *set-replication.sh* (invoked by deploy-nodes.sh) 23 | * *remove-nodes.sh* removes the nodes that were deployed. Invoke with ./remove-nodes.sh [NUM_NODES] 24 | -------------------------------------------------------------------------------- /docker-replication/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DOCKER_TMP=$HOME/docker/tmp 4 | export DOCKER_DATA=$HOME/docker/mysql 5 | export DATA_VOLUME=YES 6 | export NETWORK_NAME=my_rep_net 7 | 8 | function check_operating_system 9 | { 10 | OS=$(uname -s) 11 | if [ "$OS" == "Linux" ] 12 | then 13 | DOCKER_TMP=/opt/docker/tmp 14 | DOCKER_DATA=/opt/docker/mysql 15 | else 16 | DATA_VOLUME=no 17 | fi 18 | } 19 | 20 | function normalized_version 21 | { 22 | v=$1 23 | PARTS=$(echo $v | tr '.' ' ' | wc -w) 24 | if [[ $PARTS -lt 3 ]] 25 | then 26 | echo "# Version '$v' should have 3 components. Found only $PARTS" 27 | exit 1 28 | fi 29 | V1=$(echo $v | tr '.' ' ' | awk '{print $1}') 30 | V2=$(echo $v | tr '.' ' ' | awk '{print $2}') 31 | V3=$(echo $v | tr '.' ' ' | awk '{print $3}') 32 | printf "%02d.%02d.%02d" $V1 $V2 $V3 33 | } 34 | 35 | function create_network 36 | { 37 | exist_network=$(docker network ls | grep $NETWORK_NAME) 38 | if [ -n "$exist_network" ] 39 | then 40 | echo "# network $NETWORK_NAME already exists" 41 | else 42 | docker network create $NETWORK_NAME 43 | fi 44 | docker network ls 45 | } 46 | 47 | function check_docker_version 48 | { 49 | [ -z "$MIN_DOCKER_VERSION" ] && MIN_DOCKER_VERSION=1.11.0 50 | DOCKER_VERSION=$(docker --version | perl -lne 'print $1 if /(\d+\.\d+\.\d+)/') 51 | DOCKER_NORMALIZED_VERSION=$(normalized_version $DOCKER_VERSION) 52 | MIN_DOCKER_NORMALIZED_VERSION=$(normalized_version $MIN_DOCKER_VERSION) 53 | 54 | # To check if the current version matches the requirement, we sort numerically both 55 | # the required version and the current one. Then we get the top one. 56 | # If top version that results from sorting is the current docker version, then 57 | # the check passes. If not, we need to upgrade docker 58 | MAX_VERSION=$((echo $MIN_DOCKER_NORMALIZED_VERSION ; echo $DOCKER_NORMALIZED_VERSION ) | sort -nr| head -1 ) 59 | 60 | echo -n "# Docker version " 61 | if [ "$MAX_VERSION" == "$DOCKER_NORMALIZED_VERSION" ] 62 | then 63 | echo ok 64 | else 65 | echo "not ok: wanted $MIN_DOCKER_VERSION - Found $DOCKER_VERSION" 66 | exit 1 67 | fi 68 | } 69 | 70 | function pause 71 | { 72 | delay=$1 73 | step=$2 74 | [ -z "$delay" ] && delay=30 75 | [ -z "$step" ] && step=5 76 | echo "# Sleeping $delay seconds ... " 77 | for N in $(seq 1 $delay) 78 | do 79 | MOD=$(($N%$step)) 80 | if [ "$MOD" == "0" ] 81 | then 82 | echo -n $N 83 | else 84 | echo -n '.' 85 | fi 86 | MOD=$(($N%80)) 87 | if [ "$MOD" == "0" ] 88 | then 89 | echo '' 90 | fi 91 | sleep 1 92 | done 93 | echo '' 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /docker-replication/deploy-nodes-restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -z "$MIN_DOCKER_VERSION" ] && export MIN_DOCKER_VERSION=1.7.0 4 | curdir=$(dirname $0) 5 | 6 | . $curdir/common.sh 7 | [ -z "$MYSQL_IMAGE" ] && MYSQL_IMAGE=mysql/mysql-server 8 | 9 | check_docker_version 10 | check_operating_system 11 | 12 | NUM_NODES=$1 13 | if [ -z "$NUM_NODES" ] 14 | then 15 | NUM_NODES=3 16 | fi 17 | 18 | if [ $NUM_NODES -gt 20 ] 19 | then 20 | echo "# Can't run more than 20 nodes" 21 | exit 1 22 | fi 23 | 24 | for NODE in $( seq 1 $NUM_NODES ) 25 | do 26 | export NODE 27 | echo "# $NODE" 28 | if [ -f $DOCKER_TMP/my_$NODE.cnf ] 29 | then 30 | rm $DOCKER_TMP/my_$NODE.cnf 31 | fi 32 | if [ -f $DOCKER_TMP/home_my_$NODE.cnf ] 33 | then 34 | rm $DOCKER_TMP/home_my_$NODE.cnf 35 | fi 36 | sed "s/_SERVERID_/${NODE}00/" < my-template.cnf > $DOCKER_TMP/my_$NODE.cnf 37 | cp home_my.cnf $DOCKER_TMP/home_my_$NODE.cnf 38 | echo "[mysql]" >> $DOCKER_TMP/home_my_$NODE.cnf 39 | # echo "prompt=node$NODE >> " >> $DOCKER_TMP/home_my_$NODE.cnf 40 | if [ "$NODE" == "1" ] 41 | then 42 | NAME=master 43 | else 44 | NAME="node$NODE" 45 | fi 46 | echo "prompt='$NAME [\\h] {\\u} (\\d) > '" >> $DOCKER_TMP/home_my_$NODE.cnf 47 | if [ "$DATA_VOLUME" == "YES" ] 48 | then 49 | if [ ! -d $DOCKER_DATA ] 50 | then 51 | mkdir -p $DOCKER_DATA 52 | sudo chown -R mysql $DOCKER_DATA 53 | sudo chgrp -R mysql $DOCKER_DATA 54 | fi 55 | if [ -d $DOCKER_DATA/node_$NODE ] 56 | then 57 | sudo rm -rf $DOCKER_DATA/node_$NODE 58 | fi 59 | DATA_OPTION="-v $DOCKER_DATA/node_$NODE:/var/lib/mysql" 60 | else 61 | DATA_OPTION="" 62 | fi 63 | echo "" 64 | echo "# Deploying $MYSQL_IMAGE into container mysql-node$NODE" 65 | docker run --name mysql-node$NODE \ 66 | -v $DOCKER_TMP/my_$NODE.cnf:/etc/my_second.cnf \ 67 | -v $DOCKER_TMP/home_my_$NODE.cnf:/root/home_my.cnf \ 68 | -e MYSQL_ROOT_PASSWORD=secret $DATA_OPTION \ 69 | -d $MYSQL_IMAGE 70 | 71 | if [ "$?" != "0" ] ; then exit 1; fi 72 | done 73 | 74 | function is_ready 75 | { 76 | NODE=$1 77 | MYSQL="docker exec -it mysql-node$NODE mysql --defaults-file=/root/home_my.cnf " 78 | # 'docker exec' leaves a trailing newline in the result 79 | READY=$($MYSQL -BN -e 'select 12345' | tr -d '\n' | tr -d '\r') 80 | if [ "$READY" == "12345" ] 81 | then 82 | echo OK 83 | fi 84 | } 85 | 86 | echo "# Waiting for nodes to be ready" 87 | 88 | pause 10 89 | for NODE in $( seq 1 $NUM_NODES ) 90 | do 91 | MAX_ATTEMPTS=30 92 | ATTEMPTS=0 93 | node_ready='' 94 | echo "# Checking container mysql-node$NODE" 95 | while [ "$node_ready" != "OK" ] 96 | do 97 | ATTEMPTS=$(($ATTEMPTS+1)) 98 | if [[ $ATTEMPTS -gt $MAX_ATTEMPTS ]] 99 | then 100 | echo "## Maximum number of attempts exceeded " 101 | exit 1 102 | fi 103 | node_ready=$(is_ready $NODE) 104 | echo "# NODE $NODE - $ATTEMPTS - $node_ready" 105 | [ "$node_ready" != "OK" ] && sleep 1 106 | done 107 | echo '' 108 | done 109 | 110 | echo $NUM_NODES > DEPLOYED 111 | 112 | for NODE in $( seq 1 $NUM_NODES ) 113 | do 114 | echo '#!/bin/bash' > n$NODE 115 | echo "docker exec -it mysql-node$NODE mysql \"\$@\"" > n$NODE 116 | chmod +x n$NODE 117 | if [ "$NODE" == "1" ] 118 | then 119 | ln -s n1 m 120 | else 121 | SN=$(($NODE-1)) 122 | ln -s n$NODE s$SN 123 | fi 124 | # 125 | # Set username and password in private file 126 | # Notice that this operation cannot happen before MySQL initialization 127 | echo "# Reconfiguring server in mysql-node$NODE" 128 | docker exec -it mysql-node$NODE cp /root/home_my.cnf /root/.my.cnf 129 | docker exec -it mysql-node$NODE cp /etc/my_second.cnf /etc/my.cnf 130 | docker restart mysql-node$NODE 131 | done 132 | 133 | if [ -n "$SKIP_REPLICATION" ] 134 | then 135 | echo "# Skipping replication setup" 136 | exit 137 | fi 138 | ./set-replication.sh 139 | -------------------------------------------------------------------------------- /docker-replication/deploy-nodes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -z "$MAX_ATTEMPTS" ] && export MAX_ATTEMPTS=30 4 | 5 | [ -z "$MIN_DOCKER_VERSION" ] && export MIN_DOCKER_VERSION=1.11.0 6 | curdir=$(dirname $0) 7 | 8 | . $curdir/common.sh 9 | [ -z "$MYSQL_IMAGE" ] && MYSQL_IMAGE=mysql/mysql-server 10 | 11 | check_docker_version 12 | check_operating_system 13 | create_network 14 | 15 | NUM_NODES=$1 16 | if [ -z "$NUM_NODES" ] 17 | then 18 | NUM_NODES=3 19 | fi 20 | 21 | if [ $NUM_NODES -gt 20 ] 22 | then 23 | echo "# Can't run more than 20 nodes" 24 | exit 1 25 | fi 26 | 27 | if [ ! -d $DOCKER_TMP ] 28 | then 29 | mkdir -p $DOCKER_TMP 30 | fi 31 | if [ ! -d $DOCKER_DATA ] 32 | then 33 | mkdir -p $DOCKER_DATA 34 | fi 35 | 36 | for NODE in $( seq 1 $NUM_NODES ) 37 | do 38 | export NODE 39 | echo "# $NODE" 40 | if [ -f $DOCKER_TMP/my_$NODE.cnf ] 41 | then 42 | rm $DOCKER_TMP/my_$NODE.cnf 43 | fi 44 | if [ -f $DOCKER_TMP/home_my_$NODE.cnf ] 45 | then 46 | rm $DOCKER_TMP/home_my_$NODE.cnf 47 | fi 48 | sed "s/_SERVERID_/${NODE}00/" < my-template.cnf > $DOCKER_TMP/my_$NODE.cnf 49 | cp home_my.cnf $DOCKER_TMP/home_my_$NODE.cnf 50 | echo "[mysql]" >> $DOCKER_TMP/home_my_$NODE.cnf 51 | # echo "prompt=node$NODE >> " >> $DOCKER_TMP/home_my_$NODE.cnf 52 | if [ "$NODE" == "1" ] 53 | then 54 | NAME=master 55 | else 56 | NAME="node$NODE" 57 | fi 58 | echo "prompt='$NAME [\\h] {\\u} (\\d) > '" >> $DOCKER_TMP/home_my_$NODE.cnf 59 | if [ "$DATA_VOLUME" == "YES" ] 60 | then 61 | if [ ! -d $DOCKER_DATA ] 62 | then 63 | mkdir -p $DOCKER_DATA 64 | sudo chown -R mysql $DOCKER_DATA 65 | sudo chgrp -R mysql $DOCKER_DATA 66 | fi 67 | if [ -d $DOCKER_DATA/node_$NODE ] 68 | then 69 | sudo rm -rf $DOCKER_DATA/node_$NODE 70 | fi 71 | DATA_OPTION="-v $DOCKER_DATA/node_$NODE:/var/lib/mysql" 72 | else 73 | DATA_OPTION="" 74 | fi 75 | echo "" 76 | echo "# Deploying $MYSQL_IMAGE into container mysql-node$NODE" 77 | docker run --net $NETWORK_NAME \ 78 | --name mysql-node$NODE \ 79 | --hostname $NAME \ 80 | -v $DOCKER_TMP/my_$NODE.cnf:/etc/my.cnf \ 81 | -v $DOCKER_TMP/home_my_$NODE.cnf:/root/home_my.cnf \ 82 | -e MYSQL_ROOT_PASSWORD=secret $DATA_OPTION \ 83 | -d $MYSQL_IMAGE 84 | 85 | if [ "$?" != "0" ] ; then exit 1; fi 86 | done 87 | 88 | function is_ready 89 | { 90 | NODE=$1 91 | MYSQL="docker exec -it mysql-node$NODE mysql --defaults-file=/root/home_my.cnf " 92 | # 'docker exec' leaves a trailing newline in the result 93 | READY=$($MYSQL -BN -e 'select 12345' | tr -d '\n' | tr -d '\r') 94 | if [ "$READY" == "12345" ] 95 | then 96 | echo OK 97 | fi 98 | } 99 | 100 | echo "# Waiting for nodes to be ready" 101 | 102 | pause 10 103 | for NODE in $( seq 1 $NUM_NODES ) 104 | do 105 | ATTEMPTS=0 106 | node_ready='' 107 | echo "# Checking container mysql-node$NODE" 108 | while [ "$node_ready" != "OK" ] 109 | do 110 | ATTEMPTS=$(($ATTEMPTS+1)) 111 | if [[ $ATTEMPTS -gt $MAX_ATTEMPTS ]] 112 | then 113 | echo "## Maximum number of attempts exceeded " 114 | exit 1 115 | fi 116 | node_ready=$(is_ready $NODE) 117 | echo "# NODE $NODE - $ATTEMPTS - $node_ready" 118 | [ "$node_ready" != "OK" ] && sleep 1 119 | done 120 | echo '' 121 | done 122 | 123 | echo $NUM_NODES > DEPLOYED 124 | 125 | for NODE in $( seq 1 $NUM_NODES ) 126 | do 127 | echo '#!/bin/bash' > n$NODE 128 | echo "docker exec -it mysql-node$NODE mysql \"\$@\"" > n$NODE 129 | chmod +x n$NODE 130 | if [ "$NODE" == "1" ] 131 | then 132 | ln -s n1 m 133 | else 134 | SN=$(($NODE-1)) 135 | ln -s n$NODE s$SN 136 | fi 137 | # 138 | # Set username and password in private file 139 | # Notice that this operation cannot happen before MySQL initialization 140 | docker exec -it mysql-node$NODE cp /root/home_my.cnf /root/.my.cnf 141 | done 142 | 143 | if [ -n "$SKIP_REPLICATION" ] 144 | then 145 | echo "# Skipping replication setup" 146 | exit 147 | fi 148 | 149 | [ -z "$TOPOLOGY" ] && TOPOLOGY=simple 150 | 151 | if [ ! ./set-replication-$TOPOLOGY.sh ] 152 | then 153 | echo " file ./set-replication-$TOPOLOGY.sh not found" 154 | exit 1 155 | fi 156 | 157 | ./set-replication-$TOPOLOGY.sh 158 | -------------------------------------------------------------------------------- /docker-replication/doc_store.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exists_net=$(docker network ls | grep -w docnet ) 4 | if [ -n "$exists_net" ] 5 | then 6 | docker network rm docnet 7 | fi 8 | docker network create docnet 9 | docker network ls 10 | 11 | (set -x 12 | docker run --name mybox --net docnet \ 13 | -e MYSQL_ROOT_PASSWORD=secret -d \ 14 | -v $HOME/data:/data \ 15 | mysql/mysql-server --plugin-load=mysqlx:mysqlx.so 16 | ) 17 | echo "waiting 20 seconds" 18 | sleep 20 19 | (set -x 20 | docker exec -ti mybox mysql -psecret -e 'source /data/world_x-db/world_x.sql' 21 | docker run --name myshell --rm --net docnet -ti mysql/shell -h mybox -u root -psecret world_x 22 | ) 23 | -------------------------------------------------------------------------------- /docker-replication/fake_uuid: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | server_id=$1 4 | sequence=$2 5 | 6 | if [ -z "$sequence" ] 7 | then 8 | echo "Syntax: $0 server_id sequence" 9 | exit 1 10 | fi 11 | 12 | function number 13 | { 14 | NUMBER=$1 15 | wanted=12 16 | OUT='' 17 | if [[ $NUMBER -gt 99 ]] 18 | then 19 | printf "%012d" $NUMBER 20 | return 21 | elif [[ $NUMBER -gt 9 ]] 22 | then 23 | wanted=$(echo "$wanted/2" | bc) 24 | fi 25 | for N in $(seq 1 $wanted) 26 | do 27 | OUT="$OUT$NUMBER" 28 | done 29 | echo $OUT 30 | } 31 | 32 | printf "%08d-%04d-%04d-%04d-%12s\n" $server_id 0 0 0 $(number $sequence ) 33 | 34 | -------------------------------------------------------------------------------- /docker-replication/group-replication/gr.sh: -------------------------------------------------------------------------------- 1 | exists_net=$(docker network ls | grep -w group1 ) 2 | if [ -n "$exists_net" ] 3 | then 4 | docker network rm group1 5 | fi 6 | docker network create group1 7 | docker network ls 8 | 9 | docker run -d --name=node1 --net=group1 \ 10 | -e MYSQL_ROOT_PASSWORD=aTestPwd \ 11 | -e MYSQL_REPLICATION_USER=rpl_user \ 12 | -e MYSQL_REPLICATION_PASSWORD=rpl_pass \ 13 | mysql/mysql-gr \ 14 | --group_replication_group_seeds='node2:6606,node3:6606' --server-id=1 15 | 16 | docker run -d --name=node2 --net=group1 \ 17 | -e MYSQL_ROOT_PASSWORD=aTestPwd \ 18 | -e MYSQL_REPLICATION_USER=rpl_user \ 19 | -e MYSQL_REPLICATION_PASSWORD=rpl_pass \ 20 | mysql/mysql-gr \ 21 | --group_replication_group_seeds='node1:6606,node3:6606' --server-id=2 22 | 23 | docker run -d --name=node3 --net=group1 \ 24 | -e MYSQL_ROOT_PASSWORD=aTestPwd \ 25 | -e MYSQL_REPLICATION_USER=rpl_user \ 26 | -e MYSQL_REPLICATION_PASSWORD=rpl_pass \ 27 | mysql/mysql-gr \ 28 | --group_replication_group_seeds='node1:6606,node2:6606' --server-id=3 29 | 30 | -------------------------------------------------------------------------------- /docker-replication/group-replication/gr_test.sh: -------------------------------------------------------------------------------- 1 | 2 | function exec_node 3 | { 4 | node_num=$1 5 | query="$2" 6 | docker exec -ti node$node_num mysql -paTestPwd -ve "$query" 7 | } 8 | 9 | function pause 10 | { 11 | how_long=$1 12 | for J in $(seq 1 $how_long) 13 | do 14 | printf "." 15 | sleep 1 16 | done 17 | echo '' 18 | } 19 | 20 | for N in 1 2 3; do exec_node $N "select * from performance_schema.replication_group_members\G" ; done 21 | echo "# press enter" 22 | read dummy 23 | exec_node 1 'create schema if not exists test' 24 | 25 | pause 5 26 | for N in 1 2 3; do exec_node $N 'select @@server_id; show schemas;' ; done 27 | for N in 1 2 3; do exec_node $N 'select @@server_id; show schemas;' ; done 28 | pause 5 29 | for N in 1 2 3; do exec_node $N "create table test.t$N(id int not null primary key)" ; done 30 | pause 5 31 | for N in 1 2 3; do exec_node $N "insert into test.t$N values ($N)" ; done 32 | pause 5 33 | for N in 1 2 3; do exec_node $N 'select @@server_id; show tables from test;' ; done 34 | 35 | -------------------------------------------------------------------------------- /docker-replication/home_my.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | user=root 3 | password=secret 4 | 5 | -------------------------------------------------------------------------------- /docker-replication/my-gtid.cnf: -------------------------------------------------------------------------------- 1 | # Sample my.cnf used to enable GTID 2 | 3 | [mysqld] 4 | user = mysql 5 | port = 3306 6 | log-bin = mysql-bin 7 | relay-log = mysql-relay 8 | server-id = 12345 9 | 10 | master-info-repository=table 11 | relay-log-info-repository=table 12 | gtid_mode=ON 13 | enforce-gtid-consistency 14 | -------------------------------------------------------------------------------- /docker-replication/my-minimal.cnf: -------------------------------------------------------------------------------- 1 | 2 | [mysqld] 3 | user=mysql 4 | log-bin = mysql-bin 5 | server-id = 100 6 | -------------------------------------------------------------------------------- /docker-replication/my-template.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | user = mysql 3 | port = 3306 4 | log-bin = mysql-bin 5 | relay-log = mysql-relay 6 | server-id = _SERVERID_ 7 | 8 | log-error=/var/log/mysqld.log 9 | pid-file=/var/run/mysqld/mysqld.pid 10 | 11 | master-info-repository=table 12 | relay-log-info-repository=table 13 | gtid_mode=ON 14 | # log-slave-updates 15 | enforce-gtid-consistency 16 | -------------------------------------------------------------------------------- /docker-replication/remove-nodes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curdir=$(dirname $0) 4 | 5 | . $curdir/common.sh 6 | 7 | check_operating_system 8 | 9 | if [ -f DEPLOYED ] 10 | then 11 | NUM_NODES=$(cat DEPLOYED) 12 | else 13 | echo "#File 'DEPLOYED' not found. Assuming 3 nodes" 14 | NUM_NODES=3 15 | fi 16 | 17 | for NODE in $(seq 1 $NUM_NODES | sort -nr) 18 | do 19 | echo "# Removing node $NODE" 20 | docker stop mysql-node$NODE 21 | docker rm -v mysql-node$NODE 22 | SN=$(($NODE-1)) 23 | if [ -x n$NODE ] 24 | then 25 | rm -f n$NODE 26 | fi 27 | if [ -L s$SN ] 28 | then 29 | rm -f s$SN 30 | fi 31 | if [ $NODE -gt 3 ] 32 | then 33 | if [ -d $DOCKER_DATA/node_$NODE ] 34 | then 35 | sudo rm -rf $DOCKER_DATA/node_$NODE 36 | fi 37 | fi 38 | done 39 | if [ -L m ] 40 | then 41 | rm -f m 42 | fi 43 | rm -f DEPLOYED 44 | -------------------------------------------------------------------------------- /docker-replication/safebox.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #BOXNAME=$1 3 | #if [ -n "$BOXNAME" ] 4 | #then 5 | # shift 6 | #else 7 | [ -z "$BOXNAME" ] && BOXNAME=mybox 8 | #fi 9 | 10 | OS=$(uname -s) 11 | if [ "$OS" == "Linux" ] 12 | then 13 | CHECKSUM=sh256sum 14 | elif [ "$OS" == "Darwin" ] 15 | then 16 | CHECKSUM=shasum5.18 17 | else 18 | echo "Unrecognized operating system '$OS'" 19 | exit 1 20 | fi 21 | 22 | # Generate a random password 23 | RANDOM_PASSWORD=$(echo $RANDOM | $CHECKSUM | cut -c 1-10 ) 24 | 25 | # Save the random password to a file 26 | SECRETPASSWORD=$PWD/secretpassword.txt 27 | HOME_MY_SAFE=$PWD/home_my_safe.cnf 28 | echo $RANDOM_PASSWORD > $SECRETPASSWORD 29 | 30 | # Create the .my.cnf file 31 | echo '[client]' > $HOME_MY_SAFE 32 | echo 'user=root' >> $HOME_MY_SAFE 33 | echo "password=$RANDOM_PASSWORD" >> $HOME_MY_SAFE 34 | 35 | [ -z "$IMAGE" ] && IMAGE=mysql/mysql-server 36 | set -x 37 | docker run --name $BOXNAME --hostname $BOXNAME \ 38 | -v $SECRETPASSWORD:/root/secretpassword.txt \ 39 | -v $HOME_MY_SAFE:/etc/myuser.cnf \ 40 | -e MYSQL_ROOT_PASSWORD=/root/secretpassword.txt -d $IMAGE $@ 41 | 42 | # -v $HOME_MY_SAFE:/root/home_my.cnf \ 43 | -------------------------------------------------------------------------------- /docker-replication/set-replication-multisource.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MASTER_NODE=1 3 | if [ -f DEPLOYED ] 4 | then 5 | NUM_NODES=$(cat DEPLOYED) 6 | else 7 | echo "# File 'DEPLOYED' not found. Aborting" 8 | exit 1 9 | fi 10 | 11 | . ./common.sh 12 | 13 | if [ $NUM_NODES -lt 2 ] 14 | then 15 | echo "# For replication you need more than 1 node. Aborting" 16 | exit 1 17 | fi 18 | 19 | MASTER_PORT=3306 20 | MASTER1="docker exec -it mysql-node1 mysql" 21 | MASTER2="docker exec -it mysql-node2 mysql" 22 | $MASTER1 -e 'select @@hostname as MASTER, @@server_id, @@server_uuid, @@version' 23 | $MASTER2 -e 'select @@hostname as MASTER, @@server_id, @@server_uuid, @@version' 24 | 25 | USER_EXISTS=$($MASTER1 -BN -e 'select user from mysql.user where user="rdocker"') 26 | if [ -z "$USER_EXISTS" ] 27 | then 28 | echo "# Creating replication user in the master" 29 | $MASTER1 -ve 'create user rdocker identified by "rdocker"' 30 | $MASTER1 -ve 'grant replication slave on *.* to rdocker' 31 | $MASTER2 -ve 'create user rdocker identified by "rdocker"' 32 | $MASTER2 -ve 'grant replication slave on *.* to rdocker' 33 | fi 34 | 35 | $MASTER1 -e 'reset master' 36 | $MASTER2 -e 'reset master' 37 | 38 | # 39 | # We either use binlog name and position or reset master + MASTER_AUTO_POSITION 40 | # 41 | #MASTER_STATUS=$($MASTER -e 'show master status\G') 42 | #master_file=$(echo "$MASTER_STATUS" | grep File: | awk '{print $2}') 43 | #master_pos=$(echo "$MASTER_STATUS" | grep Position: | awk '{print $2}') 44 | #master_start="MASTER_LOG_FILE='$master_file', MASTER_LOG_POS=$master_pos" 45 | 46 | for SLAVE_NODE in $(seq 3 $NUM_NODES) 47 | do 48 | 49 | SLAVE_PORT=3306 50 | SLAVE="docker exec -it mysql-node$SLAVE_NODE mysql " 51 | 52 | echo "# Setting up replication" 53 | $SLAVE -e 'reset master' 54 | $SLAVE -e "select @@hostname as SLAVE_$SLAVE_NODE, @@server_id, @@server_uuid, @@version" 55 | SLAVE_RUNNING=$($SLAVE -BN -e 'SHOW SLAVE STATUS') 56 | [ -n "$SLAVE_RUNNING" ] && $SLAVE -ve 'STOP SLAVE' 57 | 58 | #$SLAVE -ve "change master to master_host='mysql-node1', master_port=$MASTER_PORT, master_user='rdocker', master_password='rdocker', MASTER_AUTO_POSITION=1" 59 | $SLAVE -ve "change master to master_host='mysql-node1', master_port=$MASTER_PORT, MASTER_AUTO_POSITION=1 for channel 'master1'" 60 | $SLAVE -ve "change master to master_host='mysql-node2', master_port=$MASTER_PORT, MASTER_AUTO_POSITION=1 for channel 'master2'" 61 | $SLAVE -ve 'START SLAVE user="rdocker" password="rdocker" ' 62 | $SLAVE -e 'SHOW SLAVE STATUS\G' | grep 'Channel_Name:\|Running:' 63 | done 64 | 65 | if [ -n "$SKIP_TEST" ] 66 | then 67 | exit 68 | fi 69 | echo "# Creating a table in the master" 70 | $MASTER1 -e 'create schema if not exists test' 71 | $MASTER1 -ve 'drop table if exists test.t1' 72 | $MASTER1 -ve 'create table t1 (i int not null primary key, msg varchar(50), d date, t time, dt datetime);' test 73 | $MASTER1 -ve "insert into t1 values (1, 'test1', current_date(), now() + interval 11 second, now());" test 74 | $MASTER1 -ve "insert into t1 values (2, 'test2', current_date(), now() + interval 12 second, now());" test 75 | $MASTER2 -e 'create schema if not exists test' 76 | $MASTER2 -ve 'drop table if exists test.t2' 77 | $MASTER2 -ve 'create table t2 (i int not null primary key, msg varchar(50), d date, t time, dt datetime);' test 78 | $MASTER2 -ve "insert into t2 values (1, 'test1', current_date(), now() + interval 11 second, now());" test 79 | pause 10 80 | 81 | exit_code=0 82 | echo "# Retrieving the table from the slaves" 83 | for SLAVE_NODE in $(seq 2 $NUM_NODES) 84 | do 85 | SLAVE="docker exec -it mysql-node$SLAVE_NODE mysql " 86 | $SLAVE -e 'select @@hostname, @@server_id, @@server_uuid' 87 | $SLAVE -e 'select * from test.t1' 88 | $SLAVE -e 'select * from test.t2' 89 | REPLICATED1=$($SLAVE -BN -e 'select count(*) from information_schema.tables where table_schema="test" and table_name="t2"' | tr -d '\n' | tr -d '\r') 90 | REPLICATED2=$($SLAVE -BN -e 'select count(*) from information_schema.tables where table_schema="test" and table_name="t2"' | tr -d '\n' | tr -d '\r') 91 | if [ "$REPLICATED1" == "1" ] 92 | then 93 | echo "OK - Slave $SLAVE_NODE has replicated table t1" 94 | else 95 | echo "NOT OK - Slave $SLAVE_NODE has NOT replicated table t1" 96 | exit_code=1 97 | fi 98 | if [ "$REPLICATED2" == "1" ] 99 | then 100 | echo "OK - Slave $SLAVE_NODE has replicated table t2" 101 | else 102 | echo "NOT OK - Slave $SLAVE_NODE has NOT replicated table t2" 103 | exit_code=1 104 | fi 105 | done 106 | echo "# Exit code: $exit_code" 107 | exit $exit_code 108 | -------------------------------------------------------------------------------- /docker-replication/set-replication-simple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MASTER_NODE=1 3 | if [ -f DEPLOYED ] 4 | then 5 | NUM_NODES=$(cat DEPLOYED) 6 | else 7 | echo "# File 'DEPLOYED' not found. Aborting" 8 | exit 1 9 | fi 10 | 11 | . ./common.sh 12 | 13 | if [ $NUM_NODES -lt 2 ] 14 | then 15 | echo "# For replication you need more than 1 node. Aborting" 16 | exit 1 17 | fi 18 | 19 | MASTER_PORT=3306 20 | MASTER="docker exec -it mysql-node1 mysql" 21 | $MASTER -e 'select @@hostname as MASTER, @@server_id, @@server_uuid, @@version' 22 | 23 | USER_EXISTS=$($MASTER -BN -e 'select user from mysql.user where user="rdocker"') 24 | if [ -z "$USER_EXISTS" ] 25 | then 26 | echo "# Creating replication user in the master" 27 | $MASTER -ve 'create user rdocker identified by "rdocker"' 28 | $MASTER -ve 'grant replication slave on *.* to rdocker' 29 | #$MASTER -ve 'grant select on performance_schema.global_variables to rdocker' 30 | #$MASTER -ve 'grant select on performance_schema.session_variables to rdocker' 31 | fi 32 | 33 | $MASTER -e 'reset master' 34 | 35 | # 36 | # We either use binlog name and position or reset master + MASTER_AUTO_POSITION 37 | # 38 | #MASTER_STATUS=$($MASTER -e 'show master status\G') 39 | #master_file=$(echo "$MASTER_STATUS" | grep File: | awk '{print $2}') 40 | #master_pos=$(echo "$MASTER_STATUS" | grep Position: | awk '{print $2}') 41 | #master_start="MASTER_LOG_FILE='$master_file', MASTER_LOG_POS=$master_pos" 42 | 43 | for SLAVE_NODE in $(seq 2 $NUM_NODES) 44 | do 45 | 46 | SLAVE_PORT=3306 47 | SLAVE="docker exec -it mysql-node$SLAVE_NODE mysql " 48 | 49 | echo "# Setting up replication" 50 | $SLAVE -e 'reset master' 51 | $SLAVE -e "select @@hostname as SLAVE_$SLAVE_NODE, @@server_id, @@server_uuid, @@version" 52 | SLAVE_RUNNING=$($SLAVE -BN -e 'SHOW SLAVE STATUS') 53 | [ -n "$SLAVE_RUNNING" ] && $SLAVE -ve 'STOP SLAVE' 54 | 55 | #$SLAVE -ve "change master to master_host='mysql-node1', master_port=$MASTER_PORT, master_user='rdocker', master_password='rdocker', MASTER_AUTO_POSITION=1" 56 | $SLAVE -ve "change master to master_host='mysql-node1', master_port=$MASTER_PORT, MASTER_AUTO_POSITION=1" 57 | $SLAVE -ve 'START SLAVE user="rdocker" password="rdocker" ' 58 | $SLAVE -e 'SHOW SLAVE STATUS\G' | grep 'Running:' 59 | done 60 | 61 | if [ -n "$SKIP_TEST" ] 62 | then 63 | exit 64 | fi 65 | echo "# Creating a table in the master" 66 | $MASTER -e 'create schema if not exists test' 67 | $MASTER -ve 'drop table if exists test.t1' 68 | $MASTER -ve ' create table t1 (i int not null primary key, msg varchar(50), d date, t time, dt datetime);' test 69 | $MASTER -ve " insert into t1 values (1, 'test1', current_date(), now() + interval 11 second, now());" test 70 | pause 10 71 | 72 | exit_code=0 73 | echo "# Retrieving the table from the slaves" 74 | for SLAVE_NODE in $(seq 2 $NUM_NODES) 75 | do 76 | SLAVE="docker exec -it mysql-node$SLAVE_NODE mysql " 77 | $SLAVE -e 'select @@hostname, @@server_id, @@server_uuid' 78 | $SLAVE -e 'select * from test.t1' 79 | REPLICATED=$($SLAVE -BN -e 'select count(*) from information_schema.tables where table_schema="test" and table_name="t1"' | tr -d '\n' | tr -d '\r') 80 | if [ "$REPLICATED" == "1" ] 81 | then 82 | echo "OK - Slave $SLAVE_NODE has replicated table t1" 83 | else 84 | echo "NOT OK - Slave $SLAVE_NODE has NOT replicated table t1" 85 | exit_code=1 86 | fi 87 | done 88 | echo "# Exit code: $exit_code" 89 | exit $exit_code 90 | -------------------------------------------------------------------------------- /docker-replication/simple/my-master.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | user = mysql 3 | log_error = /var/lib/mysql/my-master-error.log 4 | log-bin = mysql-bin 5 | server-id = 100 6 | 7 | -------------------------------------------------------------------------------- /docker-replication/simple/my-slave.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | user = mysql 3 | log_error = /var/lib/mysql/my-slave-error.log 4 | log-bin = mysql-bin 5 | relay-log = mysql-relay 6 | server-id = 200 7 | 8 | -------------------------------------------------------------------------------- /docker-replication/simple/mysql-2node-install.sh: -------------------------------------------------------------------------------- 1 | docker run --name mysql-master -v $PWD/my-master.cnf:/etc/my.cnf -e MYSQL_ROOT_PASSWORD=secret -d mysql 2 | if [ "$?" != "0" ] ; then exit 1; fi 3 | docker run --name mysql-slave -v $PWD/my-slave.cnf:/etc/my.cnf -e MYSQL_ROOT_PASSWORD=secret -d mysql 4 | if [ "$?" != "0" ] ; then exit 1; fi 5 | echo "# Waiting for nodes to be ready - Sleeping 30 seconds" 6 | sleep 30 7 | ./set-2node-replication.sh 8 | 9 | -------------------------------------------------------------------------------- /docker-replication/simple/remove-2node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | docker stop mysql-master 4 | docker rm mysql-master 5 | docker stop mysql-slave 6 | docker rm mysql-slave 7 | -------------------------------------------------------------------------------- /docker-replication/simple/set-2node-replication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MASTER_IP=$(docker inspect --format '{{ .NetworkSettings.IPAddress}}' mysql-master) 3 | SLAVE_IP=$(docker inspect --format '{{ .NetworkSettings.IPAddress}}' mysql-slave) 4 | echo "master: $MASTER_IP" 5 | echo "slave: $SLAVE_IP" 6 | 7 | MASTER_PORT=3306 8 | SLAVE_PORT=3306 9 | MASTER="mysql -u root -psecret -h $MASTER_IP -P $MASTER_PORT" 10 | SLAVE="mysql -u root -psecret -h $SLAVE_IP -P $SLAVE_PORT" 11 | 12 | USER_EXISTS=$($MASTER -BN -e 'select user from mysql.user where user="rdocker"') 13 | if [ -z "$USER_EXISTS" ] 14 | then 15 | echo "# Creating replication user in the master" 16 | $MASTER -ve 'create user rdocker identified by "rdocker"' 17 | $MASTER -ve 'grant replication slave on *.* to rdocker' 18 | $MASTER -ve 'grant select on performance_schema.global_variables to rdocker' 19 | $MASTER -ve 'grant select on performance_schema.session_variables to rdocker' 20 | fi 21 | $MASTER -e 'reset master' 22 | 23 | echo "# Setting up replication" 24 | SLAVE_RUNNING=$($SLAVE -BN -e 'SHOW SLAVE STATUS') 25 | [ -n "$SLAVE_RUNNING" ] && $SLAVE -ve 'STOP SLAVE' 26 | 27 | $SLAVE -ve "change master to master_host='$MASTER_IP', master_port=$MASTER_PORT, master_user='rdocker', master_password='rdocker';" 28 | $SLAVE -ve 'START SLAVE' 29 | $SLAVE -e 'SHOW SLAVE STATUS\G' | grep 'Running:' 30 | 31 | echo "# Creating a table in the master" 32 | $MASTER -e 'create schema if not exists test' 33 | $MASTER -ve 'drop table if exists test.t1' 34 | $MASTER -ve ' create table t1 (i int not null primary key, msg varchar(50), d date, t time, dt datetime);' test 35 | $MASTER -ve " insert into t1 values (1, 'test1', current_date(), now() + interval 11 second, now());" test 36 | sleep 1 37 | echo "# Retrieving the table from the slave" 38 | $SLAVE -e 'select * from test.t1' 39 | 40 | -------------------------------------------------------------------------------- /docker-replication/single_box.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | NAME=$1 3 | if [ -n "$NAME" ] 4 | then 5 | shift 6 | else 7 | NAME=mybox 8 | fi 9 | set -x 10 | 11 | VOLUME="-v $HOME/docker/mysql/single:/var/lib/mysql" 12 | PORT="-p 5000:3306" 13 | docker run --name $NAME -e MYSQL_ROOT_PASSWORD=secret -d $VOLUME $PORT mysql/mysql-server $@ 14 | 15 | -------------------------------------------------------------------------------- /docker-replication/start_percona.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # example of using experimental Percona image with Tokudb 4 | echo 'always madwise [never]' > never.txt 5 | docker run \ 6 | --name mybox \ 7 | -e MYSQL_ROOT_PASSWORD=secret -d \ 8 | -e INIT_TOKUDB=1 \ 9 | -v $PWD/never.txt:/sys/kernel/mm/transparent_hugepage/defrag \ 10 | -v $PWD/never.txt:/sys/kernel/mm/transparent_hugepage/enabled \ 11 | percona/percona-server 12 | 13 | 14 | -------------------------------------------------------------------------------- /docker-replication/two_boxes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | docker run --name mybox1 --hostname hmybox1 \ 4 | -e MYSQL_DATABASE=db1 \ 5 | -e MYSQL_USER=myuser1 \ 6 | -e MYSQL_PASSWORD=mypassword1 \ 7 | -e MYSQL_ROOT_PASSWORD=secret -d mysql/mysql-server 8 | docker run --name mybox2 --hostname hmybox2 \ 9 | -e MYSQL_DATABASE=db2 \ 10 | -e MYSQL_USER=myuser2 \ 11 | -e MYSQL_PASSWORD=mypassword2 \ 12 | -e MYSQL_ROOT_PASSWORD=secret -d mysql/mysql-server 13 | 14 | exists_net=$(docker network ls | grep my_net) 15 | [ -z "$exists_net" ] && docker network create my_net 16 | 17 | docker run --name mybox1n --hostname hmybox1n \ 18 | --net my_net \ 19 | -e MYSQL_DATABASE=db1 \ 20 | -e MYSQL_USER=myuser1 \ 21 | -e MYSQL_PASSWORD=mypassword1 \ 22 | -e MYSQL_ROOT_PASSWORD=secret -d mysql/mysql-server 23 | docker run --name mybox2n --hostname hmybox2n \ 24 | --net my_net \ 25 | -e MYSQL_DATABASE=db2 \ 26 | -e MYSQL_USER=myuser2 \ 27 | -e MYSQL_PASSWORD=mypassword2 \ 28 | -e MYSQL_ROOT_PASSWORD=secret -d mysql/mysql-server 29 | 30 | docker ps 31 | -------------------------------------------------------------------------------- /general-replication/measuring-replication-speed.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Simple script to measure MySQL replication speed 3 | # http://datacharmer.blogspot.com/2006/04/measuring-replication-speed.html 4 | use strict; 5 | use warnings; 6 | use Data::Dumper; 7 | use DBI; 8 | use Time::HiRes qw/ usleep gettimeofday tv_interval/; 9 | use English qw( -no_match_vars ); 10 | 11 | my $username1 = 'user1'; 12 | my $password1 = 'user2'; 13 | my $username2 = 'pass1'; 14 | my $password2 = 'pass2'; 15 | my $host1 = 'host_IP1'; 16 | my $host2 = 'host_IP2'; 17 | my $port1 = '3306'; 18 | my $port2 = '3306'; 19 | 20 | my $dbh1=DBI->connect("dbi:mysql:test;host=$host1;port=$port1", 21 | $username1, $password1, 22 | {RaiseError => 1}) 23 | or die "Can't connect: $DBI::errstr\n"; 24 | 25 | my $dbh2=DBI->connect("dbi:mysql:test;host=$host2;port=$port2", 26 | $username2, $password2, 27 | {RaiseError => 1}) 28 | or die "Can't connect: $DBI::errstr\n"; 29 | 30 | my $loops = 10; # how many times we loop (with size increase) 31 | my $num_of_inserts = 5; # how many records we insert for each loop 32 | my $initial_blob_size = 1_000; # how big is the record we start with 33 | my $replica_db = 'test'; # which database we use for testing 34 | 35 | my $master_dbh = $dbh1; 36 | my $slave_dbh = $dbh2; 37 | 38 | my ( $exists_db ) = $master_dbh->selectrow_array(qq{SHOW DATABASES LIKE '$replica_db'}); 39 | unless ($exists_db) { 40 | eval {$master_dbh->do(qq{CREATE DATABASE $replica_db}) }; 41 | if ( $EVAL_ERROR ) { 42 | die "execution error $DBI::errstr\n"; 43 | } 44 | } 45 | 46 | # 47 | # creating the measurement table 48 | # 49 | eval { 50 | $master_dbh->do( qq{ 51 | CREATE DATABASE IF NOT EXISTS $replica_db}); 52 | $master_dbh->do( qq{ 53 | USE $replica_db } ); 54 | $master_dbh->do( qq{ 55 | DROP TABLE IF EXISTS replica_speed }); 56 | $master_dbh->do( qq{ 57 | CREATE TABLE replica_speed ( 58 | id int(11) NOT NULL auto_increment, 59 | insert_sequence int not null, 60 | seconds bigint(20) default NULL, 61 | microseconds bigint(20) default NULL, 62 | ts timestamp(14) NOT NULL, 63 | big_one longtext, 64 | PRIMARY KEY (`id`), 65 | KEY insert_sequence (insert_sequence) 66 | ) 67 | } ); 68 | }; 69 | if ($EVAL_ERROR) { 70 | die "table creation error $DBI::errstr\n"; 71 | } 72 | 73 | # 74 | # give some time to the table creation to get replicated 75 | # 76 | usleep(200_000); 77 | my $insert_query = qq{ 78 | INSERT INTO $replica_db.replica_speed 79 | (insert_sequence, seconds, microseconds, big_one) 80 | VALUES ( ?, ?, ?, ?) }; 81 | my $retrieve_query = qq{ 82 | SELECT seconds, microseconds, id, insert_sequence 83 | FROM $replica_db.replica_speed 84 | WHERE insert_sequence = ? 85 | }; 86 | my $slave_sth = $slave_dbh->prepare($retrieve_query); 87 | 88 | # 89 | # checking max_allowed_packet to make sure that we are not 90 | # exceeding the limits 91 | # 92 | my ( undef, $master_max_allowed_packet) = $master_dbh->selectrow_array( 93 | qq{ SHOW VARIABLES LIKE "max_allowed_packet" } ); 94 | 95 | my ( undef, $slave_max_allowed_packet) = $slave_dbh->selectrow_array( 96 | qq{ SHOW VARIABLES LIKE "max_allowed_packet" } ); 97 | 98 | my $max_allowed_packet = $master_max_allowed_packet; 99 | if ( $slave_max_allowed_packet < $master_max_allowed_packet) { 100 | $max_allowed_packet = $slave_max_allowed_packet; 101 | } 102 | my @results = (); 103 | 104 | LOOP: 105 | for my $loopcount (0 .. $loops ) 106 | { 107 | usleep(200_000); 108 | 109 | # 110 | # let's start with an empty table 111 | # 112 | $master_dbh->do( qq{ TRUNCATE $replica_db.replica_speed } ); 113 | 114 | my $size = $initial_blob_size * ($loopcount || 1); 115 | if ($size > $max_allowed_packet) { 116 | $size = $max_allowed_packet - 1000; 117 | } 118 | my $master_insert_time = 0.0; 119 | my $big_blob = 'a' x $size; 120 | 121 | # 122 | # inserting several records in the master 123 | # 124 | for my $sequence (1 .. $num_of_inserts ) { 125 | my ( $secs, $msecs ) = gettimeofday(); 126 | $master_dbh->do($insert_query, undef, $sequence, $secs, $msecs, $big_blob); 127 | $master_insert_time = tv_interval( [$secs, $msecs], [gettimeofday()]); 128 | } 129 | my $replication_delay = 0; 130 | my $total_retrieval_time = 0; 131 | my $baredelay = undef; 132 | 133 | # 134 | # fetching data from the slave 135 | # 136 | RETRIEVAL: 137 | while ( ! $replication_delay ) # waiting for data to arrive from master to slave 138 | { 139 | my $retrieval_start_time = [gettimeofday()]; 140 | $slave_sth->execute( $num_of_inserts); 141 | my $info = $slave_sth->fetchrow_arrayref(); 142 | my $retrieval_stop_time = [gettimeofday()]; 143 | my $retrieval_time = 0.0; 144 | $retrieval_time = tv_interval( 145 | $retrieval_start_time, 146 | $retrieval_stop_time); 147 | next RETRIEVAL unless $info->[0]; 148 | 149 | # 150 | # retrieval time is counted only after a successful fetch 151 | # 152 | $total_retrieval_time += $retrieval_time; 153 | $replication_delay = tv_interval( [$info->[0], $info->[1]], $retrieval_stop_time); 154 | $baredelay = $replication_delay - $total_retrieval_time - $master_insert_time; 155 | printf "%4d %5d %5d %12d %12d %12d %12d\n", 156 | $loopcount, $info->[2], $info->[3] , $info->[0] , $info->[1] , 157 | $retrieval_stop_time->[0], $retrieval_stop_time->[1]; 158 | } 159 | 160 | push @results, 161 | { 162 | data_size => $size, 163 | master_insert_time => $master_insert_time, 164 | slave_retrieval_time => $total_retrieval_time, 165 | replication_time => $replication_delay, 166 | bare_replication_time => $baredelay, 167 | } 168 | } 169 | 170 | # 171 | # displaying results 172 | # 173 | my @header_sizes = qw(4 9 13 15 16 9); 174 | my @headers = ('loop', 'data size', 'master insert', 'slave retrieval', 'total repl. time', 'bare time'); 175 | printf "%s %s %s %s %s %s\n" , @headers; 176 | printf "%s %s %s %s %s %s\n" , map { '-' x $_ } @header_sizes; 177 | my $count = 0; 178 | for my $res (@results) 179 | { 180 | printf "%4d %9d %13.6f %15.6f %16.6f %9.6f\n" , ++$count, 181 | map { $res->{$_} } 182 | qw/data_size master_insert_time slave_retrieval_time replication_time bare_replication_time/; 183 | } 184 | -------------------------------------------------------------------------------- /general-replication/poor-man-monitoring.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Someone proposed a poor man's replication monitor using Python. 3 | # Having Python or Perl in your server doesn't really qualify as "poor man". 4 | # In many cases it's a luxury, and thus, here's my shot at the problem, 5 | # using a Bash shell script.Unlike its Python-based competition, this version also checks that the slave is 6 | # replicating from the intended master, and that it is not lagging behind. 7 | 8 | USERNAME=msandbox 9 | PASSWORD=msandbox 10 | EXPECTED_MASTER_HOST=127.0.0.1 11 | EXPECTED_MASTER_PORT=13454 12 | 13 | SLAVE_HOST=127.0.0.1 14 | SLAVE_PORT=13455 15 | 16 | tmpcfg=/tmp/my.cfg 17 | echo '[client]' > $tmpcfg 18 | echo "user=$USERNAME" >> $tmpcfg 19 | echo "password=$PASSWORD" >> $tmpcfg 20 | # MYSQL="mysql -u $USERNAME -p$PASSWORD " 21 | MYSQL="mysql --defaults-file=$tmpcfg" 22 | MASTER="$MYSQL -h $EXPECTED_MASTER_HOST -P $EXPECTED_MASTER_PORT" 23 | SLAVE="$MYSQL -h $SLAVE_HOST -P $SLAVE_PORT" 24 | 25 | $MASTER -e 'SHOW MASTER STATUS\G' > mstatus 26 | $SLAVE -e 'SHOW SLAVE STATUS\G' > sstatus 27 | 28 | function extract_value { 29 | FILENAME=$1 30 | VAR=$2 31 | grep -w $VAR $FILENAME | awk '{print $2}' 32 | } 33 | 34 | Master_Binlog=$(extract_value mstatus File ) 35 | Master_Position=$(extract_value mstatus Position ) 36 | 37 | Master_Host=$(extract_value sstatus Master_Host) 38 | Master_Port=$(extract_value sstatus Master_Port) 39 | Master_Log_File=$(extract_value sstatus Master_Log_File) 40 | Read_Master_Log_Pos=$(extract_value sstatus Read_Master_Log_Pos) 41 | Slave_IO_Running=$(extract_value sstatus Slave_IO_Running) 42 | Slave_SQL_Running=$(extract_value sstatus Slave_SQL_Running) 43 | 44 | ERROR_COUNT=0 45 | if [ "$Master_Host" != "$EXPECTED_MASTER_HOST" ] 46 | then 47 | ERRORS[$ERROR_COUNT]="the slave is not replicating from the host that it is supposed to" 48 | ERROR_COUNT=$(($ERROR_COUNT+1)) 49 | fi 50 | 51 | if [ "$Master_Port" != "$EXPECTED_MASTER_PORT" ] 52 | then 53 | ERRORS[$ERROR_COUNT]="the slave is not replicating from the host that it is supposed to" 54 | ERROR_COUNT=$(($ERROR_COUNT+1)) 55 | fi 56 | 57 | if [ "$Master_Binlog" != "$Master_Log_File" ] 58 | then 59 | ERRORS[$ERROR_COUNT]="master binlog ($Master_Binlog) and Master_Log_File ($Master_Log_File) differ" 60 | ERROR_COUNT=$(($ERROR_COUNT+1)) 61 | fi 62 | 63 | POS_DIFFERENCE=$(echo ${Master_Position}-$Read_Master_Log_Pos|bc) 64 | 65 | if [ $POS_DIFFERENCE -gt 1000 ] 66 | then 67 | ERRORS[$ERROR_COUNT]="The slave is lagging behind of $POS_DIFFERENCE" 68 | ERROR_COUNT=$(($ERROR_COUNT+1)) 69 | fi 70 | 71 | if [ "$Slave_IO_Running" == "No" ] 72 | then 73 | ERRORS[$ERROR_COUNT]="Replication is stopped" 74 | ERROR_COUNT=$(($ERROR_COUNT+1)) 75 | fi 76 | 77 | if [ "$Slave_SQL_Running" == "No" ] 78 | then 79 | ERRORS[$ERROR_COUNT]="Replication (SQL) is stopped" 80 | ERROR_COUNT=$(($ERROR_COUNT+1)) 81 | fi 82 | 83 | if [ $ERROR_COUNT -gt 0 ] 84 | then 85 | EMAIL=myname@gmail.com 86 | SUBJECT="ERRORS in replication" 87 | BODY='' 88 | CNT=0 89 | while [ "$CNT" != "$ERROR_COUNT" ] 90 | do 91 | BODY="$BODY ${ERRORS[$CNT]}" 92 | CNT=$(($CNT+1)) 93 | done 94 | echo $SUBJECT 95 | echo $BODY 96 | echo $BODY | mail -s "$SUBJECT" $EMAIL 97 | else 98 | echo "Replication OK" 99 | printf "file: %s at %'d\n" $Master_Log_File $Read_Master_Log_Pos 100 | fi 101 | -------------------------------------------------------------------------------- /group-replication/data/input_data.sql: -------------------------------------------------------------------------------- 1 | create schema if not exists test ; 2 | use test ; 3 | drop table if exists t1; 4 | create table t1 (i int not null primary key, msg varchar(50), d date, t time, dt datetime, ts timestamp); 5 | insert into t1 values (1, 'test1', '2014-01-11', '11:23:41','2014-01-11 12:34:51', null); 6 | -------------------------------------------------------------------------------- /group-replication/data/user.sql: -------------------------------------------------------------------------------- 1 | reset master; 2 | SET SQL_LOG_BIN=0; 3 | CREATE USER rpl_user@'%'; 4 | GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%' IDENTIFIED BY 'rpl_pass'; 5 | SET SQL_LOG_BIN=1; 6 | CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='rpl_pass' FOR CHANNEL 'group_replication_recovery'; 7 | INSTALL PLUGIN group_replication SONAME 'group_replication.so'; 8 | 9 | -------------------------------------------------------------------------------- /group-replication/data/user2.sql: -------------------------------------------------------------------------------- 1 | set GLOBAL group_replication_bootstrap_group=(select if(@@hostname='node1', 'ON', 'OFF')); 2 | START GROUP_REPLICATION; 3 | SET GLOBAL group_replication_bootstrap_group=OFF; 4 | use performance_schema ; 5 | select sleep(2); 6 | select * from replication_group_members; 7 | 8 | -------------------------------------------------------------------------------- /group-replication/gr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export BASE_IP=172.200.0 3 | export GR_NETWORK=group1 4 | exists_net=$(docker network ls | grep -w $GR_NETWORK ) 5 | if [ -n "$exists_net" ] 6 | then 7 | docker network rm $GR_NETWORK 8 | if [ "$?" != "0" ] 9 | then 10 | echo "Error removing network $GR_NETWORK " 11 | exit 1 12 | fi 13 | fi 14 | docker network create --subnet ${BASE_IP}.0/16 --gateway ${BASE_IP}.1 $GR_NETWORK 15 | if [ "$?" != "0" ] 16 | then 17 | echo "Error creating network $GR_NETWORK " 18 | exit 1 19 | fi 20 | docker network ls 21 | if [ -f gr_started ] 22 | then 23 | rm -f gr_started 24 | fi 25 | if [ -f ips ] 26 | then 27 | rm ips 28 | fi 29 | 30 | for node in 1 2 3 31 | do 32 | export SERVERID=$node 33 | export IPEND=$(($SERVERID+1)) 34 | perl -pe 's/_SERVER_ID_/$ENV{SERVERID}/;s/_IP_END_/$ENV{IPEND}/;s/_BASE_IP_/$ENV{BASE_IP}/g' my-template.cnf > my${node}.cnf 35 | datadir=ddnode${node} 36 | if [ ! -d $datadir ] 37 | then 38 | mkdir $datadir 39 | fi 40 | unset SERVERID 41 | docker run -d --name=node$node --net=$GR_NETWORK --hostname=node$node \ 42 | -v $PWD/my${node}.cnf:/etc/my.cnf \ 43 | -v $PWD/data:/data \ 44 | -v $PWD/$datadir:/var/lib/mysql \ 45 | -e MYSQL_ROOT_PASSWORD=secret \ 46 | mysql/mysql-server:5.7.17 47 | 48 | ip=$(docker inspect --format "{{ .NetworkSettings.Networks.$GR_NETWORK.IPAddress}}" node${node}) 49 | echo "${node} $ip" >> ips 50 | done 51 | 52 | cat ips 53 | date +%s > gr_started 54 | 55 | -------------------------------------------------------------------------------- /group-replication/gr_clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ ! -f gr_started ] 3 | then 4 | echo file gr_started not found 5 | exit 1 6 | fi 7 | 8 | for NODE in 1 2 3; 9 | do 10 | docker stop node$NODE 11 | docker rm -v -f node$NODE 12 | done 13 | #remove_containers.sh 14 | rm -rf ddnode? 15 | 16 | for F in gr_started gr_completed 17 | do 18 | if [ -f $F ] 19 | then 20 | rm -f $F 21 | fi 22 | done 23 | -------------------------------------------------------------------------------- /group-replication/gr_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f gr_started ] 4 | then 5 | echo file gr_started not found 6 | exit 1 7 | fi 8 | elapsed=0 9 | 10 | while [[ $elapsed -lt 60 ]] 11 | do 12 | current=$(date +%s) 13 | started=$(cat gr_started) 14 | elapsed=$(expr $current - $started) 15 | echo -n "$elapsed " 16 | sleep 2 17 | done 18 | echo '' 19 | 20 | 21 | for N in 1 2 3 22 | do 23 | docker exec -ti node$N bash -c 'mysql -psecret < /data/user.sql' 24 | done 25 | echo sleeping 10 seconds 26 | sleep 10 27 | for N in 1 2 3 28 | do 29 | docker exec -ti node$N bash -c 'mysql -psecret < /data/user2.sql' 30 | sleep 4 31 | done 32 | date +%s > gr_completed 33 | 34 | -------------------------------------------------------------------------------- /group-replication/gr_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f gr_completed ] 4 | then 5 | echo file gr_completed not found 6 | exit 1 7 | fi 8 | 9 | for N in 1 2 3 10 | do 11 | docker exec -ti node$N bash -c 'mysql -psecret -NB -e "select @@hostname"' 12 | docker exec -ti node$N bash -c 'mysql -psecret -e "show schemas"' 13 | done 14 | 15 | docker exec -ti node1 bash -c 'mysql -psecret < /data/input_data.sql ' 16 | 17 | for N in 1 2 3 18 | do 19 | docker exec -ti node$N bash -c 'mysql -psecret -t -e "select @@hostname; show tables from test"' 20 | done 21 | 22 | -------------------------------------------------------------------------------- /group-replication/my-template.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | user=mysql 3 | server_id=_SERVER_ID_ 4 | gtid_mode=ON 5 | enforce_gtid_consistency=ON 6 | master_info_repository=TABLE 7 | relay_log_info_repository=TABLE 8 | binlog_checksum=NONE 9 | log_slave_updates=ON 10 | log_bin=mysql-bin 11 | relay-log=relay 12 | binlog_format=ROW 13 | log-error=mysqld.err 14 | 15 | transaction_write_set_extraction=XXHASH64 16 | loose-group_replication_group_name="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" 17 | loose-group_replication_start_on_boot=off 18 | loose-group_replication_local_address= "_BASE_IP_._IP_END_:6606" 19 | loose-group_replication_group_seeds= "_BASE_IP_.2:6606,_BASE_IP_.3:6606,_BASE_IP_.4:6606" 20 | loose-group_replication_ip_whitelist="_BASE_IP_.2,_BASE_IP_.3,_BASE_IP_.4,127.0.0.1" 21 | loose-group_replication_bootstrap_group= off 22 | 23 | -------------------------------------------------------------------------------- /group-replication/start_gr.sql: -------------------------------------------------------------------------------- 1 | set GLOBAL group_replication_bootstrap_group=(select if(@@hostname='node1', 'ON', 'OFF')); 2 | START GROUP_REPLICATION; 3 | SET GLOBAL group_replication_bootstrap_group=OFF; 4 | use performance_schema ; 5 | select * from replication_group_members; 6 | 7 | -------------------------------------------------------------------------------- /images/circular_replication.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacharmer/mysql-replication-samples/e45b5723340884c082957e6442385f31101262b0/images/circular_replication.jpg -------------------------------------------------------------------------------- /images/fan-in.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacharmer/mysql-replication-samples/e45b5723340884c082957e6442385f31101262b0/images/fan-in.jpg -------------------------------------------------------------------------------- /images/hybrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacharmer/mysql-replication-samples/e45b5723340884c082957e6442385f31101262b0/images/hybrid.png -------------------------------------------------------------------------------- /images/master_slave.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacharmer/mysql-replication-samples/e45b5723340884c082957e6442385f31101262b0/images/master_slave.jpg -------------------------------------------------------------------------------- /images/point-to-point-all_masters.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacharmer/mysql-replication-samples/e45b5723340884c082957e6442385f31101262b0/images/point-to-point-all_masters.jpg -------------------------------------------------------------------------------- /images/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacharmer/mysql-replication-samples/e45b5723340884c082957e6442385f31101262b0/images/star.png -------------------------------------------------------------------------------- /multi-master-comparison/README.md: -------------------------------------------------------------------------------- 1 | See [MySQL Group Replication vs. multi-source](https://datacharmer.blogspot.com/2017/01/mysql-group-replication-vs-multi-source.html) for how to use these files 2 | -------------------------------------------------------------------------------- /multi-master-comparison/mm_gr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MYSQL_VERSION=$1 3 | [ -z "$MYSQL_VERSION" ] && MYSQL_VERSION=5.7.17 4 | 5 | make_multiple_sandbox --gtid --group_directory=GR $MYSQL_VERSION 6 | 7 | if [ "$?" != "0" ] ; then exit 1 ; fi 8 | multi_sb=$HOME/sandboxes/GR 9 | 10 | baseport=$($multi_sb/n1 -BN -e 'select @@port') 11 | baseport=$(($baseport+99)) 12 | 13 | port1=$(($baseport+1)) 14 | port2=$(($baseport+2)) 15 | port3=$(($baseport+3)) 16 | for N in 1 2 3 17 | do 18 | myport=$(($baseport+N)) 19 | options=( 20 | binlog_checksum=NONE 21 | log_slave_updates=ON 22 | plugin-load=group_replication.so 23 | group_replication=FORCE_PLUS_PERMANENT 24 | group_replication_start_on_boot=OFF 25 | group_replication_bootstrap_group=OFF 26 | transaction_write_set_extraction=XXHASH64 27 | loose-group_replication_group_name="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" 28 | loose-group_replication_local_address="127.0.0.1:$myport" 29 | loose-group_replication_group_seeds="127.0.0.1:$port1,127.0.0.1:$port2,127.0.0.1:$port3" 30 | loose-group-replication-single-primary-mode=off 31 | ) 32 | # group_replication_gtid_assignment_block_size=1000 33 | $multi_sb/node$N/add_option ${options[*]} 34 | 35 | user_cmd='reset master;' 36 | user_cmd="$user_cmd CHANGE MASTER TO MASTER_USER='rsandbox', MASTER_PASSWORD='rsandbox' FOR CHANNEL 'group_replication_recovery';" 37 | 38 | $multi_sb/node$N/use -v -u root -e "$user_cmd" 39 | done 40 | 41 | 42 | START_CMD="SET GLOBAL group_replication_bootstrap_group=ON;" 43 | START_CMD="$START_CMD START GROUP_REPLICATION;" 44 | START_CMD="$START_CMD SET GLOBAL group_replication_bootstrap_group=OFF;" 45 | $multi_sb/n1 -v -e "$START_CMD" 46 | sleep 1 47 | $multi_sb/n2 -v -e 'START GROUP_REPLICATION;' 48 | sleep 1 49 | $multi_sb/n3 -v -e 'START GROUP_REPLICATION;' 50 | sleep 1 51 | $multi_sb/use_all 'select * from performance_schema.replication_group_members' 52 | 53 | -------------------------------------------------------------------------------- /multi-master-comparison/mm_ms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | MYSQL_VERSION=$1 3 | [ -z "$MYSQL_VERSION" ] && MYSQL_VERSION=5.7.16 4 | 5 | make_multiple_sandbox --gtid --group_directory=MS $MYSQL_VERSION 6 | 7 | if [ "$?" != "0" ] ; then exit 1 ; fi 8 | multi_sb=$HOME/sandboxes/MS 9 | 10 | $multi_sb/use_all 'reset master' 11 | 12 | for N in 1 2 3 13 | do 14 | user_cmd='' 15 | for node in 1 2 3 16 | do 17 | if [ "$node" != "$N" ] 18 | then 19 | master_port=$($multi_sb/n$node -BN -e 'select @@port') 20 | user_cmd="$user_cmd CHANGE MASTER TO MASTER_USER='rsandbox', " 21 | user_cmd="$user_cmd MASTER_PASSWORD='rsandbox', master_host='127.0.0.1', " 22 | user_cmd="$user_cmd master_port=$master_port FOR CHANNEL 'node$node';" 23 | user_cmd="$user_cmd START SLAVE FOR CHANNEL 'node$node';" 24 | fi 25 | done 26 | $multi_sb/node$N/use -v -u root -e "$user_cmd" 27 | done 28 | 29 | 30 | -------------------------------------------------------------------------------- /multi-master-comparison/mm_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | multi_sb=$1 3 | if [ -z "$multi_sb" ] 4 | then 5 | echo multiple sandbox path needed 6 | exit 1 7 | fi 8 | if [ ! -d $multi_sb ] 9 | then 10 | echo directory $multi_sb not found 11 | exit 1 12 | fi 13 | if [ ! -d "$multi_sb/node3" ] 14 | then 15 | echo directory $multi_sb/node3 not found 16 | exit 1 17 | fi 18 | cd $multi_sb 19 | 20 | for N in 1 2 3 ; do 21 | ./n$N -e "create schema if not exists test" 22 | ./n$N -e "drop table if exists test.t$N" 23 | ./n$N -e "create table test.t$N(id int not null primary key, sid int)" 24 | ./n$N -e "insert into test.t$N values ($N, @@server_id)" 25 | done 26 | 27 | #for N in 1 2 3 ; do 28 | # ./n$N -e "insert into test.t$N values ($N + 1, @@server_id)" 29 | # ./n$N -e "insert into test.t$N values ($N + 2, @@server_id)" 30 | #done 31 | 32 | ./use_all 'select * from test.t1 union select * from test.t2 union select * from test.t3' 33 | -------------------------------------------------------------------------------- /multi_source/multi_source.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | VERSION=$1 17 | FLAVOR=$2 18 | TOPOLOGY=$3 19 | 20 | [ -z "$TOPOLOGY" ] && FLAVOR=none 21 | 22 | case $FLAVOR in 23 | mysql) 24 | ;; 25 | mariadb) 26 | ;; 27 | *) 28 | unset TOPOLOGY 29 | ;; 30 | esac 31 | 32 | case $TOPOLOGY in 33 | FAN-IN) 34 | ;; 35 | ALL-MASTERS) 36 | ;; 37 | *) 38 | unset TOPOLOGY 39 | ;; 40 | esac 41 | 42 | 43 | if [ -z "$TOPOLOGY" ] 44 | then 45 | echo "VERSION, FLAVOR, and TOPOLOGY required" 46 | echo "Where VERSION is an indentifier like 5.7.7 or ma10.0.20 " 47 | echo " FLAVOR is either mysql or mariadb" 48 | echo " TOPOLOGY is either FAN-IN or ALL-MASTERS" 49 | exit 1 50 | fi 51 | 52 | [ -z "$SANDBOX_BINARY" ] && SANDBOX_BINARY=$HOME/opt/mysql 53 | 54 | if [ ! -d $SANDBOX_BINARY ] 55 | then 56 | echo "$SANDBOX_BINARY does not exist" 57 | echo "Set the variable SANDBOX_BINARY to indicate where to find the expanded tarballs for MySQL::Sandbox" 58 | exit 1 59 | fi 60 | 61 | if [ ! -d $SANDBOX_BINARY/$VERSION ] 62 | then 63 | echo "$SANDBOX_BINARY/$VERSION not found" 64 | exit 1 65 | fi 66 | 67 | # --------------- 68 | DASHED_VERSION=$(echo $VERSION| tr '.' '_') 69 | 70 | if [ -n "$DRYRUN" -o -n "$DRY_RUN" ] 71 | then 72 | SKIP_INSTALLATION=1 73 | DRYRUN=1 74 | fi 75 | 76 | sandbox_name=$HOME/sandboxes/multi_msb_$DASHED_VERSION 77 | cd $(dirname $0) 78 | initialdir=$PWD 79 | if [ -n "$DRYRUN" ] 80 | then 81 | echo "# make_multiple_sandbox --how_many_nodes=4 $VERSION" 82 | echo "cd $sandbox_name" 83 | fi 84 | 85 | if [ -z "$SKIP_INSTALLATION" ] 86 | then 87 | make_multiple_sandbox --how_many_nodes=4 $VERSION 88 | cd $sandbox_name 89 | ./use_all 'reset master' 90 | fi 91 | 92 | 93 | function set_GTID 94 | { 95 | OPTIONS="master-info-repository=table " 96 | OPTIONS="$OPTIONS relay-log-info-repository=table" 97 | OPTIONS="$OPTIONS gtid_mode=ON" 98 | #OPTIONS="$OPTIONS log-slave-updates" 99 | OPTIONS="$OPTIONS enforce-gtid-consistency" 100 | CHANGED="" 101 | for NODE in node1 node2 node3 node4 102 | do 103 | for OPTION in $OPTIONS 104 | do 105 | option_exists=$(grep $OPTION $NODE/my.sandbox.cnf) 106 | if [ -z "$option_exists" ] 107 | then 108 | echo "$OPTION" >> $NODE/my.sandbox.cnf 109 | echo "# option '$OPTION' added to $NODE configuration file" 110 | CHANGED=1 111 | else 112 | echo "# option '$OPTION' already exists in $NODE configuration file" 113 | fi 114 | done 115 | done 116 | if [ -n "$CHANGED" ] 117 | then 118 | ./restart_all 119 | fi 120 | } 121 | 122 | CHANGE_MASTER_TEMPLATE="" 123 | CHANGE_MASTER_TEMPLATE_MYSQL="CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=_PORT_, MASTER_USER='rsandbox', MASTER_PASSWORD='rsandbox', MASTER_AUTO_POSITION=1 for channel '_CHANNEL_'" 124 | CHANGE_MASTER_TEMPLATE_MARIADB="CHANGE MASTER '_CHANNEL_' TO MASTER_HOST='127.0.0.1', MASTER_PORT=_PORT_, MASTER_USER='rsandbox', MASTER_PASSWORD='rsandbox', MASTER_USE_GTID=current_pos " 125 | START_SLAVE_TEMPLATE_MYSQL="START SLAVE for channel " 126 | START_SLAVE_TEMPLATE_MARIADB="START SLAVE " 127 | if [ $FLAVOR == mysql ] 128 | then 129 | if [ -z "$SKIP_INSTALLATION" ] 130 | then 131 | set_GTID 132 | fi 133 | CHANGE_MASTER_TEMPLATE=$CHANGE_MASTER_TEMPLATE_MYSQL 134 | START_SLAVE_TEMPLATE=$START_SLAVE_TEMPLATE_MYSQL 135 | else 136 | CHANGE_MASTER_TEMPLATE=$CHANGE_MASTER_TEMPLATE_MARIADB 137 | START_SLAVE_TEMPLATE=$START_SLAVE_TEMPLATE_MARIADB 138 | ./use_all 'set global gtid_domain_id=@@server_id*10' 139 | fi 140 | 141 | echo "# Setting topology $TOPOLOGY" 142 | 143 | if [ "$TOPOLOGY" == "ALL-MASTERS" ] 144 | then 145 | # ALL-MASTERS 146 | for NODE in node1 node2 node3 node4 147 | do 148 | echo "# node $NODE" 149 | for MASTER in node1 node2 node3 node4 150 | do 151 | if [ $NODE != $MASTER ] 152 | then 153 | #set -x 154 | if [ -n "$DRYRUN" ] 155 | then 156 | PORT='$MASTER_PORT' 157 | else 158 | PORT=$($MASTER/use -BN -e 'select @@port') 159 | fi 160 | CHANGE_MASTER=$CHANGE_MASTER_TEMPLATE 161 | CHANGE_MASTER=$(echo $CHANGE_MASTER |sed -e "s/_PORT_/$PORT/") 162 | CHANGE_MASTER=$(echo $CHANGE_MASTER |sed -e "s/_CHANNEL_/$MASTER/") 163 | START_SLAVE="$START_SLAVE_TEMPLATE '$MASTER'" 164 | #echo "#$CHANGE_MASTER#" 165 | #echo "$START_SLAVE" 166 | if [ -n "$DRYRUN" ] 167 | then 168 | # echo "# $NODE" 169 | echo "$CHANGE_MASTER" 170 | echo "$START_SLAVE" 171 | else 172 | $NODE/use -ve "$CHANGE_MASTER" 173 | $NODE/use -ve "$START_SLAVE" 174 | fi 175 | #set +x 176 | fi 177 | done 178 | done 179 | if [ -z "$DRYRUN" ] 180 | then 181 | cp -v $initialdir/test_all_masters_replication.sh $sandbox_name 182 | fi 183 | else 184 | # FAN-IN 185 | for NODE in node1 node2 node3 186 | do 187 | if [ -n "$DRYRUN" ] 188 | then 189 | PORT='$MASTER_PORT' 190 | else 191 | PORT=$($NODE/use -BN -e 'select @@port') 192 | fi 193 | CHANGE_MASTER=$CHANGE_MASTER_TEMPLATE 194 | CHANGE_MASTER=$(echo $CHANGE_MASTER |sed -e "s/_PORT_/$PORT/") 195 | CHANGE_MASTER=$(echo $CHANGE_MASTER |sed -e "s/_CHANNEL_/$NODE/") 196 | START_SLAVE="$START_SLAVE_TEMPLATE '$NODE'" 197 | #echo "$CHANGE_MASTER" 198 | #echo "$START_SLAVE" 199 | if [ -n "$DRYRUN" ] 200 | then 201 | echo "# node4" 202 | echo "$CHANGE_MASTER" 203 | else 204 | node4/use -ve "$CHANGE_MASTER" 205 | node4/use -ve "$START_SLAVE" 206 | fi 207 | done 208 | if [ -z "$DRYRUN" ] 209 | then 210 | cp -v $initialdir/test_multi_source_replication.sh $sandbox_name 211 | fi 212 | fi 213 | ### node4/use -ve "START SLAVE" 214 | -------------------------------------------------------------------------------- /multi_source/sample_runs.md: -------------------------------------------------------------------------------- 1 | ## No arguments: shows syntax 2 | $ ./multi_source.sh 3 | VERSION, FLAVOR, and TOPOLOGY required 4 | Where VERSION is an indentifier like 5.7.7 or ma10.0.20 5 | FLAVOR is either mysql or mariadb 6 | TOPOLOGY is either FAN-IN or ALL-MASTERS 7 | 8 | 9 | ## DRY-RUN installation with MySQL 5.7 (fan-in) 10 | 11 | $ DRY_RUN=1 ./multi_source.sh 5.7.8 mysql FAN-IN 12 | # make_multiple_sandbox --how_many_nodes=4 5.7.8 13 | cd $HOMEsandboxes/multi_msb_5_7_8 14 | # Setting topology FAN-IN 15 | # node4 16 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 17 | # node4 18 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 19 | # node4 20 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 21 | 22 | ## Real installation with MySQL 5.7 (fan-in) 23 | 24 | $ ./multi_source.sh 5.7.8 mysql FAN-IN 25 | installing node 1 26 | installing node 2 27 | installing node 3 28 | installing node 4 29 | group directory installed in $HOME/sandboxes/multi_msb_5_7_8 30 | # option 'master-info-repository=table' added to node1 configuration file 31 | # option 'relay-log-info-repository=table' added to node1 configuration file 32 | # option 'gtid_mode=ON' added to node1 configuration file 33 | # option 'log-slave-updates' added to node1 configuration file 34 | # option 'enforce-gtid-consistency' added to node1 configuration file 35 | # option 'master-info-repository=table' added to node2 configuration file 36 | # option 'relay-log-info-repository=table' added to node2 configuration file 37 | # option 'gtid_mode=ON' added to node2 configuration file 38 | # option 'log-slave-updates' added to node2 configuration file 39 | # option 'enforce-gtid-consistency' added to node2 configuration file 40 | # option 'master-info-repository=table' added to node3 configuration file 41 | # option 'relay-log-info-repository=table' added to node3 configuration file 42 | # option 'gtid_mode=ON' added to node3 configuration file 43 | # option 'log-slave-updates' added to node3 configuration file 44 | # option 'enforce-gtid-consistency' added to node3 configuration file 45 | # option 'master-info-repository=table' added to node4 configuration file 46 | # option 'relay-log-info-repository=table' added to node4 configuration file 47 | # option 'gtid_mode=ON' added to node4 configuration file 48 | # option 'log-slave-updates' added to node4 configuration file 49 | # option 'enforce-gtid-consistency' added to node4 configuration file 50 | # server: 1: 51 | # server: 2: 52 | # server: 3: 53 | # server: 4: 54 | # executing "stop" on $HOMEsandboxes/multi_msb_5_7_8 55 | executing "stop" on node 1 56 | executing "stop" on node 2 57 | executing "stop" on node 3 58 | executing "stop" on node 4 59 | # executing "start" on $HOMEsandboxes/multi_msb_5_7_8 60 | executing "start" on node 1 61 | . sandbox server started 62 | executing "start" on node 2 63 | . sandbox server started 64 | executing "start" on node 3 65 | . sandbox server started 66 | executing "start" on node 4 67 | . sandbox server started 68 | # Setting topology FAN-IN 69 | -------------- 70 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8379, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 71 | -------------- 72 | 73 | -------------- 74 | START SLAVE for channel 'node1' 75 | -------------- 76 | 77 | -------------- 78 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8380, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 79 | -------------- 80 | 81 | -------------- 82 | START SLAVE for channel 'node2' 83 | -------------- 84 | 85 | -------------- 86 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8381, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 87 | -------------- 88 | 89 | -------------- 90 | START SLAVE for channel 'node3' 91 | -------------- 92 | 93 | ## Testing Fan-In installation 94 | 95 | $PWD/test_multi_source_replication.sh -> $HOME/sandboxes/multi_msb_5_7_8/test_multi_source_replication.sh 96 | 97 | $ ~/sandboxes/multi_msb_5_7_8/test_multi_source_replication.sh 98 | # Tables in server 101 99 | test_node1 100 | # Tables in server 102 101 | test_node2 102 | # Tables in server 103 103 | test_node3 104 | # Tables in fan-in slave 105 | test_node1 106 | test_node2 107 | test_node3 108 | +-----------+--------+--------------+ 109 | | server_id | @@port | node | 110 | +-----------+--------+--------------+ 111 | | 104 | 8382 | fan-in slave | 112 | +-----------+--------+--------------+ 113 | +----+----------+--------+-------+---------------------+ 114 | | id | serverid | dbport | node | ts | 115 | +----+----------+--------+-------+---------------------+ 116 | | 1 | 101 | 8379 | node1 | 2015-07-26 20:33:15 | 117 | +----+----------+--------+-------+---------------------+ 118 | +----+----------+--------+-------+---------------------+ 119 | | id | serverid | dbport | node | ts | 120 | +----+----------+--------+-------+---------------------+ 121 | | 1 | 102 | 8380 | node2 | 2015-07-26 20:33:15 | 122 | +----+----------+--------+-------+---------------------+ 123 | +----+----------+--------+-------+---------------------+ 124 | | id | serverid | dbport | node | ts | 125 | +----+----------+--------+-------+---------------------+ 126 | | 1 | 103 | 8381 | node3 | 2015-07-26 20:33:16 | 127 | +----+----------+--------+-------+---------------------+ 128 | 129 | 130 | ## DRY-RUN installation with MariaDB 10.0.20 (fan-in) 131 | 132 | $ DRY_RUN=1 ./multi_source.sh ma10.0.20 mariadb FAN-IN 133 | # make_multiple_sandbox --how_many_nodes=4 ma10.0.20 134 | cd /Users/gmax/sandboxes/multi_msb_ma10_0_20 135 | # Setting topology FAN-IN 136 | # node4 137 | CHANGE MASTER 'node1' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 138 | # node4 139 | CHANGE MASTER 'node2' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 140 | # node4 141 | CHANGE MASTER 'node3' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 142 | 143 | 144 | ## DRY-RUN installation with MySQL 5.7 (all-masters) 145 | 146 | $ DRY_RUN=1 ./multi_source.sh 5.7.8 mysql ALL-MASTERS 147 | # make_multiple_sandbox --how_many_nodes=4 5.7.8 148 | cd /Users/gmax/sandboxes/multi_msb_5_7_8 149 | # Setting topology ALL-MASTERS 150 | # node node1 151 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 152 | START SLAVE for channel 'node2' 153 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 154 | START SLAVE for channel 'node3' 155 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node4' 156 | START SLAVE for channel 'node4' 157 | # node node2 158 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 159 | START SLAVE for channel 'node1' 160 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 161 | START SLAVE for channel 'node3' 162 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node4' 163 | START SLAVE for channel 'node4' 164 | # node node3 165 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 166 | START SLAVE for channel 'node1' 167 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 168 | START SLAVE for channel 'node2' 169 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node4' 170 | START SLAVE for channel 'node4' 171 | # node node4 172 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 173 | START SLAVE for channel 'node1' 174 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 175 | START SLAVE for channel 'node2' 176 | CHANGE MASTER TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 177 | START SLAVE for channel 'node3' 178 | 179 | 180 | ## DRY-RUN installation with MariaDB 10.1 (all-masters) 181 | 182 | $ DRY_RUN=1 ./multi_source.sh ma10.1.5 mariadb ALL-MASTERS 183 | # make_multiple_sandbox --how_many_nodes=4 ma10.1.5 184 | cd /Users/gmax/sandboxes/multi_msb_ma10_1_5 185 | # Setting topology ALL-MASTERS 186 | # node node1 187 | CHANGE MASTER 'node2' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 188 | START SLAVE 'node2' 189 | CHANGE MASTER 'node3' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 190 | START SLAVE 'node3' 191 | CHANGE MASTER 'node4' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 192 | START SLAVE 'node4' 193 | # node node2 194 | CHANGE MASTER 'node1' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 195 | START SLAVE 'node1' 196 | CHANGE MASTER 'node3' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 197 | START SLAVE 'node3' 198 | CHANGE MASTER 'node4' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 199 | START SLAVE 'node4' 200 | # node node3 201 | CHANGE MASTER 'node1' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 202 | START SLAVE 'node1' 203 | CHANGE MASTER 'node2' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 204 | START SLAVE 'node2' 205 | CHANGE MASTER 'node4' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 206 | START SLAVE 'node4' 207 | # node node4 208 | CHANGE MASTER 'node1' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 209 | START SLAVE 'node1' 210 | CHANGE MASTER 'node2' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 211 | START SLAVE 'node2' 212 | CHANGE MASTER 'node3' TO master_host='127.0.0.1', master_port=$MASTER_PORT, master_user='rsandbox', master_password='rsandbox' 213 | START SLAVE 'node3' 214 | 215 | 216 | ## Real installation with MySQL 5.7 (all-masters) 217 | 218 | $ ./multi_source.sh 5.7.8 mysql ALL-MASTERS 219 | installing node 1 220 | installing node 2 221 | installing node 3 222 | installing node 4 223 | group directory installed in $HOME/sandboxes/multi_msb_5_7_8 224 | # option 'master-info-repository=table' added to node1 configuration file 225 | # option 'relay-log-info-repository=table' added to node1 configuration file 226 | # option 'gtid_mode=ON' added to node1 configuration file 227 | # option 'enforce-gtid-consistency' added to node1 configuration file 228 | # option 'master-info-repository=table' added to node2 configuration file 229 | # option 'relay-log-info-repository=table' added to node2 configuration file 230 | # option 'gtid_mode=ON' added to node2 configuration file 231 | # option 'enforce-gtid-consistency' added to node2 configuration file 232 | # option 'master-info-repository=table' added to node3 configuration file 233 | # option 'relay-log-info-repository=table' added to node3 configuration file 234 | # option 'gtid_mode=ON' added to node3 configuration file 235 | # option 'enforce-gtid-consistency' added to node3 configuration file 236 | # option 'master-info-repository=table' added to node4 configuration file 237 | # option 'relay-log-info-repository=table' added to node4 configuration file 238 | # option 'gtid_mode=ON' added to node4 configuration file 239 | # option 'enforce-gtid-consistency' added to node4 configuration file 240 | # server: 1: 241 | # server: 2: 242 | # server: 3: 243 | # server: 4: 244 | # executing "stop" on /home/gmax/sandboxes/multi_msb_5_7_8 245 | executing "stop" on node 1 246 | executing "stop" on node 2 247 | executing "stop" on node 3 248 | executing "stop" on node 4 249 | # executing "start" on /home/gmax/sandboxes/multi_msb_5_7_8 250 | executing "start" on node 1 251 | . sandbox server started 252 | executing "start" on node 2 253 | . sandbox server started 254 | executing "start" on node 3 255 | . sandbox server started 256 | executing "start" on node 4 257 | . sandbox server started 258 | # Setting topology ALL-MASTERS 259 | # node node1 260 | -------------- 261 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8380, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 262 | -------------- 263 | 264 | -------------- 265 | START SLAVE for channel 'node2' 266 | -------------- 267 | 268 | -------------- 269 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8381, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 270 | -------------- 271 | 272 | -------------- 273 | START SLAVE for channel 'node3' 274 | -------------- 275 | 276 | -------------- 277 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8382, master_user='rsandbox', master_password='rsandbox' for channel 'node4' 278 | -------------- 279 | 280 | -------------- 281 | START SLAVE for channel 'node4' 282 | -------------- 283 | 284 | # node node2 285 | -------------- 286 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8379, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 287 | -------------- 288 | 289 | -------------- 290 | START SLAVE for channel 'node1' 291 | -------------- 292 | 293 | -------------- 294 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8381, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 295 | -------------- 296 | 297 | -------------- 298 | START SLAVE for channel 'node3' 299 | -------------- 300 | 301 | -------------- 302 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8382, master_user='rsandbox', master_password='rsandbox' for channel 'node4' 303 | -------------- 304 | 305 | -------------- 306 | START SLAVE for channel 'node4' 307 | -------------- 308 | 309 | # node node3 310 | -------------- 311 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8379, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 312 | -------------- 313 | 314 | -------------- 315 | START SLAVE for channel 'node1' 316 | -------------- 317 | 318 | -------------- 319 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8380, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 320 | -------------- 321 | 322 | -------------- 323 | START SLAVE for channel 'node2' 324 | -------------- 325 | 326 | -------------- 327 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8382, master_user='rsandbox', master_password='rsandbox' for channel 'node4' 328 | -------------- 329 | 330 | -------------- 331 | START SLAVE for channel 'node4' 332 | -------------- 333 | 334 | # node node4 335 | -------------- 336 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8379, master_user='rsandbox', master_password='rsandbox' for channel 'node1' 337 | -------------- 338 | 339 | -------------- 340 | START SLAVE for channel 'node1' 341 | -------------- 342 | 343 | -------------- 344 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8380, master_user='rsandbox', master_password='rsandbox' for channel 'node2' 345 | -------------- 346 | 347 | -------------- 348 | START SLAVE for channel 'node2' 349 | -------------- 350 | 351 | -------------- 352 | CHANGE MASTER TO master_host='127.0.0.1', master_port=8381, master_user='rsandbox', master_password='rsandbox' for channel 'node3' 353 | -------------- 354 | 355 | -------------- 356 | START SLAVE for channel 'node3' 357 | -------------- 358 | 359 | ‘/home/gmax/workdir/git/mysql-replication-samples/test_all_masters_replication.sh’ -> ‘/home/gmax/sandboxes/multi_msb_5_7_8/test_all_masters_replication.sh’ 360 | 361 | ## Testing all-masters installation 362 | 363 | $ ~/workdir/git/mysql-replication-samples$ ~/sandboxes/multi_msb_5_7_8/test_all_masters_replication.sh 364 | # NODE node1 created table test_node1 365 | # NODE node2 created table test_node2 366 | # NODE node3 created table test_node3 367 | # NODE node4 created table test_node4 368 | # Data in all nodes 369 | 101 370 | 1 101 8379 node1 2015-07-26 20:31:09 371 | 1 102 8380 node2 2015-07-26 20:31:10 372 | 1 103 8381 node3 2015-07-26 20:31:10 373 | 1 104 8382 node4 2015-07-26 20:31:10 374 | 102 375 | 1 101 8379 node1 2015-07-26 20:31:09 376 | 1 102 8380 node2 2015-07-26 20:31:10 377 | 1 103 8381 node3 2015-07-26 20:31:10 378 | 1 104 8382 node4 2015-07-26 20:31:10 379 | 103 380 | 1 101 8379 node1 2015-07-26 20:31:09 381 | 1 102 8380 node2 2015-07-26 20:31:10 382 | 1 103 8381 node3 2015-07-26 20:31:10 383 | 1 104 8382 node4 2015-07-26 20:31:10 384 | 104 385 | 1 101 8379 node1 2015-07-26 20:31:09 386 | 1 102 8380 node2 2015-07-26 20:31:10 387 | 1 103 8381 node3 2015-07-26 20:31:10 388 | 1 104 8382 node4 2015-07-26 20:31:10 389 | 390 | -------------------------------------------------------------------------------- /multi_source/test_all_masters_replication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | cd $(dirname $0) 17 | 18 | for N in $(seq 1 9) 19 | do 20 | if [ -d node$N ] 21 | then 22 | NODES[$N]=node$N 23 | fi 24 | done 25 | 26 | COUNT=1 27 | for NODE in ${NODES[*]} 28 | do 29 | $NODE/use test -e "drop table if exists test_$NODE" 30 | $NODE/use test -e "create table test_$NODE( id int not null primary key, serverid int, dbport int, node varchar(100), ts timestamp)" 31 | # Insert a random number of records 32 | RECS=$(($RANDOM%20+1)) 33 | # RECS=$(shuf -i1-20 -n1) 34 | [ -z "$RECS" ] && RECS=$COUNT 35 | for REC in $(seq 1 $RECS) 36 | do 37 | $NODE/use test -e "insert into test_$NODE values ($REC, @@server_id, @@port, '$NODE', null)" 38 | done 39 | COUNT=$(($COUNT+1)) 40 | echo "# NODE $NODE created table test_$NODE - inserted $RECS rows" 41 | done 42 | 43 | sleep 3 44 | echo "# Data in all nodes" 45 | for NODE in ${NODES[*]} 46 | do 47 | $NODE/use -BN -e 'select @@server_id' 48 | for TABLE_NAME in ${NODES[*]} 49 | do 50 | $NODE/use test -BN -e "select 'test_$TABLE_NAME' as table_name, count(*) from test_$TABLE_NAME" 51 | done 52 | done 53 | 54 | -------------------------------------------------------------------------------- /multi_source/test_multi_source_replication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | cd $(dirname $0) 17 | 18 | for NODE in node1 node2 node3 19 | do 20 | $NODE/use test -e "drop table if exists test_$NODE" 21 | $NODE/use test -e "create table test_$NODE( id int not null primary key, serverid int, dbport int, node varchar(100), ts timestamp)" 22 | # RECS=$(shuf -i1-20 -n1) 23 | RECS=$(($RANDOM%20+1)) 24 | [ -z "$RECS" ] && RECS=$COUNT 25 | for REC in $(seq 1 $RECS) 26 | do 27 | $NODE/use test -e "insert into test_$NODE values ($REC, @@server_id, @@port, '$NODE', null)" 28 | done 29 | echo -n "# Tables in server " 30 | $NODE/use -BN -e 'select @@server_id; show tables from test' 31 | # $NODE/use test -e "select @@server_id, id, serverid,dbport,node,ts from test_$NODE " 32 | done 33 | 34 | sleep 3 35 | echo "# Tables in fan-in slave" 36 | ./node4/use -BN -e 'show tables from test' 37 | 38 | ./node4/use -e "select @@server_id as server_id, @@port, 'fan-in slave' as node" 39 | for NODE in node1 node2 node3 40 | do 41 | ./node4/use test -e "select 'test_$NODE' as table_name, count(*) from test_$NODE" 42 | done 43 | 44 | -------------------------------------------------------------------------------- /parallel_replication/checksum.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for NODE in m s1 s2 4 | do 5 | rm -f $NODE.txt 6 | for D in $(./$NODE -BN -e 'show schemas like "db%"') test 7 | do 8 | for T in $(./$NODE -BN -e "show tables from $D" ) 9 | do 10 | ./$NODE -BN -e "checksum table $T" $D >> $NODE.txt 11 | done 12 | done 13 | done 14 | 15 | for SLAVE in s1 s2 16 | do 17 | echo -n "Checksum between m and $SLAVE: " 18 | diff -q m.txt $SLAVE.txt > /dev/null 2>&1 19 | if [ "$?" == "0" ] 20 | then 21 | echo "OK" 22 | else 23 | echo "FAILED" 24 | fi 25 | done 26 | -------------------------------------------------------------------------------- /parallel_replication/insert_many.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | client=$1 4 | DB=$2 5 | table=$3 6 | max_recs=$4 7 | if [ -z "$client" ] 8 | then 9 | echo "# Syntax mysql_client [ DB table_name [max_recs] ]" 10 | exit 1 11 | fi 12 | [ -z "$DB" ] && DB=test 13 | [ -z "$table" ] && table=t1 14 | [ -z "$max_recs" ] && max_recs=1000 15 | $client -e "drop table if exists $table" $DB 16 | $client -e "create table $table (i int not null primary key, ts timestamp) " $DB 17 | $client -e "insert into test.rollcall values ('$DB', '$table', now(), null)" 18 | 19 | for N in $(seq 1 $max_recs) 20 | do 21 | $client -e " insert into $table values ($N, null) " $DB 22 | done 23 | 24 | $client -e "update test.rollcall set ended=now() where schema_name = '$DB' and table_name= '$table'" 25 | -------------------------------------------------------------------------------- /parallel_replication/install_parallel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # 17 | # Simple example of how to set semi-synchronous replication 18 | 19 | VERSION=$1 20 | FLAVOR=$2 21 | THREADS=$3 22 | initialdir=$(dirname $0) 23 | cd $initialdir 24 | initialdir=$PWD 25 | 26 | if [ -z "$FLAVOR" ] 27 | then 28 | echo "VERSION, FLAVOR, and THREADS required" 29 | echo "Where VERSION is an indentifier like 5.7.7 or ma10.0.20 " 30 | echo " FLAVOR is either mysql or mariadb" 31 | echo " and THREADS is the number of parallel appliers to start (default 10)" 32 | exit 1 33 | fi 34 | [ -z "$THREADS" ] && THREADS=10 35 | if [ "$FLAVOR" == "mysql" ] 36 | then 37 | PARALLEL_VAR=slave_parallel_workers 38 | elif [ "$FLAVOR" == "mariadb" ] 39 | then 40 | PARALLEL_VAR=slave_parallel_threads 41 | else 42 | echo "unknown flavor <$FLAVOR>" 43 | exit 1 44 | fi 45 | 46 | 47 | [ -z "$SANDBOX_BINARY" ] && SANDBOX_BINARY=$HOME/opt/mysql 48 | 49 | if [ ! -d $SANDBOX_BINARY/$VERSION ] 50 | then 51 | echo "$SANDBOX_BINARY/$VERSION does not exist" 52 | echo "Set the variable SANDBOX_BINARY to indicate where to find the expanded tarballs for MySQL::Sandbox" 53 | exit 1 54 | fi 55 | 56 | DASHED_VERSION=$(echo $VERSION| tr '.' '_') 57 | sandbox_name=$HOME/sandboxes/rsandbox_$DASHED_VERSION 58 | make_replication_sandbox $VERSION 59 | cp -v checksum.sh insert*.sh multi_*.sh $sandbox_name 60 | cd $sandbox_name 61 | if [ "$FLAVOR" == "mysql" ] 62 | then 63 | ./enable_gtid 64 | fi 65 | 66 | for SLAVE in s1 s2 67 | do 68 | ./$SLAVE -e "stop slave" 69 | ./$SLAVE -e "set global $PARALLEL_VAR=$THREADS" 70 | ./$SLAVE -e "start slave" 71 | ./$SLAVE -e "show global variables like '$PARALLEL_VAR'" 72 | ./$SLAVE -e "show processlist" 73 | done 74 | 75 | -------------------------------------------------------------------------------- /parallel_replication/multi_inserts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -d ./tmp ] 4 | then 5 | mkdir tmp 6 | fi 7 | 8 | ./m -e 'drop table if exists rollcall' test 9 | ./m -e 'create table rollcall( schema_name varchar(50), table_name varchar(50), started datetime, ended datetime, primary key (schema_name, table_name)) ' test 10 | for D in $(seq 1 10) 11 | do 12 | ./m -e "create schema if not exists db$D" 13 | for N in $(seq 1 10) 14 | do 15 | ./insert_many.sh ./m db$D t$N 500 > ./tmp/out_db${D}_t$N.txt 2>&1 & 16 | done 17 | done 18 | -------------------------------------------------------------------------------- /parallel_replication/multi_inserts_one_db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -d ./tmp ] 4 | then 5 | mkdir tmp 6 | fi 7 | 8 | ./m -e 'drop table if exists rollcall' test 9 | ./m -e 'create table rollcall( schema_name varchar(50), table_name varchar(50), started datetime, ended datetime, primary key (schema_name, table_name)) ' test 10 | for D in $(seq 1 10) 11 | do 12 | for N in $(seq 1 10) 13 | do 14 | ./insert_many.sh ./m test d${D}_t$N 500 > ./tmp/out_d${D}_t$N.txt 2>&1 & 15 | done 16 | done 17 | -------------------------------------------------------------------------------- /replication-skeptic-roundup.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datacharmer/mysql-replication-samples/e45b5723340884c082957e6442385f31101262b0/replication-skeptic-roundup.pdf -------------------------------------------------------------------------------- /semi-synchronous/make_semi_synchronous_replication.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # 17 | # Simple example of how to set semi-synchronous replication 18 | 19 | VERSION=$1 20 | 21 | if [ -z "$VERSION" ] 22 | then 23 | echo "VERSION required" 24 | echo "Where VERSION is an indentifier like 5.7.7 or ma10.0.20 " 25 | exit 1 26 | fi 27 | 28 | [ -z "$SANDBOX_BINARY" ] && SANDBOX_BINARY=$HOME/opt/mysql 29 | 30 | if [ ! -d $SANDBOX_BINARY/$VERSION ] 31 | then 32 | echo "$SANDBOX_BINARY/$VERSION does not exist" 33 | echo "Set the variable SANDBOX_BINARY to indicate where to find the expanded tarballs for MySQL::Sandbox" 34 | exit 1 35 | fi 36 | 37 | DASHED_VERSION=$(echo $VERSION| tr '.' '_') 38 | sandbox_name=$HOME/sandboxes/rsandbox_$DASHED_VERSION 39 | # 40 | #make_replication_sandbox $VERSION 41 | #sbtool -o plugin --plugin=semisynch -s $sandbox_name 42 | 43 | M_OPTIONS="-c plugin-load=rpl_semi_sync_master=semisync_master.so" 44 | M_OPTIONS="$M_OPTIONS --post_grants_sql='set global rpl_semi_sync_master_enabled=1'" 45 | S_OPTIONS="-c plugin-load=rpl_semi_sync_slave=semisync_slave.so" 46 | S_OPTIONS="$S_OPTIONS -c rpl_semi_sync_slave_enabled=1" 47 | make_replication_sandbox --gtid --master_options="$M_OPTIONS" --slave_options="$S_OPTIONS" $VERSION 48 | sleep 3 49 | $sandbox_name/m -e 'show variables like "%rpl%"' 50 | $sandbox_name/m -e 'show status like "%rpl%"' 51 | $sandbox_name/s1 -e 'show variables like "%rpl%"' 52 | $sandbox_name/s2 -e 'show variables like "%rpl%"' 53 | -------------------------------------------------------------------------------- /star-and-hybrid/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function check_client 4 | { 5 | func=$1 6 | client=$2 7 | if [ -z "$client" ] 8 | then 9 | echo "# function $func : client_path not set" 10 | exit 1 11 | fi 12 | if [ ! -x $client ] 13 | then 14 | echo "# function $func: client $client not found or not executable" 15 | exit 1 16 | fi 17 | } 18 | 19 | function get_port 20 | { 21 | client_path=$1 22 | check_client get_port $client_path 23 | $client_path -BN -e 'select @@port' 24 | } 25 | 26 | function get_master_file_and_pos 27 | { 28 | client_path=$1 29 | check_client get_master_file_and_pos $client_path 30 | MASTER_STATUS=$($client_path -e 'show master status\G') 31 | master_file=$(echo "$MASTER_STATUS" | grep File: | awk '{print $2}') 32 | master_pos=$(echo "$MASTER_STATUS" | grep Position: | awk '{print $2}') 33 | echo "$master_file $master_pos" 34 | } 35 | 36 | function get_change_master_file_and_pos 37 | { 38 | client_path=$1 39 | check_client get_change_master_file_and_pos $client_path 40 | 41 | file_and_pos=$(get_master_file_and_pos $client_path) 42 | file_and_pos=$(get_master_file_and_pos $HUB/use) 43 | file=$(echo "$file_and_pos" | awk '{print $1}') 44 | pos=$(echo "$file_and_pos" | awk '{print $2}') 45 | echo "MASTER_LOG_FILE='$file', MASTER_LOG_POS=$pos" 46 | } 47 | -------------------------------------------------------------------------------- /star-and-hybrid/hybrid-data-flow.txt: -------------------------------------------------------------------------------- 1 | # server: 1: 2 | t1 # (created) 3 | t2 # host2-host3-host1 4 | t3 # host3-host1 5 | t4 # host4-host3-host1 6 | t7 # host7-host2-host3-host1 7 | # server: 2: 8 | t1 # host1-host3-host2 9 | t2 # (created) 10 | t3 # host3-host2 11 | t4 # host4-host3-host2 12 | t7 # host7-host2 13 | # server: 3: 14 | t1 # host1-host3 15 | t2 # host2-host3 16 | t3 # (created) 17 | t4 # host4-host3 18 | t7 # host7-host2-host3 19 | # server: 4: 20 | t1 # host1-host3-host4 21 | t2 # host2-host3-host4 22 | t3 # host3-host4 23 | t4 # (created) 24 | t5 # host5-host4 25 | t7 # host7-host2-host3-host4 26 | # server: 5: 27 | t4 # host4-host5 28 | t5 # (created) 29 | # server: 6: 30 | t4 # host4 31 | # server: 7: 32 | t1 # host1-host3-host2-host7 33 | t2 # host2-host7 34 | t3 # host3-host2-host7 35 | t4 # host4-host3-host2-host7 36 | t7 # (created) 37 | # server: 8: 38 | t1 # host1-host3-host2-host8 39 | t2 # host2-host3-host2-host8 40 | t3 # host3-host2-host8 41 | t4 # host4-host3-host2-host8 42 | t7 # host7-host2-host8 43 | 44 | -------------------------------------------------------------------------------- /star-and-hybrid/set-hybrid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | # proof of concept of a hybrid topology 18 | make_multiple_sandbox --how_many_nodes=8 5.7.8 19 | 20 | cd $HOME/sandboxes/multi_msb_5_7_8 21 | 22 | OPTIONS="master-info-repository=table " 23 | OPTIONS="$OPTIONS relay-log-info-repository=table" 24 | OPTIONS="$OPTIONS gtid_mode=ON" 25 | OPTIONS="$OPTIONS enforce-gtid-consistency" 26 | for N in $(seq 1 8) 27 | do 28 | MYOPTIONS=$OPTIONS 29 | if [ "$N" == "2" -o "$N" == "3" ] 30 | then 31 | MYOPTIONS="$OPTIONS log-slave-updates" 32 | fi 33 | echo "# NODE $N" 34 | ./node$N/add_option "$MYOPTIONS" 35 | done 36 | set -x 37 | ./node1/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8381, MASTER_AUTO_POSITION=1 for channel 'hub_node1'" 38 | ./node3/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8379, MASTER_AUTO_POSITION=1 for channel 'node1_hub'" 39 | ./node3/use -e "START SLAVE FOR CHANNEL 'node1_hub'" 40 | ./node1/use -e "START SLAVE FOR CHANNEL 'hub_node1'" 41 | # node node2 port: 8380 42 | ./node2/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8381, MASTER_AUTO_POSITION=1 for channel 'hub_node2'" 43 | ./node3/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8380, MASTER_AUTO_POSITION=1 for channel 'node2_hub'" 44 | ./node3/use -e "START SLAVE FOR CHANNEL 'node2_hub'" 45 | ./node2/use -e "START SLAVE FOR CHANNEL 'hub_node2'" 46 | # node node4 port: 8382 47 | ./node4/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8381, MASTER_AUTO_POSITION=1 for channel 'hub_node4'" 48 | ./node3/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8382, MASTER_AUTO_POSITION=1 for channel 'node4_hub'" 49 | ./node3/use -e "START SLAVE FOR CHANNEL 'node4_hub'" 50 | ./node4/use -e "START SLAVE FOR CHANNEL 'hub_node4'" 51 | # node node5 port: 8383 52 | ./node5/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8382, MASTER_AUTO_POSITION=1 for channel 'node4_node5'" 53 | ./node4/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8383, MASTER_AUTO_POSITION=1 for channel 'node5_node4'" 54 | ./node4/use -e "START SLAVE FOR CHANNEL 'node5_node4'" 55 | ./node5/use -e "START SLAVE FOR CHANNEL 'node4_node5'" 56 | 57 | # node node6 port: 8384 58 | ./node6/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8382, MASTER_AUTO_POSITION=1 for channel 'node4_node6'" 59 | ./node6/use -e "START SLAVE FOR CHANNEL 'node4_node6'" 60 | 61 | # node node7 port: 8385 62 | ./node7/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8380, MASTER_AUTO_POSITION=1 for channel 'node2_node7'" 63 | ./node2/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8385, MASTER_AUTO_POSITION=1 for channel 'node7_node2'" 64 | ./node7/use -e "START SLAVE FOR CHANNEL 'node2_node7'" 65 | ./node2/use -e "START SLAVE FOR CHANNEL 'node7_node2'" 66 | 67 | # node node8 port: 8386 68 | ./node8/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8380, MASTER_AUTO_POSITION=1 for channel 'node2_node8'" 69 | ./node8/use -e "START SLAVE FOR CHANNEL 'node2_node8'" 70 | set +x 71 | for N in 1 2 3 4 5 7 72 | do 73 | ./n$N -e "create table test.t$N(id int)" 74 | done 75 | 76 | for N in 1 2 3 4 5 6 7 8 77 | do 78 | echo "# server $N" 79 | ./n$N -BNe " show tables from test" 80 | done 81 | 82 | -------------------------------------------------------------------------------- /star-and-hybrid/set_star_topology.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # 17 | # Proof of concept for a star topology. 18 | # There is a central node (the HUB) and 4 endpoints. 19 | # Every node is a master. 20 | # Changes from the endpoints are replicated to the hub, 21 | # and from there to the other endpoints. 22 | 23 | VERSION=$1 24 | FLAVOR=$2 25 | cd $(dirname $0) 26 | initialdir=$PWD 27 | 28 | 29 | case $FLAVOR in 30 | mysql) 31 | ;; 32 | mariadb) 33 | ;; 34 | *) 35 | unset VERSION 36 | ;; 37 | esac 38 | 39 | if [ -z "$VERSION" ] 40 | then 41 | echo "VERSION and FLAVOR required" 42 | echo "Where VERSION is an indentifier like 5.7.7 or ma10.0.20 " 43 | echo " and FLAVOR is either mysql or mariadb" 44 | exit 1 45 | fi 46 | 47 | . ./common.sh 48 | 49 | [ -z "$SANDBOX_BINARY" ] && SANDBOX_BINARY=$HOME/opt/mysql 50 | 51 | if [ ! -d $SANDBOX_BINARY/$VERSION ] 52 | then 53 | echo "$SANDBOX_BINARY/$VERSION does not exist" 54 | echo "Set the variable SANDBOX_BINARY to indicate where to find the expanded tarballs for MySQL::Sandbox" 55 | exit 1 56 | fi 57 | 58 | DASHED_VERSION=$(echo $VERSION| tr '.' '_') 59 | sandbox_name=$HOME/sandboxes/multi_msb_$DASHED_VERSION 60 | 61 | make_multiple_sandbox --how_many_nodes=5 $VERSION 62 | 63 | cp -v ../multi_source/test_all_masters_replication.sh $sandbox_name 64 | cd $sandbox_name 65 | ./use_all 'reset master' 66 | 67 | #for NODE in node1 node2 node3 node4 node5 68 | #do 69 | # ./$NODE/use -e "create table test.not_replicated_$NODE(id int)" 70 | #done 71 | 72 | OPTIONS="master-info-repository=table " 73 | OPTIONS="$OPTIONS relay-log-info-repository=table" 74 | OPTIONS="$OPTIONS gtid_mode=ON" 75 | OPTIONS="$OPTIONS enforce-gtid-consistency" 76 | if [ "$FLAVOR" == "mariadb" ] 77 | then 78 | OPTIONS="" 79 | fi 80 | 81 | HUB_OPTIONS="$OPTIONS log-slave-updates " 82 | HUB=node3 83 | NODE_COUNT=0 84 | for NODE in node1 node2 node3 node4 node5 85 | do 86 | CHANGED="" 87 | NODE_COUNT=$(($NODE_COUNT+1)) 88 | LOCAL_OPTIONS=$OPTIONS 89 | if [ $NODE == $HUB ] 90 | then 91 | LOCAL_OPTIONS=$HUB_OPTIONS 92 | fi 93 | if [ "$FLAVOR" == "mariadb" ] 94 | then 95 | SERVER_ID=$($NODE/use -BN -e 'select @@server_id') 96 | DOMAIN=$(($SERVER_ID*10)) 97 | LOCAL_OPTIONS="$LOCAL_OPTIONS gtid_domain_id=$DOMAIN" 98 | fi 99 | 100 | for OPTION in $LOCAL_OPTIONS 101 | do 102 | option_exists=$(grep $OPTION $NODE/my.sandbox.cnf) 103 | if [ -z "$option_exists" ] 104 | then 105 | echo "$OPTION" >> $NODE/my.sandbox.cnf 106 | echo "# option '$OPTION' added to $NODE configuration file" 107 | CHANGED=1 108 | else 109 | echo "# option '$OPTION' already exists in $NODE configuration file" 110 | fi 111 | done 112 | if [ -n "$CHANGED" ] 113 | then 114 | $NODE/restart 115 | fi 116 | done 117 | 118 | HUB=node3 119 | ENDPOINTS=(node1 node2 node4 node5) 120 | #HUB_PORT=$(./$HUB/use -BN -e 'select @@port') 121 | HUB_PORT=$( get_port ./$HUB/use) 122 | 123 | echo "# HUB $HUB port: $HUB_PORT" 124 | 125 | for NODE in ${ENDPOINTS[*]} 126 | do 127 | NODE_PORT=$(get_port ./$NODE/use) 128 | echo "# node $NODE port: $NODE_PORT" 129 | NODE_CHANNEL=hub_${NODE} 130 | HUB_CHANNEL=${NODE}_hub 131 | CHANGE_MASTER_FIXED="master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox'" 132 | case $FLAVOR in 133 | mariadb) 134 | auto_position="MASTER_USE_GTID=current_pos" 135 | CHANGE_MASTER_NODE_TO_HUB="CHANGE MASTER '$NODE_CHANNEL' TO $CHANGE_MASTER_FIXED, master_port=$HUB_PORT, $auto_position " 136 | CHANGE_MASTER_HUB_TO_NODE="CHANGE MASTER '$HUB_CHANNEL' TO $CHANGE_MASTER_FIXED, master_port=$NODE_PORT, $auto_position " 137 | START_SLAVE_NODE="START SLAVE '$NODE_CHANNEL' " 138 | START_SLAVE_HUB="START SLAVE '$HUB_CHANNEL' " 139 | ;; 140 | mysql) 141 | auto_position="MASTER_AUTO_POSITION=1" 142 | CHANGE_MASTER_NODE_TO_HUB="CHANGE MASTER TO $CHANGE_MASTER_FIXED, master_port=$HUB_PORT, $auto_position for channel '$NODE_CHANNEL'" 143 | CHANGE_MASTER_HUB_TO_NODE="CHANGE MASTER TO $CHANGE_MASTER_FIXED, master_port=$NODE_PORT, $auto_position for channel '$HUB_CHANNEL'" 144 | START_SLAVE_NODE="START SLAVE FOR CHANNEL '$NODE_CHANNEL'" 145 | START_SLAVE_HUB="START SLAVE FOR CHANNEL '$HUB_CHANNEL'" 146 | ;; 147 | *) 148 | echo "unexpected flavor <$FLAVOR>" 149 | exit 1 150 | ;; 151 | esac 152 | echo "./$NODE/use -e \"$CHANGE_MASTER_NODE_TO_HUB\"" 153 | ./$NODE/use -e "$CHANGE_MASTER_NODE_TO_HUB" 154 | 155 | echo "./$HUB/use -e \"$CHANGE_MASTER_HUB_TO_NODE\"" 156 | ./$HUB/use -e "$CHANGE_MASTER_HUB_TO_NODE" 157 | 158 | echo "./$HUB/use -e \"$START_SLAVE_HUB\"" 159 | ./$HUB/use -e "$START_SLAVE_HUB" 160 | 161 | echo "./$NODE/use -e \"$START_SLAVE_NODE\"" 162 | ./$NODE/use -e "$START_SLAVE_NODE" 163 | done 164 | 165 | -------------------------------------------------------------------------------- /star-and-hybrid/star-change-hub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 Giuseppe Maxia 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # proof of concept of operations for hub replacement 17 | ./node4/use -e "STOP SLAVE FOR CHANNEL 'hub_node4'" 18 | ./node4/use -e "STOP SLAVE " 19 | ./node4/use -e "RESET SLAVE FOR CHANNEL 'hub_node4'" 20 | ./node4/use -e "RESET SLAVE " 21 | # HUB node4 port: 8382 22 | # node node1 port: 8379 23 | ./node1/use -e "STOP SLAVE FOR CHANNEL 'hub_node1'" 24 | ./node1/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8382, MASTER_AUTO_POSITION=1 for channel 'hub_node1'" 25 | ./node4/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8379, MASTER_AUTO_POSITION=1 for channel 'node1_hub'" 26 | ./node4/use -e "START SLAVE FOR CHANNEL 'node1_hub'" 27 | ./node1/use -e "START SLAVE FOR CHANNEL 'hub_node1'" 28 | # node node2 port: 8380 29 | ./node2/use -e "STOP SLAVE FOR CHANNEL 'hub_node2'" 30 | ./node2/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8382, MASTER_AUTO_POSITION=1 for channel 'hub_node2'" 31 | ./node4/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8380, MASTER_AUTO_POSITION=1 for channel 'node2_hub'" 32 | ./node4/use -e "START SLAVE FOR CHANNEL 'node2_hub'" 33 | ./node2/use -e "START SLAVE FOR CHANNEL 'hub_node2'" 34 | # node node5 port: 8383 35 | ./node5/use -e "STOP SLAVE FOR CHANNEL 'hub_node5'" 36 | ./node5/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8382, MASTER_AUTO_POSITION=1 for channel 'hub_node5'" 37 | ./node4/use -e "CHANGE MASTER TO master_host='127.0.0.1', master_user='rsandbox', master_password='rsandbox', master_port=8383, MASTER_AUTO_POSITION=1 for channel 'node5_hub'" 38 | ./node4/use -e "START SLAVE FOR CHANNEL 'node5_hub'" 39 | ./node5/use -e "START SLAVE FOR CHANNEL 'hub_node5'" 40 | --------------------------------------------------------------------------------