From d23f0366a8d10ef4fbce0bfc5260a2cf6a4abf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Thu, 5 Dec 2019 11:45:04 +0200 Subject: [PATCH] MXS-2785: Prevent broken replication setups When rewrite_src and rewrite_dest have different lengths, the slave must use GTID based replication. This removes the need for one-to-one matching between the slave's relay log and the master's binlog which gets broken when event lengths are modified due to event rewriting. --- .../binlogfilter/binlogfiltersession.cc | 32 +++++++++++++++++-- .../binlogfilter/binlogfiltersession.hh | 1 + 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/server/modules/filter/binlogfilter/binlogfiltersession.cc b/server/modules/filter/binlogfilter/binlogfiltersession.cc index 2e501225b..83d13131e 100644 --- a/server/modules/filter/binlogfilter/binlogfiltersession.cc +++ b/server/modules/filter/binlogfilter/binlogfiltersession.cc @@ -90,16 +90,24 @@ BinlogFilterSession* BinlogFilterSession::create(MXS_SESSION* pSession, return new BinlogFilterSession(pSession, pFilter); } -static bool is_master_binlog_checksum(GWBUF* buffer) +static bool is_matching_query(GWBUF* buffer, const char* target) { - const char target[] = "SELECT @master_binlog_checksum"; char query[1024]; // Large enough for most practical cases size_t bytes = gwbuf_copy_data(buffer, MYSQL_HEADER_LEN + 1, sizeof(query) - 1, (uint8_t*)query); query[bytes] = '\0'; - return strcasestr(query, target); } +static bool is_master_binlog_checksum(GWBUF* buffer) +{ + return is_matching_query(buffer, "SELECT @master_binlog_checksum"); +} + +static bool is_using_gtid(GWBUF* buffer) +{ + return is_matching_query(buffer, "SET @slave_connect_state="); +} + /** * Route input data from client. * @@ -129,6 +137,19 @@ int BinlogFilterSession::routeQuery(GWBUF* pPacket) // Connected Slave server is waiting for binlog events m_state = BINLOG_MODE; MXS_INFO("Slave server %u is waiting for binlog events.", m_serverid); + + if (!m_is_gtid + && m_filter.getConfig().rewrite_src.length() != m_filter.getConfig().rewrite_dest.length()) + { + gwbuf_free(pPacket); + std::ostringstream ss; + ss << "GTID replication is required when '" + << REWRITE_SRC << "' and '" << REWRITE_DEST << "' are of different length"; + mxs::FilterSession::clientReply( + modutil_create_mysql_err_msg(1, 0, ER_MASTER_FATAL_ERROR_READING_BINLOG, "HY000", + ss.str().c_str())); + return 0; + } break; case MXS_COM_QUERY: @@ -136,6 +157,11 @@ int BinlogFilterSession::routeQuery(GWBUF* pPacket) m_state = COMMAND_MODE; m_reading_checksum = is_master_binlog_checksum(pPacket); gwbuf_set_type(pPacket, GWBUF_TYPE_COLLECT_RESULT); + + if (is_using_gtid(pPacket)) + { + m_is_gtid = true; + } break; default: diff --git a/server/modules/filter/binlogfilter/binlogfiltersession.hh b/server/modules/filter/binlogfilter/binlogfiltersession.hh index ce87667aa..acd386107 100644 --- a/server/modules/filter/binlogfilter/binlogfiltersession.hh +++ b/server/modules/filter/binlogfilter/binlogfiltersession.hh @@ -124,4 +124,5 @@ private: uint32_t m_large_left = 0; // Remaining bytes of a large event bool m_is_large = false; // Large Event indicator bool m_reading_checksum = false;// Whether we are waiting for the binlog checksum response + bool m_is_gtid = false; };