From ba0dfa4797613155bbcd175112811b885a273340 Mon Sep 17 00:00:00 2001 From: Dirkjan Bussink Date: Tue, 30 Apr 2024 18:44:19 +0200 Subject: [PATCH] Use replica queries when available (#15808) Signed-off-by: Dirkjan Bussink --- examples/compose/fix_replication.sh | 4 +- go/cmd/vtbackup/cli/vtbackup.go | 15 +- go/cmd/vtcombo/cli/main.go | 8 +- go/mysql/capabilities/capability.go | 7 + go/mysql/flavor.go | 71 +++-- go/mysql/flavor_filepos.go | 16 +- go/mysql/flavor_mariadb.go | 41 ++- go/mysql/flavor_mysql.go | 265 ++++++++++++++++-- go/mysql/flavor_mysql_test.go | 56 +++- go/mysql/flavor_mysqlgr.go | 4 + go/mysql/flavor_test.go | 18 +- go/mysql/replication.go | 33 +++ go/mysql/replication/replication_status.go | 75 +++-- .../replication/replication_status_test.go | 41 ++- go/test/endtoend/utils/mysql_test.go | 72 ++--- go/vt/mysqlctl/backup_blackbox_test.go | 20 +- go/vt/mysqlctl/builtinbackupengine.go | 24 +- go/vt/mysqlctl/fakemysqldaemon.go | 61 ++-- go/vt/mysqlctl/mysql_daemon.go | 21 +- go/vt/mysqlctl/mysqld.go | 2 +- go/vt/mysqlctl/query.go | 14 +- go/vt/mysqlctl/reparent.go | 5 +- go/vt/mysqlctl/reparent_test.go | 10 +- go/vt/mysqlctl/replication.go | 104 +++---- go/vt/mysqlctl/replication_test.go | 83 ++++-- go/vt/mysqlctl/xtrabackupengine_test.go | 2 +- .../endtoend/init_shard_primary_test.go | 36 +-- go/vt/vtgate/executor.go | 29 +- go/vt/vttablet/tabletmanager/restore.go | 73 ++--- go/vt/vttablet/tabletmanager/rpc_actions.go | 2 +- .../vttablet/tabletmanager/rpc_replication.go | 69 ++--- go/vt/vttablet/tabletmanager/tm_init_test.go | 6 +- go/vt/vttablet/tabletmanager/tm_state.go | 2 +- .../vreplication/framework_test.go | 2 +- .../tabletserver/repltracker/poller.go | 7 +- .../vttablet/tabletserver/vstreamer/engine.go | 9 +- .../tabletserver/vstreamer/engine_test.go | 6 + .../tabletserver/vstreamer/testenv/testenv.go | 2 +- .../tabletserver/vstreamer/vstreamer.go | 3 +- go/vt/wrangler/testlib/backup_test.go | 114 ++++---- .../testlib/copy_schema_shard_test.go | 6 +- .../testlib/emergency_reparent_shard_test.go | 48 ++-- .../testlib/external_reparent_test.go | 28 +- go/vt/wrangler/testlib/permissions_test.go | 6 +- .../testlib/planned_reparent_shard_test.go | 224 +++++++-------- go/vt/wrangler/testlib/reparent_utils_test.go | 36 +-- go/vt/wrangler/testlib/version_test.go | 6 +- 47 files changed, 1105 insertions(+), 681 deletions(-) diff --git a/examples/compose/fix_replication.sh b/examples/compose/fix_replication.sh index 71631efc310..a528b06be6f 100755 --- a/examples/compose/fix_replication.sh +++ b/examples/compose/fix_replication.sh @@ -41,10 +41,10 @@ function get_replication_status() { function reset_replication() { # Necessary before sql file can be imported echo "Importing MysqlDump: $KEYSPACE.sql" - mysql -u$DB_USER -p$DB_PASS -h 127.0.0.1 -e "RESET MASTER;STOP SLAVE;CHANGE MASTER TO MASTER_AUTO_POSITION = 0;source $KEYSPACE.sql;START SLAVE;" + mysql -u$DB_USER -p$DB_PASS -h 127.0.0.1 -e "RESET MASTER;STOP REPLICA;CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION = 0;source $KEYSPACE.sql;START REPLICA;" # Restore Master Auto Position echo "Restoring Master Auto Setting" - mysql -u$DB_USER -p$DB_PASS -h 127.0.0.1 -e "STOP SLAVE;CHANGE MASTER TO MASTER_AUTO_POSITION = 1;START SLAVE;" + mysql -u$DB_USER -p$DB_PASS -h 127.0.0.1 -e "STOP REPLICA;CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION = 1;START REPLICA;" } # Retrieve replication status diff --git a/go/cmd/vtbackup/cli/vtbackup.go b/go/cmd/vtbackup/cli/vtbackup.go index 3afc21bda7f..ea2c0123928 100644 --- a/go/cmd/vtbackup/cli/vtbackup.go +++ b/go/cmd/vtbackup/cli/vtbackup.go @@ -387,7 +387,7 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back return fmt.Errorf("can't reset replication: %v", err) } // We need to switch off super_read_only before we create the database. - resetFunc, err := mysqld.SetSuperReadOnly(false) + resetFunc, err := mysqld.SetSuperReadOnly(ctx, false) if err != nil { return fmt.Errorf("failed to disable super_read_only during backup: %v", err) } @@ -528,7 +528,7 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back } lastStatus = status - status, statusErr = mysqld.ReplicationStatus() + status, statusErr = mysqld.ReplicationStatus(ctx) if statusErr != nil { log.Warningf("Error getting replication status: %v", statusErr) continue @@ -560,12 +560,12 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back phase.Set(phaseNameCatchupReplication, int64(0)) // Stop replication and see where we are. - if err := mysqld.StopReplication(nil); err != nil { + if err := mysqld.StopReplication(ctx, nil); err != nil { return fmt.Errorf("can't stop replication: %v", err) } // Did we make any progress? - status, statusErr = mysqld.ReplicationStatus() + status, statusErr = mysqld.ReplicationStatus(ctx) if statusErr != nil { return fmt.Errorf("can't get replication status: %v", err) } @@ -621,11 +621,10 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back } func resetReplication(ctx context.Context, pos replication.Position, mysqld mysqlctl.MysqlDaemon) error { - cmds := []string{ - "STOP SLAVE", - "RESET SLAVE ALL", // "ALL" makes it forget replication source host:port. + if err := mysqld.StopReplication(ctx, nil); err != nil { + return vterrors.Wrap(err, "failed to stop replication") } - if err := mysqld.ExecuteSuperQueryList(ctx, cmds); err != nil { + if err := mysqld.ResetReplicationParameters(ctx); err != nil { return vterrors.Wrap(err, "failed to reset replication") } diff --git a/go/cmd/vtcombo/cli/main.go b/go/cmd/vtcombo/cli/main.go index e30f809b96b..9dbdc37b61c 100644 --- a/go/cmd/vtcombo/cli/main.go +++ b/go/cmd/vtcombo/cli/main.go @@ -222,7 +222,7 @@ func run(cmd *cobra.Command, args []string) (err error) { mysqld.Shutdown(ctx, cnf, true, mysqlctl.DefaultShutdownTimeout) }) // We want to ensure we can write to this database - mysqld.SetReadOnly(false) + mysqld.SetReadOnly(cmd.Context(), false) } else { dbconfigs.GlobalDBConfigs.InitWithSocket("", env.CollationEnv()) @@ -368,12 +368,12 @@ func (mysqld *vtcomboMysqld) SetReplicationSource(ctx context.Context, host stri } // StartReplication implements the MysqlDaemon interface -func (mysqld *vtcomboMysqld) StartReplication(hookExtraEnv map[string]string) error { +func (mysqld *vtcomboMysqld) StartReplication(ctx context.Context, hookExtraEnv map[string]string) error { return nil } // RestartReplication implements the MysqlDaemon interface -func (mysqld *vtcomboMysqld) RestartReplication(hookExtraEnv map[string]string) error { +func (mysqld *vtcomboMysqld) RestartReplication(ctx context.Context, hookExtraEnv map[string]string) error { return nil } @@ -383,7 +383,7 @@ func (mysqld *vtcomboMysqld) StartReplicationUntilAfter(ctx context.Context, pos } // StopReplication implements the MysqlDaemon interface -func (mysqld *vtcomboMysqld) StopReplication(hookExtraEnv map[string]string) error { +func (mysqld *vtcomboMysqld) StopReplication(ctx context.Context, hookExtraEnv map[string]string) error { return nil } diff --git a/go/mysql/capabilities/capability.go b/go/mysql/capabilities/capability.go index 6364f950022..234707538ec 100644 --- a/go/mysql/capabilities/capability.go +++ b/go/mysql/capabilities/capability.go @@ -47,6 +47,7 @@ const ( CheckConstraintsCapability // supported in MySQL 8.0.16 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-16.html PerformanceSchemaDataLocksTableCapability // supported in MySQL 8.0.1 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-1.html InstantDDLXtrabackupCapability // Supported in 8.0.32 and above, solving a MySQL-vs-Xtrabackup bug starting 8.0.29 + ReplicaTerminologyCapability // Supported in 8.0.26 and above, using SHOW REPLICA STATUS and all variations. ) type CapableOf func(capability FlavorCapability) (bool, error) @@ -112,6 +113,12 @@ func MySQLVersionHasCapability(serverVersion string, capability FlavorCapability return atLeast(8, 0, 30) case InstantDDLXtrabackupCapability: return atLeast(8, 0, 32) + case ReplicaTerminologyCapability: + // In MySQL 8.0.22 the new replica syntax was introduced, but other changes + // like the log_replica_updates field was only present in 8.0.26 and newer. + // So be conservative here, and only use the new syntax on newer versions, + // so we don't have to have too many different flavors. + return atLeast(8, 0, 26) default: return false, nil } diff --git a/go/mysql/flavor.go b/go/mysql/flavor.go index 619496a9246..b82e7a197cf 100644 --- a/go/mysql/flavor.go +++ b/go/mysql/flavor.go @@ -46,17 +46,16 @@ const ( mariaDBReplicationHackPrefix = "5.5.5-" // mariaDBVersionString is present in mariaDBVersionString = "MariaDB" - // mysql57VersionPrefix is the prefix for 5.7 mysql version, such as 5.7.31-log - mysql57VersionPrefix = "5.7." - // mysql80VersionPrefix is the prefix for 8.0 mysql version, such as 8.0.19 - mysql80VersionPrefix = "8.0." + // mysql8VersionPrefix is the prefix for 8.x mysql version, such as 8.0.19, + // but also newer ones like 8.4.0. + mysql8VersionPrefix = "8." ) // flavor is the abstract interface for a flavor. // Flavors are auto-detected upon connection using the server version. // We have two major implementations (the main difference is the GTID // handling): -// 1. Oracle MySQL 5.6, 5.7, 8.0, ... +// 1. Oracle MySQL 5.7, 8.0, ... // 2. MariaDB 10.X type flavor interface { // primaryGTIDSet returns the current GTIDSet of a server. @@ -88,6 +87,9 @@ type flavor interface { // stopReplicationCommand returns the command to stop the replication. stopReplicationCommand() string + // resetReplicationCommand returns the command to reset the replication. + resetReplicationCommand() string + // stopIOThreadCommand returns the command to stop the replica's IO thread only. stopIOThreadCommand() string @@ -116,9 +118,9 @@ type flavor interface { // replication position at which the replica will resume. setReplicationPositionCommands(pos replication.Position) []string - // changeReplicationSourceArg returns the specific parameter to add to - // a "change primary" command. - changeReplicationSourceArg() string + // setReplicationSourceCommand returns the command to use the provided host/port + // as the new replication source (without changing any GTID position). + setReplicationSourceCommand(params *ConnParams, host string, port int32, connectRetry int) string // status returns the result of the appropriate status command, // with parsed replication position. @@ -132,6 +134,11 @@ type flavor interface { // until the context expires. It returns an error if we did not // succeed. waitUntilPosition(ctx context.Context, c *Conn, pos replication.Position) error + // catchupToGTIDCommands returns the command to catch up to a given GTID. + catchupToGTIDCommands(params *ConnParams, pos replication.Position) []string + + // binlogReplicatedUpdates returns the field to use to check replica updates. + binlogReplicatedUpdates() string baseShowTables() string baseShowTablesWithSizes() string @@ -171,13 +178,16 @@ func GetFlavor(serverVersion string, flavorFunc func() flavor) (f flavor, capabl } else { f = mariadbFlavor102{mariadbFlavor{serverVersion: fmt.Sprintf("%f", mariadbVersion)}} } - case strings.HasPrefix(serverVersion, mysql57VersionPrefix): - f = mysqlFlavor57{mysqlFlavor{serverVersion: serverVersion}} - case strings.HasPrefix(serverVersion, mysql80VersionPrefix): - f = mysqlFlavor80{mysqlFlavor{serverVersion: serverVersion}} + case strings.HasPrefix(serverVersion, mysql8VersionPrefix): + recent, _ := capabilities.MySQLVersionHasCapability(serverVersion, capabilities.ReplicaTerminologyCapability) + if recent { + f = mysqlFlavor8{mysqlFlavor{serverVersion: serverVersion}} + } else { + f = mysqlFlavor8Legacy{mysqlFlavor{serverVersion: serverVersion}} + } default: - // If unknown, return the most basic flavor: MySQL 56. - f = mysqlFlavor56{mysqlFlavor{serverVersion: serverVersion}} + // If unknown, return the most basic flavor: MySQL 57. + f = mysqlFlavor57{mysqlFlavor{serverVersion: serverVersion}} } return f, f.supportsCapability, canonicalVersion } @@ -299,6 +309,10 @@ func (c *Conn) StopReplicationCommand() string { return c.flavor.stopReplicationCommand() } +func (c *Conn) ResetReplicationCommand() string { + return c.flavor.resetReplicationCommand() +} + // StopIOThreadCommand returns the command to stop the replica's io thread. func (c *Conn) StopIOThreadCommand() string { return c.flavor.stopIOThreadCommand() @@ -351,30 +365,7 @@ func (c *Conn) SetReplicationPositionCommands(pos replication.Position) []string // It is guaranteed to be called with replication stopped. // It should not start or stop replication. func (c *Conn) SetReplicationSourceCommand(params *ConnParams, host string, port int32, connectRetry int) string { - args := []string{ - fmt.Sprintf("MASTER_HOST = '%s'", host), - fmt.Sprintf("MASTER_PORT = %d", port), - fmt.Sprintf("MASTER_USER = '%s'", params.Uname), - fmt.Sprintf("MASTER_PASSWORD = '%s'", params.Pass), - fmt.Sprintf("MASTER_CONNECT_RETRY = %d", connectRetry), - } - if params.SslEnabled() { - args = append(args, "MASTER_SSL = 1") - } - if params.SslCa != "" { - args = append(args, fmt.Sprintf("MASTER_SSL_CA = '%s'", params.SslCa)) - } - if params.SslCaPath != "" { - args = append(args, fmt.Sprintf("MASTER_SSL_CAPATH = '%s'", params.SslCaPath)) - } - if params.SslCert != "" { - args = append(args, fmt.Sprintf("MASTER_SSL_CERT = '%s'", params.SslCert)) - } - if params.SslKey != "" { - args = append(args, fmt.Sprintf("MASTER_SSL_KEY = '%s'", params.SslKey)) - } - args = append(args, c.flavor.changeReplicationSourceArg()) - return "CHANGE MASTER TO\n " + strings.Join(args, ",\n ") + return c.flavor.setReplicationSourceCommand(params, host, port, connectRetry) } // resultToMap is a helper function used by ShowReplicationStatus. @@ -415,6 +406,10 @@ func (c *Conn) WaitUntilPosition(ctx context.Context, pos replication.Position) return c.flavor.waitUntilPosition(ctx, c, pos) } +func (c *Conn) CatchupToGTIDCommands(params *ConnParams, pos replication.Position) []string { + return c.flavor.catchupToGTIDCommands(params, pos) +} + // WaitUntilFilePosition waits until the given position is reached or until // the context expires for the file position flavor. It returns an error if // we did not succeed. diff --git a/go/mysql/flavor_filepos.go b/go/mysql/flavor_filepos.go index afdc8282e19..5949c557d54 100644 --- a/go/mysql/flavor_filepos.go +++ b/go/mysql/flavor_filepos.go @@ -92,6 +92,10 @@ func (flv *filePosFlavor) startReplicationCommand() string { return "unsupported" } +func (flv *filePosFlavor) resetReplicationCommand() string { + return "unsupported" +} + func (flv *filePosFlavor) restartReplicationCommands() []string { return []string{"unsupported"} } @@ -223,8 +227,8 @@ func (flv *filePosFlavor) setReplicationPositionCommands(pos replication.Positio } } -// setReplicationPositionCommands is part of the Flavor interface. -func (flv *filePosFlavor) changeReplicationSourceArg() string { +// setReplicationSourceCommand is part of the Flavor interface. +func (flv *filePosFlavor) setReplicationSourceCommand(params *ConnParams, host string, port int32, connectRetry int) string { return "unsupported" } @@ -342,3 +346,11 @@ func (*filePosFlavor) supportsCapability(capability capabilities.FlavorCapabilit return false, nil } } + +func (*filePosFlavor) catchupToGTIDCommands(_ *ConnParams, _ replication.Position) []string { + return []string{"unsupported"} +} + +func (*filePosFlavor) binlogReplicatedUpdates() string { + return "@@global.log_slave_updates" +} diff --git a/go/mysql/flavor_mariadb.go b/go/mysql/flavor_mariadb.go index e9552b6097d..640d0f54674 100644 --- a/go/mysql/flavor_mariadb.go +++ b/go/mysql/flavor_mariadb.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "io" + "strings" "time" "vitess.io/vitess/go/mysql/capabilities" @@ -97,6 +98,10 @@ func (mariadbFlavor) stopReplicationCommand() string { return "STOP SLAVE" } +func (mariadbFlavor) resetReplicationCommand() string { + return "RESET SLAVE ALL" +} + func (mariadbFlavor) stopIOThreadCommand() string { return "STOP SLAVE IO_THREAD" } @@ -182,9 +187,31 @@ func (mariadbFlavor) setReplicationPositionCommands(pos replication.Position) [] } } -// setReplicationPositionCommands is part of the Flavor interface. -func (mariadbFlavor) changeReplicationSourceArg() string { - return "MASTER_USE_GTID = current_pos" +func (mariadbFlavor) setReplicationSourceCommand(params *ConnParams, host string, port int32, connectRetry int) string { + args := []string{ + fmt.Sprintf("MASTER_HOST = '%s'", host), + fmt.Sprintf("MASTER_PORT = %d", port), + fmt.Sprintf("MASTER_USER = '%s'", params.Uname), + fmt.Sprintf("MASTER_PASSWORD = '%s'", params.Pass), + fmt.Sprintf("MASTER_CONNECT_RETRY = %d", connectRetry), + } + if params.SslEnabled() { + args = append(args, "MASTER_SSL = 1") + } + if params.SslCa != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_CA = '%s'", params.SslCa)) + } + if params.SslCaPath != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_CAPATH = '%s'", params.SslCaPath)) + } + if params.SslCert != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_CERT = '%s'", params.SslCert)) + } + if params.SslKey != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_KEY = '%s'", params.SslKey)) + } + args = append(args, "MASTER_USE_GTID = current_pos") + return "CHANGE MASTER TO\n " + strings.Join(args, ",\n ") } // status is part of the Flavor interface. @@ -296,3 +323,11 @@ func (mariadbFlavor) supportsCapability(capability capabilities.FlavorCapability return false, nil } } + +func (mariadbFlavor) catchupToGTIDCommands(_ *ConnParams, _ replication.Position) []string { + return []string{"unsupported"} +} + +func (mariadbFlavor) binlogReplicatedUpdates() string { + return "@@global.log_slave_updates" +} diff --git a/go/mysql/flavor_mysql.go b/go/mysql/flavor_mysql.go index 05fbdd25e46..8b728253923 100644 --- a/go/mysql/flavor_mysql.go +++ b/go/mysql/flavor_mysql.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "io" + "strings" "time" "vitess.io/vitess/go/mysql/capabilities" @@ -34,19 +35,20 @@ import ( type mysqlFlavor struct { serverVersion string } -type mysqlFlavor56 struct { +type mysqlFlavor57 struct { mysqlFlavor } -type mysqlFlavor57 struct { +type mysqlFlavor8Legacy struct { mysqlFlavor } -type mysqlFlavor80 struct { + +type mysqlFlavor8 struct { mysqlFlavor } -var _ flavor = (*mysqlFlavor56)(nil) var _ flavor = (*mysqlFlavor57)(nil) -var _ flavor = (*mysqlFlavor80)(nil) +var _ flavor = (*mysqlFlavor8Legacy)(nil) +var _ flavor = (*mysqlFlavor8)(nil) // primaryGTIDSet is part of the Flavor interface. func (mysqlFlavor) primaryGTIDSet(c *Conn) (replication.GTIDSet, error) { @@ -123,6 +125,10 @@ func (mysqlFlavor) stopReplicationCommand() string { return "STOP SLAVE" } +func (mysqlFlavor) resetReplicationCommand() string { + return "RESET SLAVE ALL" +} + func (mysqlFlavor) stopIOThreadCommand() string { return "STOP SLAVE IO_THREAD" } @@ -135,6 +141,76 @@ func (mysqlFlavor) startSQLThreadCommand() string { return "START SLAVE SQL_THREAD" } +func (f mysqlFlavor8) startReplicationCommand() string { + return "START REPLICA" +} + +func (f mysqlFlavor8) restartReplicationCommands() []string { + return []string{ + "STOP REPLICA", + "RESET REPLICA", + "START REPLICA", + } +} + +func (f mysqlFlavor8) startReplicationUntilAfter(pos replication.Position) string { + return fmt.Sprintf("START REPLICA UNTIL SQL_AFTER_GTIDS = '%s'", pos) +} + +func (f mysqlFlavor8) startSQLThreadUntilAfter(pos replication.Position) string { + return fmt.Sprintf("START REPLICA SQL_THREAD UNTIL SQL_AFTER_GTIDS = '%s'", pos) +} + +func (f mysqlFlavor8) stopReplicationCommand() string { + return "STOP REPLICA" +} + +func (f mysqlFlavor8) resetReplicationCommand() string { + return "RESET REPLICA ALL" +} + +func (f mysqlFlavor8) stopIOThreadCommand() string { + return "STOP REPLICA IO_THREAD" +} + +func (f mysqlFlavor8) stopSQLThreadCommand() string { + return "STOP REPLICA SQL_THREAD" +} + +func (f mysqlFlavor8) startSQLThreadCommand() string { + return "START REPLICA SQL_THREAD" +} + +// resetReplicationCommands is part of the Flavor interface. +func (mysqlFlavor8) resetReplicationCommands(c *Conn) []string { + resetCommands := []string{ + "STOP REPLICA", + "RESET REPLICA ALL", // "ALL" makes it forget source host:port. + "RESET MASTER", // This will also clear gtid_executed and gtid_purged. + } + status, err := c.SemiSyncExtensionLoaded() + if err != nil { + return resetCommands + } + switch status { + case SemiSyncTypeSource: + resetCommands = append(resetCommands, "SET GLOBAL rpl_semi_sync_source_enabled = false, GLOBAL rpl_semi_sync_replica_enabled = false") // semi-sync will be enabled if needed when replica is started. + case SemiSyncTypeMaster: + resetCommands = append(resetCommands, "SET GLOBAL rpl_semi_sync_master_enabled = false, GLOBAL rpl_semi_sync_slave_enabled = false") // semi-sync will be enabled if needed when replica is started. + default: + // Nothing to do. + } + return resetCommands +} + +// resetReplicationParametersCommands is part of the Flavor interface. +func (mysqlFlavor8) resetReplicationParametersCommands(c *Conn) []string { + resetCommands := []string{ + "RESET REPLICA ALL", // "ALL" makes it forget source host:port. + } + return resetCommands +} + // sendBinlogDumpCommand is part of the Flavor interface. func (mysqlFlavor) sendBinlogDumpCommand(c *Conn, serverID uint32, binlogFilename string, startPos replication.Position) error { gtidSet, ok := startPos.GTIDSet.(replication.Mysql56GTIDSet) @@ -185,11 +261,6 @@ func (mysqlFlavor) setReplicationPositionCommands(pos replication.Position) []st } } -// setReplicationPositionCommands is part of the Flavor interface. -func (mysqlFlavor) changeReplicationSourceArg() string { - return "MASTER_AUTO_POSITION = 1" -} - // status is part of the Flavor interface. func (mysqlFlavor) status(c *Conn) (replication.ReplicationStatus, error) { qr, err := c.ExecuteFetch("SHOW SLAVE STATUS", 100, true /* wantfields */) @@ -207,7 +278,7 @@ func (mysqlFlavor) status(c *Conn) (replication.ReplicationStatus, error) { return replication.ReplicationStatus{}, err } - return replication.ParseMysqlReplicationStatus(resultMap) + return replication.ParseMysqlReplicationStatus(resultMap, false) } // primaryStatus is part of the Flavor interface. @@ -229,6 +300,26 @@ func (mysqlFlavor) primaryStatus(c *Conn) (replication.PrimaryStatus, error) { return replication.ParseMysqlPrimaryStatus(resultMap) } +// status is part of the Flavor interface. +func (mysqlFlavor8) status(c *Conn) (replication.ReplicationStatus, error) { + qr, err := c.ExecuteFetch("SHOW REPLICA STATUS", 100, true /* wantfields */) + if err != nil { + return replication.ReplicationStatus{}, err + } + if len(qr.Rows) == 0 { + // The query returned no data, meaning the server + // is not configured as a replica. + return replication.ReplicationStatus{}, ErrNotReplica + } + + resultMap, err := resultToMap(qr) + if err != nil { + return replication.ReplicationStatus{}, err + } + + return replication.ParseMysqlReplicationStatus(resultMap, true) +} + // waitUntilPosition is part of the Flavor interface. func (mysqlFlavor) waitUntilPosition(ctx context.Context, c *Conn, pos replication.Position) error { // A timeout of 0 means wait indefinitely. @@ -361,18 +452,6 @@ const TablesWithSize80 = `SELECT t.table_name, t.table_schema, t.table_name, t.table_type, t.create_time, t.table_comment ` -// baseShowTablesWithSizes is part of the Flavor interface. -func (mysqlFlavor56) baseShowTablesWithSizes() string { - return TablesWithSize56 -} - -// supportsCapability is part of the Flavor interface. -func (f mysqlFlavor56) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { - // We do not support MySQL 5.6. Also, for consistency, and seeing that MySQL56 is the default - // flavor if no version specified, we return false for all capabilities. - return false, nil -} - // baseShowTablesWithSizes is part of the Flavor interface. func (mysqlFlavor57) baseShowTablesWithSizes() string { return TablesWithSize57 @@ -384,11 +463,147 @@ func (f mysqlFlavor57) supportsCapability(capability capabilities.FlavorCapabili } // baseShowTablesWithSizes is part of the Flavor interface. -func (mysqlFlavor80) baseShowTablesWithSizes() string { +func (mysqlFlavor8Legacy) baseShowTablesWithSizes() string { return TablesWithSize80 } +// baseShowTablesWithSizes is part of the Flavor interface. +func (mysqlFlavor8) baseShowTablesWithSizes() string { + return TablesWithSize80 +} + +// supportsCapability is part of the Flavor interface. +func (f mysqlFlavor8Legacy) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { + return capabilities.MySQLVersionHasCapability(f.serverVersion, capability) +} + // supportsCapability is part of the Flavor interface. -func (f mysqlFlavor80) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { +func (f mysqlFlavor8) supportsCapability(capability capabilities.FlavorCapability) (bool, error) { return capabilities.MySQLVersionHasCapability(f.serverVersion, capability) } + +func (mysqlFlavor) setReplicationSourceCommand(params *ConnParams, host string, port int32, connectRetry int) string { + args := []string{ + fmt.Sprintf("MASTER_HOST = '%s'", host), + fmt.Sprintf("MASTER_PORT = %d", port), + fmt.Sprintf("MASTER_USER = '%s'", params.Uname), + fmt.Sprintf("MASTER_PASSWORD = '%s'", params.Pass), + fmt.Sprintf("MASTER_CONNECT_RETRY = %d", connectRetry), + } + if params.SslEnabled() { + args = append(args, "MASTER_SSL = 1") + } + if params.SslCa != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_CA = '%s'", params.SslCa)) + } + if params.SslCaPath != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_CAPATH = '%s'", params.SslCaPath)) + } + if params.SslCert != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_CERT = '%s'", params.SslCert)) + } + if params.SslKey != "" { + args = append(args, fmt.Sprintf("MASTER_SSL_KEY = '%s'", params.SslKey)) + } + args = append(args, "MASTER_AUTO_POSITION = 1") + return "CHANGE MASTER TO\n " + strings.Join(args, ",\n ") +} + +func (mysqlFlavor8) setReplicationSourceCommand(params *ConnParams, host string, port int32, connectRetry int) string { + args := []string{ + fmt.Sprintf("SOURCE_HOST = '%s'", host), + fmt.Sprintf("SOURCE_PORT = %d", port), + fmt.Sprintf("SOURCE_USER = '%s'", params.Uname), + fmt.Sprintf("SOURCE_PASSWORD = '%s'", params.Pass), + fmt.Sprintf("SOURCE_CONNECT_RETRY = %d", connectRetry), + } + if params.SslEnabled() { + args = append(args, "SOURCE_SSL = 1") + } + if params.SslCa != "" { + args = append(args, fmt.Sprintf("SOURCE_SSL_CA = '%s'", params.SslCa)) + } + if params.SslCaPath != "" { + args = append(args, fmt.Sprintf("SOURCE_SSL_CAPATH = '%s'", params.SslCaPath)) + } + if params.SslCert != "" { + args = append(args, fmt.Sprintf("SOURCE_SSL_CERT = '%s'", params.SslCert)) + } + if params.SslKey != "" { + args = append(args, fmt.Sprintf("SOURCE_SSL_KEY = '%s'", params.SslKey)) + } + args = append(args, "SOURCE_AUTO_POSITION = 1") + return "CHANGE REPLICATION SOURCE TO\n " + strings.Join(args, ",\n ") +} + +func (mysqlFlavor) catchupToGTIDCommands(params *ConnParams, replPos replication.Position) []string { + cmds := []string{ + "STOP SLAVE FOR CHANNEL '' ", + "STOP SLAVE IO_THREAD FOR CHANNEL ''", + } + + if params.SslCa != "" || params.SslCert != "" { + // We need to use TLS + cmd := fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='%s', MASTER_PASSWORD='%s', MASTER_AUTO_POSITION=1, MASTER_SSL=1", params.Host, params.Port, params.Uname, params.Pass) + if params.SslCa != "" { + cmd += fmt.Sprintf(", MASTER_SSL_CA='%s'", params.SslCa) + } + if params.SslCert != "" { + cmd += fmt.Sprintf(", MASTER_SSL_CERT='%s'", params.SslCert) + } + if params.SslKey != "" { + cmd += fmt.Sprintf(", MASTER_SSL_KEY='%s'", params.SslKey) + } + cmds = append(cmds, cmd+";") + } else { + // No TLS + cmds = append(cmds, fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='%s', MASTER_PASSWORD='%s', MASTER_AUTO_POSITION=1;", params.Host, params.Port, params.Uname, params.Pass)) + } + + if replPos.IsZero() { // when the there is no afterPos, that means need to replicate completely + cmds = append(cmds, "START SLAVE") + } else { + cmds = append(cmds, fmt.Sprintf("START SLAVE UNTIL SQL_BEFORE_GTIDS = '%s'", replPos.GTIDSet.Last())) + } + return cmds +} + +func (mysqlFlavor8) catchupToGTIDCommands(params *ConnParams, replPos replication.Position) []string { + cmds := []string{ + "STOP REPLICA FOR CHANNEL '' ", + "STOP REPLICA IO_THREAD FOR CHANNEL ''", + } + + if params.SslCa != "" || params.SslCert != "" { + // We need to use TLS + cmd := fmt.Sprintf("CHANGE REPLICATION SOURCE TO SOURCE_HOST='%s', SOURCE_PORT=%d, SOURCE_USER='%s', SOURCE_PASSWORD='%s', SOURCE_AUTO_POSITION=1, SOURCE_SSL=1", params.Host, params.Port, params.Uname, params.Pass) + if params.SslCa != "" { + cmd += fmt.Sprintf(", SOURCE_SSL_CA='%s'", params.SslCa) + } + if params.SslCert != "" { + cmd += fmt.Sprintf(", SOURCE_SSL_CERT='%s'", params.SslCert) + } + if params.SslKey != "" { + cmd += fmt.Sprintf(", SOURCE_SSL_KEY='%s'", params.SslKey) + } + cmds = append(cmds, cmd+";") + } else { + // No TLS + cmds = append(cmds, fmt.Sprintf("CHANGE REPLICATION SOURCE TO SOURCE_HOST='%s', SOURCE_PORT=%d, SOURCE_USER='%s', SOURCE_PASSWORD='%s', SOURCE_AUTO_POSITION=1;", params.Host, params.Port, params.Uname, params.Pass)) + } + + if replPos.IsZero() { // when the there is no afterPos, that means need to replicate completely + cmds = append(cmds, "START REPLICA") + } else { + cmds = append(cmds, fmt.Sprintf("START REPLICA UNTIL SQL_BEFORE_GTIDS = '%s'", replPos.GTIDSet.Last())) + } + return cmds +} + +func (mysqlFlavor) binlogReplicatedUpdates() string { + return "@@global.log_slave_updates" +} + +func (mysqlFlavor8) binlogReplicatedUpdates() string { + return "@@global.log_replica_updates" +} diff --git a/go/mysql/flavor_mysql_test.go b/go/mysql/flavor_mysql_test.go index 7a6ddd47005..db02de31c34 100644 --- a/go/mysql/flavor_mysql_test.go +++ b/go/mysql/flavor_mysql_test.go @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestMysql56SetReplicationSourceCommand(t *testing.T) { +func TestMysql57SetReplicationSourceCommand(t *testing.T) { params := &ConnParams{ Uname: "username", Pass: "password", @@ -44,7 +44,7 @@ func TestMysql56SetReplicationSourceCommand(t *testing.T) { } -func TestMysql56SetReplicationSourceCommandSSL(t *testing.T) { +func TestMysql57SetReplicationSourceCommandSSL(t *testing.T) { params := &ConnParams{ Uname: "username", Pass: "password", @@ -74,3 +74,55 @@ func TestMysql56SetReplicationSourceCommandSSL(t *testing.T) { got := conn.SetReplicationSourceCommand(params, host, port, connectRetry) assert.Equal(t, want, got, "mysqlFlavor.SetReplicationSourceCommand(%#v, %#v, %#v, %#v) = %#v, want %#v", params, host, port, connectRetry, got, want) } + +func TestMysql8SetReplicationSourceCommand(t *testing.T) { + params := &ConnParams{ + Uname: "username", + Pass: "password", + } + host := "localhost" + port := int32(123) + connectRetry := 1234 + want := `CHANGE REPLICATION SOURCE TO + SOURCE_HOST = 'localhost', + SOURCE_PORT = 123, + SOURCE_USER = 'username', + SOURCE_PASSWORD = 'password', + SOURCE_CONNECT_RETRY = 1234, + SOURCE_AUTO_POSITION = 1` + + conn := &Conn{flavor: mysqlFlavor8{}} + got := conn.SetReplicationSourceCommand(params, host, port, connectRetry) + assert.Equal(t, want, got, "mysqlFlavor.SetReplicationSourceCommand(%#v, %#v, %#v, %#v) = %#v, want %#v", params, host, port, connectRetry, got, want) +} + +func TestMysql8SetReplicationSourceCommandSSL(t *testing.T) { + params := &ConnParams{ + Uname: "username", + Pass: "password", + SslCa: "ssl-ca", + SslCaPath: "ssl-ca-path", + SslCert: "ssl-cert", + SslKey: "ssl-key", + } + params.EnableSSL() + host := "localhost" + port := int32(123) + connectRetry := 1234 + want := `CHANGE REPLICATION SOURCE TO + SOURCE_HOST = 'localhost', + SOURCE_PORT = 123, + SOURCE_USER = 'username', + SOURCE_PASSWORD = 'password', + SOURCE_CONNECT_RETRY = 1234, + SOURCE_SSL = 1, + SOURCE_SSL_CA = 'ssl-ca', + SOURCE_SSL_CAPATH = 'ssl-ca-path', + SOURCE_SSL_CERT = 'ssl-cert', + SOURCE_SSL_KEY = 'ssl-key', + SOURCE_AUTO_POSITION = 1` + + conn := &Conn{flavor: mysqlFlavor8{}} + got := conn.SetReplicationSourceCommand(params, host, port, connectRetry) + assert.Equal(t, want, got, "mysqlFlavor.SetReplicationSourceCommand(%#v, %#v, %#v, %#v) = %#v, want %#v", params, host, port, connectRetry, got, want) +} diff --git a/go/mysql/flavor_mysqlgr.go b/go/mysql/flavor_mysqlgr.go index c572a554ca2..0ed08b064b1 100644 --- a/go/mysql/flavor_mysqlgr.go +++ b/go/mysql/flavor_mysqlgr.go @@ -75,6 +75,10 @@ func (mysqlGRFlavor) stopReplicationCommand() string { return "" } +func (mysqlGRFlavor) resetReplicationCommand() string { + return "" +} + // stopIOThreadCommand is disabled in mysqlGRFlavor func (mysqlGRFlavor) stopIOThreadCommand() string { return "" diff --git a/go/mysql/flavor_test.go b/go/mysql/flavor_test.go index b0ef7a1aafc..172ffa67eb2 100644 --- a/go/mysql/flavor_test.go +++ b/go/mysql/flavor_test.go @@ -48,21 +48,11 @@ func TestServerVersionCapableOf(t *testing.T) { capability: capabilities.InstantAddDropColumnFlavorCapability, isCapable: false, }, - { - version: "5.6.7", - capability: capabilities.InstantDDLFlavorCapability, - isCapable: false, - }, { version: "5.7.29", capability: capabilities.TransactionalGtidExecutedFlavorCapability, isCapable: false, }, - { - version: "5.6.7", - capability: capabilities.MySQLJSONFlavorCapability, - isCapable: false, - }, { version: "5.7.29", capability: capabilities.MySQLJSONFlavorCapability, @@ -118,17 +108,11 @@ func TestServerVersionCapableOf(t *testing.T) { capability: capabilities.PerformanceSchemaDataLocksTableCapability, isCapable: true, }, - { - // What happens if server version is unspecified - version: "", - capability: capabilities.CheckConstraintsCapability, - isCapable: false, - }, { // Some ridiculous version version: "5914.234.17", capability: capabilities.CheckConstraintsCapability, - isCapable: false, + isCapable: true, }, } for _, tc := range testcases { diff --git a/go/mysql/replication.go b/go/mysql/replication.go index ec30301e328..9d046002555 100644 --- a/go/mysql/replication.go +++ b/go/mysql/replication.go @@ -17,6 +17,8 @@ limitations under the License. package mysql import ( + "fmt" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vterrors" @@ -164,3 +166,34 @@ func (c *Conn) SemiSyncExtensionLoaded() (SemiSyncType, error) { } return SemiSyncTypeOff, nil } + +func (c *Conn) BinlogInformation() (string, bool, bool, string, error) { + replicaField := c.flavor.binlogReplicatedUpdates() + + query := fmt.Sprintf("select @@global.binlog_format, @@global.log_bin, %s, @@global.binlog_row_image", replicaField) + qr, err := c.ExecuteFetch(query, 1, true) + if err != nil { + return "", false, false, "", err + } + if len(qr.Rows) != 1 { + return "", false, false, "", fmt.Errorf("unable to read global variables binlog_format, log_bin, %s, binlog_row_image", replicaField) + } + res := qr.Named().Row() + binlogFormat, err := res.ToString("@@global.binlog_format") + if err != nil { + return "", false, false, "", err + } + logBin, err := res.ToInt64("@@global.log_bin") + if err != nil { + return "", false, false, "", err + } + logReplicaUpdates, err := res.ToInt64(replicaField) + if err != nil { + return "", false, false, "", err + } + binlogRowImage, err := res.ToString("@@global.binlog_row_image") + if err != nil { + return "", false, false, "", err + } + return binlogFormat, logBin == 1, logReplicaUpdates == 1, binlogRowImage, nil +} diff --git a/go/mysql/replication/replication_status.go b/go/mysql/replication/replication_status.go index 6b3d1bf2214..9b7d674f2a9 100644 --- a/go/mysql/replication/replication_status.go +++ b/go/mysql/replication/replication_status.go @@ -25,7 +25,7 @@ import ( "vitess.io/vitess/go/vt/vterrors" ) -// ReplicationStatus holds replication information from SHOW SLAVE STATUS. +// ReplicationStatus holds replication information from SHOW REPLICA STATUS. type ReplicationStatus struct { // Position is the current position of the replica. For GTID replication implementations // it is the executed GTID set. For file replication implementation, it is same as @@ -222,9 +222,14 @@ func (s *ReplicationStatus) FindErrantGTIDs(otherReplicaStatuses []*ReplicationS return diffSet, nil } -func ParseMysqlReplicationStatus(resultMap map[string]string) (ReplicationStatus, error) { - status := ParseReplicationStatus(resultMap) - uuidString := resultMap["Master_UUID"] +func ParseMysqlReplicationStatus(resultMap map[string]string, replicaTerminology bool) (ReplicationStatus, error) { + status := ParseReplicationStatus(resultMap, replicaTerminology) + + uuidField := "Source_UUID" + if !replicaTerminology { + uuidField = "Master_UUID" + } + uuidString := resultMap[uuidField] if uuidString != "" { sid, err := ParseSID(uuidString) if err != nil { @@ -251,7 +256,7 @@ func ParseMysqlReplicationStatus(resultMap map[string]string) (ReplicationStatus } func ParseMariadbReplicationStatus(resultMap map[string]string) (ReplicationStatus, error) { - status := ParseReplicationStatus(resultMap) + status := ParseReplicationStatus(resultMap, false) var err error status.Position.GTIDSet, err = ParseMariadbGTIDSet(resultMap["Gtid_Slave_Pos"]) @@ -263,7 +268,7 @@ func ParseMariadbReplicationStatus(resultMap map[string]string) (ReplicationStat } func ParseFilePosReplicationStatus(resultMap map[string]string) (ReplicationStatus, error) { - status := ParseReplicationStatus(resultMap) + status := ParseReplicationStatus(resultMap, false) status.Position = status.FilePosition status.RelayLogPosition = status.RelayLogSourceBinlogEquivalentPosition @@ -280,27 +285,53 @@ func ParseFilePosPrimaryStatus(resultMap map[string]string) (PrimaryStatus, erro } // ParseReplicationStatus parses the common (non-flavor-specific) fields of ReplicationStatus -func ParseReplicationStatus(fields map[string]string) ReplicationStatus { +func ParseReplicationStatus(fields map[string]string, replica bool) ReplicationStatus { // The field names in the map are identical to what we receive from the database // Hence the names still contain Master + sourceHostField := "Source_Host" + sourceUserField := "Source_User" + sslAllowedField := "Source_SSL_Allowed" + replicaIOField := "Replica_IO_Running" + replicaSQLField := "Replica_SQL_Running" + sourcePortField := "Source_Port" + sourceSecondsBehindField := "Seconds_Behind_Source" + sourceServerIDField := "Source_Server_Id" + execSourceLogPosField := "Exec_Source_Log_Pos" + relaySourceLogFileField := "Relay_Source_Log_File" + readSourceLogPosField := "Read_Source_Log_Pos" + sourceLogFileField := "Source_Log_File" + if !replica { + sourceHostField = "Master_Host" + sourceUserField = "Master_User" + sslAllowedField = "Master_SSL_Allowed" + replicaIOField = "Slave_IO_Running" + replicaSQLField = "Slave_SQL_Running" + sourcePortField = "Master_Port" + sourceSecondsBehindField = "Seconds_Behind_Master" + sourceServerIDField = "Master_Server_Id" + execSourceLogPosField = "Exec_Master_Log_Pos" + relaySourceLogFileField = "Relay_Master_Log_File" + readSourceLogPosField = "Read_Master_Log_Pos" + sourceLogFileField = "Master_Log_File" + } + status := ReplicationStatus{ - SourceHost: fields["Master_Host"], - SourceUser: fields["Master_User"], - SSLAllowed: fields["Master_SSL_Allowed"] == "Yes", + SourceHost: fields[sourceHostField], + SourceUser: fields[sourceUserField], + SSLAllowed: fields[sslAllowedField] == "Yes", AutoPosition: fields["Auto_Position"] == "1", UsingGTID: fields["Using_Gtid"] != "No" && fields["Using_Gtid"] != "", HasReplicationFilters: (fields["Replicate_Do_DB"] != "") || (fields["Replicate_Ignore_DB"] != "") || (fields["Replicate_Do_Table"] != "") || (fields["Replicate_Ignore_Table"] != "") || (fields["Replicate_Wild_Do_Table"] != "") || (fields["Replicate_Wild_Ignore_Table"] != ""), - // These fields are returned from the underlying DB and cannot be renamed - IOState: ReplicationStatusToState(fields["Slave_IO_Running"]), - LastIOError: fields["Last_IO_Error"], - SQLState: ReplicationStatusToState(fields["Slave_SQL_Running"]), - LastSQLError: fields["Last_SQL_Error"], + IOState: ReplicationStatusToState(fields[replicaIOField]), + LastIOError: fields["Last_IO_Error"], + SQLState: ReplicationStatusToState(fields[replicaSQLField]), + LastSQLError: fields["Last_SQL_Error"], } - parseInt, _ := strconv.ParseInt(fields["Master_Port"], 10, 32) + parseInt, _ := strconv.ParseInt(fields[sourcePortField], 10, 32) status.SourcePort = int32(parseInt) parseInt, _ = strconv.ParseInt(fields["Connect_Retry"], 10, 32) status.ConnectRetry = int32(parseInt) - parseUint, err := strconv.ParseUint(fields["Seconds_Behind_Master"], 10, 32) + parseUint, err := strconv.ParseUint(fields[sourceSecondsBehindField], 10, 32) if err != nil { // we could not parse the value into a valid uint32 -- most commonly because the value is NULL from the // database -- so let's reflect that the underlying value was unknown on our last check @@ -309,13 +340,13 @@ func ParseReplicationStatus(fields map[string]string) ReplicationStatus { status.ReplicationLagUnknown = false status.ReplicationLagSeconds = uint32(parseUint) } - parseUint, _ = strconv.ParseUint(fields["Master_Server_Id"], 10, 32) + parseUint, _ = strconv.ParseUint(fields[sourceServerIDField], 10, 32) status.SourceServerID = uint32(parseUint) parseUint, _ = strconv.ParseUint(fields["SQL_Delay"], 10, 32) status.SQLDelay = uint32(parseUint) - executedPosStr := fields["Exec_Master_Log_Pos"] - file := fields["Relay_Master_Log_File"] + executedPosStr := fields[execSourceLogPosField] + file := fields[relaySourceLogFileField] if file != "" && executedPosStr != "" { status.FilePosition.GTIDSet, err = ParseFilePosGTIDSet(fmt.Sprintf("%s:%s", file, executedPosStr)) if err != nil { @@ -323,8 +354,8 @@ func ParseReplicationStatus(fields map[string]string) ReplicationStatus { } } - readPosStr := fields["Read_Master_Log_Pos"] - file = fields["Master_Log_File"] + readPosStr := fields[readSourceLogPosField] + file = fields[sourceLogFileField] if file != "" && readPosStr != "" { status.RelayLogSourceBinlogEquivalentPosition.GTIDSet, err = ParseFilePosGTIDSet(fmt.Sprintf("%s:%s", file, readPosStr)) if err != nil { diff --git a/go/mysql/replication/replication_status_test.go b/go/mysql/replication/replication_status_test.go index c1f5991f253..25ff48dcd9c 100644 --- a/go/mysql/replication/replication_status_test.go +++ b/go/mysql/replication/replication_status_test.go @@ -134,13 +134,24 @@ func TestMysqlShouldGetPosition(t *testing.T) { assert.Equalf(t, got.FilePosition.GTIDSet.String(), want.FilePosition.GTIDSet.String(), "got FilePosition: %v; want FilePosition: %v", got.FilePosition.GTIDSet, want.FilePosition.GTIDSet) } -func TestMysqlRetrieveSourceServerId(t *testing.T) { +func TestMysqlRetrieveMasterServerId(t *testing.T) { resultMap := map[string]string{ "Master_Server_Id": "1", } want := ReplicationStatus{SourceServerID: 1} - got, err := ParseMysqlReplicationStatus(resultMap) + got, err := ParseMysqlReplicationStatus(resultMap, false) + require.NoError(t, err) + assert.Equalf(t, got.SourceServerID, want.SourceServerID, "got SourceServerID: %v; want SourceServerID: %v", got.SourceServerID, want.SourceServerID) +} + +func TestMysqlRetrieveSourceServerId(t *testing.T) { + resultMap := map[string]string{ + "Source_Server_Id": "1", + } + + want := ReplicationStatus{SourceServerID: 1} + got, err := ParseMysqlReplicationStatus(resultMap, true) require.NoError(t, err) assert.Equalf(t, got.SourceServerID, want.SourceServerID, "got SourceServerID: %v; want SourceServerID: %v", got.SourceServerID, want.SourceServerID) } @@ -160,14 +171,14 @@ func TestMysqlRetrieveFileBasedPositions(t *testing.T) { RelayLogSourceBinlogEquivalentPosition: Position{GTIDSet: FilePosGTID{File: "master-bin.000003", Pos: 1308}}, RelayLogFilePosition: Position{GTIDSet: FilePosGTID{File: "relay-bin.000004", Pos: 1309}}, } - got, err := ParseMysqlReplicationStatus(resultMap) + got, err := ParseMysqlReplicationStatus(resultMap, false) require.NoError(t, err) assert.Equalf(t, got.FilePosition.GTIDSet, want.FilePosition.GTIDSet, "got FilePosition: %v; want FilePosition: %v", got.FilePosition.GTIDSet, want.FilePosition.GTIDSet) assert.Equalf(t, got.RelayLogFilePosition.GTIDSet, want.RelayLogFilePosition.GTIDSet, "got RelayLogFilePosition: %v; want RelayLogFilePosition: %v", got.RelayLogFilePosition.GTIDSet, want.RelayLogFilePosition.GTIDSet) assert.Equalf(t, got.RelayLogSourceBinlogEquivalentPosition.GTIDSet, want.RelayLogSourceBinlogEquivalentPosition.GTIDSet, "got RelayLogSourceBinlogEquivalentPosition: %v; want RelayLogSourceBinlogEquivalentPosition: %v", got.RelayLogSourceBinlogEquivalentPosition.GTIDSet, want.RelayLogSourceBinlogEquivalentPosition.GTIDSet) } -func TestMysqlShouldGetRelayLogPosition(t *testing.T) { +func TestMysqlShouldGetLegacyRelayLogPosition(t *testing.T) { resultMap := map[string]string{ "Executed_Gtid_Set": "3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", "Retrieved_Gtid_Set": "3e11fa47-71ca-11e1-9e33-c80aa9429562:6-9", @@ -182,7 +193,27 @@ func TestMysqlShouldGetRelayLogPosition(t *testing.T) { Position: Position{GTIDSet: Mysql56GTIDSet{sid: []interval{{start: 1, end: 5}}}}, RelayLogPosition: Position{GTIDSet: Mysql56GTIDSet{sid: []interval{{start: 1, end: 9}}}}, } - got, err := ParseMysqlReplicationStatus(resultMap) + got, err := ParseMysqlReplicationStatus(resultMap, false) + require.NoError(t, err) + assert.Equalf(t, got.RelayLogPosition.GTIDSet.String(), want.RelayLogPosition.GTIDSet.String(), "got RelayLogPosition: %v; want RelayLogPosition: %v", got.RelayLogPosition.GTIDSet, want.RelayLogPosition.GTIDSet) +} + +func TestMysqlShouldGetRelayLogPosition(t *testing.T) { + resultMap := map[string]string{ + "Executed_Gtid_Set": "3e11fa47-71ca-11e1-9e33-c80aa9429562:1-5", + "Retrieved_Gtid_Set": "3e11fa47-71ca-11e1-9e33-c80aa9429562:6-9", + "Exec_Source_Log_Pos": "1307", + "Relay_Source_Log_File": "master-bin.000002", + "Read_Source_Log_Pos": "1308", + "Source_Log_File": "master-bin.000003", + } + + sid, _ := ParseSID("3e11fa47-71ca-11e1-9e33-c80aa9429562") + want := ReplicationStatus{ + Position: Position{GTIDSet: Mysql56GTIDSet{sid: []interval{{start: 1, end: 5}}}}, + RelayLogPosition: Position{GTIDSet: Mysql56GTIDSet{sid: []interval{{start: 1, end: 9}}}}, + } + got, err := ParseMysqlReplicationStatus(resultMap, true) require.NoError(t, err) assert.Equalf(t, got.RelayLogPosition.GTIDSet.String(), want.RelayLogPosition.GTIDSet.String(), "got RelayLogPosition: %v; want RelayLogPosition: %v", got.RelayLogPosition.GTIDSet, want.RelayLogPosition.GTIDSet) } diff --git a/go/test/endtoend/utils/mysql_test.go b/go/test/endtoend/utils/mysql_test.go index f5984010924..59a5ea255ef 100644 --- a/go/test/endtoend/utils/mysql_test.go +++ b/go/test/endtoend/utils/mysql_test.go @@ -79,51 +79,51 @@ func TestCreateMySQL(t *testing.T) { func TestSetSuperReadOnlyMySQL(t *testing.T) { require.NotNil(t, mysqld) - isSuperReadOnly, _ := mysqld.IsSuperReadOnly() + isSuperReadOnly, _ := mysqld.IsSuperReadOnly(context.Background()) assert.False(t, isSuperReadOnly, "super_read_only should be set to False") - retFunc1, err := mysqld.SetSuperReadOnly(true) + retFunc1, err := mysqld.SetSuperReadOnly(context.Background(), true) assert.NotNil(t, retFunc1, "SetSuperReadOnly is supposed to return a defer function") assert.NoError(t, err, "SetSuperReadOnly should not have failed") - isSuperReadOnly, _ = mysqld.IsSuperReadOnly() + isSuperReadOnly, _ = mysqld.IsSuperReadOnly(context.Background()) assert.True(t, isSuperReadOnly, "super_read_only should be set to True") // if value is already true then retFunc2 will be nil - retFunc2, err := mysqld.SetSuperReadOnly(true) + retFunc2, err := mysqld.SetSuperReadOnly(context.Background(), true) assert.Nil(t, retFunc2, "SetSuperReadOnly is supposed to return a nil function") assert.NoError(t, err, "SetSuperReadOnly should not have failed") retFunc1() - isSuperReadOnly, _ = mysqld.IsSuperReadOnly() + isSuperReadOnly, _ = mysqld.IsSuperReadOnly(context.Background()) assert.False(t, isSuperReadOnly, "super_read_only should be set to False") - isReadOnly, _ := mysqld.IsReadOnly() + isReadOnly, _ := mysqld.IsReadOnly(context.Background()) assert.True(t, isReadOnly, "read_only should be set to True") - isSuperReadOnly, _ = mysqld.IsSuperReadOnly() + isSuperReadOnly, _ = mysqld.IsSuperReadOnly(context.Background()) assert.False(t, isSuperReadOnly, "super_read_only should be set to False") - retFunc1, err = mysqld.SetSuperReadOnly(false) + retFunc1, err = mysqld.SetSuperReadOnly(context.Background(), false) assert.Nil(t, retFunc1, "SetSuperReadOnly is supposed to return a nil function") assert.NoError(t, err, "SetSuperReadOnly should not have failed") - _, err = mysqld.SetSuperReadOnly(true) + _, err = mysqld.SetSuperReadOnly(context.Background(), true) assert.NoError(t, err) - isSuperReadOnly, _ = mysqld.IsSuperReadOnly() + isSuperReadOnly, _ = mysqld.IsSuperReadOnly(context.Background()) assert.True(t, isSuperReadOnly, "super_read_only should be set to True") - retFunc1, err = mysqld.SetSuperReadOnly(false) + retFunc1, err = mysqld.SetSuperReadOnly(context.Background(), false) assert.NotNil(t, retFunc1, "SetSuperReadOnly is supposed to return a defer function") assert.NoError(t, err, "SetSuperReadOnly should not have failed") - isSuperReadOnly, _ = mysqld.IsSuperReadOnly() + isSuperReadOnly, _ = mysqld.IsSuperReadOnly(context.Background()) assert.False(t, isSuperReadOnly, "super_read_only should be set to False") // if value is already false then retFunc2 will be nil - retFunc2, err = mysqld.SetSuperReadOnly(false) + retFunc2, err = mysqld.SetSuperReadOnly(context.Background(), false) assert.Nil(t, retFunc2, "SetSuperReadOnly is supposed to return a nil function") assert.NoError(t, err, "SetSuperReadOnly should not have failed") retFunc1() - isSuperReadOnly, _ = mysqld.IsSuperReadOnly() + isSuperReadOnly, _ = mysqld.IsSuperReadOnly(context.Background()) assert.True(t, isSuperReadOnly, "super_read_only should be set to True") - isReadOnly, _ = mysqld.IsReadOnly() + isReadOnly, _ = mysqld.IsReadOnly(context.Background()) assert.True(t, isReadOnly, "read_only should be set to True") } @@ -156,16 +156,16 @@ func TestGetServerID(t *testing.T) { func TestReplicationStatus(t *testing.T) { require.NotNil(t, mysqld) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + // Initially we should expect an error for no replication status - _, err := mysqld.ReplicationStatus() + _, err := mysqld.ReplicationStatus(context.Background()) assert.ErrorContains(t, err, "no replication status") - ctx := context.Background() conn, err := mysql.Connect(ctx, &mysqlParams) require.NoError(t, err) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() port, err := mysqld.GetMysqlPort(ctx) require.NoError(t, err) host := "localhost" @@ -174,7 +174,7 @@ func TestReplicationStatus(t *testing.T) { res := Exec(t, conn, q) require.NotNil(t, res) - r, err := mysqld.ReplicationStatus() + r, err := mysqld.ReplicationStatus(ctx) assert.NoError(t, err) assert.Equal(t, port, r.SourcePort) assert.Equal(t, host, r.SourceHost) @@ -186,7 +186,7 @@ func TestPrimaryStatus(t *testing.T) { res, err := mysqld.PrimaryStatus(context.Background()) assert.NoError(t, err) - r, err := mysqld.ReplicationStatus() + r, err := mysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.True(t, res.Position.Equal(r.Position), "primary replication status should be same as replication status here") @@ -199,7 +199,7 @@ func TestGTID(t *testing.T) { assert.Empty(t, res.String()) assert.NoError(t, err) - primaryPosition, err := mysqld.PrimaryPosition() + primaryPosition, err := mysqld.PrimaryPosition(context.Background()) assert.NotNil(t, primaryPosition) assert.NoError(t, err) @@ -215,7 +215,7 @@ func TestGTID(t *testing.T) { assert.NoError(t, err) assert.Equal(t, gtid, res.String()) - primaryPosition, err = mysqld.PrimaryPosition() + primaryPosition, err = mysqld.PrimaryPosition(context.Background()) assert.NoError(t, err) assert.Contains(t, primaryPosition.String(), gtid) } @@ -248,7 +248,7 @@ func TestSetAndResetReplication(t *testing.T) { err = mysqld.SetReplicationSource(context.Background(), host, port, true, true) assert.NoError(t, err) - r, err := mysqld.ReplicationStatus() + r, err := mysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.Equal(t, port, r.SourcePort) assert.Equal(t, host, r.SourceHost) @@ -256,7 +256,7 @@ func TestSetAndResetReplication(t *testing.T) { err = mysqld.ResetReplication(context.Background()) assert.NoError(t, err) - r, err = mysqld.ReplicationStatus() + r, err = mysqld.ReplicationStatus(context.Background()) assert.ErrorContains(t, err, "no replication status") assert.Equal(t, "", r.SourceHost) assert.Equal(t, int32(0), r.SourcePort) @@ -264,7 +264,7 @@ func TestSetAndResetReplication(t *testing.T) { err = mysqld.SetReplicationSource(context.Background(), host, port, true, true) assert.NoError(t, err) - r, err = mysqld.ReplicationStatus() + r, err = mysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.Equal(t, port, r.SourcePort) assert.Equal(t, host, r.SourceHost) @@ -272,7 +272,7 @@ func TestSetAndResetReplication(t *testing.T) { err = mysqld.ResetReplication(context.Background()) assert.NoError(t, err) - r, err = mysqld.ReplicationStatus() + r, err = mysqld.ReplicationStatus(context.Background()) assert.ErrorContains(t, err, "no replication status") assert.Equal(t, "", r.SourceHost) assert.Equal(t, int32(0), r.SourcePort) @@ -391,7 +391,7 @@ func TestSemiSyncEnabled(t *testing.T) { func TestWaitForReplicationStart(t *testing.T) { require.NotNil(t, mysqld) - err := mysqlctl.WaitForReplicationStart(mysqld, 1) + err := mysqlctl.WaitForReplicationStart(context.Background(), mysqld, 1) assert.ErrorContains(t, err, "no replication status") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -403,7 +403,7 @@ func TestWaitForReplicationStart(t *testing.T) { err = mysqld.SetReplicationSource(context.Background(), host, port, true, true) assert.NoError(t, err) - err = mysqlctl.WaitForReplicationStart(mysqld, 1) + err = mysqlctl.WaitForReplicationStart(context.Background(), mysqld, 1) assert.NoError(t, err) err = mysqld.ResetReplication(context.Background()) @@ -413,7 +413,7 @@ func TestWaitForReplicationStart(t *testing.T) { func TestStartReplication(t *testing.T) { require.NotNil(t, mysqld) - err := mysqld.StartReplication(map[string]string{}) + err := mysqld.StartReplication(context.Background(), map[string]string{}) assert.ErrorContains(t, err, "The server is not configured as replica") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -426,7 +426,7 @@ func TestStartReplication(t *testing.T) { err = mysqld.SetReplicationSource(context.Background(), host, port, true, false) assert.NoError(t, err) - err = mysqld.StartReplication(map[string]string{}) + err = mysqld.StartReplication(context.Background(), map[string]string{}) assert.NoError(t, err) err = mysqld.ResetReplication(context.Background()) @@ -445,16 +445,16 @@ func TestStopReplication(t *testing.T) { err = mysqld.SetReplicationSource(context.Background(), host, port, true, true) assert.NoError(t, err) - r, err := mysqld.ReplicationStatus() + r, err := mysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.Equal(t, host, r.SourceHost) assert.Equal(t, port, r.SourcePort) assert.Equal(t, replication.ReplicationStateRunning, r.SQLState) - err = mysqld.StopReplication(map[string]string{}) + err = mysqld.StopReplication(context.Background(), map[string]string{}) assert.NoError(t, err) - r, err = mysqld.ReplicationStatus() + r, err = mysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.Equal(t, replication.ReplicationStateStopped, r.SQLState) } @@ -471,7 +471,7 @@ func TestStopSQLThread(t *testing.T) { err = mysqld.SetReplicationSource(context.Background(), host, port, true, true) assert.NoError(t, err) - r, err := mysqld.ReplicationStatus() + r, err := mysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.Equal(t, host, r.SourceHost) assert.Equal(t, port, r.SourcePort) @@ -480,7 +480,7 @@ func TestStopSQLThread(t *testing.T) { err = mysqld.StopSQLThread(context.Background()) assert.NoError(t, err) - r, err = mysqld.ReplicationStatus() + r, err = mysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.Equal(t, replication.ReplicationStateStopped, r.SQLState) } diff --git a/go/vt/mysqlctl/backup_blackbox_test.go b/go/vt/mysqlctl/backup_blackbox_test.go index 4508c4e4306..15244fb8782 100644 --- a/go/vt/mysqlctl/backup_blackbox_test.go +++ b/go/vt/mysqlctl/backup_blackbox_test.go @@ -139,12 +139,12 @@ func TestExecuteBackup(t *testing.T) { bh := filebackupstorage.NewBackupHandle(nil, "", "", false) // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: - // "STOP SLAVE", "START SLAVE", in that order. + // "STOP REPLICA", "START REPLICA", in that order. fakedb := fakesqldb.New(t) defer fakedb.Close() mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) defer mysqld.Close() - mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} // mysqld.ShutdownTime = time.Minute fakeStats := backupstats.NewFakeStats() @@ -285,13 +285,13 @@ func TestExecuteBackupWithSafeUpgrade(t *testing.T) { bh := filebackupstorage.NewBackupHandle(nil, "", "", false) // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: - // "STOP SLAVE", "START SLAVE", in that order. + // "STOP REPLICA", "START REPLICA", in that order. // It also needs to be allowed to receive the query to disable the innodb_fast_shutdown flag. fakedb := fakesqldb.New(t) defer fakedb.Close() mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) defer mysqld.Close() - mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} mysqld.FetchSuperQueryMap = map[string]*sqltypes.Result{ "SET GLOBAL innodb_fast_shutdown=0": {}, } @@ -372,12 +372,12 @@ func TestExecuteBackupWithCanceledContext(t *testing.T) { be := &mysqlctl.BuiltinBackupEngine{} bh := filebackupstorage.NewBackupHandle(nil, "", "", false) // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: - // "STOP SLAVE", "START SLAVE", in that order. + // "STOP REPLICA", "START REPLICA", in that order. fakedb := fakesqldb.New(t) defer fakedb.Close() mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) defer mysqld.Close() - mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} // Cancel the context deliberately cancelledCtx, cancelCtx := context.WithCancel(context.Background()) @@ -461,12 +461,12 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { be := &mysqlctl.BuiltinBackupEngine{} bh := filebackupstorage.NewBackupHandle(nil, "", "", false) // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: - // "STOP SLAVE", "START SLAVE", in that order. + // "STOP REPLICA", "START REPLICA", in that order. fakedb := fakesqldb.New(t) defer fakedb.Close() mysqld := mysqlctl.NewFakeMysqlDaemon(fakedb) defer mysqld.Close() - mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} backupResult, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ Logger: logutil.NewConsoleLogger(), @@ -494,7 +494,7 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { defer fakedb.Close() mysqld = mysqlctl.NewFakeMysqlDaemon(fakedb) defer mysqld.Close() - mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} fakeStats := backupstats.NewFakeStats() @@ -571,7 +571,7 @@ func TestExecuteRestoreWithTimedOutContext(t *testing.T) { defer fakedb.Close() mysqld = mysqlctl.NewFakeMysqlDaemon(fakedb) defer mysqld.Close() - mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} + mysqld.ExpectedExecuteSuperQueryList = []string{"STOP REPLICA", "START REPLICA"} restoreParams.Mysqld = mysqld timedOutCtx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go index 0755d4c93c1..3bc39e45e72 100644 --- a/go/vt/mysqlctl/builtinbackupengine.go +++ b/go/vt/mysqlctl/builtinbackupengine.go @@ -408,7 +408,7 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac // See if we need to restart replication after backup. params.Logger.Infof("getting current replication status") - replicaStatus, err := params.Mysqld.ReplicationStatus() + replicaStatus, err := params.Mysqld.ReplicationStatus(ctx) switch err { case nil: replicaStartRequired = replicaStatus.Healthy() && !DisableActiveReparents @@ -420,11 +420,11 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac } // get the read-only flag - readOnly, err = params.Mysqld.IsReadOnly() + readOnly, err = params.Mysqld.IsReadOnly(ctx) if err != nil { return BackupUnusable, vterrors.Wrap(err, "failed to get read_only status") } - superReadOnly, err = params.Mysqld.IsSuperReadOnly() + superReadOnly, err = params.Mysqld.IsSuperReadOnly(ctx) if err != nil { return BackupUnusable, vterrors.Wrap(err, "can't get super_read_only status") } @@ -435,28 +435,28 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac // No need to set read_only because super_read_only will implicitly set read_only to true as well. if !superReadOnly { params.Logger.Infof("Enabling super_read_only on primary prior to backup") - if _, err = params.Mysqld.SetSuperReadOnly(true); err != nil { + if _, err = params.Mysqld.SetSuperReadOnly(ctx, true); err != nil { return BackupUnusable, vterrors.Wrap(err, "failed to enable super_read_only") } defer func() { // Resetting super_read_only back to its original value params.Logger.Infof("resetting mysqld super_read_only to %v", superReadOnly) - if _, err := params.Mysqld.SetSuperReadOnly(false); err != nil { + if _, err := params.Mysqld.SetSuperReadOnly(ctx, false); err != nil { log.Error("Failed to set super_read_only back to its original value") } }() } - replicationPosition, err = params.Mysqld.PrimaryPosition() + replicationPosition, err = params.Mysqld.PrimaryPosition(ctx) if err != nil { return BackupUnusable, vterrors.Wrap(err, "can't get position on primary") } } else { // This is a replica - if err := params.Mysqld.StopReplication(params.HookExtraEnv); err != nil { + if err := params.Mysqld.StopReplication(ctx, params.HookExtraEnv); err != nil { return BackupUnusable, vterrors.Wrapf(err, "can't stop replica") } - replicaStatus, err := params.Mysqld.ReplicationStatus() + replicaStatus, err := params.Mysqld.ReplicationStatus(ctx) if err != nil { return BackupUnusable, vterrors.Wrap(err, "can't get replica status") } @@ -509,7 +509,7 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac // Resetting super_read_only back to its original value params.Logger.Infof("resetting mysqld super_read_only to %v", superReadOnly) - if _, err := params.Mysqld.SetSuperReadOnly(superReadOnly); err != nil { + if _, err := params.Mysqld.SetSuperReadOnly(ctx, superReadOnly); err != nil { return backupResult, err } @@ -526,12 +526,12 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac } if replicaStartRequired { params.Logger.Infof("restarting mysql replication") - if err := params.Mysqld.StartReplication(params.HookExtraEnv); err != nil { + if err := params.Mysqld.StartReplication(ctx, params.HookExtraEnv); err != nil { return backupResult, vterrors.Wrap(err, "cannot restart replica") } // this should be quick, but we might as well just wait - if err := WaitForReplicationStart(params.Mysqld, replicationStartDeadline); err != nil { + if err := WaitForReplicationStart(ctx, params.Mysqld, replicationStartDeadline); err != nil { return backupResult, vterrors.Wrap(err, "replica is not restarting") } @@ -557,7 +557,7 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac if err := ctx.Err(); err != nil { return backupResult, err } - status, err := params.Mysqld.ReplicationStatus() + status, err := params.Mysqld.ReplicationStatus(ctx) if err != nil { return backupResult, err } diff --git a/go/vt/mysqlctl/fakemysqldaemon.go b/go/vt/mysqlctl/fakemysqldaemon.go index c1be5f7ae24..4897503d0dd 100644 --- a/go/vt/mysqlctl/fakemysqldaemon.go +++ b/go/vt/mysqlctl/fakemysqldaemon.go @@ -299,7 +299,7 @@ func (fmd *FakeMysqlDaemon) CurrentPrimaryPositionLocked(pos replication.Positio } // ReplicationStatus is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) ReplicationStatus() (replication.ReplicationStatus, error) { +func (fmd *FakeMysqlDaemon) ReplicationStatus(ctx context.Context) (replication.ReplicationStatus, error) { if fmd.ReplicationStatusError != nil { return replication.ReplicationStatus{}, fmd.ReplicationStatusError } @@ -385,49 +385,49 @@ func (fmd *FakeMysqlDaemon) GetPreviousGTIDs(ctx context.Context, binlog string) } // PrimaryPosition is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) PrimaryPosition() (replication.Position, error) { +func (fmd *FakeMysqlDaemon) PrimaryPosition(ctx context.Context) (replication.Position, error) { return fmd.CurrentPrimaryPosition, nil } // IsReadOnly is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) IsReadOnly() (bool, error) { +func (fmd *FakeMysqlDaemon) IsReadOnly(ctx context.Context) (bool, error) { return fmd.ReadOnly, nil } // IsSuperReadOnly is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) IsSuperReadOnly() (bool, error) { +func (fmd *FakeMysqlDaemon) IsSuperReadOnly(ctx context.Context) (bool, error) { return fmd.SuperReadOnly.Load(), nil } // SetReadOnly is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) SetReadOnly(on bool) error { +func (fmd *FakeMysqlDaemon) SetReadOnly(ctx context.Context, on bool) error { fmd.ReadOnly = on return nil } // SetSuperReadOnly is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error) { +func (fmd *FakeMysqlDaemon) SetSuperReadOnly(ctx context.Context, on bool) (ResetSuperReadOnlyFunc, error) { fmd.SuperReadOnly.Store(on) fmd.ReadOnly = on return nil, nil } // StartReplication is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) StartReplication(hookExtraEnv map[string]string) error { +func (fmd *FakeMysqlDaemon) StartReplication(ctx context.Context, hookExtraEnv map[string]string) error { if fmd.StartReplicationError != nil { return fmd.StartReplicationError } - return fmd.ExecuteSuperQueryList(context.Background(), []string{ - "START SLAVE", + return fmd.ExecuteSuperQueryList(ctx, []string{ + "START REPLICA", }) } // RestartReplication is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) RestartReplication(hookExtraEnv map[string]string) error { - return fmd.ExecuteSuperQueryList(context.Background(), []string{ - "STOP SLAVE", - "RESET SLAVE", - "START SLAVE", +func (fmd *FakeMysqlDaemon) RestartReplication(ctx context.Context, hookExtraEnv map[string]string) error { + return fmd.ExecuteSuperQueryList(ctx, []string{ + "STOP REPLICA", + "RESET REPLICA", + "START REPLICA", }) } @@ -437,25 +437,25 @@ func (fmd *FakeMysqlDaemon) StartReplicationUntilAfter(ctx context.Context, pos return fmt.Errorf("wrong pos for StartReplicationUntilAfter: expected %v got %v", fmd.SetReplicationPositionPos, pos) } - return fmd.ExecuteSuperQueryList(context.Background(), []string{ - "START SLAVE UNTIL AFTER", + return fmd.ExecuteSuperQueryList(ctx, []string{ + "START REPLICA UNTIL AFTER", }) } // StopReplication is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) StopReplication(hookExtraEnv map[string]string) error { +func (fmd *FakeMysqlDaemon) StopReplication(ctx context.Context, hookExtraEnv map[string]string) error { if fmd.StopReplicationError != nil { return fmd.StopReplicationError } - return fmd.ExecuteSuperQueryList(context.Background(), []string{ - "STOP SLAVE", + return fmd.ExecuteSuperQueryList(ctx, []string{ + "STOP REPLICA", }) } // StopIOThread is part of the MysqlDaemon interface. func (fmd *FakeMysqlDaemon) StopIOThread(ctx context.Context) error { - return fmd.ExecuteSuperQueryList(context.Background(), []string{ - "STOP SLAVE IO_THREAD", + return fmd.ExecuteSuperQueryList(ctx, []string{ + "STOP REPLICA IO_THREAD", }) } @@ -465,7 +465,7 @@ func (fmd *FakeMysqlDaemon) SetReplicationPosition(ctx context.Context, pos repl return fmt.Errorf("wrong pos for SetReplicationPosition: expected %v got %v", fmd.SetReplicationPositionPos, pos) } return fmd.ExecuteSuperQueryList(ctx, []string{ - "FAKE SET SLAVE POSITION", + "FAKE SET REPLICA POSITION", }) } @@ -486,11 +486,11 @@ func (fmd *FakeMysqlDaemon) SetReplicationSource(ctx context.Context, host strin } cmds := []string{} if stopReplicationBefore { - cmds = append(cmds, "STOP SLAVE") + cmds = append(cmds, "STOP REPLICA") } - cmds = append(cmds, "FAKE SET MASTER") + cmds = append(cmds, "FAKE SET SOURCE") if startReplicationAfter { - cmds = append(cmds, "START SLAVE") + cmds = append(cmds, "START REPLICA") } fmd.CurrentSourceHost = host fmd.CurrentSourcePort = port @@ -515,8 +515,13 @@ func (fmd *FakeMysqlDaemon) WaitSourcePos(_ context.Context, pos replication.Pos return fmt.Errorf("wrong input for WaitSourcePos: expected a value in %v got %v", fmd.WaitPrimaryPositions, pos) } +// CatchupToGTID is part of the MysqlDaemon interface. +func (fmd *FakeMysqlDaemon) CatchupToGTID(_ context.Context, pos replication.Position) error { + return nil +} + // Promote is part of the MysqlDaemon interface. -func (fmd *FakeMysqlDaemon) Promote(hookExtraEnv map[string]string) (replication.Position, error) { +func (fmd *FakeMysqlDaemon) Promote(ctx context.Context, hookExtraEnv map[string]string) (replication.Position, error) { if fmd.PromoteLag > 0 { time.Sleep(fmd.PromoteLag) } @@ -551,9 +556,9 @@ func (fmd *FakeMysqlDaemon) ExecuteSuperQueryList(ctx context.Context, queryList // Intercept some queries to update our status. switch query { - case "START SLAVE": + case "START REPLICA": fmd.Replicating = true - case "STOP SLAVE": + case "STOP REPLICA": fmd.Replicating = false } } diff --git a/go/vt/mysqlctl/mysql_daemon.go b/go/vt/mysqlctl/mysql_daemon.go index e396f128ee0..d63162a29cc 100644 --- a/go/vt/mysqlctl/mysql_daemon.go +++ b/go/vt/mysqlctl/mysql_daemon.go @@ -53,12 +53,12 @@ type MysqlDaemon interface { GetServerUUID(ctx context.Context) (string, error) // replication related methods - StartReplication(hookExtraEnv map[string]string) error - RestartReplication(hookExtraEnv map[string]string) error + StartReplication(ctx context.Context, hookExtraEnv map[string]string) error + RestartReplication(ctx context.Context, hookExtraEnv map[string]string) error StartReplicationUntilAfter(ctx context.Context, pos replication.Position) error - StopReplication(hookExtraEnv map[string]string) error + StopReplication(ctx context.Context, hookExtraEnv map[string]string) error StopIOThread(ctx context.Context) error - ReplicationStatus() (replication.ReplicationStatus, error) + ReplicationStatus(ctx context.Context) (replication.ReplicationStatus, error) PrimaryStatus(ctx context.Context) (replication.PrimaryStatus, error) GetGTIDPurged(ctx context.Context) (replication.Position, error) SetSemiSyncEnabled(ctx context.Context, source, replica bool) error @@ -77,20 +77,21 @@ type MysqlDaemon interface { // reparenting related methods ResetReplication(ctx context.Context) error - PrimaryPosition() (replication.Position, error) - IsReadOnly() (bool, error) - IsSuperReadOnly() (bool, error) - SetReadOnly(on bool) error - SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error) + PrimaryPosition(ctx context.Context) (replication.Position, error) + IsReadOnly(ctx context.Context) (bool, error) + IsSuperReadOnly(ctx context.Context) (bool, error) + SetReadOnly(ctx context.Context, on bool) error + SetSuperReadOnly(ctx context.Context, on bool) (ResetSuperReadOnlyFunc, error) SetReplicationPosition(ctx context.Context, pos replication.Position) error SetReplicationSource(ctx context.Context, host string, port int32, stopReplicationBefore bool, startReplicationAfter bool) error WaitForReparentJournal(ctx context.Context, timeCreatedNS int64) error WaitSourcePos(context.Context, replication.Position) error + CatchupToGTID(context.Context, replication.Position) error // Promote makes the current server the primary. It will not change // the read_only state of the server. - Promote(map[string]string) (replication.Position, error) + Promote(context.Context, map[string]string) (replication.Position, error) // Schema related methods GetSchema(ctx context.Context, dbName string, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go index 1ee7c72bf86..952c0987c82 100644 --- a/go/vt/mysqlctl/mysqld.go +++ b/go/vt/mysqlctl/mysqld.go @@ -1383,7 +1383,7 @@ func (mysqld *Mysqld) ApplyBinlogFile(ctx context.Context, req *mysqlctlpb.Apply // parameters. We do it blindly, since this will fail on MariaDB, which doesn't // have super_read_only This is safe, since we're restarting MySQL after the restore anyway log.Infof("ApplyBinlogFile: disabling super_read_only") - resetFunc, err := mysqld.SetSuperReadOnly(false) + resetFunc, err := mysqld.SetSuperReadOnly(ctx, false) if err != nil { if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERUnknownSystemVariable { log.Warningf("ApplyBinlogFile: server does not know about super_read_only, continuing anyway...") diff --git a/go/vt/mysqlctl/query.go b/go/vt/mysqlctl/query.go index 5e21913c617..7a1816e3cfb 100644 --- a/go/vt/mysqlctl/query.go +++ b/go/vt/mysqlctl/query.go @@ -227,6 +227,8 @@ func (mysqld *Mysqld) fetchStatuses(ctx context.Context, pattern string) (map[st } const ( + sourcePasswordStart = " SOURCE_PASSWORD = '" + sourcePasswordEnd = "',\n" masterPasswordStart = " MASTER_PASSWORD = '" masterPasswordEnd = "',\n" passwordStart = " PASSWORD = '" @@ -234,7 +236,17 @@ const ( ) func redactPassword(input string) string { - i := strings.Index(input, masterPasswordStart) + i := strings.Index(input, sourcePasswordStart) + // We have primary password in the query, try to redact it + if i != -1 { + j := strings.Index(input[i+len(sourcePasswordStart):], sourcePasswordEnd) + if j == -1 { + return input + } + input = input[:i+len(sourcePasswordStart)] + strings.Repeat("*", 4) + input[i+len(masterPasswordStart)+j:] + } + + i = strings.Index(input, masterPasswordStart) // We have primary password in the query, try to redact it if i != -1 { j := strings.Index(input[i+len(masterPasswordStart):], masterPasswordEnd) diff --git a/go/vt/mysqlctl/reparent.go b/go/vt/mysqlctl/reparent.go index 0cd89c59ab3..08326390f97 100644 --- a/go/vt/mysqlctl/reparent.go +++ b/go/vt/mysqlctl/reparent.go @@ -85,8 +85,7 @@ func (mysqld *Mysqld) WaitForReparentJournal(ctx context.Context, timeCreatedNS } // Promote will promote this server to be the new primary. -func (mysqld *Mysqld) Promote(hookExtraEnv map[string]string) (replication.Position, error) { - ctx := context.TODO() +func (mysqld *Mysqld) Promote(ctx context.Context, hookExtraEnv map[string]string) (replication.Position, error) { conn, err := getPoolReconnect(ctx, mysqld.dbaPool) if err != nil { return replication.Position{}, err @@ -96,7 +95,7 @@ func (mysqld *Mysqld) Promote(hookExtraEnv map[string]string) (replication.Posit // Since we handle replication, just stop it. cmds := []string{ conn.Conn.StopReplicationCommand(), - "RESET SLAVE ALL", // "ALL" makes it forget primary host:port. + conn.Conn.ResetReplicationCommand(), // When using semi-sync and GTID, a replica first connects to the new primary with a given GTID set, // it can take a long time to scan the current binlog file to find the corresponding position. // This can cause commits that occur soon after the primary is promoted to take a long time waiting diff --git a/go/vt/mysqlctl/reparent_test.go b/go/vt/mysqlctl/reparent_test.go index 7d43ffe9d30..d882d594233 100644 --- a/go/vt/mysqlctl/reparent_test.go +++ b/go/vt/mysqlctl/reparent_test.go @@ -30,13 +30,13 @@ import ( ) func TestPopulateReparentJournal(t *testing.T) { - input := `MySQL binlog position: filename 'vt-0476396352-bin.000005', position '310088991', GTID of the last change '145e508e-ae54-11e9-8ce6-46824dd1815e:1-3, + input := `MySQL replica position: filename 'vt-0476396352-bin.000005', position '310088991', GTID of the last change '145e508e-ae54-11e9-8ce6-46824dd1815e:1-3, 1e51f8be-ae54-11e9-a7c6-4280a041109b:1-3, 47b59de1-b368-11e9-b48b-624401d35560:1-152981, 557def0a-b368-11e9-84ed-f6fffd91cc57:1-3, 599ef589-ae55-11e9-9688-ca1f44501925:1-14857169, b9ce485d-b36b-11e9-9b17-2a6e0a6011f4:1-371262' - MySQL slave binlog position: master host '10.128.0.43', purge list '145e508e-ae54-11e9-8ce6-46824dd1815e:1-3, 1e51f8be-ae54-11e9-a7c6-4280a041109b:1-3, 47b59de1-b368-11e9-b48b-624401d35560:1-152981, 557def0a-b368-11e9-84ed-f6fffd91cc57:1-3, 599ef589-ae55-11e9-9688-ca1f44501925:1-14857169, b9ce485d-b36b-11e9-9b17-2a6e0a6011f4:1-371262', channel name: '' + MySQL replica binlog position: master host '10.128.0.43', purge list '145e508e-ae54-11e9-8ce6-46824dd1815e:1-3, 1e51f8be-ae54-11e9-a7c6-4280a041109b:1-3, 47b59de1-b368-11e9-b48b-624401d35560:1-152981, 557def0a-b368-11e9-84ed-f6fffd91cc57:1-3, 599ef589-ae55-11e9-9688-ca1f44501925:1-14857169, b9ce485d-b36b-11e9-9b17-2a6e0a6011f4:1-371262', channel name: '' 190809 00:15:44 [00] Streaming 190809 00:15:44 [00] ...done @@ -81,15 +81,15 @@ func TestPromote(t *testing.T) { dbc := dbconfigs.NewTestDBConfigs(cp, cp, "fakesqldb") db.AddQuery("SELECT 1", &sqltypes.Result{}) - db.AddQuery("STOP SLAVE", &sqltypes.Result{}) - db.AddQuery("RESET SLAVE ALL", &sqltypes.Result{}) + db.AddQuery("STOP REPLICA", &sqltypes.Result{}) + db.AddQuery("RESET REPLICA ALL", &sqltypes.Result{}) db.AddQuery("FLUSH BINARY LOGS", &sqltypes.Result{}) db.AddQuery("SELECT @@global.gtid_executed", sqltypes.MakeTestResult(sqltypes.MakeTestFields("test_field", "varchar"), "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:12-17")) testMysqld := NewMysqld(dbc) defer testMysqld.Close() - pos, err := testMysqld.Promote(map[string]string{}) + pos, err := testMysqld.Promote(context.Background(), map[string]string{}) assert.NoError(t, err) assert.Equal(t, "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8:12-17", pos.String()) } diff --git a/go/vt/mysqlctl/replication.go b/go/vt/mysqlctl/replication.go index 47d0a727fed..71f850f0ca5 100644 --- a/go/vt/mysqlctl/replication.go +++ b/go/vt/mysqlctl/replication.go @@ -41,10 +41,10 @@ type ResetSuperReadOnlyFunc func() error // WaitForReplicationStart waits until the deadline for replication to start. // This validates the current primary is correct and can be connected to. -func WaitForReplicationStart(mysqld MysqlDaemon, replicaStartDeadline int) (err error) { +func WaitForReplicationStart(ctx context.Context, mysqld MysqlDaemon, replicaStartDeadline int) (err error) { var replicaStatus replication.ReplicationStatus for replicaWait := 0; replicaWait < replicaStartDeadline; replicaWait++ { - replicaStatus, err = mysqld.ReplicationStatus() + replicaStatus, err = mysqld.ReplicationStatus(ctx) if err != nil { return err } @@ -69,8 +69,7 @@ func WaitForReplicationStart(mysqld MysqlDaemon, replicaStartDeadline int) (err } // StartReplication starts replication. -func (mysqld *Mysqld) StartReplication(hookExtraEnv map[string]string) error { - ctx := context.TODO() +func (mysqld *Mysqld) StartReplication(ctx context.Context, hookExtraEnv map[string]string) error { conn, err := getPoolReconnect(ctx, mysqld.dbaPool) if err != nil { return err @@ -113,13 +112,12 @@ func (mysqld *Mysqld) StartSQLThreadUntilAfter(ctx context.Context, targetPos re } // StopReplication stops replication. -func (mysqld *Mysqld) StopReplication(hookExtraEnv map[string]string) error { +func (mysqld *Mysqld) StopReplication(ctx context.Context, hookExtraEnv map[string]string) error { h := hook.NewSimpleHook("preflight_stop_slave") h.ExtraEnv = hookExtraEnv if err := h.ExecuteOptional(); err != nil { return err } - ctx := context.TODO() conn, err := getPoolReconnect(ctx, mysqld.dbaPool) if err != nil { return err @@ -152,13 +150,12 @@ func (mysqld *Mysqld) StopSQLThread(ctx context.Context) error { } // RestartReplication stops, resets and starts replication. -func (mysqld *Mysqld) RestartReplication(hookExtraEnv map[string]string) error { +func (mysqld *Mysqld) RestartReplication(ctx context.Context, hookExtraEnv map[string]string) error { h := hook.NewSimpleHook("preflight_stop_slave") h.ExtraEnv = hookExtraEnv if err := h.ExecuteOptional(); err != nil { return err } - ctx := context.TODO() conn, err := getPoolReconnect(ctx, mysqld.dbaPool) if err != nil { return err @@ -231,8 +228,8 @@ func (mysqld *Mysqld) GetServerUUID(ctx context.Context) (string, error) { } // IsReadOnly return true if the instance is read only -func (mysqld *Mysqld) IsReadOnly() (bool, error) { - qr, err := mysqld.FetchSuperQuery(context.TODO(), "SHOW VARIABLES LIKE 'read_only'") +func (mysqld *Mysqld) IsReadOnly(ctx context.Context) (bool, error) { + qr, err := mysqld.FetchSuperQuery(ctx, "SHOW VARIABLES LIKE 'read_only'") if err != nil { return true, err } @@ -246,8 +243,8 @@ func (mysqld *Mysqld) IsReadOnly() (bool, error) { } // IsSuperReadOnly return true if the instance is super read only -func (mysqld *Mysqld) IsSuperReadOnly() (bool, error) { - qr, err := mysqld.FetchSuperQuery(context.TODO(), "SELECT @@global.super_read_only") +func (mysqld *Mysqld) IsSuperReadOnly(ctx context.Context) (bool, error) { + qr, err := mysqld.FetchSuperQuery(ctx, "SELECT @@global.super_read_only") if err != nil { return false, err } @@ -263,29 +260,19 @@ func (mysqld *Mysqld) IsSuperReadOnly() (bool, error) { } // SetReadOnly set/unset the read_only flag -func (mysqld *Mysqld) SetReadOnly(on bool) error { - // temp logging, to be removed in v17 - var newState string - switch on { - case false: - newState = "ReadWrite" - case true: - newState = "ReadOnly" - } - log.Infof("SetReadOnly setting to : %s", newState) - +func (mysqld *Mysqld) SetReadOnly(ctx context.Context, on bool) error { query := "SET GLOBAL read_only = " if on { query += "ON" } else { query += "OFF" } - return mysqld.ExecuteSuperQuery(context.TODO(), query) + return mysqld.ExecuteSuperQuery(ctx, query) } // SetSuperReadOnly set/unset the super_read_only flag. // Returns a function which is called to set super_read_only back to its original value. -func (mysqld *Mysqld) SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error) { +func (mysqld *Mysqld) SetSuperReadOnly(ctx context.Context, on bool) (ResetSuperReadOnlyFunc, error) { // return function for switching `OFF` super_read_only var resetFunc ResetSuperReadOnlyFunc var disableFunc = func() error { @@ -301,7 +288,7 @@ func (mysqld *Mysqld) SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error) return err } - superReadOnlyEnabled, err := mysqld.IsSuperReadOnly() + superReadOnlyEnabled, err := mysqld.IsSuperReadOnly(ctx) if err != nil { return nil, err } @@ -372,9 +359,24 @@ func (mysqld *Mysqld) WaitSourcePos(ctx context.Context, targetPos replication.P return nil } +func (mysqld *Mysqld) CatchupToGTID(ctx context.Context, targetPos replication.Position) error { + params, err := mysqld.dbcfgs.ReplConnector().MysqlParams() + if err != nil { + return err + } + conn, err := getPoolReconnect(ctx, mysqld.dbaPool) + if err != nil { + return err + } + defer conn.Recycle() + + cmds := conn.Conn.CatchupToGTIDCommands(params, targetPos) + return mysqld.executeSuperQueryListConn(ctx, conn, cmds) +} + // ReplicationStatus returns the server replication status -func (mysqld *Mysqld) ReplicationStatus() (replication.ReplicationStatus, error) { - conn, err := getPoolReconnect(context.TODO(), mysqld.dbaPool) +func (mysqld *Mysqld) ReplicationStatus(ctx context.Context) (replication.ReplicationStatus, error) { + conn, err := getPoolReconnect(ctx, mysqld.dbaPool) if err != nil { return replication.ReplicationStatus{}, err } @@ -406,8 +408,8 @@ func (mysqld *Mysqld) GetGTIDPurged(ctx context.Context) (replication.Position, } // PrimaryPosition returns the primary replication position. -func (mysqld *Mysqld) PrimaryPosition() (replication.Position, error) { - conn, err := getPoolReconnect(context.TODO(), mysqld.dbaPool) +func (mysqld *Mysqld) PrimaryPosition(ctx context.Context) (replication.Position, error) { + conn, err := getPoolReconnect(ctx, mysqld.dbaPool) if err != nil { return replication.Position{}, err } @@ -479,12 +481,12 @@ func (mysqld *Mysqld) ResetReplicationParameters(ctx context.Context) error { return mysqld.executeSuperQueryListConn(ctx, conn, cmds) } -// +------+---------+---------------------+------+-------------+------+----------------------------------------------------------------+------------------+ -// | Id | User | Host | db | Command | Time | State | Info | -// +------+---------+---------------------+------+-------------+------+----------------------------------------------------------------+------------------+ -// | 9792 | vt_repl | host:port | NULL | Binlog Dump | 54 | Has sent all binlog to slave; waiting for binlog to be updated | NULL | -// | 9797 | vt_dba | localhost | NULL | Query | 0 | NULL | show processlist | -// +------+---------+---------------------+------+-------------+------+----------------------------------------------------------------+------------------+ +// +------+---------+---------------------+------+-------------+------+------------------------------------------------------------------+------------------+ +// | Id | User | Host | db | Command | Time | State | Info | +// +------+---------+---------------------+------+-------------+------+------------------------------------------------------------------+------------------+ +// | 9792 | vt_repl | host:port | NULL | Binlog Dump | 54 | Has sent all binlog to replica; waiting for binlog to be updated | NULL | +// | 9797 | vt_dba | localhost | NULL | Query | 0 | NULL | show processlist | +// +------+---------+---------------------+------+-------------+------+------------------------------------------------------------------+------------------+ // // Array indices for the results of SHOW PROCESSLIST. const ( @@ -501,8 +503,8 @@ const ( ) // FindReplicas gets IP addresses for all currently connected replicas. -func FindReplicas(mysqld MysqlDaemon) ([]string, error) { - qr, err := mysqld.FetchSuperQuery(context.TODO(), "SHOW PROCESSLIST") +func FindReplicas(ctx context.Context, mysqld MysqlDaemon) ([]string, error) { + qr, err := mysqld.FetchSuperQuery(ctx, "SHOW PROCESSLIST") if err != nil { return nil, err } @@ -536,31 +538,13 @@ func FindReplicas(mysqld MysqlDaemon) ([]string, error) { // GetBinlogInformation gets the binlog format, whether binlog is enabled and if updates on replica logging is enabled. func (mysqld *Mysqld) GetBinlogInformation(ctx context.Context) (string, bool, bool, string, error) { - qr, err := mysqld.FetchSuperQuery(ctx, "select @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates, @@global.binlog_row_image") - if err != nil { - return "", false, false, "", err - } - if len(qr.Rows) != 1 { - return "", false, false, "", errors.New("unable to read global variables binlog_format, log_bin, log_slave_updates, gtid_mode, binlog_rowge") - } - res := qr.Named().Row() - binlogFormat, err := res.ToString("@@global.binlog_format") - if err != nil { - return "", false, false, "", err - } - logBin, err := res.ToInt64("@@global.log_bin") - if err != nil { - return "", false, false, "", err - } - logReplicaUpdates, err := res.ToInt64("@@global.log_slave_updates") - if err != nil { - return "", false, false, "", err - } - binlogRowImage, err := res.ToString("@@global.binlog_row_image") + conn, err := getPoolReconnect(ctx, mysqld.dbaPool) if err != nil { return "", false, false, "", err } - return binlogFormat, logBin == 1, logReplicaUpdates == 1, binlogRowImage, nil + defer conn.Recycle() + + return conn.Conn.BinlogInformation() } // GetGTIDMode gets the GTID mode for the server diff --git a/go/vt/mysqlctl/replication_test.go b/go/vt/mysqlctl/replication_test.go index cb84ea8ff04..636fdf415fd 100644 --- a/go/vt/mysqlctl/replication_test.go +++ b/go/vt/mysqlctl/replication_test.go @@ -37,6 +37,37 @@ func testRedacted(t *testing.T, source, expected string) { assert.Equal(t, expected, redactPassword(source)) } +func TestRedactSourcePassword(t *testing.T) { + + // regular test case + testRedacted(t, `CHANGE REPLICATION SOURCE TO + SOURCE_PASSWORD = 'AAA', + SOURCE_CONNECT_RETRY = 1 +`, + `CHANGE REPLICATION SOURCE TO + SOURCE_PASSWORD = '****', + SOURCE_CONNECT_RETRY = 1 +`) + + // empty password + testRedacted(t, `CHANGE REPLICATION SOURCE TO + SOURCE_PASSWORD = '', + SOURCE_CONNECT_RETRY = 1 +`, + `CHANGE REPLICATION SOURCE TO + SOURCE_PASSWORD = '****', + SOURCE_CONNECT_RETRY = 1 +`) + + // no beginning match + testRedacted(t, "aaaaaaaaaaaaaa", "aaaaaaaaaaaaaa") + + // no end match + testRedacted(t, `CHANGE REPLICATION SOURCE TO + SOURCE_PASSWORD = 'AAA`, `CHANGE REPLICATION SOURCE TO + SOURCE_PASSWORD = 'AAA`) +} + func TestRedactMasterPassword(t *testing.T) { // regular test case @@ -83,11 +114,11 @@ func TestRedactPassword(t *testing.T) { // both primary password and password testRedacted(t, `START xxx - MASTER_PASSWORD = 'AAA', + SOURCE_PASSWORD = 'AAA', PASSWORD = 'BBB' `, `START xxx - MASTER_PASSWORD = '****', + SOURCE_PASSWORD = '****', PASSWORD = '****' `) } @@ -101,11 +132,11 @@ func TestWaitForReplicationStart(t *testing.T) { fakemysqld.Close() }() - err := WaitForReplicationStart(fakemysqld, 2) + err := WaitForReplicationStart(context.Background(), fakemysqld, 2) assert.NoError(t, err) fakemysqld.ReplicationStatusError = fmt.Errorf("test error") - err = WaitForReplicationStart(fakemysqld, 2) + err = WaitForReplicationStart(context.Background(), fakemysqld, 2) assert.ErrorContains(t, err, "test error") params := db.ConnParams() @@ -116,9 +147,9 @@ func TestWaitForReplicationStart(t *testing.T) { defer testMysqld.Close() db.AddQuery("SELECT 1", &sqltypes.Result{}) - db.AddQuery("SHOW SLAVE STATUS", sqltypes.MakeTestResult(sqltypes.MakeTestFields("Last_SQL_Error|Last_IO_Error", "varchar|varchar"), "test sql error|test io error")) + db.AddQuery("SHOW REPLICA STATUS", sqltypes.MakeTestResult(sqltypes.MakeTestFields("Last_SQL_Error|Last_IO_Error", "varchar|varchar"), "test sql error|test io error")) - err = WaitForReplicationStart(testMysqld, 2) + err = WaitForReplicationStart(context.Background(), testMysqld, 2) assert.ErrorContains(t, err, "Last_SQL_Error: test sql error, Last_IO_Error: test io error") } @@ -230,17 +261,17 @@ func TestReplicationStatus(t *testing.T) { dbc := dbconfigs.NewTestDBConfigs(cp, cp, "fakesqldb") db.AddQuery("SELECT 1", &sqltypes.Result{}) - db.AddQuery("SHOW SLAVE STATUS", sqltypes.MakeTestResult(sqltypes.MakeTestFields("test_field", "varchar"), "test_status")) + db.AddQuery("SHOW REPLICA STATUS", sqltypes.MakeTestResult(sqltypes.MakeTestFields("test_field", "varchar"), "test_status")) testMysqld := NewMysqld(dbc) defer testMysqld.Close() - res, err := testMysqld.ReplicationStatus() + res, err := testMysqld.ReplicationStatus(context.Background()) assert.NoError(t, err) assert.True(t, res.ReplicationLagUnknown) - db.AddQuery("SHOW SLAVE STATUS", &sqltypes.Result{}) - res, err = testMysqld.ReplicationStatus() + db.AddQuery("SHOW REPLICA STATUS", &sqltypes.Result{}) + res, err = testMysqld.ReplicationStatus(context.Background()) assert.Error(t, err) assert.False(t, res.ReplicationLagUnknown) } @@ -303,7 +334,7 @@ func TestPrimaryPosition(t *testing.T) { testMysqld := NewMysqld(dbc) defer testMysqld.Close() - res, err := testMysqld.PrimaryPosition() + res, err := testMysqld.PrimaryPosition(context.Background()) assert.NoError(t, err) assert.Equal(t, "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8:12-17", res.String()) } @@ -348,7 +379,7 @@ func TestSetReplicationSource(t *testing.T) { db.AddQuery("SELECT 1", &sqltypes.Result{}) db.AddQuery("RESET MASTER", &sqltypes.Result{}) - db.AddQuery("STOP SLAVE", &sqltypes.Result{}) + db.AddQuery("STOP REPLICA", &sqltypes.Result{}) testMysqld := NewMysqld(dbc) defer testMysqld.Close() @@ -357,9 +388,9 @@ func TestSetReplicationSource(t *testing.T) { // We expect query containing passed host and port to be executed err := testMysqld.SetReplicationSource(ctx, "test_host", 2, true, true) - assert.ErrorContains(t, err, `MASTER_HOST = 'test_host'`) - assert.ErrorContains(t, err, `MASTER_PORT = 2`) - assert.ErrorContains(t, err, `CHANGE MASTER TO`) + assert.ErrorContains(t, err, `SOURCE_HOST = 'test_host'`) + assert.ErrorContains(t, err, `SOURCE_PORT = 2`) + assert.ErrorContains(t, err, `CHANGE REPLICATION SOURCE TO`) } func TestResetReplication(t *testing.T) { @@ -372,17 +403,17 @@ func TestResetReplication(t *testing.T) { db.AddQuery("SELECT 1", &sqltypes.Result{}) db.AddQuery("SHOW GLOBAL VARIABLES LIKE 'rpl_semi_sync%'", &sqltypes.Result{}) - db.AddQuery("STOP SLAVE", &sqltypes.Result{}) + db.AddQuery("STOP REPLICA", &sqltypes.Result{}) testMysqld := NewMysqld(dbc) defer testMysqld.Close() ctx := context.Background() err := testMysqld.ResetReplication(ctx) - assert.ErrorContains(t, err, "RESET SLAVE ALL") + assert.ErrorContains(t, err, "RESET REPLICA ALL") // We expect this query to be executed - db.AddQuery("RESET SLAVE ALL", &sqltypes.Result{}) + db.AddQuery("RESET REPLICA ALL", &sqltypes.Result{}) err = testMysqld.ResetReplication(ctx) assert.ErrorContains(t, err, "RESET MASTER") @@ -402,17 +433,17 @@ func TestResetReplicationParameters(t *testing.T) { db.AddQuery("SELECT 1", &sqltypes.Result{}) db.AddQuery("SHOW GLOBAL VARIABLES LIKE 'rpl_semi_sync%'", &sqltypes.Result{}) - db.AddQuery("STOP SLAVE", &sqltypes.Result{}) + db.AddQuery("STOP REPLICA", &sqltypes.Result{}) testMysqld := NewMysqld(dbc) defer testMysqld.Close() ctx := context.Background() err := testMysqld.ResetReplicationParameters(ctx) - assert.ErrorContains(t, err, "RESET SLAVE ALL") + assert.ErrorContains(t, err, "RESET REPLICA ALL") // We expect this query to be executed - db.AddQuery("RESET SLAVE ALL", &sqltypes.Result{}) + db.AddQuery("RESET REPLICA ALL", &sqltypes.Result{}) err = testMysqld.ResetReplicationParameters(ctx) assert.NoError(t, err) } @@ -427,10 +458,10 @@ func TestFindReplicas(t *testing.T) { }() fakemysqld.FetchSuperQueryMap = map[string]*sqltypes.Result{ - "SHOW PROCESSLIST": sqltypes.MakeTestResult(sqltypes.MakeTestFields("Id|User|Host|db|Command|Time|State|Info", "varchar|varchar|varchar|varchar|varchar|varchar|varchar|varchar"), "1|user1|localhost:12|db1|Binlog Dump|54|Has sent all binlog to slave|NULL"), + "SHOW PROCESSLIST": sqltypes.MakeTestResult(sqltypes.MakeTestFields("Id|User|Host|db|Command|Time|State|Info", "varchar|varchar|varchar|varchar|varchar|varchar|varchar|varchar"), "1|user1|localhost:12|db1|Binlog Dump|54|Has sent all binlog to replica|NULL"), } - res, err := FindReplicas(fakemysqld) + res, err := FindReplicas(context.Background(), fakemysqld) assert.NoError(t, err) want, err := net.LookupHost("localhost") @@ -448,18 +479,18 @@ func TestGetBinlogInformation(t *testing.T) { dbc := dbconfigs.NewTestDBConfigs(cp, cp, "fakesqldb") db.AddQuery("SELECT 1", &sqltypes.Result{}) - db.AddQuery("SELECT @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates, @@global.binlog_row_image", sqltypes.MakeTestResult(sqltypes.MakeTestFields("@@global.binlog_format|@@global.log_bin|@@global.log_slave_updates|@@global.binlog_row_image", "varchar|int64|int64|varchar"), "binlog|1|2|row_image")) + db.AddQuery("SELECT @@global.binlog_format, @@global.log_bin, @@global.log_replica_updates, @@global.binlog_row_image", sqltypes.MakeTestResult(sqltypes.MakeTestFields("@@global.binlog_format|@@global.log_bin|@@global.log_replica_updates|@@global.binlog_row_image", "varchar|int64|int64|varchar"), "binlog|1|2|row_image")) testMysqld := NewMysqld(dbc) defer testMysqld.Close() ctx := context.Background() - bin, logBin, slaveUpdate, rowImage, err := testMysqld.GetBinlogInformation(ctx) + bin, logBin, replicaUpdate, rowImage, err := testMysqld.GetBinlogInformation(ctx) assert.NoError(t, err) assert.Equal(t, "binlog", bin) assert.Equal(t, "row_image", rowImage) assert.True(t, logBin) - assert.False(t, slaveUpdate) + assert.False(t, replicaUpdate) } func TestGetGTIDMode(t *testing.T) { diff --git a/go/vt/mysqlctl/xtrabackupengine_test.go b/go/vt/mysqlctl/xtrabackupengine_test.go index 4ceec2f960d..f560833d278 100644 --- a/go/vt/mysqlctl/xtrabackupengine_test.go +++ b/go/vt/mysqlctl/xtrabackupengine_test.go @@ -35,7 +35,7 @@ func TestFindReplicationPosition(t *testing.T) { 557def0a-b368-11e9-84ed-f6fffd91cc57:1-3, 599ef589-ae55-11e9-9688-ca1f44501925:1-14857169, b9ce485d-b36b-11e9-9b17-2a6e0a6011f4:1-371262' - MySQL slave binlog position: master host '10.128.0.43', purge list '145e508e-ae54-11e9-8ce6-46824dd1815e:1-3, 1e51f8be-ae54-11e9-a7c6-4280a041109b:1-3, 47b59de1-b368-11e9-b48b-624401d35560:1-152981, 557def0a-b368-11e9-84ed-f6fffd91cc57:1-3, 599ef589-ae55-11e9-9688-ca1f44501925:1-14857169, b9ce485d-b36b-11e9-9b17-2a6e0a6011f4:1-371262', channel name: '' + MySQL replica binlog position: master host '10.128.0.43', purge list '145e508e-ae54-11e9-8ce6-46824dd1815e:1-3, 1e51f8be-ae54-11e9-a7c6-4280a041109b:1-3, 47b59de1-b368-11e9-b48b-624401d35560:1-152981, 557def0a-b368-11e9-84ed-f6fffd91cc57:1-3, 599ef589-ae55-11e9-9688-ca1f44501925:1-14857169, b9ce485d-b36b-11e9-9b17-2a6e0a6011f4:1-371262', channel name: '' 190809 00:15:44 [00] Streaming 190809 00:15:44 [00] ...done diff --git a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go index f83861d7fc8..91f2b1303a8 100644 --- a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go +++ b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go @@ -65,25 +65,25 @@ func TestInitShardPrimary(t *testing.T) { tablet2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // These come from InitShardPrimary "FAKE RESET ALL REPLICATION", - "FAKE SET SLAVE POSITION", - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET REPLICA POSITION", + "FAKE SET SOURCE", + "START REPLICA", } tablet2.FakeMysqlDaemon.SetReplicationSourceInputs = append(tablet2.FakeMysqlDaemon.SetReplicationSourceInputs, fmt.Sprintf("%v:%v", tablet1.Tablet.Hostname, tablet1.Tablet.MysqlPort)) tablet3.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", "FAKE RESET ALL REPLICATION", - "FAKE SET SLAVE POSITION", - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET REPLICA POSITION", + "FAKE SET SOURCE", + "START REPLICA", } tablet3.FakeMysqlDaemon.SetReplicationSourceInputs = append(tablet3.FakeMysqlDaemon.SetReplicationSourceInputs, fmt.Sprintf("%v:%v", tablet1.Tablet.Hostname, tablet1.Tablet.MysqlPort)) @@ -128,17 +128,17 @@ func TestInitShardPrimaryNoFormerPrimary(t *testing.T) { tablet2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ "FAKE RESET ALL REPLICATION", - "FAKE SET SLAVE POSITION", - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET REPLICA POSITION", + "FAKE SET SOURCE", + "START REPLICA", } tablet2.FakeMysqlDaemon.SetReplicationSourceInputs = append(tablet2.FakeMysqlDaemon.SetReplicationSourceInputs, fmt.Sprintf("%v:%v", tablet1.Tablet.Hostname, tablet1.Tablet.MysqlPort)) tablet3.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ "FAKE RESET ALL REPLICATION", - "FAKE SET SLAVE POSITION", - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET REPLICA POSITION", + "FAKE SET SOURCE", + "START REPLICA", } tablet3.FakeMysqlDaemon.SetReplicationSourceInputs = append(tablet3.FakeMysqlDaemon.SetReplicationSourceInputs, fmt.Sprintf("%v:%v", tablet1.Tablet.Hostname, tablet1.Tablet.MysqlPort)) diff --git a/go/vt/vtgate/executor.go b/go/vt/vtgate/executor.go index 70f53bcd769..0c4f963019e 100644 --- a/go/vt/vtgate/executor.go +++ b/go/vt/vtgate/executor.go @@ -32,6 +32,7 @@ import ( "vitess.io/vitess/go/acl" "vitess.io/vitess/go/cache/theine" + "vitess.io/vitess/go/mysql/capabilities" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqltypes" @@ -952,15 +953,29 @@ func (e *Executor) showVitessReplicationStatus(ctx context.Context, filter *sqlp replSQLThreadHealth := "" replLastError := "" replLag := "-1" // A string to support NULL as a value - sql := "show slave status" + replicaQueries, _ := capabilities.MySQLVersionHasCapability(e.env.MySQLVersion(), capabilities.ReplicaTerminologyCapability) + sql := "show replica status" + sourceHostField := "Source_Host" + sourcePortField := "Source_Port" + replicaIORunningField := "Replica_IO_Running" + replicaSQLRunningField := "Replica_SQL_Running" + secondsBehindSourceField := "Seconds_Behind_Source" + if !replicaQueries { + sql = "show slave status" + sourceHostField = "Master_Host" + sourcePortField = "Master_Port" + replicaIORunningField = "Slave_IO_Running" + replicaSQLRunningField = "Slave_SQL_Running" + secondsBehindSourceField = "Seconds_Behind_Master" + } results, err := e.txConn.tabletGateway.Execute(ctx, ts.Target, sql, nil, 0, 0, nil) if err != nil || results == nil { log.Warningf("Could not get replication status from %s: %v", tabletHostPort, err) } else if row := results.Named().Row(); row != nil { - replSourceHost = row["Master_Host"].ToString() - replSourcePort, _ = row["Master_Port"].ToInt64() - replIOThreadHealth = row["Slave_IO_Running"].ToString() - replSQLThreadHealth = row["Slave_SQL_Running"].ToString() + replSourceHost = row[sourceHostField].ToString() + replSourcePort, _ = row[sourcePortField].ToInt64() + replIOThreadHealth = row[replicaIORunningField].ToString() + replSQLThreadHealth = row[replicaSQLRunningField].ToString() replLastError = row["Last_Error"].ToString() // We cannot check the tablet's tabletenv config from here so // we only use the tablet's stat -- which is managed by the @@ -976,10 +991,10 @@ func (e *Executor) showVitessReplicationStatus(ctx context.Context, filter *sqlp if ts.Stats != nil && ts.Stats.ReplicationLagSeconds > 0 { // Use the value we get from the ReplicationTracker replLag = fmt.Sprintf("%d", ts.Stats.ReplicationLagSeconds) } else { // Use the value from mysqld - if row["Seconds_Behind_Master"].IsNull() { + if row[secondsBehindSourceField].IsNull() { replLag = strings.ToUpper(sqltypes.NullStr) // Uppercase to match mysqld's output in SHOW REPLICA STATUS } else { - replLag = row["Seconds_Behind_Master"].ToString() + replLag = row[secondsBehindSourceField].ToString() } } } diff --git a/go/vt/vttablet/tabletmanager/restore.go b/go/vt/vttablet/tabletmanager/restore.go index 3e4b4a89555..d09535ba8d6 100644 --- a/go/vt/vttablet/tabletmanager/restore.go +++ b/go/vt/vttablet/tabletmanager/restore.go @@ -493,13 +493,13 @@ func (tm *TabletManager) getGTIDFromTimestamp(ctx context.Context, pos replicati // waits till all events to GTID replicated // once done, it will reset the replication func (tm *TabletManager) catchupToGTID(ctx context.Context, afterGTIDPos string, beforeGTIDPos string) error { - var afterGTIDStr string + var afterGTID replication.Position if afterGTIDPos != "" { - afterGTIDParsed, err := replication.DecodePosition(afterGTIDPos) + var err error + afterGTID, err = replication.DecodePosition(afterGTIDPos) if err != nil { return err } - afterGTIDStr = afterGTIDParsed.GTIDSet.Last() } beforeGTIDPosParsed, err := replication.DecodePosition(beforeGTIDPos) @@ -507,48 +507,18 @@ func (tm *TabletManager) catchupToGTID(ctx context.Context, afterGTIDPos string, return err } - // it uses mysql specific queries here - cmds := []string{ - "STOP SLAVE FOR CHANNEL '' ", - "STOP SLAVE IO_THREAD FOR CHANNEL ''", - } - - if binlogSslCa != "" || binlogSslCert != "" { - // We need to use TLS - cmd := fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='%s', MASTER_PASSWORD='%s', MASTER_AUTO_POSITION=1, MASTER_SSL=1", binlogHost, binlogPort, binlogUser, binlogPwd) - if binlogSslCa != "" { - cmd += fmt.Sprintf(", MASTER_SSL_CA='%s'", binlogSslCa) - } - if binlogSslCert != "" { - cmd += fmt.Sprintf(", MASTER_SSL_CERT='%s'", binlogSslCert) - } - if binlogSslKey != "" { - cmd += fmt.Sprintf(", MASTER_SSL_KEY='%s'", binlogSslKey) - } - cmds = append(cmds, cmd+";") - } else { - // No TLS - cmds = append(cmds, fmt.Sprintf("CHANGE MASTER TO MASTER_HOST='%s', MASTER_PORT=%d, MASTER_USER='%s', MASTER_PASSWORD='%s', MASTER_AUTO_POSITION=1;", binlogHost, binlogPort, binlogUser, binlogPwd)) - } - - if afterGTIDPos == "" { // when the there is no afterPos, that means need to replicate completely - cmds = append(cmds, "START SLAVE") - } else { - cmds = append(cmds, fmt.Sprintf("START SLAVE UNTIL SQL_BEFORE_GTIDS = '%s'", afterGTIDStr)) - } - - if err := tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds); err != nil { - return vterrors.Wrap(err, fmt.Sprintf("failed to restart the replication until %s GTID", afterGTIDStr)) + if err := tm.MysqlDaemon.CatchupToGTID(ctx, afterGTID); err != nil { + return vterrors.Wrap(err, fmt.Sprintf("failed to restart the replication until %s GTID", afterGTID.GTIDSet.Last())) } log.Infof("Waiting for position to reach", beforeGTIDPosParsed.GTIDSet.Last()) - // Could not use `agent.MysqlDaemon.WaitSourcePos` as replication is stopped with `START SLAVE UNTIL SQL_BEFORE_GTIDS` - // this is as per https://dev.mysql.com/doc/refman/5.6/en/start-slave.html + // Could not use `agent.MysqlDaemon.WaitSourcePos` as replication is stopped with `START REPLICA UNTIL SQL_BEFORE_GTIDS` + // this is as per https://dev.mysql.com/doc/refman/8.0/en/start-replica.html // We need to wait until replication catches upto the specified afterGTIDPos chGTIDCaughtup := make(chan bool) go func() { timeToWait := time.Now().Add(timeoutForGTIDLookup) for time.Now().Before(timeToWait) { - pos, err := tm.MysqlDaemon.PrimaryPosition() + pos, err := tm.MysqlDaemon.PrimaryPosition(ctx) if err != nil { chGTIDCaughtup <- false } @@ -567,13 +537,13 @@ func (tm *TabletManager) catchupToGTID(ctx context.Context, afterGTIDPos string, select { case resp := <-chGTIDCaughtup: if resp { - cmds := []string{ - "STOP SLAVE", - "RESET SLAVE ALL", - } - if err := tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds); err != nil { + if err := tm.MysqlDaemon.StopReplication(ctx, nil); err != nil { return vterrors.Wrap(err, "failed to stop replication") } + if err := tm.MysqlDaemon.ResetReplicationParameters(ctx); err != nil { + return vterrors.Wrap(err, "failed to reset replication") + } + return nil } return vterrors.Wrap(err, "error while fetching the current GTID position") @@ -587,13 +557,13 @@ func (tm *TabletManager) catchupToGTID(ctx context.Context, afterGTIDPos string, // source params, so that the replica can't possibly reconnect. It would take a `CHANGE [MASTER|REPLICATION SOURCE] TO ...` to // make the mysql server replicate again (available via tm.MysqlDaemon.SetReplicationPosition) func (tm *TabletManager) disableReplication(ctx context.Context) error { - cmds := []string{ - "STOP SLAVE", - "RESET SLAVE ALL", // "ALL" makes it forget primary host:port. + if err := tm.MysqlDaemon.StopReplication(ctx, nil); err != nil { + return vterrors.Wrap(err, "failed to stop replication") } - if err := tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds); err != nil { + if err := tm.MysqlDaemon.ResetReplicationParameters(ctx); err != nil { return vterrors.Wrap(err, "failed to reset replication") } + if err := tm.MysqlDaemon.SetReplicationSource(ctx, "//", 0, false /* stopReplicationBefore */, true /* startReplicationAfter */); err != nil { return vterrors.Wrap(err, "failed to disable replication") } @@ -602,11 +572,10 @@ func (tm *TabletManager) disableReplication(ctx context.Context) error { } func (tm *TabletManager) startReplication(ctx context.Context, pos replication.Position, tabletType topodatapb.TabletType) error { - cmds := []string{ - "STOP SLAVE", - "RESET SLAVE ALL", // "ALL" makes it forget primary host:port. + if err := tm.MysqlDaemon.StopReplication(ctx, nil); err != nil { + return vterrors.Wrap(err, "failed to stop replication") } - if err := tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds); err != nil { + if err := tm.MysqlDaemon.ResetReplicationParameters(ctx); err != nil { return vterrors.Wrap(err, "failed to reset replication") } @@ -651,7 +620,7 @@ func (tm *TabletManager) startReplication(ctx context.Context, pos replication.P if err := ctx.Err(); err != nil { return err } - status, err := tm.MysqlDaemon.ReplicationStatus() + status, err := tm.MysqlDaemon.ReplicationStatus(ctx) if err != nil { return vterrors.Wrap(err, "can't get replication status") } diff --git a/go/vt/vttablet/tabletmanager/rpc_actions.go b/go/vt/vttablet/tabletmanager/rpc_actions.go index bcf6aadf22e..8abb3fe702d 100644 --- a/go/vt/vttablet/tabletmanager/rpc_actions.go +++ b/go/vt/vttablet/tabletmanager/rpc_actions.go @@ -73,7 +73,7 @@ func (tm *TabletManager) SetReadOnly(ctx context.Context, rdonly bool) error { } defer tm.unlock() - return tm.MysqlDaemon.SetReadOnly(rdonly) + return tm.MysqlDaemon.SetReadOnly(ctx, rdonly) } // ChangeType changes the tablet type diff --git a/go/vt/vttablet/tabletmanager/rpc_replication.go b/go/vt/vttablet/tabletmanager/rpc_replication.go index d202e13e2d9..3bc0e7970e1 100644 --- a/go/vt/vttablet/tabletmanager/rpc_replication.go +++ b/go/vt/vttablet/tabletmanager/rpc_replication.go @@ -42,7 +42,7 @@ func (tm *TabletManager) ReplicationStatus(ctx context.Context) (*replicationdat if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { return nil, err } - status, err := tm.MysqlDaemon.ReplicationStatus() + status, err := tm.MysqlDaemon.ReplicationStatus(ctx) if err != nil { return nil, err } @@ -67,7 +67,7 @@ func (tm *TabletManager) FullStatus(ctx context.Context) (*replicationdatapb.Ful } // Replication status - "SHOW REPLICA STATUS" - replicationStatus, err := tm.MysqlDaemon.ReplicationStatus() + replicationStatus, err := tm.MysqlDaemon.ReplicationStatus(ctx) var replicationStatusProto *replicationdatapb.Status if err != nil && err != mysql.ErrNotReplica { return nil, err @@ -110,18 +110,18 @@ func (tm *TabletManager) FullStatus(ctx context.Context) (*replicationdatapb.Ful } // Read only - "SHOW VARIABLES LIKE 'read_only'" - readOnly, err := tm.MysqlDaemon.IsReadOnly() + readOnly, err := tm.MysqlDaemon.IsReadOnly(ctx) if err != nil { return nil, err } // superReadOnly - "SELECT @@global.super_read_only" - superReadOnly, err := tm.MysqlDaemon.IsSuperReadOnly() + superReadOnly, err := tm.MysqlDaemon.IsSuperReadOnly(ctx) if err != nil { return nil, err } - // Binlog Information - "select @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates, @@global.binlog_row_image" + // Binlog Information - "select @@global.binlog_format, @@global.log_bin, @@global.log_replica_updates, @@global.binlog_row_image" binlogFormat, logBin, logReplicaUpdates, binlogRowImage, err := tm.MysqlDaemon.GetBinlogInformation(ctx) if err != nil { return nil, err @@ -187,7 +187,7 @@ func (tm *TabletManager) PrimaryPosition(ctx context.Context) (string, error) { if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { return "", err } - pos, err := tm.MysqlDaemon.PrimaryPosition() + pos, err := tm.MysqlDaemon.PrimaryPosition(ctx) if err != nil { return "", err } @@ -223,7 +223,7 @@ func (tm *TabletManager) StopReplication(ctx context.Context) error { } func (tm *TabletManager) stopReplicationLocked(ctx context.Context) error { - return tm.MysqlDaemon.StopReplication(tm.hookExtraEnv()) + return tm.MysqlDaemon.StopReplication(ctx, tm.hookExtraEnv()) } func (tm *TabletManager) stopIOThreadLocked(ctx context.Context) error { @@ -255,7 +255,7 @@ func (tm *TabletManager) StopReplicationMinimum(ctx context.Context, position st if err := tm.stopReplicationLocked(ctx); err != nil { return "", err } - pos, err = tm.MysqlDaemon.PrimaryPosition() + pos, err = tm.MysqlDaemon.PrimaryPosition(ctx) if err != nil { return "", err } @@ -282,7 +282,7 @@ func (tm *TabletManager) StartReplication(ctx context.Context, semiSync bool) er if err := tm.fixSemiSync(ctx, tm.Tablet().Type, semiSyncAction); err != nil { return err } - return tm.MysqlDaemon.StartReplication(tm.hookExtraEnv()) + return tm.MysqlDaemon.StartReplication(ctx, tm.hookExtraEnv()) } // StartReplicationUntilAfter will start the replication and let it catch up @@ -313,7 +313,7 @@ func (tm *TabletManager) GetReplicas(ctx context.Context) ([]string, error) { if err := tm.waitForGrantsToHaveApplied(ctx); err != nil { return nil, err } - return mysqlctl.FindReplicas(tm.MysqlDaemon) + return mysqlctl.FindReplicas(ctx, tm.MysqlDaemon) } // ResetReplication completely resets the replication on the host. @@ -343,7 +343,7 @@ func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string defer tm.unlock() // Setting super_read_only `OFF` so that we can run the DDL commands - if _, err := tm.MysqlDaemon.SetSuperReadOnly(false); err != nil { + if _, err := tm.MysqlDaemon.SetSuperReadOnly(ctx, false); err != nil { if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERUnknownSystemVariable { log.Warningf("server does not know about super_read_only, continuing anyway...") } else { @@ -358,7 +358,7 @@ func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string } // get the current replication position - pos, err := tm.MysqlDaemon.PrimaryPosition() + pos, err := tm.MysqlDaemon.PrimaryPosition(ctx) if err != nil { return "", err } @@ -495,7 +495,7 @@ func (tm *TabletManager) demotePrimary(ctx context.Context, revertPartialFailure tablet := tm.Tablet() wasPrimary := tablet.Type == topodatapb.TabletType_PRIMARY wasServing := tm.QueryServiceControl.IsServing() - wasReadOnly, err := tm.MysqlDaemon.IsReadOnly() + wasReadOnly, err := tm.MysqlDaemon.IsReadOnly(ctx) if err != nil { return nil, err } @@ -528,7 +528,7 @@ func (tm *TabletManager) demotePrimary(ctx context.Context, revertPartialFailure // set MySQL to super_read_only mode. If we are already super_read_only because of a // previous demotion, or because we are not primary anyway, this should be // idempotent. - if _, err := tm.MysqlDaemon.SetSuperReadOnly(true); err != nil { + if _, err := tm.MysqlDaemon.SetSuperReadOnly(ctx, true); err != nil { if sqlErr, ok := err.(*sqlerror.SQLError); ok && sqlErr.Number() == sqlerror.ERUnknownSystemVariable { log.Warningf("server does not know about super_read_only, continuing anyway...") } else { @@ -539,7 +539,7 @@ func (tm *TabletManager) demotePrimary(ctx context.Context, revertPartialFailure defer func() { if finalErr != nil && revertPartialFailure && !wasReadOnly { // setting read_only OFF will also set super_read_only OFF if it was set - if err := tm.MysqlDaemon.SetReadOnly(false); err != nil { + if err := tm.MysqlDaemon.SetReadOnly(ctx, false); err != nil { log.Warningf("SetReadOnly(false) failed during revert: %v", err) } } @@ -594,7 +594,7 @@ func (tm *TabletManager) UndoDemotePrimary(ctx context.Context, semiSync bool) e } // Now, set the server read-only false. - if err := tm.MysqlDaemon.SetReadOnly(false); err != nil { + if err := tm.MysqlDaemon.SetReadOnly(ctx, false); err != nil { return err } @@ -631,7 +631,7 @@ func (tm *TabletManager) ResetReplicationParameters(ctx context.Context) error { } defer tm.unlock() - err := tm.MysqlDaemon.StopReplication(tm.hookExtraEnv()) + err := tm.MysqlDaemon.StopReplication(ctx, tm.hookExtraEnv()) if err != nil { return err } @@ -692,7 +692,7 @@ func (tm *TabletManager) setReplicationSourceLocked(ctx context.Context, parentA // See if we were replicating at all, and should be replicating. wasReplicating := false shouldbeReplicating := false - status, err := tm.MysqlDaemon.ReplicationStatus() + status, err := tm.MysqlDaemon.ReplicationStatus(ctx) if err == mysql.ErrNotReplica { // This is a special error that means we actually succeeded in reading // the status, but the status is empty because replication is not @@ -742,20 +742,20 @@ func (tm *TabletManager) setReplicationSourceLocked(ctx context.Context, parentA if status.SourceHost != host || status.SourcePort != port { // This handles both changing the address and starting replication. if err := tm.MysqlDaemon.SetReplicationSource(ctx, host, port, wasReplicating, shouldbeReplicating); err != nil { - if err := tm.handleRelayLogError(err); err != nil { + if err := tm.handleRelayLogError(ctx, err); err != nil { return err } } } else if shouldbeReplicating { // The address is correct. We need to restart replication so that any semi-sync changes if any // are taken into account - if err := tm.MysqlDaemon.StopReplication(tm.hookExtraEnv()); err != nil { - if err := tm.handleRelayLogError(err); err != nil { + if err := tm.MysqlDaemon.StopReplication(ctx, tm.hookExtraEnv()); err != nil { + if err := tm.handleRelayLogError(ctx, err); err != nil { return err } } - if err := tm.MysqlDaemon.StartReplication(tm.hookExtraEnv()); err != nil { - if err := tm.handleRelayLogError(err); err != nil { + if err := tm.MysqlDaemon.StartReplication(ctx, tm.hookExtraEnv()); err != nil { + if err := tm.handleRelayLogError(ctx, err); err != nil { return err } } @@ -821,7 +821,7 @@ func (tm *TabletManager) StopReplicationAndGetStatus(ctx context.Context, stopRe // Get the status before we stop replication. // Doing this first allows us to return the status in the case that stopping replication // returns an error, so a user can optionally inspect the status before a stop was called. - rs, err := tm.MysqlDaemon.ReplicationStatus() + rs, err := tm.MysqlDaemon.ReplicationStatus(ctx) if err != nil { return StopReplicationAndGetStatusResponse{}, vterrors.Wrap(err, "before status failed") } @@ -863,7 +863,7 @@ func (tm *TabletManager) StopReplicationAndGetStatus(ctx context.Context, stopRe } // Get the status after we stop replication so we have up to date position and relay log positions. - rsAfter, err := tm.MysqlDaemon.ReplicationStatus() + rsAfter, err := tm.MysqlDaemon.ReplicationStatus(ctx) if err != nil { return StopReplicationAndGetStatusResponse{ Status: &replicationdatapb.StopReplicationStatus{ @@ -904,7 +904,7 @@ func (tm *TabletManager) PromoteReplica(ctx context.Context, semiSync bool) (str } defer tm.unlock() - pos, err := tm.MysqlDaemon.Promote(tm.hookExtraEnv()) + pos, err := tm.MysqlDaemon.Promote(ctx, tm.hookExtraEnv()) if err != nil { return "", err } @@ -974,7 +974,7 @@ func (tm *TabletManager) fixSemiSyncAndReplication(ctx context.Context, tabletTy // If replication is running, but the status is wrong, // we should restart replication. First, let's make sure // replication is running. - status, err := tm.MysqlDaemon.ReplicationStatus() + status, err := tm.MysqlDaemon.ReplicationStatus(ctx) if err != nil { // Replication is not configured, nothing to do. return nil @@ -996,10 +996,10 @@ func (tm *TabletManager) fixSemiSyncAndReplication(ctx context.Context, tabletTy // We need to restart replication log.Infof("Restarting replication for semi-sync flag change to take effect from %v to %v", acking, shouldAck) - if err := tm.MysqlDaemon.StopReplication(tm.hookExtraEnv()); err != nil { + if err := tm.MysqlDaemon.StopReplication(ctx, tm.hookExtraEnv()); err != nil { return vterrors.Wrap(err, "failed to StopReplication") } - if err := tm.MysqlDaemon.StartReplication(tm.hookExtraEnv()); err != nil { + if err := tm.MysqlDaemon.StartReplication(ctx, tm.hookExtraEnv()); err != nil { return vterrors.Wrap(err, "failed to StartReplication") } return nil @@ -1009,15 +1009,16 @@ func (tm *TabletManager) fixSemiSyncAndReplication(ctx context.Context, tabletTy // This is required because sometimes MySQL gets stuck due to improper initialization of // master info structure or related failures and throws errors like // ERROR 1201 (HY000): Could not initialize master info structure; more error messages can be found in the MySQL error log -// These errors can only be resolved by resetting the replication, otherwise START SLAVE fails. -func (tm *TabletManager) handleRelayLogError(err error) error { +// These errors can only be resolved by resetting the replication, otherwise START REPLICA fails. +func (tm *TabletManager) handleRelayLogError(ctx context.Context, err error) error { // attempt to fix this error: - // Slave failed to initialize relay log info structure from the repository (errno 1872) (sqlstate HY000) during query: START SLAVE + // Replica failed to initialize relay log info structure from the repository (errno 1872) (sqlstate HY000) during query: START REPLICA // see https://bugs.mysql.com/bug.php?id=83713 or https://github.com/vitessio/vitess/issues/5067 // The same fix also works for https://github.com/vitessio/vitess/issues/10955. - if strings.Contains(err.Error(), "Slave failed to initialize relay log info structure from the repository") || strings.Contains(err.Error(), "Could not initialize master info structure") { + if strings.Contains(err.Error(), "Replica failed to initialize relay log info structure from the repository") || + strings.Contains(err.Error(), "Could not initialize master info structure") { // Stop, reset and start replication again to resolve this error - if err := tm.MysqlDaemon.RestartReplication(tm.hookExtraEnv()); err != nil { + if err := tm.MysqlDaemon.RestartReplication(ctx, tm.hookExtraEnv()); err != nil { return err } return nil diff --git a/go/vt/vttablet/tabletmanager/tm_init_test.go b/go/vt/vttablet/tabletmanager/tm_init_test.go index 1ba77638780..d0c0075eda3 100644 --- a/go/vt/vttablet/tabletmanager/tm_init_test.go +++ b/go/vt/vttablet/tabletmanager/tm_init_test.go @@ -379,9 +379,9 @@ func TestCheckPrimaryShip(t *testing.T) { fakeMysql := tm.MysqlDaemon.(*mysqlctl.FakeMysqlDaemon) fakeMysql.SetReplicationSourceInputs = append(fakeMysql.SetReplicationSourceInputs, fmt.Sprintf("%v:%v", otherTablet.MysqlHostname, otherTablet.MysqlPort)) fakeMysql.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } err = tm.Start(tablet, nil) require.NoError(t, err) diff --git a/go/vt/vttablet/tabletmanager/tm_state.go b/go/vt/vttablet/tabletmanager/tm_state.go index df814ba5bee..312c675fce7 100644 --- a/go/vt/vttablet/tabletmanager/tm_state.go +++ b/go/vt/vttablet/tabletmanager/tm_state.go @@ -216,7 +216,7 @@ func (ts *tmState) ChangeTabletType(ctx context.Context, tabletType topodatapb.T if action == DBActionSetReadWrite { // We call SetReadOnly only after the topo has been updated to avoid // situations where two tablets are primary at the DB level but not at the vitess level - if err := ts.tm.MysqlDaemon.SetReadOnly(false); err != nil { + if err := ts.tm.MysqlDaemon.SetReadOnly(ctx, false); err != nil { return err } } diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index ec7d2d4529d..f4c3525e4d7 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -212,7 +212,7 @@ func resetBinlogClient() { func primaryPosition(t *testing.T) string { t.Helper() - pos, err := env.Mysqld.PrimaryPosition() + pos, err := env.Mysqld.PrimaryPosition(context.Background()) if err != nil { t.Fatal(err) } diff --git a/go/vt/vttablet/tabletserver/repltracker/poller.go b/go/vt/vttablet/tabletserver/repltracker/poller.go index 6fc964bef57..7023562f0d6 100644 --- a/go/vt/vttablet/tabletserver/repltracker/poller.go +++ b/go/vt/vttablet/tabletserver/repltracker/poller.go @@ -17,6 +17,7 @@ limitations under the License. package repltracker import ( + "context" "sync" "time" @@ -45,14 +46,16 @@ func (p *poller) Status() (time.Duration, error) { p.mu.Lock() defer p.mu.Unlock() - status, err := p.mysqld.ReplicationStatus() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + status, err := p.mysqld.ReplicationStatus(ctx) if err != nil { return 0, err } // If replication is not currently running or we don't know what the lag is -- most commonly // because the replica mysqld is in the process of trying to start replicating from its source - // but it hasn't yet reached the point where it can calculate the seconds_behind_master + // but it hasn't yet reached the point where it can calculate the seconds_behind_source // value and it's thus NULL -- then we will estimate the lag ourselves using the last seen // value + the time elapsed since. if !status.Healthy() || status.ReplicationLagUnknown { diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index 7a0d1aaaf3d..b56dbe3a5d7 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -558,7 +558,7 @@ func (vse *Engine) getInnoDBTrxHistoryLen(ctx context.Context, db dbconfigs.Conn return histLen } -// getMySQLReplicationLag attempts to get the seconds_behind_master value. +// getMySQLReplicationLag attempts to get the seconds_behind_source value. // If the value cannot be determined for any reason then -1 is returned, which // means "unknown" or "irrelevant" (meaning it's not actively replicating). func (vse *Engine) getMySQLReplicationLag(ctx context.Context, db dbconfigs.Connector) int64 { @@ -569,12 +569,11 @@ func (vse *Engine) getMySQLReplicationLag(ctx context.Context, db dbconfigs.Conn } defer conn.Close() - res, err := conn.ExecuteFetch(replicaLagQuery, 1, true) - if err != nil || len(res.Rows) != 1 || res.Rows[0] == nil { + status, err := conn.ShowReplicationStatus() + if err != nil { return lagSecs } - row := res.Named().Row() - return row.AsInt64("Seconds_Behind_Master", -1) + return int64(status.ReplicationLagSeconds) } // getMySQLEndpoint returns the host:port value for the vstreamer (MySQL) instance diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine_test.go b/go/vt/vttablet/tabletserver/vstreamer/engine_test.go index 05f21fca271..b0b31e256cc 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine_test.go @@ -187,6 +187,11 @@ func TestVStreamerWaitForMySQL(t *testing.T) { "1000", ) sbmres := sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "Seconds_Behind_Source", + "int64"), + "10", + ) + sbmlegacyres := sqltypes.MakeTestResult(sqltypes.MakeTestFields( "Seconds_Behind_Master", "int64"), "10", @@ -242,6 +247,7 @@ func TestVStreamerWaitForMySQL(t *testing.T) { testDB.AddQuery(hostQuery, hostres) testDB.AddQuery(trxHistoryLenQuery, thlres) testDB.AddQuery(replicaLagQuery, sbmres) + testDB.AddQuery(legacyLagQuery, sbmlegacyres) for _, tt := range tests { tt.fields.cp = dbconfigs.New(testDB.ConnParams()) diff --git a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go index 35c65cf831d..1d49db8c503 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go +++ b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go @@ -151,7 +151,7 @@ func Init(ctx context.Context) (*Env, error) { } te.TabletEnv = tabletenv.NewEnv(vtenv, conf, "VStreamerTest") te.Mysqld = mysqlctl.NewMysqld(te.Dbcfgs) - pos, _ := te.Mysqld.PrimaryPosition() + pos, _ := te.Mysqld.PrimaryPosition(ctx) if strings.HasPrefix(strings.ToLower(pos.GTIDSet.Flavor()), string(mysqlctl.FlavorMariaDB)) { te.DBType = string(mysqlctl.FlavorMariaDB) } else { diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 3aede20f650..d8de85db0df 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -50,7 +50,8 @@ import ( const ( trxHistoryLenQuery = `select count as history_len from information_schema.INNODB_METRICS where name = 'trx_rseg_history_len'` - replicaLagQuery = `show slave status` + replicaLagQuery = `show replica status` + legacyLagQuery = `show slave status` hostQuery = `select @@hostname as hostname, @@port as port` ) diff --git a/go/vt/wrangler/testlib/backup_test.go b/go/vt/wrangler/testlib/backup_test.go index ce46734a5e9..e0a94033360 100644 --- a/go/vt/wrangler/testlib/backup_test.go +++ b/go/vt/wrangler/testlib/backup_test.go @@ -181,19 +181,19 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { } sourceTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // This first set of STOP and START commands come from // the builtinBackupEngine implementation which stops the replication // while taking the backup - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "START REPLICA", // These commands come from SetReplicationSource RPC called // to set the correct primary and semi-sync after Backup has concluded. // Since the primary hasn't changed, we only restart replication after fixing semi-sync. - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "START REPLICA", } sourceTablet.FakeMysqlDaemon.FetchSuperQueryMap = map[string]*sqltypes.Result{ "SHOW DATABASES": {}, @@ -234,15 +234,15 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { } destTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "RESET SLAVE ALL", - "FAKE SET SLAVE POSITION", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE RESET REPLICA ALL", + "FAKE SET REPLICA POSITION", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } destTablet.FakeMysqlDaemon.FetchSuperQueryMap = map[string]*sqltypes.Result{ "SHOW DATABASES": {}, @@ -294,11 +294,11 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { "SET GLOBAL gtid_purged": {}, } primary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "RESET SLAVE ALL", - "FAKE SET SLAVE POSITION", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE RESET REPLICA ALL", + "FAKE SET REPLICA POSITION", + "FAKE SET SOURCE", + "START REPLICA", } primary.FakeMysqlDaemon.SetReplicationPositionPos = primary.FakeMysqlDaemon.CurrentPrimaryPosition @@ -419,19 +419,19 @@ func TestBackupRestoreLagged(t *testing.T) { sourceTablet.FakeMysqlDaemon.SetReplicationSourceInputs = []string{fmt.Sprintf("%s:%d", primary.Tablet.MysqlHostname, primary.Tablet.MysqlPort)} sourceTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // This first set of STOP and START commands come from // the builtinBackupEngine implementation which stops the replication // while taking the backup - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "START REPLICA", // These commands come from SetReplicationSource RPC called // to set the correct primary and semi-sync after Backup has concluded. // Since the primary hasn't changed, we only restart replication after fixing semi-sync. - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "START REPLICA", } sourceTablet.StartActionLoop(t, wr) defer sourceTablet.StopActionLoop(t) @@ -488,15 +488,15 @@ func TestBackupRestoreLagged(t *testing.T) { } destTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "RESET SLAVE ALL", - "FAKE SET SLAVE POSITION", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE RESET REPLICA ALL", + "FAKE SET REPLICA POSITION", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } destTablet.FakeMysqlDaemon.FetchSuperQueryMap = map[string]*sqltypes.Result{ "SHOW DATABASES": {}, @@ -637,19 +637,19 @@ func TestRestoreUnreachablePrimary(t *testing.T) { sourceTablet.FakeMysqlDaemon.SetReplicationSourceInputs = []string{fmt.Sprintf("%s:%d", primary.Tablet.MysqlHostname, primary.Tablet.MysqlPort)} sourceTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // This first set of STOP and START commands come from // the builtinBackupEngine implementation which stops the replication // while taking the backup - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "START REPLICA", // These commands come from SetReplicationSource RPC called // to set the correct primary and semi-sync after Backup has concluded. // Since the primary hasn't changed, we only restart replication after fixing semi-sync. - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "START REPLICA", } sourceTablet.StartActionLoop(t, wr) defer sourceTablet.StopActionLoop(t) @@ -678,15 +678,15 @@ func TestRestoreUnreachablePrimary(t *testing.T) { } destTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "RESET SLAVE ALL", - "FAKE SET SLAVE POSITION", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE RESET REPLICA ALL", + "FAKE SET REPLICA POSITION", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } destTablet.FakeMysqlDaemon.FetchSuperQueryMap = map[string]*sqltypes.Result{ "SHOW DATABASES": {}, @@ -811,7 +811,7 @@ func TestDisableActiveReparents(t *testing.T) { }, } sourceTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", + "STOP REPLICA", } sourceTablet.StartActionLoop(t, wr) defer sourceTablet.StopActionLoop(t) @@ -844,9 +844,9 @@ func TestDisableActiveReparents(t *testing.T) { }, } destTablet.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "RESET SLAVE ALL", - "FAKE SET SLAVE POSITION", + "STOP REPLICA", + "FAKE RESET REPLICA ALL", + "FAKE SET REPLICA POSITION", } destTablet.FakeMysqlDaemon.FetchSuperQueryMap = map[string]*sqltypes.Result{ "SHOW DATABASES": {}, diff --git a/go/vt/wrangler/testlib/copy_schema_shard_test.go b/go/vt/wrangler/testlib/copy_schema_shard_test.go index 2e113eec3ae..d91fdad76eb 100644 --- a/go/vt/wrangler/testlib/copy_schema_shard_test.go +++ b/go/vt/wrangler/testlib/copy_schema_shard_test.go @@ -75,9 +75,9 @@ func copySchema(t *testing.T, useShardAsSource bool) { topodatapb.TabletType_RDONLY, sourceRdonlyDb, TabletKeyspaceShard(t, "ks", "-80")) sourceRdonly.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } sourceRdonly.FakeMysqlDaemon.SetReplicationSourceInputs = append(sourceRdonly.FakeMysqlDaemon.SetReplicationSourceInputs, fmt.Sprintf("%v:%v", sourcePrimary.Tablet.MysqlHostname, sourcePrimary.Tablet.MysqlPort)) diff --git a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go index a23562153e2..96f9df74405 100644 --- a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go +++ b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go @@ -95,7 +95,7 @@ func TestEmergencyReparentShard(t *testing.T) { } newPrimary.FakeMysqlDaemon.WaitPrimaryPositions = append(newPrimary.FakeMysqlDaemon.WaitPrimaryPositions, newPrimary.FakeMysqlDaemon.CurrentSourceFilePosition) newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE IO_THREAD", + "STOP REPLICA IO_THREAD", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.FakeMysqlDaemon.PromoteResult = replication.Position{ @@ -115,7 +115,7 @@ func TestEmergencyReparentShard(t *testing.T) { oldPrimary.FakeMysqlDaemon.ReplicationStatusError = mysql.ErrNotReplica oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", + "STOP REPLICA", } oldPrimary.StartActionLoop(t, wr) defer oldPrimary.StopActionLoop(t) @@ -140,13 +140,13 @@ func TestEmergencyReparentShard(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE IO_THREAD", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA IO_THREAD", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -171,10 +171,10 @@ func TestEmergencyReparentShard(t *testing.T) { goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "FAKE SET MASTER", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "FAKE SET SOURCE", } goodReplica2.StartActionLoop(t, wr) defer goodReplica2.StopActionLoop(t) @@ -233,10 +233,10 @@ func TestEmergencyReparentShardPrimaryElectNotBest(t *testing.T) { newPrimary.FakeMysqlDaemon.WaitPrimaryPositions = append(newPrimary.FakeMysqlDaemon.WaitPrimaryPositions, newPrimary.FakeMysqlDaemon.CurrentSourceFilePosition) newPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(newPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(moreAdvancedReplica.Tablet)) newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE IO_THREAD", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA IO_THREAD", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -268,13 +268,13 @@ func TestEmergencyReparentShardPrimaryElectNotBest(t *testing.T) { newPrimary.FakeMysqlDaemon.WaitPrimaryPositions = append(newPrimary.FakeMysqlDaemon.WaitPrimaryPositions, moreAdvancedReplica.FakeMysqlDaemon.CurrentPrimaryPosition) moreAdvancedReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE IO_THREAD", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA IO_THREAD", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } moreAdvancedReplica.StartActionLoop(t, wr) defer moreAdvancedReplica.StopActionLoop(t) diff --git a/go/vt/wrangler/testlib/external_reparent_test.go b/go/vt/wrangler/testlib/external_reparent_test.go index 59d7c05d0f3..556debae64a 100644 --- a/go/vt/wrangler/testlib/external_reparent_test.go +++ b/go/vt/wrangler/testlib/external_reparent_test.go @@ -91,7 +91,7 @@ func TestTabletExternallyReparentedBasic(t *testing.T) { oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", + "FAKE SET SOURCE", "START Replica", } @@ -171,7 +171,7 @@ func TestTabletExternallyReparentedToReplica(t *testing.T) { // primary is still good to go. oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", + "FAKE SET SOURCE", "START Replica", } @@ -250,7 +250,7 @@ func TestTabletExternallyReparentedWithDifferentMysqlPort(t *testing.T) { oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", + "FAKE SET SOURCE", "START Replica", } // On the old primary, we will only respond to @@ -263,9 +263,9 @@ func TestTabletExternallyReparentedWithDifferentMysqlPort(t *testing.T) { goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica.StartActionLoop(t, wr) defer goodReplica.StopActionLoop(t) @@ -339,7 +339,7 @@ func TestTabletExternallyReparentedContinueOnUnexpectedPrimary(t *testing.T) { oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", + "FAKE SET SOURCE", "START Replica", } // On the old primary, we will only respond to @@ -352,9 +352,9 @@ func TestTabletExternallyReparentedContinueOnUnexpectedPrimary(t *testing.T) { goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica.StartActionLoop(t, wr) defer goodReplica.StopActionLoop(t) @@ -424,7 +424,7 @@ func TestTabletExternallyReparentedRerun(t *testing.T) { oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", + "FAKE SET SOURCE", "START Replica", } // On the old primary, we will only respond to @@ -437,9 +437,9 @@ func TestTabletExternallyReparentedRerun(t *testing.T) { // TabletActionReplicaWasRestarted. goodReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica.StartActionLoop(t, wr) defer goodReplica.StopActionLoop(t) diff --git a/go/vt/wrangler/testlib/permissions_test.go b/go/vt/wrangler/testlib/permissions_test.go index 35c92a0233d..a1b14350b7f 100644 --- a/go/vt/wrangler/testlib/permissions_test.go +++ b/go/vt/wrangler/testlib/permissions_test.go @@ -566,9 +566,9 @@ func TestPermissions(t *testing.T) { replica.FakeMysqlDaemon.SetReplicationSourceInputs = append(replica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(primary.Tablet)) replica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } replica.StartActionLoop(t, wr) defer replica.StopActionLoop(t) diff --git a/go/vt/wrangler/testlib/planned_reparent_shard_test.go b/go/vt/wrangler/testlib/planned_reparent_shard_test.go index 65babcaf48d..453117f0138 100644 --- a/go/vt/wrangler/testlib/planned_reparent_shard_test.go +++ b/go/vt/wrangler/testlib/planned_reparent_shard_test.go @@ -84,9 +84,9 @@ func TestPlannedReparentShardNoPrimaryProvided(t *testing.T) { }, } newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -99,13 +99,13 @@ func TestPlannedReparentShardNoPrimaryProvided(t *testing.T) { oldPrimary.FakeMysqlDaemon.CurrentPrimaryPosition = newPrimary.FakeMysqlDaemon.WaitPrimaryPositions[0] oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", // We might end up calling SetReplicationSource twice on the old primary // one coming from `PlannedReparentShard` and one coming from `endPrimaryTerm`. // This is a race though between SetReplicationSource on this tablet and `PromoteReplica` on the new primary. - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", } oldPrimary.StartActionLoop(t, wr) defer oldPrimary.StopActionLoop(t) @@ -120,12 +120,12 @@ func TestPlannedReparentShardNoPrimaryProvided(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -201,9 +201,9 @@ func TestPlannedReparentShardNoError(t *testing.T) { }, } newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -216,13 +216,13 @@ func TestPlannedReparentShardNoError(t *testing.T) { oldPrimary.FakeMysqlDaemon.CurrentPrimaryPosition = newPrimary.FakeMysqlDaemon.WaitPrimaryPositions[0] oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", // We might end up calling SetReplicationSource twice on the old primary // one coming from `PlannedReparentShard` and one coming from `endPrimaryTerm`. // This is a race though between SetReplicationSource on this tablet and `PromoteReplica` on the new primary. - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", } oldPrimary.StartActionLoop(t, wr) defer oldPrimary.StopActionLoop(t) @@ -237,12 +237,12 @@ func TestPlannedReparentShardNoError(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -252,10 +252,10 @@ func TestPlannedReparentShardNoError(t *testing.T) { goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "FAKE SET MASTER", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "FAKE SET SOURCE", } goodReplica2.StartActionLoop(t, wr) goodReplica2.FakeMysqlDaemon.Replicating = false @@ -338,9 +338,9 @@ func TestPlannedReparentInitialization(t *testing.T) { goodReplica1.FakeMysqlDaemon.Replicating = true goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -351,7 +351,7 @@ func TestPlannedReparentInitialization(t *testing.T) { goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) goodReplica2.StartActionLoop(t, wr) goodReplica2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", + "FAKE SET SOURCE", } defer goodReplica2.StopActionLoop(t) @@ -422,9 +422,9 @@ func TestPlannedReparentShardWaitForPositionFail(t *testing.T) { }, } newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -437,8 +437,8 @@ func TestPlannedReparentShardWaitForPositionFail(t *testing.T) { oldPrimary.FakeMysqlDaemon.CurrentPrimaryPosition = newPrimary.FakeMysqlDaemon.PromoteResult oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", } oldPrimary.StartActionLoop(t, wr) defer oldPrimary.StopActionLoop(t) @@ -452,12 +452,12 @@ func TestPlannedReparentShardWaitForPositionFail(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -467,10 +467,10 @@ func TestPlannedReparentShardWaitForPositionFail(t *testing.T) { goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "FAKE SET MASTER", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "FAKE SET SOURCE", } goodReplica2.StartActionLoop(t, wr) goodReplica2.FakeMysqlDaemon.Replicating = false @@ -531,9 +531,9 @@ func TestPlannedReparentShardWaitForPositionTimeout(t *testing.T) { }, } newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -545,8 +545,8 @@ func TestPlannedReparentShardWaitForPositionTimeout(t *testing.T) { oldPrimary.FakeMysqlDaemon.CurrentPrimaryPosition = newPrimary.FakeMysqlDaemon.WaitPrimaryPositions[0] oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", } oldPrimary.StartActionLoop(t, wr) defer oldPrimary.StopActionLoop(t) @@ -560,12 +560,12 @@ func TestPlannedReparentShardWaitForPositionTimeout(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -575,10 +575,10 @@ func TestPlannedReparentShardWaitForPositionTimeout(t *testing.T) { goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "FAKE SET MASTER", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "FAKE SET SOURCE", } goodReplica2.StartActionLoop(t, wr) goodReplica2.FakeMysqlDaemon.Replicating = false @@ -638,17 +638,17 @@ func TestPlannedReparentShardRelayLogError(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(primary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // simulate error that will trigger a call to RestartReplication - "STOP SLAVE", - "RESET SLAVE", - "START SLAVE", - "START SLAVE", + "STOP REPLICA", + "RESET REPLICA", + "START REPLICA", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) - goodReplica1.FakeMysqlDaemon.StopReplicationError = errors.New("Slave failed to initialize relay log info structure from the repository") + goodReplica1.FakeMysqlDaemon.StopReplicationError = errors.New("Replica failed to initialize relay log info structure from the repository") defer goodReplica1.StopActionLoop(t) // run PlannedReparentShard @@ -723,19 +723,19 @@ func TestPlannedReparentShardRelayLogErrorStartReplication(t *testing.T) { goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // simulate error that will trigger a call to RestartReplication // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // In SetReplicationSource, we find that the source host and port was already set correctly, - // So we try to stop and start replication. The first STOP SLAVE comes from there - "STOP SLAVE", - // During the START SLAVE call, we find a relay log error, so we try to restart replication. - "STOP SLAVE", - "RESET SLAVE", - "START SLAVE", + // So we try to stop and start replication. The first STOP REPLICA comes from there + "STOP REPLICA", + // During the START REPLICA call, we find a relay log error, so we try to restart replication. + "STOP REPLICA", + "RESET REPLICA", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) - goodReplica1.FakeMysqlDaemon.StartReplicationError = errors.New("Slave failed to initialize relay log info structure from the repository") + goodReplica1.FakeMysqlDaemon.StartReplicationError = errors.New("Replica failed to initialize relay log info structure from the repository") defer goodReplica1.StopActionLoop(t) // run PlannedReparentShard @@ -803,9 +803,9 @@ func TestPlannedReparentShardPromoteReplicaFail(t *testing.T) { }, } newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -818,14 +818,14 @@ func TestPlannedReparentShardPromoteReplicaFail(t *testing.T) { oldPrimary.FakeMysqlDaemon.CurrentPrimaryPosition = newPrimary.FakeMysqlDaemon.WaitPrimaryPositions[0] oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs = append(oldPrimary.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet)) oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", // We call a SetReplicationSource explicitly - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", // extra SetReplicationSource call due to retry - "FAKE SET MASTER", - "START SLAVE", + "FAKE SET SOURCE", + "START REPLICA", } oldPrimary.StartActionLoop(t, wr) defer oldPrimary.StopActionLoop(t) @@ -839,15 +839,15 @@ func TestPlannedReparentShardPromoteReplicaFail(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // extra SetReplicationSource call due to retry - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -857,10 +857,10 @@ func TestPlannedReparentShardPromoteReplicaFail(t *testing.T) { goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(newPrimary.Tablet), topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "FAKE SET MASTER", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "FAKE SET SOURCE", } goodReplica2.StartActionLoop(t, wr) goodReplica2.FakeMysqlDaemon.Replicating = false @@ -944,11 +944,11 @@ func TestPlannedReparentShardSamePrimary(t *testing.T) { goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica1.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "START REPLICA", } goodReplica1.StartActionLoop(t, wr) defer goodReplica1.StopActionLoop(t) @@ -958,10 +958,10 @@ func TestPlannedReparentShardSamePrimary(t *testing.T) { goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs = append(goodReplica2.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(oldPrimary.Tablet)) goodReplica2.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "FAKE SET MASTER", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "FAKE SET SOURCE", } goodReplica2.StartActionLoop(t, wr) goodReplica2.FakeMysqlDaemon.Replicating = false diff --git a/go/vt/wrangler/testlib/reparent_utils_test.go b/go/vt/wrangler/testlib/reparent_utils_test.go index 8dfc3efb20d..e0a2077c778 100644 --- a/go/vt/wrangler/testlib/reparent_utils_test.go +++ b/go/vt/wrangler/testlib/reparent_utils_test.go @@ -94,9 +94,9 @@ func TestShardReplicationStatuses(t *testing.T) { replica.FakeMysqlDaemon.SetReplicationSourceInputs = append(replica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(primary.Tablet)) replica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } replica.StartActionLoop(t, wr) defer replica.StopActionLoop(t) @@ -164,11 +164,11 @@ func TestReparentTablet(t *testing.T) { replica.FakeMysqlDaemon.SetReplicationSourceInputs = append(replica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(primary.Tablet)) replica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", - "STOP SLAVE", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", + "STOP REPLICA", + "START REPLICA", } replica.StartActionLoop(t, wr) defer replica.StopActionLoop(t) @@ -222,14 +222,14 @@ func TestSetReplicationSource(t *testing.T) { replica.FakeMysqlDaemon.SetReplicationSourceInputs = append(replica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(primary.Tablet)) replica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // We stop and reset the replication parameters because of relay log issues. - "STOP SLAVE", - "STOP SLAVE", - "RESET SLAVE", - "START SLAVE", + "STOP REPLICA", + "STOP REPLICA", + "RESET REPLICA", + "START REPLICA", } replica.StartActionLoop(t, wr) defer replica.StopActionLoop(t) @@ -255,9 +255,9 @@ func TestSetReplicationSource(t *testing.T) { replica.FakeMysqlDaemon.SetReplicationSourceInputs = append(replica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(primary.Tablet)) replica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", // For the SetReplicationSource call, we shouldn't get any queries at all! } replica.StartActionLoop(t, wr) diff --git a/go/vt/wrangler/testlib/version_test.go b/go/vt/wrangler/testlib/version_test.go index ea13a43bc8f..cf5f3fd1487 100644 --- a/go/vt/wrangler/testlib/version_test.go +++ b/go/vt/wrangler/testlib/version_test.go @@ -94,9 +94,9 @@ func TestVersion(t *testing.T) { sourceReplica.FakeMysqlDaemon.SetReplicationSourceInputs = append(sourceReplica.FakeMysqlDaemon.SetReplicationSourceInputs, topoproto.MysqlAddr(sourcePrimary.Tablet)) sourceReplica.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ // These 3 statements come from tablet startup - "STOP SLAVE", - "FAKE SET MASTER", - "START SLAVE", + "STOP REPLICA", + "FAKE SET SOURCE", + "START REPLICA", } sourceReplica.StartActionLoop(t, wr) sourceReplica.HTTPServer.Handler.(*http.ServeMux).HandleFunc("/debug/vars", expvarHandler(&sourceReplicaGitRev))