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  }