зеркало из https://github.com/github/vitess-gh.git
156 строки
5.6 KiB
Go
156 строки
5.6 KiB
Go
// Copyright 2014, Google Inc. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package mysqlctl
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
log "github.com/golang/glog"
|
|
"github.com/youtube/vitess/go/sqldb"
|
|
blproto "github.com/youtube/vitess/go/vt/binlog/proto"
|
|
"github.com/youtube/vitess/go/vt/mysqlctl/proto"
|
|
)
|
|
|
|
/*
|
|
This file handles the differences between flavors of mysql.
|
|
*/
|
|
|
|
// MysqlFlavor is the abstract interface for a flavor.
|
|
type MysqlFlavor interface {
|
|
// VersionMatch returns true if the version string (from
|
|
// SELECT VERSION()) represents a server that this flavor
|
|
// knows how to talk to.
|
|
VersionMatch(version string) bool
|
|
|
|
// MasterPosition returns the ReplicationPosition of a master.
|
|
MasterPosition(mysqld *Mysqld) (proto.ReplicationPosition, error)
|
|
|
|
// SlaveStatus returns the ReplicationStatus of a slave.
|
|
SlaveStatus(mysqld *Mysqld) (proto.ReplicationStatus, error)
|
|
|
|
// ResetReplicationCommands returns the commands to completely reset
|
|
// replication on the host.
|
|
ResetReplicationCommands() []string
|
|
|
|
// PromoteSlaveCommands returns the commands to run to change
|
|
// a slave into a master.
|
|
PromoteSlaveCommands() []string
|
|
|
|
// SetSlavePositionCommands returns the commands to set the
|
|
// replication position at which the slave will resume
|
|
// when it is later reparented with SetMasterCommands.
|
|
SetSlavePositionCommands(pos proto.ReplicationPosition) ([]string, error)
|
|
|
|
// SetMasterCommands returns the commands to use the provided master
|
|
// as the new master (without changing any GTID position).
|
|
// It is guaranteed to be called with replication stopped.
|
|
// It should not start or stop replication.
|
|
SetMasterCommands(params *sqldb.ConnParams, masterHost string, masterPort int, masterConnectRetry int) ([]string, error)
|
|
|
|
// ParseGTID parses a GTID in the canonical format of this
|
|
// MySQL flavor into a proto.GTID interface value.
|
|
ParseGTID(string) (proto.GTID, error)
|
|
|
|
// ParseReplicationPosition parses a replication position in
|
|
// the canonical format of this MySQL flavor into a
|
|
// proto.ReplicationPosition struct.
|
|
ParseReplicationPosition(string) (proto.ReplicationPosition, error)
|
|
|
|
// SendBinlogDumpCommand sends the flavor-specific version of
|
|
// the COM_BINLOG_DUMP command to start dumping raw binlog
|
|
// events over a slave connection, starting at a given GTID.
|
|
SendBinlogDumpCommand(conn *SlaveConnection, startPos proto.ReplicationPosition) error
|
|
|
|
// MakeBinlogEvent takes a raw packet from the MySQL binlog
|
|
// stream connection and returns a BinlogEvent through which
|
|
// the packet can be examined.
|
|
MakeBinlogEvent(buf []byte) blproto.BinlogEvent
|
|
|
|
// WaitMasterPos waits until slave replication reaches at
|
|
// least targetPos.
|
|
WaitMasterPos(mysqld *Mysqld, targetPos proto.ReplicationPosition, waitTimeout time.Duration) error
|
|
|
|
// EnableBinlogPlayback prepares the server to play back
|
|
// events from a binlog stream. Whatever it does for a given
|
|
// flavor, it must be idempotent.
|
|
EnableBinlogPlayback(mysqld *Mysqld) error
|
|
|
|
// DisableBinlogPlayback returns the server to the normal
|
|
// state after playback is done. Whatever it does for a given
|
|
// flavor, it must be idempotent.
|
|
DisableBinlogPlayback(mysqld *Mysqld) error
|
|
}
|
|
|
|
var mysqlFlavors = make(map[string]MysqlFlavor)
|
|
|
|
// registerFlavorBuiltin adds a flavor to the map only if the name is unused.
|
|
// The flavor implementation passed to this function will only be used if there
|
|
// are no calls to registerFlavorOverride with the same flavor name.
|
|
// This should only be called from the init() goroutine.
|
|
func registerFlavorBuiltin(name string, flavor MysqlFlavor) {
|
|
if _, ok := mysqlFlavors[name]; !ok {
|
|
mysqlFlavors[name] = flavor
|
|
}
|
|
}
|
|
|
|
// registerFlavorOverride adds a flavor to the map, overriding any built-in
|
|
// implementations registered with registerFlavorBuiltin. There should only
|
|
// be one override per name, or else there's no guarantee which will win.
|
|
// This should only be called from the init() goroutine.
|
|
func registerFlavorOverride(name string, flavor MysqlFlavor) {
|
|
mysqlFlavors[name] = flavor
|
|
}
|
|
|
|
// detectFlavor decides which flavor to assume, based on the MYSQL_FLAVOR
|
|
// environment variable. If that variable is empty or unset, we will try to
|
|
// auto-detect the flavor.
|
|
func (mysqld *Mysqld) detectFlavor() (MysqlFlavor, error) {
|
|
// Check environment variable, which overrides auto-detect.
|
|
if env := os.Getenv("MYSQL_FLAVOR"); env != "" {
|
|
if flavor, ok := mysqlFlavors[env]; ok {
|
|
log.Infof("Using MySQL flavor %v (set by MYSQL_FLAVOR)", env)
|
|
return flavor, nil
|
|
}
|
|
return nil, fmt.Errorf("Unknown flavor (MYSQL_FLAVOR=%v)", env)
|
|
}
|
|
|
|
// If no environment variable set, fall back to auto-detect.
|
|
log.Infof("MYSQL_FLAVOR empty or unset, attempting to auto-detect...")
|
|
qr, err := mysqld.FetchSuperQuery("SELECT VERSION()")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't SELECT VERSION(): %v", err)
|
|
}
|
|
if len(qr.Rows) != 1 || len(qr.Rows[0]) != 1 {
|
|
return nil, fmt.Errorf("unexpected result for SELECT VERSION(): %#v", qr)
|
|
}
|
|
version := qr.Rows[0][0].String()
|
|
log.Infof("SELECT VERSION() = %s", version)
|
|
|
|
for name, flavor := range mysqlFlavors {
|
|
if flavor.VersionMatch(version) {
|
|
log.Infof("Using MySQL flavor %v (auto-detect match)", name)
|
|
return flavor, nil
|
|
}
|
|
}
|
|
|
|
return nil, fmt.Errorf("MYSQL_FLAVOR empty or unset, no auto-detect match found for VERSION() = %v", version)
|
|
}
|
|
|
|
func (mysqld *Mysqld) flavor() (MysqlFlavor, error) {
|
|
mysqld.mutex.Lock()
|
|
defer mysqld.mutex.Unlock()
|
|
|
|
if mysqld.mysqlFlavor == nil {
|
|
flavor, err := mysqld.detectFlavor()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("couldn't detect MySQL flavor: %v", err)
|
|
}
|
|
mysqld.mysqlFlavor = flavor
|
|
}
|
|
return mysqld.mysqlFlavor, nil
|
|
}
|