From 6235187379f522469125d8e87aa30500556be353 Mon Sep 17 00:00:00 2001 From: Miriam Lauter Date: Tue, 9 Apr 2019 15:50:51 -0400 Subject: [PATCH] Add vttablet option to set super_read_only during planned reparent Signed-off-by: Miriam Lauter --- .../fakemysqldaemon/fakemysqldaemon.go | 10 +++++++ go/vt/mysqlctl/mysql_daemon.go | 1 + go/vt/mysqlctl/replication.go | 11 ++++++++ .../vttablet/tabletmanager/rpc_replication.go | 26 ++++++++++++++----- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go b/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go index 61b66338ec5..0487a7aa29e 100644 --- a/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go +++ b/go/vt/mysqlctl/fakemysqldaemon/fakemysqldaemon.go @@ -76,6 +76,9 @@ type FakeMysqlDaemon struct { // ReadOnly is the current value of the flag ReadOnly bool + // SuperReadOnly is the current value of the flag + SuperReadOnly bool + // SetSlavePositionPos is matched against the input of SetSlavePosition. // If it doesn't match, SetSlavePosition will return an error. SetSlavePositionPos mysql.Position @@ -240,6 +243,13 @@ func (fmd *FakeMysqlDaemon) SetReadOnly(on bool) error { return nil } +// SetSuperReadOnly is part of the MysqlDaemon interface +func (fmd *FakeMysqlDaemon) SetSuperReadOnly(on bool) error { + fmd.SuperReadOnly = on + fmd.ReadOnly = on + return nil +} + // StartSlave is part of the MysqlDaemon interface. func (fmd *FakeMysqlDaemon) StartSlave(hookExtraEnv map[string]string) error { return fmd.ExecuteSuperQueryList(context.Background(), []string{ diff --git a/go/vt/mysqlctl/mysql_daemon.go b/go/vt/mysqlctl/mysql_daemon.go index 9d6e18295d8..b3e62f9d3c8 100644 --- a/go/vt/mysqlctl/mysql_daemon.go +++ b/go/vt/mysqlctl/mysql_daemon.go @@ -53,6 +53,7 @@ type MysqlDaemon interface { MasterPosition() (mysql.Position, error) IsReadOnly() (bool, error) SetReadOnly(on bool) error + SetSuperReadOnly(on bool) error SetSlavePosition(ctx context.Context, pos mysql.Position) error SetMaster(ctx context.Context, masterHost string, masterPort int, slaveStopBefore bool, slaveStartAfter bool) error WaitForReparentJournal(ctx context.Context, timeCreatedNS int64) error diff --git a/go/vt/mysqlctl/replication.go b/go/vt/mysqlctl/replication.go index 23b4c55efa5..eb28b23c353 100644 --- a/go/vt/mysqlctl/replication.go +++ b/go/vt/mysqlctl/replication.go @@ -161,6 +161,17 @@ var ( ErrNotMaster = errors.New("no master status") ) +// SetSuperReadOnly set/unset the super_read_only flag +func (mysqld *Mysqld) SetSuperReadOnly(on bool) error { + query := "SET GLOBAL super_read_only = " + if on { + query += "ON" + } else { + query += "OFF" + } + return mysqld.ExecuteSuperQuery(context.TODO(), query) +} + // WaitMasterPos lets slaves wait to given replication position func (mysqld *Mysqld) WaitMasterPos(ctx context.Context, targetPos mysql.Position) error { // Get a connection. diff --git a/go/vt/vttablet/tabletmanager/rpc_replication.go b/go/vt/vttablet/tabletmanager/rpc_replication.go index 802d6141e1e..49413f44b16 100644 --- a/go/vt/vttablet/tabletmanager/rpc_replication.go +++ b/go/vt/vttablet/tabletmanager/rpc_replication.go @@ -37,7 +37,8 @@ import ( ) var ( - enableSemiSync = flag.Bool("enable_semi_sync", false, "Enable semi-sync when configuring replication, on master and replica tablets only (rdonly tablets will not ack).") + enableSemiSync = flag.Bool("enable_semi_sync", false, "Enable semi-sync when configuring replication, on master and replica tablets only (rdonly tablets will not ack).") + setSuperReadOnly = flag.Bool("use_super_read_only", false, "Set super_read_only flag when performing planned failover.") ) // SlaveStatus returns the replication status @@ -331,17 +332,29 @@ func (agent *ActionAgent) DemoteMaster(ctx context.Context) (string, error) { // Now, set the server read-only. Note all active connections are not // affected. - if err := agent.MysqlDaemon.SetReadOnly(true); err != nil { - // if this failed, revert the change to serving - if _ /* state changed */, err1 := agent.QueryServiceControl.SetServingType(tablet.Type, true, nil); err1 != nil { - log.Warningf("SetServingType(serving=true) failed after failed SetReadOnly %v", err1) + if *setSuperReadOnly { + // Setting super_read_only also sets read_only + if err := agent.MysqlDaemon.SetSuperReadOnly(true); err != nil { + // if this failed, revert the change to serving + if _ /* state changed */, err1 := agent.QueryServiceControl.SetServingType(tablet.Type, true, nil); err1 != nil { + log.Warningf("SetServingType(serving=true) failed after failed SetSuperReadOnly %v", err1) + } + return "", err + } + } else { + if err := agent.MysqlDaemon.SetReadOnly(true); err != nil { + // if this failed, revert the change to serving + if _ /* state changed */, err1 := agent.QueryServiceControl.SetServingType(tablet.Type, true, nil); err1 != nil { + log.Warningf("SetServingType(serving=true) failed after failed SetReadOnly %v", err1) + } + return "", err } - return "", err } // If using semi-sync, we need to disable master-side. if err := agent.fixSemiSync(topodatapb.TabletType_REPLICA); err != nil { // if this failed, set server read-only back to false, set tablet back to serving + // setting read_only OFF will also set super_read_only OFF if it was set if err1 := agent.MysqlDaemon.SetReadOnly(false); err1 != nil { log.Warningf("SetReadOnly(false) failed after failed fixSemiSync %v", err1) } @@ -355,6 +368,7 @@ func (agent *ActionAgent) DemoteMaster(ctx context.Context) (string, error) { if err != nil { // if DemoteMaster failed, undo all the steps before // 1. set server back to read-only false + // setting read_only OFF will also set super_read_only OFF if it was set if err1 := agent.MysqlDaemon.SetReadOnly(false); err1 != nil { log.Warningf("SetReadOnly(false) failed after failed DemoteMaster %v", err1) }