├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── binaries └── .gitignore ├── docker-compose.yml ├── docker ├── build │ ├── Makefile │ ├── build-centos7 │ │ └── Dockerfile │ ├── build-centos8 │ │ └── Dockerfile │ ├── build-debian10 │ │ └── Dockerfile │ ├── build-debian11 │ │ └── Dockerfile │ ├── build-debian9 │ │ └── Dockerfile │ ├── build-ubuntu16 │ │ └── Dockerfile │ ├── build-ubuntu18 │ │ └── Dockerfile │ ├── build-ubuntu20 │ │ └── Dockerfile │ ├── build-ubuntu22 │ │ └── Dockerfile │ └── entrypoint.bash └── images │ ├── .gitignore │ ├── Makefile │ ├── mysqlbinlog-centos7 │ └── Dockerfile │ ├── mysqlbinlog-centos8 │ └── Dockerfile │ ├── mysqlbinlog-debian10 │ └── Dockerfile │ ├── mysqlbinlog-debian11 │ └── Dockerfile │ ├── mysqlbinlog-debian9 │ └── Dockerfile │ ├── mysqlbinlog-ubuntu16 │ └── Dockerfile │ ├── mysqlbinlog-ubuntu18 │ └── Dockerfile │ ├── mysqlbinlog-ubuntu20 │ └── Dockerfile │ └── mysqlbinlog-ubuntu22 │ └── Dockerfile ├── libdaemon ├── libdaemon-0.14.tar.gz ├── libev ├── libev-4.24.tar.gz ├── libslave ├── libslave-20171226.tar.gz ├── patches ├── libslave_DBUG_ASSERT.patch ├── libslave_DBUG_ASSERT.patch.O ├── libslave_ER_MALFORMED_GTID_SET_ENCODING.patch ├── libslave_MySQL_8_new_events.patch ├── libslave_SSL_MODE_DISABLED.patch └── slave_allow_rep_formats.patch ├── proxysql_binlog_reader.cpp ├── proxysql_binlog_reader_macro.h └── test ├── Makefile ├── test1.cpp ├── test2.cpp ├── test3.cpp ├── test4.cpp └── test5.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | test/test1 2 | test/test2 3 | test/test3 4 | test/test4 5 | test/test5 6 | 7 | libev-4.24 8 | libdaemon-0.14 9 | libslave-20171226 10 | 11 | proxysql_binlog_reader* 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/bin/make -f 2 | 3 | 4 | ifndef GIT_VERSION 5 | GIT_VERSION := $(shell git describe --long --abbrev=7) 6 | ifndef GIT_VERSION 7 | $(error GIT_VERSION is not set) 8 | endif 9 | endif 10 | 11 | 12 | 13 | .PHONY: default 14 | default: proxysql_binlog_reader 15 | 16 | 17 | proxysql_binlog_reader: proxysql_binlog_reader.cpp libev/.libs/libev.a libdaemon/libdaemon/.libs/libdaemon.a libslave/libslave.a 18 | g++ -o proxysql_binlog_reader proxysql_binlog_reader.cpp -std=c++11 -DGITVERSION=\"$(GIT_VERSION)\" -ggdb ./libslave/libslave.a ./libev/.libs/libev.a libdaemon/libdaemon/.libs/libdaemon.a -I./libslave/ -I./libev/ -I./libdaemon/ -L/usr/lib64/mysql -rdynamic -lz -ldl -lssl -lcrypto -lpthread -lboost_system -lrt -Wl,-Bstatic -lmysqlclient -Wl,-Bdynamic -ldl -lssl -lcrypto -pthread 19 | # -lperconaserverclient if compiled with percona server 20 | 21 | libev/.libs/libev.a: 22 | rm -rf libev-4.24 || true 23 | tar -zxf libev-4.24.tar.gz 24 | cd libev-4.24 && ./configure 25 | cd libev && CC=${CC} CXX=${CXX} ${MAKE} 26 | 27 | libdaemon/libdaemon/.libs/libdaemon.a: 28 | rm -rf libdaemon-0.14 || true 29 | tar -zxf libdaemon-0.14.tar.gz 30 | cd libdaemon && ./configure --disable-examples 31 | cd libdaemon && CC=${CC} CXX=${CXX} ${MAKE} 32 | 33 | libslave/libslave.a: 34 | rm -rf libslave-20171226 35 | tar -zxf libslave-20171226.tar.gz 36 | # Enable for allowing other replication formats (STATEMENT, MIXED) for debugging purposes 37 | #patch -p0 < patches/slave_allow_rep_formats.patch 38 | patch -p0 < patches/libslave_DBUG_ASSERT.patch 39 | patch -p0 < patches/libslave_ER_MALFORMED_GTID_SET_ENCODING.patch 40 | patch -p0 < patches/libslave_SSL_MODE_DISABLED.patch 41 | patch -p0 < patches/libslave_MySQL_8_new_events.patch 42 | cd libslave && cmake . 43 | cd libslave && make slave_a 44 | 45 | 46 | 47 | .PHONY: build 48 | build: build-ubuntu16 build-ubuntu18 build-ubuntu20 build-ubuntu22 build-debian9 build-debian10 build-debian11 build-centos7 build-centos8 49 | 50 | 51 | # universal distro target 52 | .SILENT: 53 | #.PHONY: $(filter $@,$(distros)) 54 | #$(filter $@,$(distros)): IMG_NAME=$(patsubst build-%,%,$@) 55 | #$(filter $@,$(distros)): 56 | .PHONY: build-% 57 | build-%: IMG_NAME=$(patsubst build-%,%,$@) 58 | build-%: PKG_TYPE=$(if $(filter $(shell echo ${IMG_NAME} | grep -Po '[a-z]+'),debian ubuntu),deb,rpm) 59 | build-%: PKG_VERS=$(shell echo ${GIT_VERSION} | grep -Po '(?<=^v|^)[\d\.]+') 60 | build-%: 61 | echo 'building $@' 62 | # build in docker-compose.yml has templating bug, make the image here 63 | # cd ./docker/build/ && ${MAKE} build-${IMG_NAME} 64 | # docker run --rm -v "$(shell pwd)":/opt/proxysql_mysqlbinlog proxysql/packaging:build-$(IMG_NAME) /opt/proxysql_mysqlbinlog/docker/entrypoint-$(PKG_TYPE)/entrypoint.bash 65 | IMG_NAME=$(IMG_NAME) PKG_TYPE=$(PKG_TYPE) PKG_VERS=$(PKG_VERS) GIT_VERS=$(GIT_VERSION) docker compose -p $(IMG_NAME) up mysqlbinlog 66 | docker compose -p $(IMG_NAME) rm -f 67 | 68 | 69 | 70 | .PHONY: cleanbuild 71 | cleanbuild: 72 | rm -f proxysql_binlog_reader || true 73 | rm -f proxysql-mysqlbinlog* || true 74 | rm -rf libev-4.24 75 | rm -rf libdaemon-0.14 76 | rm -rf libslave-20171226 77 | find . -name '*.a' -delete 78 | find . -name '*.o' -delete 79 | 80 | .PHONY: cleanall 81 | cleanall: cleanbuild 82 | rm -rf binaries/* 83 | # rm -f proxysql_binlog_reader || true 84 | # rm -f proxysql-mysqlbinlog* || true 85 | # rm -rf libev-4.24 86 | # rm -rf libdaemon-0.14 87 | # rm -rf libslave-20171226 88 | 89 | 90 | 91 | 92 | 93 | 94 | #################################################################################################### 95 | # OBSOLETE stuff beyond this point 96 | # kept for reference only 97 | #################################################################################################### 98 | 99 | 100 | centos7: binaries/proxysql_binlog_reader-centos7 101 | .PHONY: centos7 102 | 103 | centos8: binaries/proxysql_binlog_reader-centos8 104 | .PHONY: centos8 105 | 106 | ubuntu18: binaries/proxysql_binlog_reader-ubuntu18 107 | .PHONY: ubuntu18 108 | 109 | ubuntu16: binaries/proxysql_binlog_reader-ubuntu16 110 | .PHONY: ubuntu16 111 | 112 | ubuntu14: binaries/proxysql_binlog_reader-ubuntu14 113 | .PHONY: ubuntu14 114 | 115 | debian10: binaries/proxysql_binlog_reader-debian10 116 | .PHONY: debian10 117 | 118 | debian9: binaries/proxysql_binlog_reader-debian9 119 | .PHONY: debian9 120 | 121 | debian8: binaries/proxysql_binlog_reader-debian8 122 | .PHONY: debian8 123 | 124 | debian7: binaries/proxysql_binlog_reader-debian7 125 | .PHONY: debian7 126 | 127 | 128 | binaries/proxysql_binlog_reader-centos7: 129 | docker stop centos7_build || true 130 | docker rm centos7_build || true 131 | docker create --name centos7_build proxysql/packaging:build-centos7 bash -c "while : ; do sleep 10 ; done" 132 | docker start centos7_build 133 | docker exec centos7_build bash -c "yum install -y nss curl libcurl libtool boost boost-devel" || true 134 | docker exec centos7_build bash -c "wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64//mysql-community-devel-5.7.28-1.el7.x86_64.rpm" 135 | docker exec centos7_build bash -c "wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64//mysql-community-libs-5.7.28-1.el7.x86_64.rpm" 136 | docker exec centos7_build bash -c "wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64//mysql-community-common-5.7.28-1.el7.x86_64.rpm" 137 | docker exec centos7_build bash -c "rpm -ihv mysql-community-common-5.7.28-1.el7.x86_64.rpm mysql-community-devel-5.7.28-1.el7.x86_64.rpm mysql-community-libs-5.7.28-1.el7.x86_64.rpm" 138 | docker exec centos7_build bash -c "wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h" 139 | docker exec centos7_build bash -c "cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a && cd /opt/proxysql_mysqlbinlog && make" 140 | sleep 2 141 | docker cp centos7_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-centos7 142 | docker exec centos7_build bash -c "yum -y install ruby rubygems ruby-devel && gem install fpm && fpm -s dir -t rpm -v1.0 --license GPLv3 --category 'Development/Tools' --rpm-summary 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks.' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 143 | docker cp centos7_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog-1.0-1.x86_64.rpm ./binaries/proxysql-mysqlbinlog-1.0-1-centos7.x86_64.rpm 144 | 145 | binaries/proxysql_binlog_reader-centos8: 146 | docker stop centos8_build || true 147 | docker rm centos8_build || true 148 | docker create --name centos8_build proxysql/packaging:build-centos8 bash -c "while : ; do sleep 10 ; done" 149 | docker start centos8_build 150 | docker exec centos8_build bash -c "yum install -y --allowerasing nss curl libcurl libtool boost boost-devel" || true 151 | docker exec centos8_build bash -c "wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64//mysql-community-devel-5.7.28-1.el7.x86_64.rpm" 152 | docker exec centos8_build bash -c "wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64//mysql-community-libs-5.7.28-1.el7.x86_64.rpm" 153 | docker exec centos8_build bash -c "wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64//mysql-community-common-5.7.28-1.el7.x86_64.rpm" 154 | docker exec centos8_build bash -c "rpm -ihv mysql-community-common-5.7.28-1.el7.x86_64.rpm mysql-community-devel-5.7.28-1.el7.x86_64.rpm mysql-community-libs-5.7.28-1.el7.x86_64.rpm" 155 | docker exec centos8_build bash -c "wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h" 156 | docker exec centos8_build bash -c "cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a && cd /opt/proxysql_mysqlbinlog && make" 157 | sleep 2 158 | docker cp centos8_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-centos8 159 | docker exec centos8_build bash -c "yum -y install ruby rubygems ruby-devel && gem install fpm && fpm -s dir -t rpm -v1.0 --license GPLv3 --category 'Development/Tools' --rpm-summary 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks.' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 160 | docker cp centos8_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog-1.0-1.x86_64.rpm ./binaries/proxysql-mysqlbinlog-1.0-1-centos8.x86_64.rpm 161 | 162 | binaries/proxysql_binlog_reader-ubuntu18: 163 | docker stop ubuntu18_build || true 164 | docker rm ubuntu18_build || true 165 | docker create --name ubuntu18_build proxysql/packaging:build-ubuntu18 bash -c "while : ; do sleep 10 ; done" 166 | docker start ubuntu18_build 167 | docker exec ubuntu18_build bash -c "cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a && cd /opt/proxysql_mysqlbinlog && make" 168 | # Enable for allowing other replication formats (STATEMENT, MIXED) for debugging purposes 169 | # # 170 | # docker cp patches/slave_allow_rep_formats.patch ubuntu18_build:/opt/proxysql_mysqlbinlog 171 | # docker exec ubuntu18_build bash -c "cd /opt; cd /opt/proxysql_mysqlbinlog; patch -p0 < patches/slave_allow_rep_formats.patch; cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a && cd /opt/proxysql_mysqlbinlog && rm proxysql_binlog_reader && make" 172 | # # 173 | sleep 2 174 | docker cp ubuntu18_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-ubuntu18 175 | docker exec ubuntu18_build bash -c "apt-get update && apt-get -y install ruby rubygems ruby-dev && gem install fpm && fpm -s dir -t deb -v1.0 --license GPLv3 --category 'Development/Tools' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 176 | docker cp ubuntu18_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog_1.0_amd64.deb ./binaries/proxysql-mysqlbinlog_1.0-ubuntu18_amd64.deb 177 | 178 | binaries/proxysql_binlog_reader-ubuntu16: 179 | docker stop ubuntu16_build || true 180 | docker rm ubuntu16_build || true 181 | docker create --name ubuntu16_build proxysql/packaging:build-ubuntu16 bash -c "while : ; do sleep 10 ; done" 182 | docker start ubuntu16_build 183 | docker exec ubuntu16_build bash -c "cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a && cd /opt/proxysql_mysqlbinlog && make" 184 | sleep 2 185 | docker cp ubuntu16_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-ubuntu16 186 | docker exec ubuntu16_build bash -c "apt-get update && apt-get -y install ruby rubygems ruby-dev && gem install fpm && fpm -s dir -t deb -v1.0 --license GPLv3 --category 'Development/Tools' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 187 | docker cp ubuntu16_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog_1.0_amd64.deb ./binaries/proxysql-mysqlbinlog_1.0-ubuntu16_amd64.deb 188 | 189 | binaries/proxysql_binlog_reader-ubuntu14: 190 | docker stop ubuntu14_build || true 191 | docker rm ubuntu14_build || true 192 | docker create --name ubuntu14_build proxysql/packaging:build-ubuntu14 bash -c "while : ; do sleep 10 ; done" 193 | docker start ubuntu14_build 194 | docker exec ubuntu14_build bash -c "cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a && cd /opt/proxysql_mysqlbinlog && make" 195 | sleep 2 196 | docker cp ubuntu14_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-ubuntu14 197 | ############################################################################## 198 | ## Package build for Ubuntu14 is broken - Ruby version issues in Ubuntu 14: ## 199 | ############################################################################## 200 | ## docker exec ubuntu14_build bash -c "apt-get update && apt-get -y install ruby2.0 ruby2.0-dev && gem2.0 install fpm && fpm -s dir -t deb -v1.0 --license GPLv3 --category 'Development/Tools' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 201 | ## docker cp ubuntu14_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog_1.0_amd64.deb ./binaries/proxysql-mysqlbinlog_1.0-ubuntu14_amd64.deb 202 | 203 | binaries/proxysql_binlog_reader-debian7: 204 | docker stop debian7_build || true 205 | docker rm debian7_build || true 206 | docker create --name debian7_build proxysql/packaging:build-debian7 bash -c "while : ; do sleep 10 ; done" 207 | docker start debian7_build 208 | docker exec debian7_build bash -c "apt-get update && apt-get -y --force-yes install libboost-all-dev && cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a ; cd /opt/proxysql_mysqlbinlog && make" 209 | sleep 2 210 | docker cp debian7_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-debian7 211 | ## docker exec debian7_build bash -c "apt-get update && apt-get -y install ruby rubygems ruby-dev && gem install fpm && fpm -s dir -t deb -v1.0 --license GPLv3 --category 'Development/Tools' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 212 | ## docker cp debian7_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog_1.0_amd64.deb ./binaries/proxysql-mysqlbinlog_1.0-debian7_amd64.deb 213 | 214 | binaries/proxysql_binlog_reader-debian8: 215 | docker stop debian8_build || true 216 | docker rm debian8_build || true 217 | docker create --name debian8_build proxysql/packaging:build-debian8 bash -c "while : ; do sleep 10 ; done" 218 | docker start debian8_build 219 | docker exec debian8_build bash -c "apt-get -y install lsb-release && wget https://dev.mysql.com/get/mysql-apt-config_0.8.9-1_all.deb && export DEBIAN_FRONTEND=noninteractive && echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | debconf-set-selections && dpkg -i mysql-apt-config_0.8.9-1_all.deb && apt-get update && apt-get -y --force-yes upgrade libmysqlclient-dev libmysqlclient18: && wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h && apt-get -y --force-yes install libboost-all-dev && cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a ; cd /opt/proxysql_mysqlbinlog && make" 220 | sleep 2 221 | docker cp debian8_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-debian8 222 | docker exec debian8_build bash -c "apt-get update && apt-get -y install ruby rubygems ruby-dev && gem install fpm && fpm -s dir -t deb -v1.0 --license GPLv3 --category 'Development/Tools' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 223 | docker cp debian8_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog_1.0_amd64.deb ./binaries/proxysql-mysqlbinlog_1.0-debian8_amd64.deb 224 | 225 | binaries/proxysql_binlog_reader-debian9: 226 | docker stop debian9_build || true 227 | docker rm debian9_build || true 228 | docker create --name debian9_build proxysql/packaging:build-debian9 bash -c "while : ; do sleep 10 ; done" 229 | docker start debian9_build 230 | docker exec debian9_build bash -c "apt-get -y install wget lsb-release && wget https://dev.mysql.com/get/mysql-apt-config_0.8.9-1_all.deb && export DEBIAN_FRONTEND=noninteractive && echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | debconf-set-selections && dpkg -i mysql-apt-config_0.8.9-1_all.deb && apt-get update && apt-get -y --force-yes install libmysqlclient-dev libmysqlclient20 && wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h && apt-get -y --force-yes install libboost-all-dev && cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a ; cd /opt/proxysql_mysqlbinlog && make" 231 | sleep 2 232 | docker cp debian9_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-debian9 233 | docker exec debian9_build bash -c "apt-get update && apt-get -y install ruby rubygems ruby-dev && gem install fpm && fpm -s dir -t deb -v1.0 --license GPLv3 --category 'Development/Tools' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 234 | docker cp debian9_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog_1.0_amd64.deb ./binaries/proxysql-mysqlbinlog_1.0-debian9_amd64.deb 235 | 236 | binaries/proxysql_binlog_reader-debian10: 237 | docker stop debian10_build || true 238 | docker rm debian10_build || true 239 | docker create --name debian10_build proxysql/packaging:build-debian10 bash -c "while : ; do sleep 10 ; done" 240 | docker start debian10_build 241 | docker exec debian10_build bash -c "apt-get -y install wget lsb-release && wget https://dev.mysql.com/get/mysql-apt-config_0.8.9-1_all.deb && export DEBIAN_FRONTEND=noninteractive && echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | debconf-set-selections && dpkg -i mysql-apt-config_0.8.9-1_all.deb && apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 8C718D3B5072E1F5; apt-get update && apt-get -y --force-yes install libmysqlclient-dev libmysqlclient20 && wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h && apt-get -y --force-yes install libboost-all-dev && cd /opt; git clone https://github.com/sysown/proxysql_mysqlbinlog.git && cd /opt/proxysql_mysqlbinlog/libslave/ && cmake . && make slave_a ; cd /opt/proxysql_mysqlbinlog && make" 242 | sleep 2 243 | docker cp debian10_build:/opt/proxysql_mysqlbinlog/proxysql_binlog_reader ./binaries/proxysql_binlog_reader-debian10 244 | docker exec debian10_build bash -c "apt-get update && apt-get -y install ruby rubygems ruby-dev && gem install fpm && fpm -s dir -t deb -v1.0 --license GPLv3 --category 'Development/Tools' --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' --url 'https://proxysql.com' --vendor 'ProxySQL LLC' --debug-workspace --workdir /tmp/ --package=/opt/proxysql_mysqlbinlog/ -n proxysql-mysqlbinlog /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/" 245 | docker cp debian10_build:/opt/proxysql_mysqlbinlog/proxysql-mysqlbinlog_1.0_amd64.deb ./binaries/proxysql-mysqlbinlog_1.0-debian10_amd64.deb 246 | 247 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProxySQL - MySQL Binlog Reader 2 | 3 | MySQL Binlog reader and parser to be used with ProxySQL and MySQL. 4 | 5 | MySQL Binlog reader is a service that runs on MySQL host, tracks GTIDs and provides them to ProxySQL for use in adaptive query routing. 6 | https://www.proxysql.com/blog/proxysql-gtid-causal-reads 7 | 8 | ### Requirements 9 | 10 | building requires docker.io, install as per instructions: 11 | 12 | https://docs.docker.com/engine/install/ 13 | 14 | ### Building 15 | 16 | Currently supported targets are: 17 | 18 | - build 19 | - build-centos 20 | - build-centos7 21 | - build-centos8 22 | - build-debian 23 | - build-debian9 24 | - build-debian10 25 | - build-debian11 26 | - build-ubuntu 27 | - build-ubuntu16 28 | - build-ubuntu18 29 | - build-ubuntu20 30 | 31 | to build do: 32 | ``` 33 | cd proxysql_mysqlbinlog 34 | make build-ubuntu18 35 | ``` 36 | 37 | executable and package can be found in `./binaries` 38 | ``` 39 | -rw-r--r-- 1 root root 1679864 May 2 15:56 proxysql-mysqlbinlog_2.0-0-g663ab16-ubuntu18_amd64.deb 40 | -rwxr-xr-x 1 root root 5717720 May 2 15:54 proxysql_binlog_reader-2.0-0-g663ab16-ubuntu18 41 | ``` 42 | 43 | ### Containers 44 | 45 | Ready to use docker containers: 46 | 47 | https://hub.docker.com/r/proxysql/proxysql-mysqlbinlog 48 | 49 | ### HowTo 50 | 51 | on each MySQL server instance run the `proxysql_binlog_reader`, e.g: 52 | 53 | ``` 54 | ./proxysql_binlog_reader -h 127.0.0.1 -u root -p rootpass -P 3306 -l 6020 -f 55 | ``` 56 | 57 | #### Arguments: 58 | 59 | + `-h`: MySQL host 60 | + `-u`: MySQL username 61 | + `-p`: MySQL password 62 | + `-P`: MySQL port 63 | + `-l`: listening port 64 | + `-f`: run in foreground - all logging goes to stdout/stderr 65 | + `-L`: path to log file 66 | 67 | configure ProxySQL `mysql_servers` with coresponding `gtid_port` for each server, and also `mysql_replication_hostgroups`: 68 | 69 | ``` 70 | INSERT INTO mysql_servers (hostgroup_id,hostname,gtid_port,port,max_replication_lag,comment) VALUES (10,'mysql1',6020,3306,1,'mysql1'); 71 | INSERT INTO mysql_servers (hostgroup_id,hostname,gtid_port,port,max_replication_lag,comment) VALUES (20,'mysql2',6020,3306,1,'mysql2'); 72 | INSERT INTO mysql_servers (hostgroup_id,hostname,gtid_port,port,max_replication_lag,comment) VALUES (20,'mysql3',6020,3306,1,'mysql3'); 73 | INSERT INTO mysql_replication_hostgroups (writer_hostgroup,reader_hostgroup,comment) VALUES (10,20,'gtid_replication'); 74 | LOAD MYSQL SERVERS TO RUNTIME; 75 | ``` 76 | 77 | monitor the ProxySQL `stats_mysql_gtid_executed` table for executed GTIDs: 78 | 79 | ``` 80 | SELECT hostname,gtid_executed FROM stats_mysql_gtid_executed\G 81 | 82 | *************************** 1. row *************************** 83 | hostname: mysql1 84 | gtid_executed: 85c17137-4258-11e8-8090-0242ac130002:1-146301 85 | *************************** 2. row *************************** 86 | hostname: mysql2 87 | gtid_executed: 85c17137-4258-11e8-8090-0242ac130002:1-146300,8a093f5f-4258-11e8-8037-0242ac130004:1-5 88 | *************************** 3. row *************************** 89 | hostname: mysql3 90 | gtid_executed: 85c17137-4258-11e8-8090-0242ac130002:1-146301,8a0ac961-4258-11e8-8003-0242ac130003:1-5 91 | ``` 92 | 93 | 94 | ### More 95 | 96 | https://proxysql.com/blog/proxysql-gtid-causal-reads/ 97 | -------------------------------------------------------------------------------- /binaries/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | mysqlbinlog: 4 | image: proxysql/proxysql-mysqlbinlog:build-${IMG_NAME} 5 | # build: ./docker/build/build-${IMG_NAME} 6 | volumes: 7 | - ./docker/build/:/opt/entrypoint/ 8 | - ./:/opt/proxysql_mysqlbinlog/ 9 | environment: 10 | - IMG_NAME=${IMG_NAME} 11 | - PKG_NAME=${PKG_NAME} 12 | - PKG_VERS=${PKG_VERS} 13 | - PKG_TYPE=${PKG_TYPE} 14 | - GIT_VERS=${GIT_VERS} 15 | command: 16 | - /opt/entrypoint/entrypoint.bash 17 | -------------------------------------------------------------------------------- /docker/build/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | NPROCS := $(shell nproc) 4 | export MAKEFLAGS=-j ${NPROCS} 5 | 6 | ifdef PUSH 7 | override PUSH=--push 8 | endif 9 | 10 | ifdef SILENT 11 | override SILENT=-q 12 | endif 13 | 14 | GIT_VERSION=$(shell git describe --long --abbrev=7) 15 | VER=$(shell git describe --long --abbrev=7 | cut -d- -f1) 16 | 17 | 18 | .PHONY: default 19 | default: build 20 | 21 | .PHONY: clean 22 | clean: 23 | docker images -a | grep 'proxysql/packaging-mysqlbinlog' | awk '{print $$3}' | sort | uniq | xargs -r docker rmi -f 24 | 25 | .PHONY: cleanall 26 | cleanall: 27 | docker system prune -a 28 | 29 | 30 | .PHONY: build 31 | build: $(wildcard build-*) 32 | 33 | 34 | .PHONY: build-centos 35 | build-centos: 36 | make $(wildcard build-centos*) 37 | 38 | .PHONY: build-debian 39 | build-debian: 40 | make $(wildcard build-debian*) 41 | 42 | .PHONY: build-ubuntu 43 | build-ubuntu: 44 | make $(wildcard build-ubuntu*) 45 | 46 | 47 | .SILENT: 48 | .PHONY: build-* 49 | build-*: DIST=$(patsubst build-%,%,$@) 50 | build-*: 51 | echo 'building $@' 52 | ifndef MULTIARCH 53 | # build for local only 54 | docker build -t proxysql/proxysql-mysqlbinlog:build-$(DIST)-v$(VER) -t proxysql/proxysql-mysqlbinlog:build-$(DIST) --pull $@ ${PUSH} ${SILENT} 55 | endif 56 | ifdef MULTIARCH 57 | # try building both amd64 and arm64, fallback to native 58 | # docker buildx build -t proxysql/packaging-mysqlbinlog:build-$(DIST) --pull --platform linux/arm64/v8,linux/amd64 $@ ${PUSH} ${SILENT} || \ 59 | # docker buildx build -t proxysql/packaging-mysqlbinlog:build-$(DIST) --pull $@ ${PUSH} ${SILENT} 60 | docker buildx build -t proxysql/packaging-mysqlbinlog:build-$(DIST) --pull $@ ${PUSH} ${SILENT} 61 | endif 62 | echo 'tagged $@' 63 | 64 | 65 | 66 | .ONESHELL: images 67 | images: 68 | echo 'Finished building' 69 | docker images | grep "proxysql/proxysql-mysqlbinlog" 70 | -------------------------------------------------------------------------------- /docker/build/build-centos7/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-centos7-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # fix repos 6 | RUN sed -i 's/mirrorlist/#mirrorlist/' /etc/yum.repos.d/CentOS-*.repo 7 | RUN sed -i 's/#baseurl/baseurl/' /etc/yum.repos.d/CentOS-*.repo 8 | RUN sed -i 's/mirror./vault./' /etc/yum.repos.d/CentOS-*.repo 9 | 10 | # dependencies & tools 11 | RUN yum update -y && \ 12 | yum install -y \ 13 | nss curl libcurl libtool bison \ 14 | boost boost-devel \ 15 | libffi-devel readline-devel sqlite-devel 16 | 17 | # install epel 18 | RUN yum install -y epel-release && \ 19 | rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 20 | 21 | # set git safe dir 22 | RUN git config --system --add safe.directory /opt/ 23 | 24 | # mysql 5.7 libs 25 | RUN wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql-community-devel-5.7.28-1.el7.x86_64.rpm && \ 26 | wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql-community-libs-5.7.28-1.el7.x86_64.rpm && \ 27 | wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql-community-common-5.7.28-1.el7.x86_64.rpm && \ 28 | rpm -ihv mysql-community-common-5.7.28-1.el7.x86_64.rpm mysql-community-devel-5.7.28-1.el7.x86_64.rpm mysql-community-libs-5.7.28-1.el7.x86_64.rpm && \ 29 | wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h 30 | 31 | # fpm packaging 32 | RUN curl -sSL https://rvm.io/mpapis.asc | gpg2 --import - 33 | RUN curl -sSL https://rvm.io/pkuczynski.asc | gpg2 --import - 34 | RUN curl -sSL https://get.rvm.io | bash -s stable 35 | SHELL [ "/bin/bash", "-c" ] 36 | RUN source /etc/profile.d/rvm.sh && rvm install ruby 3.0.2 && rvm --default use ruby-3.0.2 && gem install fpm 37 | 38 | # cleanup rpm cache 39 | RUN yum clean all && \ 40 | rm -rf /var/cache/yum 41 | 42 | ENTRYPOINT [ "bash", "-c", "source /etc/profile.d/rvm.sh && /opt/entrypoint/entrypoint.bash" ] 43 | -------------------------------------------------------------------------------- /docker/build/build-centos8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-centos8-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # set git safe dir 6 | RUN git config --system --add safe.directory / 7 | 8 | # mysql 5.7 libs 9 | RUN yum update -y && \ 10 | yum install -y nss libtool boost boost-devel && \ 11 | wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql-community-devel-5.7.28-1.el7.x86_64.rpm && \ 12 | wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql-community-libs-5.7.28-1.el7.x86_64.rpm && \ 13 | wget --quiet http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql-community-common-5.7.28-1.el7.x86_64.rpm && \ 14 | rpm -ihv mysql-community-common-5.7.28-1.el7.x86_64.rpm mysql-community-devel-5.7.28-1.el7.x86_64.rpm mysql-community-libs-5.7.28-1.el7.x86_64.rpm && \ 15 | wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h 16 | 17 | # fpm packaging 18 | RUN yum install -y @ruby:3.0 ruby-devel rubygems && \ 19 | gem install fpm 20 | 21 | # cleanup rpm cache 22 | RUN yum clean all && \ 23 | rm -rf /var/cache/yum 24 | -------------------------------------------------------------------------------- /docker/build/build-debian10/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-debian10-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # set git safe dir 6 | RUN git config --system --add safe.directory /opt/ 7 | 8 | # mysql 5.7 libs 9 | RUN apt-get -y update && \ 10 | apt-get -y purge mysql-client libmariadbclient-dev libmariadb-dev && \ 11 | wget http://repo.mysql.com/apt/debian/pool/mysql-5.7/m/mysql-community/libmysqlclient20_5.7.37-1debian10_amd64.deb && \ 12 | dpkg -i libmysqlclient20_5.7.37-1debian10_amd64.deb && \ 13 | wget http://repo.mysql.com/apt/debian/pool/mysql-5.7/m/mysql-community/libmysqlclient-dev_5.7.37-1debian10_amd64.deb && \ 14 | dpkg -i libmysqlclient-dev_5.7.37-1debian10_amd64.deb && \ 15 | apt-get -y install libboost-all-dev && \ 16 | wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h 17 | 18 | # fpm packaging 19 | RUN apt-get -y install ruby ruby-dev ruby-ffi ruby-dotenv && \ 20 | gem install fpm 21 | 22 | # clean apt cache 23 | RUN apt clean && \ 24 | rm -rf /var/cache/apt/* && \ 25 | rm -rf /var/lib/apt/lists/* 26 | -------------------------------------------------------------------------------- /docker/build/build-debian11/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-debian11-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # set git safe dir 6 | RUN git config --system --add safe.directory /opt/ 7 | 8 | # mysql 5.7 libs 9 | RUN apt-get -y update && \ 10 | apt-get -y purge mysql-client libmariadbclient-dev libmariadb-dev && \ 11 | wget http://repo.mysql.com/apt/debian/pool/mysql-5.7/m/mysql-community/libmysqlclient20_5.7.37-1debian10_amd64.deb && \ 12 | dpkg -i libmysqlclient20_5.7.37-1debian10_amd64.deb && \ 13 | wget http://repo.mysql.com/apt/debian/pool/mysql-5.7/m/mysql-community/libmysqlclient-dev_5.7.37-1debian10_amd64.deb && \ 14 | dpkg -i libmysqlclient-dev_5.7.37-1debian10_amd64.deb && \ 15 | apt-get -y install libboost-all-dev && \ 16 | wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h 17 | 18 | # fpm packaging 19 | RUN apt-get -y install ruby ruby-dev ruby-ffi ruby-dotenv && \ 20 | gem install fpm 21 | 22 | # clean apt cache 23 | RUN apt clean && \ 24 | rm -rf /var/cache/apt/* && \ 25 | rm -rf /var/lib/apt/lists/* 26 | -------------------------------------------------------------------------------- /docker/build/build-debian9/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-debian9-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # fix repos 6 | RUN sed -i '/stretch-updates/d' /etc/apt/sources.list; \ 7 | sed -i 's/deb.debian/archive.debian/' /etc/apt/sources.list; \ 8 | sed -i 's/security.debian/archive.debian/' /etc/apt/sources.list; \ 9 | sed -i 's/deb /deb [trusted=yes] /' /etc/apt/sources.list 10 | 11 | # set git safe dir 12 | RUN git config --system --add safe.directory /opt/ 13 | 14 | # mysql 5.7 libs 15 | RUN apt-get -y update && \ 16 | apt-get -y purge mysql-client libmariadbclient-dev libmariadb-dev && \ 17 | wget http://repo.mysql.com/apt/debian/pool/mysql-5.7/m/mysql-community/libmysqlclient20_5.7.34-1debian9_amd64.deb && \ 18 | dpkg -i libmysqlclient20_5.7.34-1debian9_amd64.deb && \ 19 | wget http://repo.mysql.com/apt/debian/pool/mysql-5.7/m/mysql-community/libmysqlclient-dev_5.7.34-1debian9_amd64.deb && \ 20 | dpkg -i libmysqlclient-dev_5.7.34-1debian9_amd64.deb && \ 21 | apt-get -y install libboost-all-dev && \ 22 | wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h 23 | 24 | # fpm packaging 25 | RUN apt-get -y install ruby ruby-dev ruby-ffi ruby-dotenv && \ 26 | gem install fpm -v '1.11.0' 27 | 28 | # clean apt cache 29 | RUN apt clean && \ 30 | rm -rf /var/cache/apt/* && \ 31 | rm -rf /var/lib/apt/lists/* 32 | -------------------------------------------------------------------------------- /docker/build/build-ubuntu16/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-ubuntu16-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # set git safe dir 6 | RUN git config --system --add safe.directory /opt/ 7 | 8 | # fpm packaging 9 | RUN apt-get -y update && \ 10 | apt-get -y install ruby ruby-dev ruby-ffi ruby-dotenv && \ 11 | gem install --no-ri --no-rdoc fpm -v '1.11.0' 12 | 13 | # clean apt cache 14 | RUN apt clean && \ 15 | rm -rf /var/cache/apt/* && \ 16 | rm -rf /var/lib/apt/lists/* 17 | -------------------------------------------------------------------------------- /docker/build/build-ubuntu18/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-ubuntu18-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # set git safe dir 6 | RUN git config --system --add safe.directory /opt/ 7 | 8 | # fpm packaging 9 | RUN apt-get -y update && \ 10 | apt-get -y install ruby ruby-dev ruby-ffi ruby-dotenv && \ 11 | gem install fpm 12 | 13 | # clean apt cache 14 | RUN apt clean && \ 15 | rm -rf /var/cache/apt/* && \ 16 | rm -rf /var/lib/apt/lists/* 17 | -------------------------------------------------------------------------------- /docker/build/build-ubuntu20/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-ubuntu20-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # set git safe dir 6 | RUN git config --system --add safe.directory /opt/ 7 | 8 | # mysql 5.7 libs 9 | RUN apt-get -y update && \ 10 | apt-get -y purge mysql-client libmariadbclient-dev libmariadb-dev && \ 11 | wget -q https://repo.mysql.com/apt/ubuntu/pool/mysql-5.7/m/mysql-community/libmysqlclient20_5.7.38-1ubuntu18.04_amd64.deb && \ 12 | dpkg -i libmysqlclient20_5.7.38-1ubuntu18.04_amd64.deb && \ 13 | wget -q https://repo.mysql.com/apt/ubuntu/pool/mysql-5.7/m/mysql-community/libmysqlclient-dev_5.7.38-1ubuntu18.04_amd64.deb && \ 14 | dpkg -i libmysqlclient-dev_5.7.38-1ubuntu18.04_amd64.deb && \ 15 | apt-get -y install libboost-all-dev && \ 16 | wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h 17 | 18 | # fpm packaging 19 | RUN apt-get -y install ruby ruby-dev ruby-ffi ruby-dotenv && \ 20 | gem install fpm 21 | 22 | # clean apt cache 23 | RUN apt clean && \ 24 | rm -rf /var/cache/apt/* && \ 25 | rm -rf /var/lib/apt/lists/* 26 | -------------------------------------------------------------------------------- /docker/build/build-ubuntu22/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM proxysql/packaging:build-ubuntu22-v2.5.3 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # set git safe dir 6 | RUN git config --system --add safe.directory /opt/ 7 | 8 | # mysql 5.7 libs 9 | RUN apt-get -y update && \ 10 | apt-get -y purge mysql-client libmariadbclient-dev libmariadb-dev && \ 11 | wget -q https://repo.mysql.com/apt/ubuntu/pool/mysql-5.7/m/mysql-community/libmysqlclient20_5.7.38-1ubuntu18.04_amd64.deb && \ 12 | dpkg -i libmysqlclient20_5.7.38-1ubuntu18.04_amd64.deb && \ 13 | wget -q https://repo.mysql.com/apt/ubuntu/pool/mysql-5.7/m/mysql-community/libmysqlclient-dev_5.7.38-1ubuntu18.04_amd64.deb && \ 14 | dpkg -i libmysqlclient-dev_5.7.38-1ubuntu18.04_amd64.deb && \ 15 | apt-get -y install libboost-all-dev && \ 16 | wget -q -O /usr/include/mysql/hash.h https://raw.githubusercontent.com/mysql/mysql-server/5.7/include/hash.h 17 | 18 | # fpm packaging 19 | RUN apt-get -y install ruby ruby-dev ruby-ffi ruby-dotenv && \ 20 | gem install fpm 21 | 22 | # clean apt cache 23 | RUN apt clean && \ 24 | rm -rf /var/cache/apt/* && \ 25 | rm -rf /var/lib/apt/lists/* 26 | -------------------------------------------------------------------------------- /docker/build/entrypoint.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -x 3 | 4 | set -eu 5 | 6 | echo "==> Build environment:" 7 | env 8 | 9 | ARCH=$(dpkg --print-architecture || rpm --eval '%{_arch}') 10 | echo "==> '${ARCH}' architecture detected for package" 11 | 12 | DIST=$(source /etc/os-release; echo ${ID%%[-._ ]*}${VERSION%%[-._ ]*}) 13 | echo "==> '${DIST}' distro detected for package" 14 | 15 | echo -e "==> C compiler: ${CC} -> $(readlink -e $(which ${CC}))\n$(${CC} --version)" 16 | echo -e "==> C++ compiler: ${CXX} -> $(readlink -e $(which ${CXX}))\n$(${CXX} --version)" 17 | #echo -e "==> linker version:\n$ ${LD} -> $(readlink -e $(which ${LD}))\n$(${LD} --version)" 18 | 19 | echo "==> Dependecies" 20 | #git config --system --add safe.directory /opt/proxysql_mysqlbinlog 21 | cd /opt/proxysql_mysqlbinlog 22 | 23 | export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD) 24 | echo "==> Epoch: ${SOURCE_DATE_EPOCH}" 25 | 26 | echo "==> Cleaning" 27 | make cleanbuild 28 | #make cleanall 29 | 30 | echo "==> Building" 31 | make -j $(ncpu) 32 | 33 | echo "==> Packaging" 34 | cp -f ./proxysql_binlog_reader ./binaries/proxysql_binlog_reader-${GIT_VERS#v}-${IMG_NAME} 35 | ls -l binaries/ 36 | 37 | fpm \ 38 | --debug \ 39 | -s dir \ 40 | -t ${PKG_TYPE} \ 41 | --version ${PKG_VERS} \ 42 | --source-date-epoch-default ${SOURCE_DATE_EPOCH} \ 43 | --architecture native \ 44 | --license GPLv2 \ 45 | --category 'Development/Tools' \ 46 | --rpm-summary 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks.' \ 47 | --description 'ProxySQL is a high performance, high availability, protocol aware proxy for MySQL and forks (like Percona Server and MariaDB). All the while getting the unlimited freedom that comes with a GPL license. Its development is driven by the lack of open source proxies that provide high performance.' \ 48 | --url 'https://proxysql.com' \ 49 | --vendor 'ProxySQL LLC' \ 50 | --maintainer '' \ 51 | --debug-workspace \ 52 | --workdir /tmp/ \ 53 | --package /opt/proxysql_mysqlbinlog/ \ 54 | --name proxysql-mysqlbinlog \ 55 | /opt/proxysql_mysqlbinlog/proxysql_binlog_reader/=/bin/ 56 | 57 | 58 | if [[ "${PKG_TYPE}" = "deb" ]]; then 59 | mv -f ./proxysql-mysqlbinlog_${PKG_VERS}_${ARCH}.deb ./binaries/proxysql-mysqlbinlog_${GIT_VERS#v}-${IMG_NAME}_${ARCH}.deb 60 | else 61 | mv -f ./proxysql-mysqlbinlog-${PKG_VERS}-1.${ARCH}.rpm ./binaries/proxysql-mysqlbinlog-${GIT_VERS#v}-${IMG_NAME}.${ARCH}.rpm 62 | fi 63 | ls -l binaries/ 64 | -------------------------------------------------------------------------------- /docker/images/.gitignore: -------------------------------------------------------------------------------- 1 | */*.deb 2 | */*.rpm 3 | -------------------------------------------------------------------------------- /docker/images/Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | NPROCS := $(shell nproc) 4 | export MAKEFLAGS=-j ${NPROCS} 5 | 6 | ifdef PUSH 7 | override PUSH=--push 8 | endif 9 | 10 | ifdef SILENT 11 | override SILENT=-q 12 | endif 13 | 14 | GIT_VERSION=$(shell git describe --long --abbrev=7) 15 | VER=$(shell git describe --long --abbrev=7 | cut -d- -f1) 16 | 17 | 18 | .PHONY: default 19 | default: mysqlbinlog 20 | 21 | .PHONY: clean 22 | clean: 23 | docker images -a | grep 'proxysql/proxysql-mysqlbinlog' | awk '{print $$3}' | sort | uniq | xargs -r docker rmi -f 24 | 25 | .PHONY: cleanall 26 | cleanall: 27 | docker system prune -a 28 | 29 | 30 | .PHONY: mysqlbinlog 31 | mysqlbinlog: $(wildcard mysqlbinlog-*) 32 | 33 | 34 | .PHONY: mysqlbinlog-centos 35 | mysqlbinlog-centos: 36 | make $(wildcard mysqlbinlog-centos*) 37 | 38 | .PHONY: mysqlbinlog-debian 39 | mysqlbinlog-debian: 40 | make $(wildcard mysqlbinlog-debian*) 41 | 42 | .PHONY: mysqlbinlog-ubuntu 43 | mysqlbinlog-ubuntu: 44 | make $(wildcard mysqlbinlog-ubuntu*) 45 | 46 | 47 | .SILENT: 48 | .PHONY: mysqlbinlog-* 49 | mysqlbinlog-*: DIST=$(patsubst mysqlbinlog-%,%,$@) 50 | mysqlbinlog-*: TAGV=$(shell echo "${VER}-${DIST}") 51 | mysqlbinlog-*: TAGL=$(shell echo "latest-${DIST}") 52 | mysqlbinlog-*: 53 | echo 'building $@' 54 | rm -f ./$@/proxysql-mysqlbinlog*$(DIST)* 55 | cp ../../binaries/proxysql-mysqlbinlog*$(DIST)* ./$@/ 56 | ifndef MULTIARCH 57 | # build for local only 58 | docker build -t proxysql/proxysql-mysqlbinlog:$(TAGV) -t proxysql/proxysql-mysqlbinlog:$(TAGL) --pull $@ ${PUSH} ${SILENT} 59 | endif 60 | ifdef MULTIARCH 61 | # try building both amd64 and arm64, fallback to native 62 | # docker buildx build -t proxysql/proxysql-mysqlbinlog:$(TAGV) -t proxysql/proxysql-mysqlbinlog:$(TAGL) --pull --platform linux/arm64/v8,linux/amd64 $@ ${PUSH} ${SILENT} || \ 63 | # docker buildx build -t proxysql/proxysql-mysqlbinlog:$(TAGV) -t proxysql/proxysql-mysqlbinlog:$(TAGL) --pull $@ ${PUSH} ${SILENT} 64 | docker buildx build -t proxysql/proxysql-mysqlbinlog:$(TAGV) -t proxysql/proxysql-mysqlbinlog:$(TAGL) -t proxysql/proxysql-mysqlbinlog:latest --pull $@ ${PUSH} ${SILENT} 65 | endif 66 | echo 'tagged $@' 67 | 68 | 69 | 70 | .ONESHELL: images 71 | images: 72 | echo 'Finished building' 73 | docker images | grep "proxysql/proxysql-mysqlbinlog" 74 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-centos7/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # fix keys 6 | RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 7 | 8 | # fix repos 9 | RUN sed -i 's/mirrorlist/#mirrorlist/' /etc/yum.repos.d/CentOS-*.repo 10 | RUN sed -i 's/#baseurl/baseurl/' /etc/yum.repos.d/CentOS-*.repo 11 | RUN sed -i 's/mirror./vault./' /etc/yum.repos.d/CentOS-*.repo 12 | 13 | RUN yum -y update 14 | 15 | # dependencies 16 | RUN yum -y install \ 17 | boost-system 18 | 19 | # copy package from context 20 | COPY proxysql-mysqlbinlog-*.rpm ./ 21 | RUN bash -c "rpm -i proxysql-mysqlbinlog-*$(rpm --eval '%{_arch}').rpm" && \ 22 | rm -f proxysql-mysqlbinlog-*.rpm 23 | 24 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 25 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-centos8/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:8 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # fix keys 6 | RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial 7 | 8 | # fix repos 9 | RUN sed -i 's/mirrorlist/#mirrorlist/' /etc/yum.repos.d/CentOS-*.repo 10 | RUN sed -i 's/#baseurl/baseurl/' /etc/yum.repos.d/CentOS-*.repo 11 | RUN sed -i 's/mirror./vault./' /etc/yum.repos.d/CentOS-*.repo 12 | 13 | RUN yum -y update 14 | 15 | # dependencies 16 | RUN yum -y install \ 17 | boost-system 18 | 19 | # copy package from context 20 | COPY proxysql-mysqlbinlog-*.rpm ./ 21 | RUN bash -c "rpm -i proxysql-mysqlbinlog-*$(rpm --eval '%{_arch}').rpm" && \ 22 | rm -f proxysql-mysqlbinlog-*.rpm 23 | 24 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 25 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-debian10/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:10 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # dependencies 6 | RUN apt-get -y update && \ 7 | apt-get -y install \ 8 | libssl1.1 \ 9 | libboost-system1.67.0 10 | 11 | # copy package from context 12 | COPY proxysql-mysqlbinlog_*.deb ./ 13 | RUN bash -c "yes | dpkg -i proxysql-mysqlbinlog_*$(dpkg --print-architecture).deb" && \ 14 | rm -f proxysql-mysqlbinlog_*.deb 15 | 16 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 17 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-debian11/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:11 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # dependencies 6 | RUN apt-get -y update && \ 7 | apt-get -y install \ 8 | libssl1.1 \ 9 | libboost-system1.74.0 10 | 11 | # copy package from context 12 | COPY proxysql-mysqlbinlog_*.deb ./ 13 | RUN bash -c "yes | dpkg -i proxysql-mysqlbinlog_*$(dpkg --print-architecture).deb" && \ 14 | rm -f proxysql-mysqlbinlog_*.deb 15 | 16 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 17 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-debian9/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:9 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # fix repos 6 | RUN sed -i '/stretch-updates/d' /etc/apt/sources.list; \ 7 | sed -i 's/deb.debian/archive.debian/' /etc/apt/sources.list; \ 8 | sed -i 's/security.debian/archive.debian/' /etc/apt/sources.list; \ 9 | sed -i 's/deb /deb [trusted=yes] /' /etc/apt/sources.list 10 | 11 | # dependencies 12 | RUN apt-get -y update && \ 13 | apt-get -y install \ 14 | libssl1.1 \ 15 | libboost-system1.62.0 16 | 17 | # copy package from context 18 | COPY proxysql-mysqlbinlog_*.deb ./ 19 | RUN bash -c "yes | dpkg -i proxysql-mysqlbinlog_*$(dpkg --print-architecture).deb" && \ 20 | rm -f proxysql-mysqlbinlog_*.deb 21 | 22 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 23 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-ubuntu16/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # dependencies 6 | RUN apt-get -y update && \ 7 | apt-get -y install \ 8 | libssl1.0.0 9 | 10 | # copy package from context 11 | COPY proxysql-mysqlbinlog_*.deb ./ 12 | RUN bash -c "yes | dpkg -i proxysql-mysqlbinlog_*$(dpkg --print-architecture).deb" && \ 13 | rm -f proxysql-mysqlbinlog_*.deb 14 | 15 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 16 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-ubuntu18/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # dependencies 6 | RUN apt-get -y update && \ 7 | apt-get -y install \ 8 | libssl1.1 9 | 10 | # copy package from context 11 | COPY proxysql-mysqlbinlog_*.deb ./ 12 | RUN bash -c "yes | dpkg -i proxysql-mysqlbinlog_*$(dpkg --print-architecture).deb" && \ 13 | rm -f proxysql-mysqlbinlog_*.deb 14 | 15 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 16 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-ubuntu20/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # dependencies 6 | RUN apt-get -y update && \ 7 | apt-get -y install \ 8 | libssl1.1 9 | 10 | # copy package from context 11 | COPY proxysql-mysqlbinlog_*.deb ./ 12 | RUN bash -c "yes | dpkg -i proxysql-mysqlbinlog_*$(dpkg --print-architecture).deb" && \ 13 | rm -f proxysql-mysqlbinlog_*.deb 14 | 15 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 16 | -------------------------------------------------------------------------------- /docker/images/mysqlbinlog-ubuntu22/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | LABEL authors="Miro Stauder " 4 | 5 | # dependencies 6 | RUN apt update -y && \ 7 | apt install -y \ 8 | wget 9 | 10 | # install PPA:nrbrtx/libssl1 11 | RUN wget -O /etc/apt/keyrings/nrbrtx.asc 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x19bfcaf5168d33a9' && \ 12 | echo "deb [signed-by=/etc/apt/keyrings/nrbrtx.asc] https://ppa.launchpadcontent.net/nrbrtx/libssl1/ubuntu jammy main" > /etc/apt/sources.list.d/nrbrt.list 13 | 14 | # install libssl1.1 15 | RUN apt update && \ 16 | apt install -y \ 17 | libssl1.1 18 | 19 | # copy package from context 20 | COPY proxysql-mysqlbinlog_*.deb ./ 21 | RUN bash -c "yes | dpkg -i proxysql-mysqlbinlog_*$(dpkg --print-architecture).deb" && \ 22 | rm -f proxysql-mysqlbinlog_*.deb 23 | 24 | CMD ["sh", "-c", "proxysql_binlog_reader -h \"${MYSQL_HOST:-127.0.0.1}\" -u \"${MYSQL_USER:=root}\" -p \"${MYSQL_PASSWORD:-root}\" -P \"${MYSQL_PORT:-3306}\" -l \"${LISTEN_PORT:-6020}\" -f"] 25 | -------------------------------------------------------------------------------- /libdaemon: -------------------------------------------------------------------------------- 1 | libdaemon-0.14 -------------------------------------------------------------------------------- /libdaemon-0.14.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysown/proxysql_mysqlbinlog/9a8d58f8ecd30172184c669c0158b00e413eb542/libdaemon-0.14.tar.gz -------------------------------------------------------------------------------- /libev: -------------------------------------------------------------------------------- 1 | libev-4.24 -------------------------------------------------------------------------------- /libev-4.24.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysown/proxysql_mysqlbinlog/9a8d58f8ecd30172184c669c0158b00e413eb542/libev-4.24.tar.gz -------------------------------------------------------------------------------- /libslave: -------------------------------------------------------------------------------- 1 | libslave-20171226 -------------------------------------------------------------------------------- /libslave-20171226.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysown/proxysql_mysqlbinlog/9a8d58f8ecd30172184c669c0158b00e413eb542/libslave-20171226.tar.gz -------------------------------------------------------------------------------- /patches/libslave_DBUG_ASSERT.patch: -------------------------------------------------------------------------------- 1 | --- libslave/dec_util.cpp 2 | +++ libslave/dec_util.cpp 3 | @@ -3,6 +3,10 @@ 4 | #include 5 | #include 6 | 7 | +#ifndef DBUG_ASSERT 8 | +#define DBUG_ASSERT(A) assert(A) 9 | +#endif 10 | + 11 | namespace slave 12 | { 13 | 14 | -------------------------------------------------------------------------------- /patches/libslave_DBUG_ASSERT.patch.O: -------------------------------------------------------------------------------- 1 | diff --git a/libslave/dec_util.cpp b/libslave/dec_util.cpp 2 | index dc527cb..1213cde 100644 3 | --- a/libslave/dec_util.cpp 4 | +++ b/libslave/dec_util.cpp 5 | @@ -3,6 +3,10 @@ 6 | #include 7 | #include 8 | 9 | +#ifndef DBUG_ASSERT 10 | +#define DBUG_ASSERT(A) assert(A) 11 | +#endif 12 | + 13 | namespace slave 14 | { 15 | 16 | -------------------------------------------------------------------------------- /patches/libslave_ER_MALFORMED_GTID_SET_ENCODING.patch: -------------------------------------------------------------------------------- 1 | --- libslave/Slave.cpp 2022-03-17 18:40:17.902099104 +0000 2 | +++ libslave/Slave.cpp.N 2022-03-17 18:40:30.449979715 +0000 3 | @@ -39,6 +39,10 @@ 4 | #define ER_MASTER_FATAL_ERROR_READING_BINLOG 1236 5 | #define BIN_LOG_HEADER_SIZE 4 6 | 7 | +#ifndef ER_MALFORMED_GTID_SET_ENCODING 8 | +#define ER_MALFORMED_GTID_SET_ENCODING 1773 9 | +#endif 10 | + 11 | 12 | namespace 13 | { 14 | unsigned char *net_store_length_fast(unsigned char *pkg, unsigned int length) 15 | -------------------------------------------------------------------------------- /patches/libslave_MySQL_8_new_events.patch: -------------------------------------------------------------------------------- 1 | diff --git libslave/slave_log_event.cpp libslave/slave_log_event.cpp 2 | index 6277100..6a86497 100644 3 | --- libslave/slave_log_event.cpp 4 | +++ libslave/slave_log_event.cpp 5 | @@ -364,6 +364,13 @@ bool read_log_event(const char* buf, uint event_len, Basic_event_info& bei, Even 6 | case TRANSACTION_CONTEXT_EVENT: 7 | case VIEW_CHANGE_EVENT: 8 | case XA_PREPARE_LOG_EVENT: 9 | + // UNSUPPORTED: MySQL 8 events - We only acknowledge the existence of this 10 | + // events, they remain UNSUPPORTED by this library. 11 | + // ======================================================================== 12 | + case PARTIAL_UPDATE_ROWS_EVENT: 13 | + case TRANSACTION_PAYLOAD_EVENT: 14 | + case HEARTBEAT_LOG_EVENT_V2: 15 | + // ======================================================================== 16 | if (event_stat) 17 | event_stat->tickOther(); 18 | return false; 19 | diff --git libslave/slave_log_event.h libslave/slave_log_event.h 20 | index 9c66d9f..b5eb714 100644 21 | --- libslave/slave_log_event.h 22 | +++ libslave/slave_log_event.h 23 | @@ -87,6 +87,19 @@ enum Log_event_type 24 | 25 | XA_PREPARE_LOG_EVENT= 38, 26 | 27 | + // 8.0 UNSUPPORTED: MySQL 8 events - We only acknowledge the existence of this 28 | + // events, they remain UNSUPPORTED by this library. Source: 29 | + // - https://github.com/mysql/mysql-server/blob/8.0/libbinlogevents/include/binlog_event.h#L347 30 | + // ======================================================================== 31 | + 32 | + PARTIAL_UPDATE_ROWS_EVENT= 39, 33 | + 34 | + TRANSACTION_PAYLOAD_EVENT= 40, 35 | + 36 | + HEARTBEAT_LOG_EVENT_V2= 41, 37 | + 38 | + // ======================================================================== 39 | + 40 | ENUM_END_EVENT 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /patches/libslave_SSL_MODE_DISABLED.patch: -------------------------------------------------------------------------------- 1 | --- libslave/nanomysql.h 2022-03-17 11:19:01.000000000 +0000 2 | +++ libslave/nanomysql.h.N 2022-03-17 18:56:39.530388394 +0000 3 | @@ -21,6 +21,10 @@ 4 | #include 5 | #include 6 | 7 | +#ifndef SSL_MODE_DISABLED 8 | +#define SSL_MODE_DISABLED 1 9 | +#endif 10 | + 11 | namespace nanomysql { 12 | 13 | struct mysql_conn_opts 14 | -------------------------------------------------------------------------------- /patches/slave_allow_rep_formats.patch: -------------------------------------------------------------------------------- 1 | diff --git libslave/Slave.cpp libslave/Slave.cpp 2 | index 8bfa4a4..c53348f 100644 3 | --- libslave/Slave.cpp 4 | +++ libslave/Slave.cpp 5 | @@ -752,7 +752,8 @@ void Slave::check_master_binlog_format() 6 | return; 7 | 8 | } else { 9 | - throw std::runtime_error("Slave::check_binlog_format(): got invalid binlog format: " + tmp); 10 | + return; 11 | + // throw std::runtime_error("Slave::check_binlog_format(): got invalid binlog format: " + tmp); 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /proxysql_binlog_reader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | #include "Slave.h" 28 | #include "DefaultExtState.h" 29 | 30 | #define BINLOG_VERSION GITVERSION 31 | 32 | #define ioctl_FIONBIO(fd, mode) \ 33 | { \ 34 | int ioctl_mode=mode; \ 35 | ioctl(fd, FIONBIO, (char *)&ioctl_mode); \ 36 | } 37 | 38 | void proxy_error_func(const char *fmt, ...) { 39 | va_list ap; 40 | va_start(ap, fmt); 41 | vfprintf(stderr, fmt, ap); 42 | va_end(ap); 43 | }; 44 | 45 | #define proxy_info(fmt, ...) \ 46 | do { \ 47 | time_t __timer; \ 48 | char __buffer[25]; \ 49 | struct tm *__tm_info; \ 50 | time(&__timer); \ 51 | __tm_info = localtime(&__timer); \ 52 | strftime(__buffer, 25, "%Y-%m-%d %H:%M:%S", __tm_info); \ 53 | proxy_error_func("%s [INFO] " fmt , __buffer , ## __VA_ARGS__); \ 54 | } while(0) 55 | 56 | 57 | unsigned int listen_port = 6020; 58 | struct ev_async async; 59 | std::vector Clients; 60 | 61 | pid_t pid; 62 | time_t laststart; 63 | pthread_mutex_t pos_mutex; 64 | std::vector server_uuids; 65 | std::vector trx_ids; 66 | 67 | std::string gtid_executed_to_string(slave::Position &curpos); 68 | 69 | static struct ev_loop *loop; 70 | 71 | #define NETBUFLEN 256 72 | volatile sig_atomic_t stopflag = 0; 73 | slave::Slave* sl = NULL; 74 | 75 | slave::Position curpos; 76 | 77 | int pipefd[2]; 78 | 79 | char last_server_uuid[256]; 80 | uint64_t last_trxid = 0; 81 | 82 | bool foreground = false; 83 | 84 | char *errorlog = NULL; 85 | 86 | static const char * proxysql_binlog_pid_file() { 87 | static char fn[512]; 88 | snprintf(fn, sizeof(fn), "%s", daemon_pid_file_ident); 89 | return fn; 90 | } 91 | 92 | void flush_error_log() { 93 | if (foreground==false) { 94 | int outfd=0; 95 | int errfd=0; 96 | outfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 97 | if (outfd>0) { 98 | dup2(outfd, STDOUT_FILENO); 99 | close(outfd); 100 | } else { 101 | fprintf(stderr,"Impossible to open file\n"); 102 | } 103 | errfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 104 | if (errfd>0) { 105 | dup2(errfd, STDERR_FILENO); 106 | close(errfd); 107 | } else { 108 | fprintf(stderr,"Impossible to open file\n"); 109 | } 110 | } 111 | } 112 | 113 | 114 | void daemonize_wait_daemon() { 115 | int ret; 116 | if ((ret = daemon_retval_wait(2)) < 0) { 117 | daemon_log(LOG_ERR, "Could not receive return value from daemon process: %s", strerror(errno)); 118 | exit(EXIT_FAILURE); 119 | } 120 | 121 | if (ret) { 122 | daemon_log(LOG_ERR, "Daemon returned %i as return value.", ret); 123 | } 124 | exit(ret); 125 | } 126 | 127 | 128 | bool daemonize_phase2() { 129 | int rc; 130 | /* Close FDs */ 131 | if (daemon_close_all(-1) < 0) { 132 | daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno)); 133 | /* Send the error condition to the parent process */ 134 | daemon_retval_send(1); 135 | return false; 136 | } 137 | 138 | rc=chdir("/tmp"); 139 | if (rc) { 140 | daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", "/tmp", strerror(errno)); 141 | exit(EXIT_FAILURE); 142 | } 143 | 144 | /* Create the PID file */ 145 | if (daemon_pid_file_create() < 0) { 146 | daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); 147 | daemon_retval_send(2); 148 | return false; 149 | } 150 | 151 | /* Send OK to parent process */ 152 | daemon_retval_send(0); 153 | flush_error_log(); 154 | fprintf(stderr,"Starting ProxySQL MySQL Binlog\n"); 155 | fprintf(stderr,"Sucessfully started\n"); 156 | 157 | return true; 158 | } 159 | 160 | void parent_open_error_log() { 161 | if (foreground==false) { 162 | int outfd=0; 163 | int errfd=0; 164 | outfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 165 | if (outfd>0) { 166 | dup2(outfd, STDOUT_FILENO); 167 | close(outfd); 168 | } else { 169 | fprintf(stderr,"Impossible to open file\n"); 170 | } 171 | errfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 172 | if (errfd>0) { 173 | dup2(errfd, STDERR_FILENO); 174 | close(errfd); 175 | } else { 176 | fprintf(stderr,"Impossible to open file\n"); 177 | } 178 | } 179 | } 180 | 181 | 182 | void parent_close_error_log() { 183 | if (foreground==false) { 184 | close(STDOUT_FILENO); 185 | close(STDERR_FILENO); 186 | } 187 | } 188 | 189 | 190 | bool daemonize_phase3() { 191 | int rc; 192 | int status; 193 | //daemon_log(LOG_INFO, "Angel process started ProxySQL process %d\n", pid); 194 | parent_open_error_log(); 195 | fprintf(stderr,"Angel process started ProxySQL MySQL Binlog process %d\n", pid); 196 | parent_close_error_log(); 197 | rc=waitpid(pid, &status, 0); 198 | if (rc==-1) { 199 | parent_open_error_log(); 200 | perror("waitpid"); 201 | //proxy_error("[FATAL]: waitpid: %s\n", perror("waitpid")); 202 | exit(EXIT_FAILURE); 203 | } 204 | rc=WIFEXITED(status); 205 | if (rc) { // client exit()ed 206 | rc=WEXITSTATUS(status); 207 | if (rc==0) { 208 | //daemon_log(LOG_INFO, "Shutdown angel process\n"); 209 | parent_open_error_log(); 210 | fprintf(stderr,"Shutdown angel process\n"); 211 | exit(EXIT_SUCCESS); 212 | } else { 213 | //daemon_log(LOG_INFO, "ProxySQL exited with code %d . Restarting!\n", rc); 214 | parent_open_error_log(); 215 | fprintf(stderr,"ProxySQL exited with code %d . Restarting!\n", rc); 216 | parent_close_error_log(); 217 | return false; 218 | } 219 | } else { 220 | parent_open_error_log(); 221 | fprintf(stderr,"ProxySQL crashed. Restarting!\n"); 222 | parent_close_error_log(); 223 | return false; 224 | } 225 | return true; 226 | } 227 | 228 | void daemonize_phase1(char *argv0) { 229 | int rc; 230 | daemon_pid_file_ident="/tmp/proxysql_mysqlbinlog.pid"; 231 | daemon_log_ident=daemon_ident_from_argv0(argv0); 232 | rc=chdir("/tmp"); 233 | if (rc) { 234 | daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", "/tmp", strerror(errno)); 235 | exit(EXIT_FAILURE); 236 | } 237 | daemon_pid_file_proc=proxysql_binlog_pid_file; 238 | pid=daemon_pid_file_is_running(); 239 | if (pid>=0) { 240 | daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid); 241 | exit(EXIT_FAILURE); 242 | } 243 | if (daemon_retval_init() < 0) { 244 | daemon_log(LOG_ERR, "Failed to create pipe."); 245 | exit(EXIT_FAILURE); 246 | } 247 | } 248 | 249 | 250 | 251 | class Client_Data { 252 | public: 253 | char *data; 254 | size_t len; 255 | size_t size; 256 | size_t pos; 257 | struct ev_io *w; 258 | char uuid_server[64]; 259 | char *ip = NULL; 260 | Client_Data(struct ev_io *_w) { 261 | w = _w; 262 | size = NETBUFLEN; 263 | data = (char *)malloc(size); 264 | uuid_server[0] = 0; 265 | pos = 0; 266 | len = 0; 267 | ip = strdup("unknown"); 268 | } 269 | void resize(size_t _s) { 270 | char *data_ = (char *)malloc(_s); 271 | memcpy(data_, data, (_s > size ? size : _s)); 272 | size = _s; 273 | free(data); 274 | data = data_; 275 | } 276 | void add_string(const char *_ptr, size_t _s) { 277 | if (size < len + _s) { 278 | resize(len + ( _s < 50 ? _s * 10 : _s)); 279 | } 280 | memcpy(data+len,_ptr,_s); 281 | len += _s; 282 | } 283 | ~Client_Data() { 284 | if (ip) free(ip); 285 | free(data); 286 | } 287 | void set_ip(char *a,int p) { 288 | if (ip) free(ip); 289 | ip = (char *)malloc(strlen(a)+16); 290 | sprintf(ip,"%s:%d",a,p); 291 | } 292 | 293 | bool writeout() { 294 | bool ret = true; 295 | if (len==0) { 296 | return ret; 297 | } 298 | int rc = 0; 299 | rc = write(w->fd,data+pos,len-pos); 300 | if (rc > 0) { 301 | pos += rc; 302 | if (pos >= len/2) { 303 | memmove(data,data+pos,len-pos); 304 | len = len-pos; 305 | pos = 0; 306 | } 307 | int new_events = EV_READ; 308 | if (len) { 309 | new_events |= EV_WRITE; 310 | } 311 | if (new_events != w->events) { 312 | ev_io_stop(loop, w); 313 | ev_io_set(w, w->fd, new_events); 314 | ev_io_start(loop, w); 315 | } 316 | } else { 317 | int myerr = errno; 318 | if ( 319 | (rc==0) || 320 | (rc==-1 && myerr != EINTR && myerr != EAGAIN) 321 | ) { 322 | ret = false; 323 | } 324 | } 325 | if (ret == false) { 326 | //std::vector::iterator it; 327 | //it = std::find(Clients.begin(), Clients.end(), w); 328 | //if (it != Clients.end()) { 329 | // proxy_info("Remove client with FD %d\n" , w->fd); 330 | // Clients.erase(it); 331 | ev_io_stop(loop,w); 332 | shutdown(w->fd,SHUT_RDWR); 333 | close(w->fd); 334 | //Client_Data *custom_data = (Client_Data *)watcher->data; 335 | //delete custom_data; 336 | //watcher->data = NULL; 337 | //free(w); 338 | //} 339 | } 340 | return ret; 341 | } 342 | }; 343 | 344 | /* 345 | void write_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) { 346 | Client_Data * custom_data = (Client_Data *)watcher->data; 347 | bool rc = custom_data->writeout(); 348 | if (rc == false) { 349 | delete custom_data; 350 | free(watcher); 351 | } 352 | } 353 | */ 354 | 355 | void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) { 356 | std::vector::iterator it; 357 | it = std::find(Clients.begin(), Clients.end(), watcher); 358 | if (it != Clients.end()) { 359 | //proxy_info("Remove client with FD %d\n", watcher->fd); 360 | Clients.erase(it); 361 | } 362 | if(EV_ERROR & revents) { 363 | perror("got invalid event"); 364 | ev_io_stop(loop,watcher); 365 | shutdown(watcher->fd,SHUT_RDWR); 366 | close(watcher->fd); 367 | Client_Data *custom_data = (Client_Data *)watcher->data; 368 | delete custom_data; 369 | watcher->data = NULL; 370 | free(watcher); 371 | return; 372 | } 373 | ev_io_stop(loop,watcher); 374 | shutdown(watcher->fd,SHUT_RDWR); 375 | close(watcher->fd); 376 | Client_Data *custom_data = (Client_Data *)watcher->data; 377 | delete custom_data; 378 | watcher->data = NULL; 379 | free(watcher); 380 | } 381 | 382 | void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) { 383 | typedef union { 384 | struct sockaddr_in in; 385 | struct sockaddr_in6 in6; 386 | } custom_sockaddr; 387 | custom_sockaddr client_addr; 388 | memset(&client_addr, 0, sizeof(custom_sockaddr)); 389 | socklen_t client_len = sizeof(custom_sockaddr); 390 | int client_sd; 391 | struct ev_io *client = (struct ev_io*) malloc(sizeof(struct ev_io)); 392 | if(EV_ERROR & revents) { 393 | perror("got invalid event"); 394 | free(client); 395 | return; 396 | } 397 | 398 | // Accept client request 399 | client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len); 400 | if (client_sd < 0) { 401 | perror("accept error"); 402 | free(client); 403 | return; 404 | } 405 | ioctl_FIONBIO(client_sd,1); 406 | Client_Data * custom_data = new Client_Data(client); 407 | struct sockaddr *addr = (struct sockaddr *)&client_addr; 408 | switch (addr->sa_family) { 409 | case AF_INET: { 410 | struct sockaddr_in *ipv4 = (struct sockaddr_in *)&client_addr; 411 | char buf[INET_ADDRSTRLEN]; 412 | inet_ntop(addr->sa_family, &ipv4->sin_addr, buf, INET_ADDRSTRLEN); 413 | custom_data->set_ip(buf, ipv4->sin_port); 414 | break; 415 | } 416 | case AF_INET6: { 417 | struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&client_addr; 418 | char buf[INET6_ADDRSTRLEN]; 419 | inet_ntop(addr->sa_family, &ipv6->sin6_addr, buf, INET6_ADDRSTRLEN); 420 | custom_data->set_ip(buf, ipv6->sin6_port); 421 | break; 422 | } 423 | } 424 | client->data = (void *)custom_data; 425 | ev_io_init(client, read_cb, client_sd, EV_READ); 426 | ev_io_start(loop, client); 427 | pthread_mutex_lock(&pos_mutex); 428 | std::string s1 = gtid_executed_to_string(curpos); 429 | pthread_mutex_unlock(&pos_mutex); 430 | s1 = "ST=" + s1 + "\n"; 431 | custom_data->add_string(s1.c_str(), s1.length()); 432 | bool ret = custom_data->writeout(); 433 | if (ret) { 434 | //proxy_info("Adding client with FD %d\n", client->fd); 435 | Clients.push_back(client); 436 | } else { 437 | proxy_info("Error accepting client with FD %d\n", client->fd); 438 | delete custom_data; 439 | free(client); 440 | } 441 | } 442 | 443 | void async_cb(struct ev_loop *loop, struct ev_async *watcher, int revents) { 444 | pthread_mutex_lock(&pos_mutex); 445 | std::vector to_remove; 446 | for (std::vector::iterator it=Clients.begin(); it!=Clients.end(); ++it) { 447 | struct ev_io *w = *it; 448 | Client_Data * custom_data = (Client_Data *)w->data; 449 | for (std::vector::size_type i=0; iuuid_server[0]==0 || strncmp(custom_data->uuid_server, server_uuids.at(i), strlen(server_uuids.at(i)))) { 451 | strcpy(custom_data->uuid_server,server_uuids.at(i)); 452 | custom_data->add_string((const char *)"I1=",3); 453 | std::string s2 = server_uuids.at(i); 454 | s2 += ":" + std::to_string(trx_ids.at(i)) + "\n"; 455 | custom_data->add_string(s2.c_str(),s2.length()); 456 | } else { 457 | std::string s2 = "I2=" + std::to_string(trx_ids.at(i)) + "\n"; 458 | custom_data->add_string(s2.c_str(),s2.length()); 459 | } 460 | } 461 | bool rc = false; 462 | //if (w->active) 463 | rc = custom_data->writeout(); 464 | if (rc == false) { 465 | delete custom_data; 466 | to_remove.push_back(w); 467 | } else { 468 | if (custom_data->size > 2048) { 469 | ev_io_stop(loop,w); 470 | shutdown(w->fd,SHUT_RDWR); 471 | close(w->fd); 472 | delete custom_data; 473 | to_remove.push_back(w); 474 | } 475 | } 476 | } 477 | for (std::vector::iterator it=to_remove.begin(); it!=to_remove.end(); ++it) { 478 | struct ev_io *w = *it; 479 | std::vector::iterator it2 = find(Clients.begin(), Clients.end(), w); 480 | if (it2 != Clients.end()) { 481 | Clients.erase(it2); 482 | free(w); 483 | } 484 | } 485 | for (std::vector::size_type i=0; iclose_connection(); 499 | //std::cout << " Received signal. Stopping at:" << std::endl; 500 | std::string s1 = gtid_executed_to_string(curpos); 501 | //std::cout << s1 << std::endl; 502 | proxy_info("Received signal. Stopping at: %s\n", s1.c_str()); 503 | ev_break(loop, EVBREAK_ALL); 504 | } 505 | 506 | class GTID_Server_Dumper { 507 | private: 508 | struct sockaddr_in addr; 509 | int sd; 510 | int port; 511 | struct ev_io ev_accept; 512 | struct ev_loop *my_loop; 513 | struct ev_timer timer; 514 | public: 515 | GTID_Server_Dumper(int _port) { 516 | port = _port; 517 | sd = socket(PF_INET, SOCK_STREAM, 0); 518 | addr.sin_family = AF_INET; 519 | addr.sin_port = htons(port); 520 | addr.sin_addr.s_addr = INADDR_ANY; 521 | int arg_on = 1; 522 | if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&arg_on, sizeof(arg_on)) == -1) { 523 | perror("setsocketopt()"); 524 | close(sd); 525 | exit(EXIT_FAILURE); 526 | } 527 | 528 | if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 529 | perror("bind"); 530 | exit(EXIT_FAILURE); 531 | } 532 | ioctl_FIONBIO(sd,1); 533 | listen(sd,30); 534 | //struct ev_loop *my_loop = NULL; 535 | my_loop = NULL; 536 | my_loop = ev_loop_new (EVBACKEND_POLL | EVFLAG_NOENV); 537 | loop = my_loop; 538 | if (my_loop == NULL) { 539 | fprintf(stderr,"could not initialise new loop"); 540 | exit(EXIT_FAILURE); 541 | } 542 | ev_io_init(&ev_accept, accept_cb, sd, EV_READ); 543 | ev_io_start(my_loop, &ev_accept); 544 | ev_async_init(&async, async_cb); 545 | ev_async_start(my_loop, &async); 546 | ev_signal signal_watcher1; 547 | ev_signal signal_watcher2; 548 | ev_signal_init (&signal_watcher1, sigint_cb, SIGINT); 549 | ev_signal_init (&signal_watcher2, sigint_cb, SIGTERM); 550 | ev_signal_start (loop, &signal_watcher1); 551 | ev_signal_start (loop, &signal_watcher2); 552 | ev_run(my_loop, 0); 553 | } 554 | ~GTID_Server_Dumper() { 555 | close(sd); 556 | } 557 | }; 558 | 559 | 560 | 561 | void bench_xid_callback(unsigned int server_id) { 562 | pthread_mutex_lock(&pos_mutex); 563 | if (last_trxid==sl->gtid_next.second && strcmp(last_server_uuid,sl->gtid_next.first.c_str())==0) { 564 | // do nothing 565 | pthread_mutex_unlock(&pos_mutex); 566 | } else { 567 | //std::cout << sl->gtid_next.first << ":" << sl->gtid_next.second << std::endl; 568 | strcpy(last_server_uuid,sl->gtid_next.first.c_str()); 569 | last_trxid = sl->gtid_next.second; 570 | char *str=strdup(sl->gtid_next.first.c_str()); 571 | server_uuids.push_back(str); 572 | trx_ids.push_back(sl->gtid_next.second); 573 | curpos.addGtid(sl->gtid_next); 574 | pthread_mutex_unlock(&pos_mutex); 575 | ev_async_send(loop, &async); 576 | } 577 | } 578 | 579 | 580 | bool isStopping() { 581 | return stopflag; 582 | } 583 | 584 | std::string gtid_executed_to_string(slave::Position &curpos) { 585 | std::string gtid_set { "" }; 586 | for (auto it=curpos.gtid_executed.begin(); it!=curpos.gtid_executed.end(); ++it) { 587 | std::string s = it->first; 588 | s.insert(8,"-"); 589 | s.insert(13,"-"); 590 | s.insert(18,"-"); 591 | s.insert(23,"-"); 592 | s = s + ":"; 593 | for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { 594 | std::string s2 = s; 595 | s2 = s2 + std::to_string(itr->first); 596 | s2 = s2 + "-"; 597 | s2 = s2 + std::to_string(itr->second); 598 | s2 = s2 + ","; 599 | gtid_set = gtid_set + s2; 600 | } 601 | } 602 | if (gtid_set.empty() == false) { 603 | gtid_set.pop_back(); 604 | } 605 | return gtid_set; 606 | } 607 | 608 | 609 | 610 | 611 | 612 | void usage(const char* name) { 613 | std::cout << "Usage: " << name << " -h -u -p -P -l -L " << std::endl; 614 | } 615 | 616 | 617 | void * server(void *args) { 618 | GTID_Server_Dumper * serv_dump = new GTID_Server_Dumper(listen_port); 619 | return NULL; 620 | } 621 | 622 | int main(int argc, char** argv) { 623 | std::string host; 624 | std::string user; 625 | std::string password; 626 | std::string errorstr; 627 | unsigned int port = 3306; 628 | 629 | 630 | bool error = false; 631 | 632 | 633 | int c; 634 | while (-1 != (c = ::getopt(argc, argv, "vfh:u:p:P:l:L:"))) { 635 | switch (c) { 636 | case 'f': foreground=true; break; 637 | case 'h': host = optarg; break; 638 | case 'u': user = optarg; break; 639 | case 'p': 640 | password = optarg; 641 | memset(optarg,'x',strlen(optarg)); 642 | break; 643 | case 'P': port = std::stoi(optarg); break; 644 | case 'l': listen_port = std::stoi(optarg); break; 645 | case 'L' : errorstr = optarg; break; 646 | case 'v': 647 | std::cout << "proxysql_binlog_reader version " << BINLOG_VERSION << std::endl; 648 | return 1; 649 | default: 650 | usage(argv[0]); 651 | return 1; 652 | } 653 | } 654 | 655 | if (errorstr.empty()) { 656 | errorlog = (char *)"/tmp/proxysql_mysqlbinlog.log"; 657 | } else { 658 | errorlog = strdup(errorstr.c_str()); 659 | } 660 | 661 | if (host.empty() || user.empty()) 662 | { 663 | usage(argv[0]); 664 | return 1; 665 | } 666 | if (!ev_default_loop (EVBACKEND_POLL | EVFLAG_NOENV)) { 667 | fprintf(stderr,"could not initialise libev"); 668 | exit(EXIT_FAILURE); 669 | } 670 | 671 | 672 | if (foreground==false) { 673 | daemonize_phase1((char *)argv[0]); 674 | if ((pid = daemon_fork()) < 0) { 675 | /* Exit on error */ 676 | daemon_retval_done(); 677 | exit(EXIT_FAILURE); 678 | 679 | } else if (pid) { /* The parent */ 680 | 681 | daemonize_wait_daemon(); 682 | 683 | } else { 684 | if (daemonize_phase2()==false) { 685 | goto finish; 686 | } 687 | 688 | } 689 | 690 | 691 | laststart=0; 692 | if (true) { 693 | gotofork: 694 | if (laststart) { 695 | int currenttime=time(NULL); 696 | if (currenttime == laststart) { /// we do not want to restart multiple times in the same second 697 | // if restart is too frequent, something really bad is going on 698 | parent_open_error_log(); 699 | fprintf(stderr,"Angel process is waiting %d seconds before starting a new process\n", 1); 700 | parent_close_error_log(); 701 | sleep(1); 702 | } 703 | } 704 | laststart=time(NULL); 705 | pid = fork(); 706 | if (pid < 0) { 707 | parent_open_error_log(); 708 | fprintf(stderr,"[FATAL]: Error in fork()\n"); 709 | exit(EXIT_FAILURE); 710 | } 711 | 712 | if (pid) { /* The parent */ 713 | 714 | parent_close_error_log(); 715 | if (daemonize_phase3()==false) { 716 | goto gotofork; 717 | } 718 | 719 | } else { /* The daemon */ 720 | // we open the files also on the child process 721 | // this is required if the child process was created after a crash 722 | parent_open_error_log(); 723 | } 724 | } 725 | 726 | 727 | 728 | } else { 729 | flush_error_log(); 730 | } 731 | 732 | __start_label: 733 | 734 | { 735 | pthread_mutex_init(&pos_mutex, NULL); 736 | 737 | slave::MasterInfo masterinfo; 738 | 739 | masterinfo.conn_options.mysql_host = host; 740 | masterinfo.conn_options.mysql_port = port; 741 | masterinfo.conn_options.mysql_user = user; 742 | masterinfo.conn_options.mysql_pass = password; 743 | 744 | try { 745 | proxy_info("proxysql_binlog_reader version %s\n", BINLOG_VERSION); 746 | 747 | slave::DefaultExtState sDefExtState; 748 | slave::Slave slave(masterinfo, sDefExtState); 749 | sl = &slave; 750 | 751 | slave.setXidCallback(bench_xid_callback); 752 | 753 | //std::cout << "Initializing client..." << std::endl; 754 | proxy_info("Initializing client...\n"); 755 | slave.init(); 756 | // enable GTID 757 | slave.enableGtid(); 758 | 759 | curpos = slave.getLastBinlogPos(); 760 | std::string s1 = gtid_executed_to_string(curpos); 761 | 762 | // Wait until a valid 'GTID' has been executed for requesting binlog 763 | while (s1.empty() && !isStopping()) { 764 | proxy_info("'Executed_Gtid_Set' found empty, retrying...\n"); 765 | usleep(1000 * 1000); 766 | 767 | curpos = slave.getLastBinlogPos(); 768 | s1 = gtid_executed_to_string(curpos); 769 | } 770 | proxy_info("Last executed GTID: '%s'\n", s1.c_str()); 771 | 772 | sDefExtState.setMasterPosition(curpos); 773 | 774 | pthread_t thread_id; 775 | pthread_create(&thread_id, NULL, server , NULL); 776 | 777 | try { 778 | 779 | proxy_info("Reading binlogs...\n"); 780 | slave.get_remote_binlog([&] () 781 | { 782 | const slave::MasterInfo& sMasterInfo = slave.masterInfo(); 783 | return (isStopping()); 784 | }); 785 | 786 | 787 | } catch (std::exception& ex) { 788 | std::cout << "Error in reading binlogs: " << ex.what() << std::endl; 789 | error = true; 790 | } 791 | 792 | pthread_join(thread_id, NULL); 793 | } catch (std::exception& ex) { 794 | std::cout << "Error in initializing slave: " << ex.what() << std::endl; 795 | error = true; 796 | } 797 | } 798 | 799 | finish: 800 | proxy_info("Exiting...\n"); 801 | daemon_retval_send(255); 802 | daemon_signal_done(); 803 | daemon_pid_file_remove(); 804 | return 0; 805 | } 806 | -------------------------------------------------------------------------------- /proxysql_binlog_reader_macro.h: -------------------------------------------------------------------------------- 1 | #define ioctl_FIONBIO(fd, mode) \ 2 | { \ 3 | int ioctl_mode=mode; \ 4 | ioctl(fd, FIONBIO, (char *)&ioctl_mode); \ 5 | } 6 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default 2 | default: test1 test2 test3 test4 test5 3 | 4 | test1: test1.cpp 5 | g++ -o test1 -std=c++11 test1.cpp -ggdb ../libslave/build/libslave.a -I../libslave/ -rdynamic ../libslave/build/libslave.a -lz -ldl -lpthread -lboost_system -lrt -Wl,-Bstatic -lmysqlclient -Wl,-Bdynamic -ldl 6 | 7 | test2: test2.cpp 8 | g++ -o test2 -std=c++11 test2.cpp -ggdb ../libslave/build/libslave.a -I../libslave/ -rdynamic ../libslave/build/libslave.a -lz -ldl -lpthread -lboost_system -lrt -Wl,-Bstatic -lmysqlclient -Wl,-Bdynamic -ldl 9 | 10 | test3: test3.cpp 11 | g++ -o test3 -std=c++11 test3.cpp -ggdb ../libslave/build/libslave.a -I../libslave/ -rdynamic ../libslave/build/libslave.a -lz -ldl -lpthread -lboost_system -lrt -Wl,-Bstatic -lmysqlclient -Wl,-Bdynamic -ldl 12 | 13 | test4: test4.cpp 14 | g++ -o test4 -std=c++11 test4.cpp -ggdb ../libslave/build/libslave.a -I../libslave/ -rdynamic ../libslave/build/libslave.a -lz -ldl -lpthread -lboost_system -lrt -Wl,-Bstatic -lmysqlclient -Wl,-Bdynamic -ldl -lev 15 | 16 | test5: test5.cpp 17 | g++ -o test5 -std=c++11 test5.cpp -ggdb ../libslave/build/libslave.a -I../libslave/ -rdynamic ../libslave/build/libslave.a -lz -ldl -lpthread -lboost_system -lrt -Wl,-Bstatic -lmysqlclient -Wl,-Bdynamic -ldl -lev -ldaemon 18 | 19 | -------------------------------------------------------------------------------- /test/test1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Slave.h" 7 | #include "DefaultExtState.h" 8 | 9 | 10 | volatile sig_atomic_t stopflag = 0; 11 | slave::Slave* sl = NULL; 12 | 13 | void bench_xid_callback(unsigned int server_id) { 14 | std::cout << sl->gtid_next.first << ":" << sl->gtid_next.second << std::endl; 15 | } 16 | 17 | void sighandler(int sig) { 18 | stopflag = 1; 19 | sl->close_connection(); 20 | } 21 | 22 | bool isStopping() { 23 | return stopflag; 24 | } 25 | 26 | std::string gtid_executed_to_string(slave::Position &curpos) { 27 | std::string gtid_set; 28 | for (auto it=curpos.gtid_executed.begin(); it!=curpos.gtid_executed.end(); ++it) { 29 | std::string s = it->first; 30 | s.insert(8,"-"); 31 | s.insert(13,"-"); 32 | s.insert(18,"-"); 33 | s.insert(23,"-"); 34 | s = s + ":"; 35 | //std::cout << s << '\n'; 36 | for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { 37 | std::string s2 = s; 38 | s2 = s2 + std::to_string(itr->first); 39 | s2 = s2 + "-"; 40 | s2 = s2 + std::to_string(itr->second); 41 | //std::cout << s2 << '\n'; 42 | s2 = s2 + ","; 43 | gtid_set = gtid_set + s2; 44 | } 45 | } 46 | gtid_set.pop_back(); 47 | return gtid_set; 48 | } 49 | 50 | 51 | void usage(const char* name) { 52 | std::cout << "Usage: " << name << " -h -u -p -P " << std::endl; 53 | } 54 | 55 | 56 | int main(int argc, char** argv) { 57 | std::string host; 58 | std::string user; 59 | std::string password; 60 | std::string database; 61 | unsigned int port = 3306; 62 | 63 | 64 | int c; 65 | while (-1 != (c = ::getopt(argc, argv, "h:u:p:P:"))) { 66 | switch (c) { 67 | case 'h': host = optarg; break; 68 | case 'u': user = optarg; break; 69 | case 'p': password = optarg; break; 70 | case 'P': port = std::stoi(optarg); break; 71 | default: 72 | usage(argv[0]); 73 | return 1; 74 | } 75 | } 76 | 77 | if (host.empty() || user.empty()) 78 | { 79 | usage(argv[0]); 80 | return 1; 81 | } 82 | 83 | slave::MasterInfo masterinfo; 84 | 85 | masterinfo.conn_options.mysql_host = host; 86 | masterinfo.conn_options.mysql_port = port; 87 | masterinfo.conn_options.mysql_user = user; 88 | masterinfo.conn_options.mysql_pass = password; 89 | signal(SIGINT, sighandler); 90 | signal(SIGTERM, sighandler); 91 | 92 | bool error = false; 93 | 94 | try { 95 | 96 | slave::DefaultExtState sDefExtState; 97 | slave::Slave slave(masterinfo, sDefExtState); 98 | sl = &slave; 99 | 100 | slave.setXidCallback(bench_xid_callback); 101 | 102 | std::cout << "Initializing client..." << std::endl; 103 | slave.init(); 104 | // enable GTID 105 | slave.enableGtid(); 106 | 107 | slave::Position curpos = slave.getLastBinlogPos(); 108 | std::string s1 = gtid_executed_to_string(curpos); 109 | std::cout << s1 << std::endl; 110 | 111 | sDefExtState.setMasterPosition(curpos); 112 | 113 | try { 114 | 115 | std::cout << "Reading binlogs..." << std::endl; 116 | slave.get_remote_binlog([&] () 117 | { 118 | const slave::MasterInfo& sMasterInfo = slave.masterInfo(); 119 | return (isStopping()); 120 | }); 121 | 122 | 123 | } catch (std::exception& ex) { 124 | std::cout << "Error in reading binlogs: " << ex.what() << std::endl; 125 | error = true; 126 | } 127 | 128 | } catch (std::exception& ex) { 129 | std::cout << "Error in initializing slave: " << ex.what() << std::endl; 130 | error = true; 131 | } 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /test/test2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Slave.h" 7 | #include "DefaultExtState.h" 8 | 9 | 10 | volatile sig_atomic_t stopflag = 0; 11 | slave::Slave* sl = NULL; 12 | 13 | slave::Position curpos; 14 | pthread_mutex_t pos_mutex; 15 | 16 | std::string gtid_executed_to_string(slave::Position &curpos); 17 | 18 | void bench_xid_callback(unsigned int server_id) { 19 | pthread_mutex_lock(&pos_mutex); 20 | std::cout << sl->gtid_next.first << ":" << sl->gtid_next.second << std::endl; 21 | curpos.addGtid(sl->gtid_next); 22 | pthread_mutex_unlock(&pos_mutex); 23 | } 24 | 25 | void sighandler(int sig) { 26 | stopflag = 1; 27 | sl->close_connection(); 28 | std::cout << " Received signal. Stopping at:" << std::endl; 29 | std::string s1 = gtid_executed_to_string(curpos); 30 | std::cout << s1 << std::endl; 31 | } 32 | 33 | bool isStopping() { 34 | return stopflag; 35 | } 36 | 37 | std::string gtid_executed_to_string(slave::Position &curpos) { 38 | std::string gtid_set; 39 | for (auto it=curpos.gtid_executed.begin(); it!=curpos.gtid_executed.end(); ++it) { 40 | std::string s = it->first; 41 | s.insert(8,"-"); 42 | s.insert(13,"-"); 43 | s.insert(18,"-"); 44 | s.insert(23,"-"); 45 | s = s + ":"; 46 | //std::cout << s << '\n'; 47 | for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { 48 | std::string s2 = s; 49 | s2 = s2 + std::to_string(itr->first); 50 | s2 = s2 + "-"; 51 | s2 = s2 + std::to_string(itr->second); 52 | //std::cout << s2 << '\n'; 53 | s2 = s2 + ","; 54 | gtid_set = gtid_set + s2; 55 | } 56 | } 57 | gtid_set.pop_back(); 58 | return gtid_set; 59 | } 60 | 61 | 62 | void usage(const char* name) { 63 | std::cout << "Usage: " << name << " -h -u -p -P " << std::endl; 64 | } 65 | 66 | 67 | int main(int argc, char** argv) { 68 | std::string host; 69 | std::string user; 70 | std::string password; 71 | std::string database; 72 | unsigned int port = 3306; 73 | 74 | pthread_mutex_init(&pos_mutex, NULL); 75 | 76 | int c; 77 | while (-1 != (c = ::getopt(argc, argv, "h:u:p:P:"))) { 78 | switch (c) { 79 | case 'h': host = optarg; break; 80 | case 'u': user = optarg; break; 81 | case 'p': password = optarg; break; 82 | case 'P': port = std::stoi(optarg); break; 83 | default: 84 | usage(argv[0]); 85 | return 1; 86 | } 87 | } 88 | 89 | if (host.empty() || user.empty()) 90 | { 91 | usage(argv[0]); 92 | return 1; 93 | } 94 | 95 | slave::MasterInfo masterinfo; 96 | 97 | masterinfo.conn_options.mysql_host = host; 98 | masterinfo.conn_options.mysql_port = port; 99 | masterinfo.conn_options.mysql_user = user; 100 | masterinfo.conn_options.mysql_pass = password; 101 | signal(SIGINT, sighandler); 102 | signal(SIGTERM, sighandler); 103 | 104 | bool error = false; 105 | 106 | try { 107 | 108 | slave::DefaultExtState sDefExtState; 109 | slave::Slave slave(masterinfo, sDefExtState); 110 | sl = &slave; 111 | 112 | slave.setXidCallback(bench_xid_callback); 113 | 114 | std::cout << "Initializing client..." << std::endl; 115 | slave.init(); 116 | // enable GTID 117 | slave.enableGtid(); 118 | 119 | curpos = slave.getLastBinlogPos(); 120 | std::string s1 = gtid_executed_to_string(curpos); 121 | std::cout << s1 << std::endl; 122 | 123 | sDefExtState.setMasterPosition(curpos); 124 | 125 | try { 126 | 127 | std::cout << "Reading binlogs..." << std::endl; 128 | slave.get_remote_binlog([&] () 129 | { 130 | const slave::MasterInfo& sMasterInfo = slave.masterInfo(); 131 | return (isStopping()); 132 | }); 133 | 134 | 135 | } catch (std::exception& ex) { 136 | std::cout << "Error in reading binlogs: " << ex.what() << std::endl; 137 | error = true; 138 | } 139 | 140 | } catch (std::exception& ex) { 141 | std::cout << "Error in initializing slave: " << ex.what() << std::endl; 142 | error = true; 143 | } 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /test/test3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Slave.h" 8 | #include "DefaultExtState.h" 9 | 10 | #define ioctl_FIONBIO(fd, mode) \ 11 | { \ 12 | int ioctl_mode=mode; \ 13 | ioctl(fd, FIONBIO, (char *)&ioctl_mode); \ 14 | } 15 | 16 | #define NGTIDS 2000000 17 | volatile sig_atomic_t stopflag = 0; 18 | slave::Slave* sl = NULL; 19 | 20 | slave::Position curpos; 21 | //pthread_mutex_t pos_mutex; 22 | 23 | int pipefd[2]; 24 | 25 | std::string gtid_executed_to_string(slave::Position &curpos); 26 | 27 | void bench_xid_callback(unsigned int server_id) { 28 | // pthread_mutex_lock(&pos_mutex); 29 | std::cout << sl->gtid_next.first << ":" << sl->gtid_next.second << std::endl; 30 | //curpos.addGtid(sl->gtid_next); 31 | // pthread_mutex_unlock(&pos_mutex); 32 | } 33 | 34 | void sighandler(int sig) { 35 | stopflag = 1; 36 | sl->close_connection(); 37 | std::cout << " Received signal. Stopping at:" << std::endl; 38 | std::string s1 = gtid_executed_to_string(curpos); 39 | std::cout << s1 << std::endl; 40 | } 41 | 42 | bool isStopping() { 43 | return stopflag; 44 | } 45 | 46 | std::string gtid_executed_to_string(slave::Position &curpos) { 47 | std::string gtid_set; 48 | for (auto it=curpos.gtid_executed.begin(); it!=curpos.gtid_executed.end(); ++it) { 49 | std::string s = it->first; 50 | s.insert(8,"-"); 51 | s.insert(13,"-"); 52 | s.insert(18,"-"); 53 | s.insert(23,"-"); 54 | s = s + ":"; 55 | //std::cout << s << '\n'; 56 | for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { 57 | std::string s2 = s; 58 | s2 = s2 + std::to_string(itr->first); 59 | s2 = s2 + "-"; 60 | s2 = s2 + std::to_string(itr->second); 61 | //std::cout << s2 << '\n'; 62 | s2 = s2 + ","; 63 | gtid_set = gtid_set + s2; 64 | } 65 | } 66 | gtid_set.pop_back(); 67 | return gtid_set; 68 | } 69 | 70 | 71 | void usage(const char* name) { 72 | std::cout << "Usage: " << name << " -h -u -p -P " << std::endl; 73 | } 74 | 75 | 76 | void *pipe_reader (void *args) { 77 | char buff[128]; 78 | char uuid[33]; 79 | uint64_t id; 80 | uuid[32]=0; 81 | for (int i=0; i gtid_next = std::make_pair(suuid,id); 88 | curpos.addGtid(gtid_next); 89 | } 90 | return NULL; 91 | } 92 | 93 | int main(int argc, char** argv) { 94 | std::string host; 95 | std::string user; 96 | std::string password; 97 | unsigned int port = 3306; 98 | 99 | //pthread_mutex_init(&pos_mutex, NULL); 100 | 101 | 102 | int c; 103 | while (-1 != (c = ::getopt(argc, argv, "h:u:p:P:"))) { 104 | switch (c) { 105 | case 'h': host = optarg; break; 106 | case 'u': user = optarg; break; 107 | case 'p': password = optarg; break; 108 | case 'P': port = std::stoi(optarg); break; 109 | default: 110 | usage(argv[0]); 111 | return 1; 112 | } 113 | } 114 | 115 | if (host.empty() || user.empty()) 116 | { 117 | usage(argv[0]); 118 | return 1; 119 | } 120 | 121 | int rc; 122 | rc=pipe(pipefd); 123 | //ioctl_FIONBIO(pipefd[0],1); 124 | //ioctl_FIONBIO(pipefd[1],1); 125 | 126 | slave::MasterInfo masterinfo; 127 | 128 | masterinfo.conn_options.mysql_host = host; 129 | masterinfo.conn_options.mysql_port = port; 130 | masterinfo.conn_options.mysql_user = user; 131 | masterinfo.conn_options.mysql_pass = password; 132 | signal(SIGINT, sighandler); 133 | signal(SIGTERM, sighandler); 134 | 135 | bool error = false; 136 | 137 | try { 138 | 139 | slave::DefaultExtState sDefExtState; 140 | slave::Slave slave(masterinfo, sDefExtState); 141 | sl = &slave; 142 | 143 | slave.setXidCallback(bench_xid_callback); 144 | 145 | std::cout << "Initializing client..." << std::endl; 146 | slave.init(); 147 | // enable GTID 148 | slave.enableGtid(); 149 | 150 | curpos = slave.getLastBinlogPos(); 151 | std::string s1 = gtid_executed_to_string(curpos); 152 | std::cout << s1 << std::endl; 153 | 154 | sDefExtState.setMasterPosition(curpos); 155 | 156 | pthread_t thread_id; 157 | pthread_create(&thread_id, NULL, pipe_reader , NULL); 158 | 159 | for (int i=0; i 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | #include "Slave.h" 20 | #include "DefaultExtState.h" 21 | 22 | #define ioctl_FIONBIO(fd, mode) \ 23 | { \ 24 | int ioctl_mode=mode; \ 25 | ioctl(fd, FIONBIO, (char *)&ioctl_mode); \ 26 | } 27 | 28 | unsigned int listen_port = 6020; 29 | struct ev_async async; 30 | std::vector Clients; 31 | 32 | pthread_mutex_t pos_mutex; 33 | std::vector server_uuids; 34 | std::vector trx_ids; 35 | 36 | std::string gtid_executed_to_string(slave::Position &curpos); 37 | 38 | #define NETBUFLEN 256 39 | 40 | class Client_Data { 41 | public: 42 | char *data; 43 | size_t len; 44 | size_t size; 45 | size_t pos; 46 | struct ev_io *w; 47 | char uuid_server[64]; 48 | Client_Data(struct ev_io *_w) { 49 | w = _w; 50 | size = NETBUFLEN; 51 | data = (char *)malloc(size); 52 | uuid_server[0] = 0; 53 | pos = 0; 54 | len = 0; 55 | } 56 | void resize(size_t _s) { 57 | char *data_ = (char *)malloc(_s); 58 | memcpy(data_, data, (_s > size ? size : _s)); 59 | size = _s; 60 | free(data); 61 | data = data_; 62 | } 63 | void add_string(const char *_ptr, size_t _s) { 64 | if (size < len + _s) { 65 | resize(len + _s); 66 | } 67 | memcpy(data+len,_ptr,_s); 68 | len += _s; 69 | } 70 | ~Client_Data() { 71 | free(data); 72 | } 73 | bool writeout() { 74 | bool ret = true; 75 | if (len==0) { 76 | return ret; 77 | } 78 | int rc = 0; 79 | rc = write(w->fd,data+pos,len-pos); 80 | if (rc > 0) { 81 | pos += rc; 82 | if (pos >= len/2) { 83 | memmove(data,data+pos,len-pos); 84 | len = len-pos; 85 | pos = 0; 86 | } 87 | } 88 | return ret; 89 | } 90 | }; 91 | 92 | 93 | void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) { 94 | std::vector::iterator it; 95 | it = std::find(Clients.begin(), Clients.end(), watcher); 96 | if (it != Clients.end()) { 97 | std::cout << "Remove client with FD " << watcher->fd << std::endl; 98 | Clients.erase(it); 99 | } 100 | if(EV_ERROR & revents) { 101 | perror("got invalid event"); 102 | ev_io_stop(loop,watcher); 103 | close(watcher->fd); 104 | Client_Data *custom_data = (Client_Data *)watcher->data; 105 | delete custom_data; 106 | watcher->data = NULL; 107 | free(watcher); 108 | return; 109 | } 110 | close(watcher->fd); 111 | ev_io_stop(loop,watcher); 112 | free(watcher); 113 | } 114 | 115 | #define NGTIDS 2000000 116 | volatile sig_atomic_t stopflag = 0; 117 | slave::Slave* sl = NULL; 118 | 119 | slave::Position curpos; 120 | 121 | int pipefd[2]; 122 | 123 | void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) { 124 | struct sockaddr_in client_addr; 125 | socklen_t client_len = sizeof(client_addr); 126 | int client_sd; 127 | struct ev_io *client = (struct ev_io*) malloc(sizeof(struct ev_io)); 128 | if(EV_ERROR & revents) { 129 | perror("got invalid event"); 130 | free(client); 131 | return; 132 | } 133 | 134 | // Accept client request 135 | client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len); 136 | if (client_sd < 0) { 137 | perror("accept error"); 138 | free(client); 139 | return; 140 | } 141 | Client_Data * custom_data = new Client_Data(client); 142 | client->data = (void *)custom_data; 143 | ev_io_init(client, read_cb, client_sd, EV_READ); 144 | ev_io_start(loop, client); 145 | pthread_mutex_lock(&pos_mutex); 146 | std::string s1 = gtid_executed_to_string(curpos); 147 | pthread_mutex_unlock(&pos_mutex); 148 | s1 = "ST=" + s1 + "\n"; 149 | custom_data->add_string(s1.c_str(), s1.length()); 150 | std::cout << "Push back client with FD " << client->fd << std::endl; 151 | custom_data->writeout(); 152 | Clients.push_back(client); 153 | } 154 | 155 | /*void timeout_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) { 156 | std::cout << "Timeout..." << std::endl; 157 | ev_timer_again (loop, watcher); 158 | } 159 | */ 160 | 161 | void async_cb(struct ev_loop *loop, struct ev_async *watcher, int revents) { 162 | pthread_mutex_lock(&pos_mutex); 163 | //std::cout << sl->gtid_next.first << ":" << sl->gtid_next.second << std::endl; 164 | //std::string s = sl->gtid_next.first; 165 | //s.append(":"); 166 | //s += std::to_string(sl->gtid_next.second); 167 | //s += '\n'; 168 | for (std::vector::iterator it=Clients.begin(); it!=Clients.end(); ++it) { 169 | struct ev_io *w = *it; 170 | Client_Data * custom_data = (Client_Data *)w->data; 171 | for (std::vector::size_type i=0; iuuid_server[0]==0 || memcmp(custom_data->uuid_server,sl->gtid_next.first.c_str(),sl->gtid_next.first.length())) { 173 | if (custom_data->uuid_server[0]==0 || strncmp(custom_data->uuid_server, server_uuids.at(i), strlen(server_uuids.at(i)))) { 174 | //memcpy(custom_data->uuid_server,sl->gtid_next.first.c_str(),sl->gtid_next.first.length()); 175 | strcpy(custom_data->uuid_server,server_uuids.at(i)); 176 | custom_data->add_string((const char *)"I1=",3); 177 | std::string s2 = server_uuids.at(i); 178 | s2 += ":" + std::to_string(trx_ids.at(i)) + "\n"; 179 | //s2 += ":" + std::to_string(sl->gtid_next.second); 180 | custom_data->add_string(s2.c_str(),s2.length()); 181 | } else { 182 | //custom_data->add_string((const char *)"I2=",3); 183 | //std::string s2 = std::to_string(sl->gtid_next.second); 184 | //s2 += '\n'; 185 | //custom_data->add_string(s2.c_str(),s2.length()); 186 | std::string s2 = "I2=" + std::to_string(trx_ids.at(i)) + "\n"; 187 | custom_data->add_string(s2.c_str(),s2.length()); 188 | } 189 | } 190 | custom_data->writeout(); 191 | //write(w->fd,s.c_str(),s.length()); 192 | } 193 | for (std::vector::size_type i=0; iclose_connection(); 207 | std::cout << " Received signal. Stopping at:" << std::endl; 208 | std::string s1 = gtid_executed_to_string(curpos); 209 | std::cout << s1 << std::endl; 210 | ev_break (loop, EVBREAK_ALL); 211 | } 212 | 213 | static struct ev_loop *loop; 214 | class GTID_Server_Dumper { 215 | private: 216 | struct sockaddr_in addr; 217 | int sd; 218 | int port; 219 | struct ev_io ev_accept; 220 | struct ev_loop *my_loop; 221 | struct ev_timer timer; 222 | public: 223 | GTID_Server_Dumper(int _port) { 224 | port = _port; 225 | sd = socket(PF_INET, SOCK_STREAM, 0); 226 | addr.sin_family = AF_INET; 227 | addr.sin_port = htons(port); 228 | addr.sin_addr.s_addr = INADDR_ANY; 229 | int arg_on = 1; 230 | if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&arg_on, sizeof(arg_on)) == -1) { 231 | perror("setsocketopt()"); 232 | close(sd); 233 | exit(EXIT_FAILURE); 234 | } 235 | 236 | if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 237 | perror("bind"); 238 | exit(EXIT_FAILURE); 239 | } 240 | ioctl_FIONBIO(sd,1); 241 | listen(sd,10); 242 | //struct ev_loop *my_loop = NULL; 243 | my_loop = NULL; 244 | my_loop = ev_loop_new (EVBACKEND_POLL | EVFLAG_NOENV); 245 | loop = my_loop; 246 | if (my_loop == NULL) { 247 | fprintf(stderr,"could not initialise new loop"); 248 | exit(EXIT_FAILURE); 249 | } 250 | ev_io_init(&ev_accept, accept_cb, sd, EV_READ); 251 | ev_io_start(my_loop, &ev_accept); 252 | ev_async_init(&async, async_cb); 253 | ev_async_start(my_loop, &async); 254 | ev_signal signal_watcher1; 255 | ev_signal signal_watcher2; 256 | ev_signal_init (&signal_watcher1, sigint_cb, SIGINT); 257 | ev_signal_init (&signal_watcher2, sigint_cb, SIGTERM); 258 | ev_signal_start (loop, &signal_watcher1); 259 | ev_signal_start (loop, &signal_watcher2); 260 | //ev_timer_init (&timer, timeout_cb, 5.5, 0.); 261 | //timer.repeat = 1.; 262 | //ev_timer_again (my_loop, &timer); 263 | //ev_timer_start (my_loop, &timer); 264 | ev_run(my_loop, 0); 265 | } 266 | ~GTID_Server_Dumper() { 267 | close(sd); 268 | } 269 | }; 270 | 271 | 272 | 273 | void bench_xid_callback(unsigned int server_id) { 274 | pthread_mutex_lock(&pos_mutex); 275 | std::cout << sl->gtid_next.first << ":" << sl->gtid_next.second << std::endl; 276 | char *str=strdup(sl->gtid_next.first.c_str()); 277 | server_uuids.push_back(str); 278 | trx_ids.push_back(sl->gtid_next.second); 279 | curpos.addGtid(sl->gtid_next); 280 | pthread_mutex_unlock(&pos_mutex); 281 | ev_async_send(loop, &async); 282 | } 283 | 284 | /* 285 | void sighandler(int sig) { 286 | stopflag = 1; 287 | sl->close_connection(); 288 | std::cout << " Received signal. Stopping at:" << std::endl; 289 | std::string s1 = gtid_executed_to_string(curpos); 290 | std::cout << s1 << std::endl; 291 | ev_break(EV_A_ EVBREAK_ALL); 292 | } 293 | */ 294 | 295 | 296 | 297 | bool isStopping() { 298 | return stopflag; 299 | } 300 | 301 | std::string gtid_executed_to_string(slave::Position &curpos) { 302 | std::string gtid_set; 303 | for (auto it=curpos.gtid_executed.begin(); it!=curpos.gtid_executed.end(); ++it) { 304 | std::string s = it->first; 305 | s.insert(8,"-"); 306 | s.insert(13,"-"); 307 | s.insert(18,"-"); 308 | s.insert(23,"-"); 309 | s = s + ":"; 310 | //std::cout << s << '\n'; 311 | for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { 312 | std::string s2 = s; 313 | s2 = s2 + std::to_string(itr->first); 314 | s2 = s2 + "-"; 315 | s2 = s2 + std::to_string(itr->second); 316 | //std::cout << s2 << '\n'; 317 | s2 = s2 + ","; 318 | gtid_set = gtid_set + s2; 319 | } 320 | } 321 | gtid_set.pop_back(); 322 | return gtid_set; 323 | } 324 | 325 | 326 | void usage(const char* name) { 327 | std::cout << "Usage: " << name << " -h -u -p -P " << std::endl; 328 | } 329 | 330 | 331 | void * server(void *args) { 332 | GTID_Server_Dumper * serv_dump = new GTID_Server_Dumper(listen_port); 333 | return NULL; 334 | } 335 | 336 | /* 337 | void *pipe_reader (void *args) { 338 | char buff[128]; 339 | char uuid[33]; 340 | uint64_t id; 341 | uuid[32]=0; 342 | for (int i=0; i gtid_next = std::make_pair(suuid,id); 349 | curpos.addGtid(gtid_next); 350 | } 351 | return NULL; 352 | } 353 | */ 354 | 355 | int main(int argc, char** argv) { 356 | std::string host; 357 | std::string user; 358 | std::string password; 359 | unsigned int port = 3306; 360 | 361 | 362 | pthread_mutex_init(&pos_mutex, NULL); 363 | 364 | 365 | int c; 366 | while (-1 != (c = ::getopt(argc, argv, "h:u:p:P:l:"))) { 367 | switch (c) { 368 | case 'h': host = optarg; break; 369 | case 'u': user = optarg; break; 370 | case 'p': password = optarg; break; 371 | case 'P': port = std::stoi(optarg); break; 372 | case 'l': listen_port = std::stoi(optarg); break; 373 | default: 374 | usage(argv[0]); 375 | return 1; 376 | } 377 | } 378 | 379 | if (host.empty() || user.empty()) 380 | { 381 | usage(argv[0]); 382 | return 1; 383 | } 384 | if (!ev_default_loop (EVBACKEND_POLL | EVFLAG_NOENV)) { 385 | fprintf(stderr,"could not initialise libev"); 386 | exit(EXIT_FAILURE); 387 | } 388 | 389 | /* 390 | int rc; 391 | rc=pipe(pipefd); 392 | //ioctl_FIONBIO(pipefd[0],1); 393 | //ioctl_FIONBIO(pipefd[1],1); 394 | */ 395 | slave::MasterInfo masterinfo; 396 | 397 | masterinfo.conn_options.mysql_host = host; 398 | masterinfo.conn_options.mysql_port = port; 399 | masterinfo.conn_options.mysql_user = user; 400 | masterinfo.conn_options.mysql_pass = password; 401 | //signal(SIGINT, sighandler); 402 | //signal(SIGTERM, sighandler); 403 | 404 | bool error = false; 405 | 406 | try { 407 | 408 | slave::DefaultExtState sDefExtState; 409 | slave::Slave slave(masterinfo, sDefExtState); 410 | sl = &slave; 411 | 412 | slave.setXidCallback(bench_xid_callback); 413 | 414 | std::cout << "Initializing client..." << std::endl; 415 | slave.init(); 416 | // enable GTID 417 | slave.enableGtid(); 418 | 419 | curpos = slave.getLastBinlogPos(); 420 | std::string s1 = gtid_executed_to_string(curpos); 421 | std::cout << s1 << std::endl; 422 | 423 | sDefExtState.setMasterPosition(curpos); 424 | 425 | pthread_t thread_id; 426 | //pthread_create(&thread_id, NULL, pipe_reader , NULL); 427 | pthread_create(&thread_id, NULL, server , NULL); 428 | 429 | /* 430 | for (int i=0; i 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | #include "Slave.h" 27 | #include "DefaultExtState.h" 28 | 29 | #define ioctl_FIONBIO(fd, mode) \ 30 | { \ 31 | int ioctl_mode=mode; \ 32 | ioctl(fd, FIONBIO, (char *)&ioctl_mode); \ 33 | } 34 | 35 | unsigned int listen_port = 6020; 36 | struct ev_async async; 37 | std::vector Clients; 38 | 39 | pid_t pid; 40 | time_t laststart; 41 | pthread_mutex_t pos_mutex; 42 | std::vector server_uuids; 43 | std::vector trx_ids; 44 | 45 | std::string gtid_executed_to_string(slave::Position &curpos); 46 | 47 | 48 | #define NETBUFLEN 256 49 | volatile sig_atomic_t stopflag = 0; 50 | slave::Slave* sl = NULL; 51 | 52 | slave::Position curpos; 53 | 54 | int pipefd[2]; 55 | 56 | 57 | bool foreground = false; 58 | 59 | char *errorlog = NULL; 60 | 61 | static const char * proxysql_binlog_pid_file() { 62 | static char fn[512]; 63 | snprintf(fn, sizeof(fn), "%s", daemon_pid_file_ident); 64 | return fn; 65 | } 66 | 67 | void flush_error_log() { 68 | if (foreground==false) { 69 | int outfd=0; 70 | int errfd=0; 71 | outfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 72 | if (outfd>0) { 73 | dup2(outfd, STDOUT_FILENO); 74 | close(outfd); 75 | } else { 76 | fprintf(stderr,"Impossible to open file\n"); 77 | } 78 | errfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 79 | if (errfd>0) { 80 | dup2(errfd, STDERR_FILENO); 81 | close(errfd); 82 | } else { 83 | fprintf(stderr,"Impossible to open file\n"); 84 | } 85 | } 86 | } 87 | 88 | 89 | void daemonize_wait_daemon() { 90 | int ret; 91 | if ((ret = daemon_retval_wait(2)) < 0) { 92 | daemon_log(LOG_ERR, "Could not receive return value from daemon process: %s", strerror(errno)); 93 | exit(EXIT_FAILURE); 94 | } 95 | 96 | if (ret) { 97 | daemon_log(LOG_ERR, "Daemon returned %i as return value.", ret); 98 | } 99 | exit(ret); 100 | } 101 | 102 | 103 | bool daemonize_phase2() { 104 | int rc; 105 | /* Close FDs */ 106 | if (daemon_close_all(-1) < 0) { 107 | daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno)); 108 | /* Send the error condition to the parent process */ 109 | daemon_retval_send(1); 110 | return false; 111 | } 112 | 113 | rc=chdir("/tmp"); 114 | if (rc) { 115 | daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", "/tmp", strerror(errno)); 116 | exit(EXIT_FAILURE); 117 | } 118 | 119 | /* Create the PID file */ 120 | if (daemon_pid_file_create() < 0) { 121 | daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); 122 | daemon_retval_send(2); 123 | return false; 124 | } 125 | 126 | /* Send OK to parent process */ 127 | daemon_retval_send(0); 128 | flush_error_log(); 129 | fprintf(stderr,"Starting ProxySQL MySQL Binlog\n"); 130 | fprintf(stderr,"Sucessfully started\n"); 131 | 132 | return true; 133 | } 134 | 135 | void parent_open_error_log() { 136 | if (foreground==false) { 137 | int outfd=0; 138 | int errfd=0; 139 | outfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 140 | if (outfd>0) { 141 | dup2(outfd, STDOUT_FILENO); 142 | close(outfd); 143 | } else { 144 | fprintf(stderr,"Impossible to open file\n"); 145 | } 146 | errfd=open(errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 147 | if (errfd>0) { 148 | dup2(errfd, STDERR_FILENO); 149 | close(errfd); 150 | } else { 151 | fprintf(stderr,"Impossible to open file\n"); 152 | } 153 | } 154 | } 155 | 156 | 157 | void parent_close_error_log() { 158 | if (foreground==false) { 159 | close(STDOUT_FILENO); 160 | close(STDERR_FILENO); 161 | } 162 | } 163 | 164 | 165 | bool daemonize_phase3() { 166 | int rc; 167 | int status; 168 | //daemon_log(LOG_INFO, "Angel process started ProxySQL process %d\n", pid); 169 | parent_open_error_log(); 170 | fprintf(stderr,"Angel process started ProxySQL MySQL Binlog process %d\n", pid); 171 | parent_close_error_log(); 172 | rc=waitpid(pid, &status, 0); 173 | if (rc==-1) { 174 | parent_open_error_log(); 175 | perror("waitpid"); 176 | //proxy_error("[FATAL]: waitpid: %s\n", perror("waitpid")); 177 | exit(EXIT_FAILURE); 178 | } 179 | rc=WIFEXITED(status); 180 | if (rc) { // client exit()ed 181 | rc=WEXITSTATUS(status); 182 | if (rc==0) { 183 | //daemon_log(LOG_INFO, "Shutdown angel process\n"); 184 | parent_open_error_log(); 185 | fprintf(stderr,"Shutdown angel process\n"); 186 | exit(EXIT_SUCCESS); 187 | } else { 188 | //daemon_log(LOG_INFO, "ProxySQL exited with code %d . Restarting!\n", rc); 189 | parent_open_error_log(); 190 | fprintf(stderr,"ProxySQL exited with code %d . Restarting!\n", rc); 191 | parent_close_error_log(); 192 | return false; 193 | } 194 | } else { 195 | parent_open_error_log(); 196 | fprintf(stderr,"ProxySQL crashed. Restarting!\n"); 197 | parent_close_error_log(); 198 | return false; 199 | } 200 | return true; 201 | } 202 | 203 | void daemonize_phase1(char *argv0) { 204 | int rc; 205 | daemon_pid_file_ident="/tmp/proxysql_mysqlbinlog.pid"; 206 | daemon_log_ident=daemon_ident_from_argv0(argv0); 207 | rc=chdir("/tmp"); 208 | if (rc) { 209 | daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", "/tmp", strerror(errno)); 210 | exit(EXIT_FAILURE); 211 | } 212 | daemon_pid_file_proc=proxysql_binlog_pid_file; 213 | pid=daemon_pid_file_is_running(); 214 | if (pid>=0) { 215 | daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid); 216 | exit(EXIT_FAILURE); 217 | } 218 | if (daemon_retval_init() < 0) { 219 | daemon_log(LOG_ERR, "Failed to create pipe."); 220 | exit(EXIT_FAILURE); 221 | } 222 | } 223 | 224 | 225 | 226 | class Client_Data { 227 | public: 228 | char *data; 229 | size_t len; 230 | size_t size; 231 | size_t pos; 232 | struct ev_io *w; 233 | char uuid_server[64]; 234 | Client_Data(struct ev_io *_w) { 235 | w = _w; 236 | size = NETBUFLEN; 237 | data = (char *)malloc(size); 238 | uuid_server[0] = 0; 239 | pos = 0; 240 | len = 0; 241 | } 242 | void resize(size_t _s) { 243 | char *data_ = (char *)malloc(_s); 244 | memcpy(data_, data, (_s > size ? size : _s)); 245 | size = _s; 246 | free(data); 247 | data = data_; 248 | } 249 | void add_string(const char *_ptr, size_t _s) { 250 | if (size < len + _s) { 251 | resize(len + _s); 252 | } 253 | memcpy(data+len,_ptr,_s); 254 | len += _s; 255 | } 256 | ~Client_Data() { 257 | free(data); 258 | } 259 | bool writeout() { 260 | bool ret = true; 261 | if (len==0) { 262 | return ret; 263 | } 264 | int rc = 0; 265 | rc = write(w->fd,data+pos,len-pos); 266 | if (rc > 0) { 267 | pos += rc; 268 | if (pos >= len/2) { 269 | memmove(data,data+pos,len-pos); 270 | len = len-pos; 271 | pos = 0; 272 | } 273 | } else { 274 | int myerr = errno; 275 | if ( 276 | (rc==0) || 277 | (rc==-1 && myerr != EINTR && myerr != EAGAIN) 278 | ) { 279 | ret = false; 280 | } 281 | } 282 | return ret; 283 | } 284 | }; 285 | 286 | 287 | void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) { 288 | std::vector::iterator it; 289 | it = std::find(Clients.begin(), Clients.end(), watcher); 290 | if (it != Clients.end()) { 291 | std::cout << "Remove client with FD " << watcher->fd << std::endl; 292 | Clients.erase(it); 293 | } 294 | if(EV_ERROR & revents) { 295 | perror("got invalid event"); 296 | ev_io_stop(loop,watcher); 297 | close(watcher->fd); 298 | Client_Data *custom_data = (Client_Data *)watcher->data; 299 | delete custom_data; 300 | watcher->data = NULL; 301 | free(watcher); 302 | return; 303 | } 304 | close(watcher->fd); 305 | ev_io_stop(loop,watcher); 306 | free(watcher); 307 | } 308 | 309 | void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) { 310 | struct sockaddr_in client_addr; 311 | socklen_t client_len = sizeof(client_addr); 312 | int client_sd; 313 | struct ev_io *client = (struct ev_io*) malloc(sizeof(struct ev_io)); 314 | if(EV_ERROR & revents) { 315 | perror("got invalid event"); 316 | free(client); 317 | return; 318 | } 319 | 320 | // Accept client request 321 | client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len); 322 | if (client_sd < 0) { 323 | perror("accept error"); 324 | free(client); 325 | return; 326 | } 327 | Client_Data * custom_data = new Client_Data(client); 328 | client->data = (void *)custom_data; 329 | ev_io_init(client, read_cb, client_sd, EV_READ); 330 | ev_io_start(loop, client); 331 | pthread_mutex_lock(&pos_mutex); 332 | std::string s1 = gtid_executed_to_string(curpos); 333 | pthread_mutex_unlock(&pos_mutex); 334 | s1 = "ST=" + s1 + "\n"; 335 | custom_data->add_string(s1.c_str(), s1.length()); 336 | std::cout << "Push back client with FD " << client->fd << std::endl; 337 | custom_data->writeout(); 338 | Clients.push_back(client); 339 | } 340 | 341 | void async_cb(struct ev_loop *loop, struct ev_async *watcher, int revents) { 342 | pthread_mutex_lock(&pos_mutex); 343 | for (std::vector::iterator it=Clients.begin(); it!=Clients.end(); ++it) { 344 | struct ev_io *w = *it; 345 | Client_Data * custom_data = (Client_Data *)w->data; 346 | for (std::vector::size_type i=0; iuuid_server[0]==0 || strncmp(custom_data->uuid_server, server_uuids.at(i), strlen(server_uuids.at(i)))) { 348 | strcpy(custom_data->uuid_server,server_uuids.at(i)); 349 | custom_data->add_string((const char *)"I1=",3); 350 | std::string s2 = server_uuids.at(i); 351 | s2 += ":" + std::to_string(trx_ids.at(i)) + "\n"; 352 | custom_data->add_string(s2.c_str(),s2.length()); 353 | } else { 354 | std::string s2 = "I2=" + std::to_string(trx_ids.at(i)) + "\n"; 355 | custom_data->add_string(s2.c_str(),s2.length()); 356 | } 357 | } 358 | custom_data->writeout(); 359 | } 360 | for (std::vector::size_type i=0; iclose_connection(); 374 | std::cout << " Received signal. Stopping at:" << std::endl; 375 | std::string s1 = gtid_executed_to_string(curpos); 376 | std::cout << s1 << std::endl; 377 | ev_break (loop, EVBREAK_ALL); 378 | } 379 | 380 | static struct ev_loop *loop; 381 | class GTID_Server_Dumper { 382 | private: 383 | struct sockaddr_in addr; 384 | int sd; 385 | int port; 386 | struct ev_io ev_accept; 387 | struct ev_loop *my_loop; 388 | struct ev_timer timer; 389 | public: 390 | GTID_Server_Dumper(int _port) { 391 | port = _port; 392 | sd = socket(PF_INET, SOCK_STREAM, 0); 393 | addr.sin_family = AF_INET; 394 | addr.sin_port = htons(port); 395 | addr.sin_addr.s_addr = INADDR_ANY; 396 | int arg_on = 1; 397 | if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&arg_on, sizeof(arg_on)) == -1) { 398 | perror("setsocketopt()"); 399 | close(sd); 400 | exit(EXIT_FAILURE); 401 | } 402 | 403 | if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 404 | perror("bind"); 405 | exit(EXIT_FAILURE); 406 | } 407 | ioctl_FIONBIO(sd,1); 408 | listen(sd,10); 409 | //struct ev_loop *my_loop = NULL; 410 | my_loop = NULL; 411 | my_loop = ev_loop_new (EVBACKEND_POLL | EVFLAG_NOENV); 412 | loop = my_loop; 413 | if (my_loop == NULL) { 414 | fprintf(stderr,"could not initialise new loop"); 415 | exit(EXIT_FAILURE); 416 | } 417 | ev_io_init(&ev_accept, accept_cb, sd, EV_READ); 418 | ev_io_start(my_loop, &ev_accept); 419 | ev_async_init(&async, async_cb); 420 | ev_async_start(my_loop, &async); 421 | ev_signal signal_watcher1; 422 | ev_signal signal_watcher2; 423 | ev_signal_init (&signal_watcher1, sigint_cb, SIGINT); 424 | ev_signal_init (&signal_watcher2, sigint_cb, SIGTERM); 425 | ev_signal_start (loop, &signal_watcher1); 426 | ev_signal_start (loop, &signal_watcher2); 427 | ev_run(my_loop, 0); 428 | } 429 | ~GTID_Server_Dumper() { 430 | close(sd); 431 | } 432 | }; 433 | 434 | 435 | 436 | void bench_xid_callback(unsigned int server_id) { 437 | pthread_mutex_lock(&pos_mutex); 438 | std::cout << sl->gtid_next.first << ":" << sl->gtid_next.second << std::endl; 439 | char *str=strdup(sl->gtid_next.first.c_str()); 440 | server_uuids.push_back(str); 441 | trx_ids.push_back(sl->gtid_next.second); 442 | curpos.addGtid(sl->gtid_next); 443 | pthread_mutex_unlock(&pos_mutex); 444 | ev_async_send(loop, &async); 445 | } 446 | 447 | 448 | bool isStopping() { 449 | return stopflag; 450 | } 451 | 452 | std::string gtid_executed_to_string(slave::Position &curpos) { 453 | std::string gtid_set; 454 | for (auto it=curpos.gtid_executed.begin(); it!=curpos.gtid_executed.end(); ++it) { 455 | std::string s = it->first; 456 | s.insert(8,"-"); 457 | s.insert(13,"-"); 458 | s.insert(18,"-"); 459 | s.insert(23,"-"); 460 | s = s + ":"; 461 | for (auto itr = it->second.begin(); itr != it->second.end(); ++itr) { 462 | std::string s2 = s; 463 | s2 = s2 + std::to_string(itr->first); 464 | s2 = s2 + "-"; 465 | s2 = s2 + std::to_string(itr->second); 466 | s2 = s2 + ","; 467 | gtid_set = gtid_set + s2; 468 | } 469 | } 470 | gtid_set.pop_back(); 471 | return gtid_set; 472 | } 473 | 474 | 475 | 476 | 477 | 478 | void usage(const char* name) { 479 | std::cout << "Usage: " << name << " -h -u -p -P " << std::endl; 480 | } 481 | 482 | 483 | void * server(void *args) { 484 | GTID_Server_Dumper * serv_dump = new GTID_Server_Dumper(listen_port); 485 | return NULL; 486 | } 487 | 488 | int main(int argc, char** argv) { 489 | std::string host; 490 | std::string user; 491 | std::string password; 492 | std::string errorstr; 493 | unsigned int port = 3306; 494 | 495 | 496 | bool error = false; 497 | 498 | 499 | int c; 500 | while (-1 != (c = ::getopt(argc, argv, "fh:u:p:P:l:"))) { 501 | switch (c) { 502 | case 'f': foreground=true; break; 503 | case 'h': host = optarg; break; 504 | case 'u': user = optarg; break; 505 | case 'p': password = optarg; break; 506 | case 'P': port = std::stoi(optarg); break; 507 | case 'l': listen_port = std::stoi(optarg); break; 508 | case 'L' : errorstr = optarg; break; 509 | default: 510 | usage(argv[0]); 511 | return 1; 512 | } 513 | } 514 | 515 | if (errorstr.empty()) { 516 | errorlog = (char *)"/tmp/proxysql_mysqlbinlog.log"; 517 | } else { 518 | errorlog = strdup(errorstr.c_str()); 519 | } 520 | 521 | if (host.empty() || user.empty()) 522 | { 523 | usage(argv[0]); 524 | return 1; 525 | } 526 | if (!ev_default_loop (EVBACKEND_POLL | EVFLAG_NOENV)) { 527 | fprintf(stderr,"could not initialise libev"); 528 | exit(EXIT_FAILURE); 529 | } 530 | 531 | 532 | if (foreground==false) { 533 | daemonize_phase1((char *)argv[0]); 534 | if ((pid = daemon_fork()) < 0) { 535 | /* Exit on error */ 536 | daemon_retval_done(); 537 | exit(EXIT_FAILURE); 538 | 539 | } else if (pid) { /* The parent */ 540 | 541 | daemonize_wait_daemon(); 542 | 543 | } else { 544 | if (daemonize_phase2()==false) { 545 | goto finish; 546 | } 547 | 548 | } 549 | 550 | 551 | laststart=0; 552 | if (true) { 553 | gotofork: 554 | if (laststart) { 555 | int currenttime=time(NULL); 556 | if (currenttime == laststart) { /// we do not want to restart multiple times in the same second 557 | // if restart is too frequent, something really bad is going on 558 | parent_open_error_log(); 559 | fprintf(stderr,"Angel process is waiting %d seconds before starting a new process\n", 1); 560 | parent_close_error_log(); 561 | sleep(1); 562 | } 563 | } 564 | laststart=time(NULL); 565 | pid = fork(); 566 | if (pid < 0) { 567 | parent_open_error_log(); 568 | fprintf(stderr,"[FATAL]: Error in fork()\n"); 569 | exit(EXIT_FAILURE); 570 | } 571 | 572 | if (pid) { /* The parent */ 573 | 574 | parent_close_error_log(); 575 | if (daemonize_phase3()==false) { 576 | goto gotofork; 577 | } 578 | 579 | } else { /* The daemon */ 580 | // we open the files also on the child process 581 | // this is required if the child process was created after a crash 582 | parent_open_error_log(); 583 | } 584 | } 585 | 586 | 587 | 588 | } else { 589 | flush_error_log(); 590 | } 591 | 592 | __start_label: 593 | 594 | { 595 | pthread_mutex_init(&pos_mutex, NULL); 596 | 597 | slave::MasterInfo masterinfo; 598 | 599 | masterinfo.conn_options.mysql_host = host; 600 | masterinfo.conn_options.mysql_port = port; 601 | masterinfo.conn_options.mysql_user = user; 602 | masterinfo.conn_options.mysql_pass = password; 603 | 604 | try { 605 | 606 | slave::DefaultExtState sDefExtState; 607 | slave::Slave slave(masterinfo, sDefExtState); 608 | sl = &slave; 609 | 610 | slave.setXidCallback(bench_xid_callback); 611 | 612 | std::cout << "Initializing client..." << std::endl; 613 | slave.init(); 614 | // enable GTID 615 | slave.enableGtid(); 616 | 617 | curpos = slave.getLastBinlogPos(); 618 | std::string s1 = gtid_executed_to_string(curpos); 619 | std::cout << s1 << std::endl; 620 | 621 | sDefExtState.setMasterPosition(curpos); 622 | 623 | pthread_t thread_id; 624 | pthread_create(&thread_id, NULL, server , NULL); 625 | 626 | try { 627 | 628 | std::cout << "Reading binlogs..." << std::endl; 629 | slave.get_remote_binlog([&] () 630 | { 631 | const slave::MasterInfo& sMasterInfo = slave.masterInfo(); 632 | return (isStopping()); 633 | }); 634 | 635 | 636 | } catch (std::exception& ex) { 637 | std::cout << "Error in reading binlogs: " << ex.what() << std::endl; 638 | error = true; 639 | } 640 | 641 | pthread_join(thread_id, NULL); 642 | } catch (std::exception& ex) { 643 | std::cout << "Error in initializing slave: " << ex.what() << std::endl; 644 | error = true; 645 | } 646 | } 647 | 648 | finish: 649 | fprintf(stderr,"Exiting...\n"); 650 | daemon_retval_send(255); 651 | daemon_signal_done(); 652 | daemon_pid_file_remove(); 653 | return 0; 654 | } 655 | --------------------------------------------------------------------------------