vitess.io/vitess@v0.16.2/go/mysql/replication.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package mysql 18 19 import ( 20 "vitess.io/vitess/go/vt/proto/vtrpc" 21 "vitess.io/vitess/go/vt/vterrors" 22 ) 23 24 const ( 25 semiSyncIndicator byte = 0xef 26 semiSyncAckRequested byte = 0x01 27 ) 28 29 // This file contains the methods related to replication. 30 31 // WriteComBinlogDump writes a ComBinlogDump command. 32 // See http://dev.mysql.com/doc/internals/en/com-binlog-dump.html for syntax. 33 // Returns a SQLError. 34 func (c *Conn) WriteComBinlogDump(serverID uint32, binlogFilename string, binlogPos uint32, flags uint16) error { 35 c.sequence = 0 36 length := 1 + // ComBinlogDump 37 4 + // binlog-pos 38 2 + // flags 39 4 + // server-id 40 len(binlogFilename) // binlog-filename 41 data, pos := c.startEphemeralPacketWithHeader(length) 42 pos = writeByte(data, pos, ComBinlogDump) 43 pos = writeUint32(data, pos, binlogPos) 44 pos = writeUint16(data, pos, flags) 45 pos = writeUint32(data, pos, serverID) 46 _ = writeEOFString(data, pos, binlogFilename) 47 if err := c.writeEphemeralPacket(); err != nil { 48 return NewSQLError(CRServerGone, SSUnknownSQLState, "%v", err) 49 } 50 return nil 51 } 52 53 // AnalyzeSemiSyncAckRequest is given a packet and checks if the packet has a semi-sync Ack request. 54 // This is only applicable to binlog dump event packets. 55 // If semi sync information exists, then the function returns a stopped buf that should be then 56 // processed as the event data 57 func (c *Conn) AnalyzeSemiSyncAckRequest(buf []byte) (strippedBuf []byte, ackRequested bool, err error) { 58 if !c.ExpectSemiSyncIndicator { 59 return buf, false, nil 60 } 61 // semi sync indicator is expected 62 // see https://dev.mysql.com/doc/internals/en/semi-sync-binlog-event.html 63 if len(buf) < 2 { 64 return buf, false, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "semi sync indicator expected, but packet too small") 65 } 66 if buf[0] != semiSyncIndicator { 67 return buf, false, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "semi sync indicator expected, but not found") 68 } 69 return buf[2:], buf[1] == semiSyncAckRequested, nil 70 } 71 72 // WriteComBinlogDumpGTID writes a ComBinlogDumpGTID command. 73 // Only works with MySQL 5.6+ (and not MariaDB). 74 // See http://dev.mysql.com/doc/internals/en/com-binlog-dump-gtid.html for syntax. 75 func (c *Conn) WriteComBinlogDumpGTID(serverID uint32, binlogFilename string, binlogPos uint64, flags uint16, gtidSet []byte) error { 76 c.sequence = 0 77 length := 1 + // ComBinlogDumpGTID 78 2 + // flags 79 4 + // server-id 80 4 + // binlog-filename-len 81 len(binlogFilename) + // binlog-filename 82 8 + // binlog-pos 83 4 + // data-size 84 len(gtidSet) // data 85 data, pos := c.startEphemeralPacketWithHeader(length) 86 pos = writeByte(data, pos, ComBinlogDumpGTID) //nolint 87 pos = writeUint16(data, pos, flags) //nolint 88 pos = writeUint32(data, pos, serverID) //nolint 89 pos = writeUint32(data, pos, uint32(len(binlogFilename))) //nolint 90 pos = writeEOFString(data, pos, binlogFilename) //nolint 91 pos = writeUint64(data, pos, binlogPos) //nolint 92 pos = writeUint32(data, pos, uint32(len(gtidSet))) //nolint 93 pos += copy(data[pos:], gtidSet) //nolint 94 if err := c.writeEphemeralPacket(); err != nil { 95 return NewSQLError(CRServerGone, SSUnknownSQLState, "%v", err) 96 } 97 return nil 98 } 99 100 // SendSemiSyncAck sends an ACK to the source, in response to binlog events 101 // the source has tagged with a SEMI_SYNC_ACK_REQ 102 // see https://dev.mysql.com/doc/internals/en/semi-sync-ack-packet.html 103 func (c *Conn) SendSemiSyncAck(binlogFilename string, binlogPos uint64) error { 104 c.sequence = 0 105 length := 1 + // ComSemiSyncAck 106 8 + // binlog-pos 107 len(binlogFilename) // binlog-filename 108 data, pos := c.startEphemeralPacketWithHeader(length) 109 pos = writeByte(data, pos, ComSemiSyncAck) 110 pos = writeUint64(data, pos, binlogPos) 111 _ = writeEOFString(data, pos, binlogFilename) 112 if err := c.writeEphemeralPacket(); err != nil { 113 return NewSQLError(CRServerGone, SSUnknownSQLState, "%v", err) 114 } 115 return nil 116 117 } 118 119 // WriteBinlogEvent writes a binlog event as part of a replication stream 120 // https://dev.mysql.com/doc/internals/en/binlog-network-stream.html 121 // https://dev.mysql.com/doc/internals/en/binlog-event.html 122 func (c *Conn) WriteBinlogEvent(ev BinlogEvent, semiSyncEnabled bool) error { 123 extraBytes := 1 // OK packet 124 if semiSyncEnabled { 125 extraBytes += 2 126 } 127 data, pos := c.startEphemeralPacketWithHeader(len(ev.Bytes()) + extraBytes) 128 pos = writeByte(data, pos, 0) // "OK" prefix 129 if semiSyncEnabled { 130 pos = writeByte(data, pos, 0xef) // semi sync indicator 131 pos = writeByte(data, pos, 0) // no ack expected 132 } 133 _ = writeEOFString(data, pos, string(ev.Bytes())) 134 if err := c.writeEphemeralPacket(); err != nil { 135 return NewSQLError(CRServerGone, SSUnknownSQLState, "%v", err) 136 } 137 return nil 138 } 139 140 // SemiSyncExtensionLoaded checks if the semisync extension has been loaded. 141 // It should work for both MariaDB and MySQL. 142 func (c *Conn) SemiSyncExtensionLoaded() bool { 143 qr, err := c.ExecuteFetch("SHOW GLOBAL VARIABLES LIKE 'rpl_semi_sync%'", 10, false) 144 if err != nil { 145 return false 146 } 147 return len(qr.Rows) >= 1 148 }