Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions go/vt/vtctl/reparent.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ func init() {
commandEmergencyReparentShard,
"-keyspace_shard=<keyspace/shard> -new_master=<tablet alias>",
"Reparents the shard to the new master. Assumes the old master is dead and not responsding."})
addCommand("Shards", command{
"TabletExternallyReparented",
commandTabletExternallyReparented,
"<tablet alias>",
"Changes metadata in the topology server to acknowledge a shard master change performed by an external tool. See the Reparenting guide for more information:" +
"https://vitess.io/docs/user-guides/reparenting/#external-reparenting"})
}

func commandReparentTablet(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
Expand Down Expand Up @@ -171,3 +177,18 @@ func commandEmergencyReparentShard(ctx context.Context, wr *wrangler.Wrangler, s
}
return wr.EmergencyReparentShard(ctx, keyspace, shard, tabletAlias, *waitSlaveTimeout)
}

func commandTabletExternallyReparented(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
if err := subFlags.Parse(args); err != nil {
return err
}
if subFlags.NArg() != 1 {
return fmt.Errorf("action TabletExternallyReparented requires <tablet alias>")
}

tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0))
if err != nil {
return err
}
return wr.TabletExternallyReparented(ctx, tabletAlias)
}
23 changes: 0 additions & 23 deletions go/vt/vtctl/vtctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,6 @@ var commands = []commandGroup{
{"GetShard", commandGetShard,
"<keyspace/shard>",
"Outputs a JSON structure that contains information about the Shard."},
{"TabletExternallyReparented", commandTabletExternallyReparented,
"<tablet alias>",
"Changes metadata in the topology server to acknowledge a shard master change performed by an external tool. See the Reparenting guide for more information:" +
"https://github.com/vitessio/vitess/blob/master/doc/Reparenting.md#external-reparents."},
{"ValidateShard", commandValidateShard,
"[-ping-tablets] <keyspace/shard>",
"Validates that all nodes that are reachable from this shard are consistent."},
Expand Down Expand Up @@ -1203,25 +1199,6 @@ func commandGetShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.
return printJSON(wr.Logger(), shardInfo.Shard)
}

func commandTabletExternallyReparented(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
if err := subFlags.Parse(args); err != nil {
return err
}
if subFlags.NArg() != 1 {
return fmt.Errorf("the <tablet alias> argument is required for the TabletExternallyReparented command")
}

tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0))
if err != nil {
return err
}
ti, err := wr.TopoServer().GetTablet(ctx, tabletAlias)
if err != nil {
return err
}
return wr.TabletManagerClient().TabletExternallyReparented(ctx, ti.Tablet, "")
}

func commandValidateShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {
pingTablets := subFlags.Bool("ping-tablets", true, "Indicates whether all tablets should be pinged during the validation process")
if err := subFlags.Parse(args); err != nil {
Expand Down
55 changes: 52 additions & 3 deletions go/vt/wrangler/reparent.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ import (
)

const (
initShardMasterOperation = "InitShardMaster"
plannedReparentShardOperation = "PlannedReparentShard"
emergencyReparentShardOperation = "EmergencyReparentShard"
initShardMasterOperation = "InitShardMaster"
plannedReparentShardOperation = "PlannedReparentShard"
emergencyReparentShardOperation = "EmergencyReparentShard"
tabletExternallyReparentedOperation = "TabletExternallyReparented"
)

// ShardReplicationStatuses returns the ReplicationStatus for each tablet in a shard.
Expand Down Expand Up @@ -812,3 +813,51 @@ func (wr *Wrangler) emergencyReparentShardLocked(ctx context.Context, ev *events

return nil
}

// TabletExternallyReparented changes the type of new master for this shard to MASTER
// and updates it's tablet record in the topo. Updating the shard record is handled
// by the new master tablet
func (wr *Wrangler) TabletExternallyReparented(ctx context.Context, newMasterAlias *topodatapb.TabletAlias) error {

tabletInfo, err := wr.ts.GetTablet(ctx, newMasterAlias)
if err != nil {
log.Warningf("TabletExternallyReparented: failed to read tablet record for %v: %v", newMasterAlias, err)
return err
}

// Check the global shard record.
tablet := tabletInfo.Tablet
si, err := wr.ts.GetShard(ctx, tablet.Keyspace, tablet.Shard)
if err != nil {
log.Warningf("TabletExternallyReparented: failed to read global shard record for %v/%v: %v", tablet.Keyspace, tablet.Shard, err)
return err
}

// We update the tablet only if it is not currently master
if tablet.Type != topodatapb.TabletType_MASTER {
log.Infof("TabletExternallyReparented: executing tablet type change to MASTER")

// Create a reusable Reparent event with available info.
ev := &events.Reparent{
ShardInfo: *si,
NewMaster: *tablet,
OldMaster: topodatapb.Tablet{
Alias: si.MasterAlias,
Type: topodatapb.TabletType_MASTER,
},
}
defer func() {
if err != nil {
event.DispatchUpdate(ev, "failed: "+err.Error())
}
}()
event.DispatchUpdate(ev, "starting external reparent")

if err := wr.tmc.ChangeType(ctx, tablet, topodatapb.TabletType_MASTER); err != nil {
log.Warningf("Error calling ChangeType on new master %v: %v", topoproto.TabletAliasString(newMasterAlias), err)
return err
}
event.DispatchUpdate(ev, "finished")
}
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ func TestTabletExternallyReparented(t *testing.T) {
ctx := context.Background()
ts := memorytopo.NewServer("cell1", "cell2")
wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient())
vp := NewVtctlPipe(t, ts)
defer vp.Close()

// Create an old master, a new master, two good slaves, one bad slave
oldMaster := NewFakeTablet(t, wr, "cell1", 0, topodatapb.TabletType_MASTER, nil)
Expand Down Expand Up @@ -127,7 +125,8 @@ func TestTabletExternallyReparented(t *testing.T) {
if err != nil {
t.Fatalf("GetTablet failed: %v", err)
}
if err := vp.Run([]string{"TabletExternallyReparented", topoproto.TabletAliasString(oldMaster.Tablet.Alias)}); err != nil {
waitID := makeWaitID()
if err := tmc.TabletExternallyReparented(context.Background(), oldMaster.Tablet, waitID); err != nil {
t.Fatalf("TabletExternallyReparented(same master) should have worked: %v", err)
}

Expand All @@ -140,7 +139,7 @@ func TestTabletExternallyReparented(t *testing.T) {
if err != nil {
t.Fatalf("GetTablet failed: %v", err)
}
waitID := makeWaitID()
waitID = makeWaitID()
if err := tmc.TabletExternallyReparented(context.Background(), ti.Tablet, waitID); err != nil {
t.Fatalf("TabletExternallyReparented(slave) error: %v", err)
}
Expand Down