vitess.io/vitess@v0.16.2/go/vt/vtctl/reparent.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package vtctl
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/spf13/pflag"
    24  
    25  	"vitess.io/vitess/go/vt/mysqlctl"
    26  	"vitess.io/vitess/go/vt/topo"
    27  	"vitess.io/vitess/go/vt/topo/topoproto"
    28  	"vitess.io/vitess/go/vt/wrangler"
    29  
    30  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    31  )
    32  
    33  func init() {
    34  	addCommand("Tablets", command{
    35  		name:   "ReparentTablet",
    36  		method: commandReparentTablet,
    37  		params: "<tablet alias>",
    38  		help:   "Reparent a tablet to the current primary in the shard. This only works if the current replication position matches the last known reparent action.",
    39  	})
    40  	addCommand("Shards", command{
    41  		name:         "InitShardPrimary",
    42  		method:       commandInitShardPrimary,
    43  		params:       "[--force] [--wait_replicas_timeout=<duration>] <keyspace/shard> <tablet alias>",
    44  		help:         "Sets the initial primary for a shard. Will make all other tablets in the shard replicas of the provided tablet. WARNING: this could cause data loss on an already replicating shard. PlannedReparentShard or EmergencyReparentShard should be used instead.",
    45  		deprecated:   true,
    46  		deprecatedBy: "PlannedReparentShard",
    47  	})
    48  	addCommand("Shards", command{
    49  		name:   "PlannedReparentShard",
    50  		method: commandPlannedReparentShard,
    51  		params: "--keyspace_shard=<keyspace/shard> [--new_primary=<tablet alias>] [--avoid_tablet=<tablet alias>] [--wait_replicas_timeout=<duration>]",
    52  		help:   "Reparents the shard to the new primary, or away from old primary. Both old and new primary need to be up and running.",
    53  	})
    54  	addCommand("Shards", command{
    55  		name:   "EmergencyReparentShard",
    56  		method: commandEmergencyReparentShard,
    57  		params: "--keyspace_shard=<keyspace/shard> [--new_primary=<tablet alias>] [--wait_replicas_timeout=<duration>] [--ignore_replicas=<tablet alias list>] [--prevent_cross_cell_promotion=<true/false>]",
    58  		help:   "Reparents the shard to the new primary. Assumes the old primary is dead and not responding.",
    59  	})
    60  	addCommand("Shards", command{
    61  		name:   "TabletExternallyReparented",
    62  		method: commandTabletExternallyReparented,
    63  		params: "<tablet alias>",
    64  		help: "Changes metadata in the topology server to acknowledge a shard primary change performed by an external tool. See the Reparenting guide for more information:" +
    65  			"https://vitess.io/docs/user-guides/reparenting/#external-reparenting",
    66  	})
    67  }
    68  
    69  func commandReparentTablet(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error {
    70  	if mysqlctl.DisableActiveReparents {
    71  		return fmt.Errorf("active reparent commands disabled (unset the --disable_active_reparents flag to enable)")
    72  	}
    73  
    74  	if err := subFlags.Parse(args); err != nil {
    75  		return err
    76  	}
    77  	if subFlags.NArg() != 1 {
    78  		return fmt.Errorf("action ReparentTablet requires <tablet alias>")
    79  	}
    80  	tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0))
    81  	if err != nil {
    82  		return err
    83  	}
    84  	return wr.ReparentTablet(ctx, tabletAlias)
    85  }
    86  
    87  func commandInitShardPrimary(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error {
    88  	if mysqlctl.DisableActiveReparents {
    89  		return fmt.Errorf("active reparent commands disabled (unset the --disable_active_reparents flag to enable)")
    90  	}
    91  
    92  	force := subFlags.Bool("force", false, "will force the reparent even if the provided tablet is not writable or the shard primary")
    93  	waitReplicasTimeout := subFlags.Duration("wait_replicas_timeout", topo.RemoteOperationTimeout, "time to wait for replicas to catch up in reparenting")
    94  	if err := subFlags.Parse(args); err != nil {
    95  		return err
    96  	}
    97  	if subFlags.NArg() != 2 {
    98  		return fmt.Errorf("action InitShardPrimary requires <keyspace/shard> <tablet alias>")
    99  	}
   100  	keyspace, shard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0))
   101  	if err != nil {
   102  		return err
   103  	}
   104  	tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(1))
   105  	if err != nil {
   106  		return err
   107  	}
   108  	return wr.InitShardPrimary(ctx, keyspace, shard, tabletAlias, *force, *waitReplicasTimeout)
   109  }
   110  
   111  func commandPlannedReparentShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error {
   112  	if mysqlctl.DisableActiveReparents {
   113  		return fmt.Errorf("active reparent commands disabled (unset the --disable_active_reparents flag to enable)")
   114  	}
   115  
   116  	waitReplicasTimeout := subFlags.Duration("wait_replicas_timeout", topo.RemoteOperationTimeout, "time to wait for replicas to catch up on replication before and after reparenting")
   117  	keyspaceShard := subFlags.String("keyspace_shard", "", "keyspace/shard of the shard that needs to be reparented")
   118  	newPrimary := subFlags.String("new_primary", "", "alias of a tablet that should be the new primary")
   119  	avoidTablet := subFlags.String("avoid_tablet", "", "alias of a tablet that should not be the primary, i.e. reparent to any other tablet if this one is the primary")
   120  
   121  	if err := subFlags.Parse(args); err != nil {
   122  		return err
   123  	}
   124  	if subFlags.NArg() == 2 {
   125  		// Legacy syntax: "<keyspace/shard> <tablet alias>".
   126  		if *keyspaceShard != "" || *newPrimary != "" {
   127  			return fmt.Errorf("cannot use legacy syntax and flags --keyspace_shard and --new_primary for action PlannedReparentShard at the same time")
   128  		}
   129  		*keyspaceShard = subFlags.Arg(0)
   130  		*newPrimary = subFlags.Arg(1)
   131  	} else if subFlags.NArg() != 0 {
   132  		return fmt.Errorf("action PlannedReparentShard requires --keyspace_shard=<keyspace/shard> [--new_primary=<tablet alias>] [--avoid_tablet=<tablet alias>]")
   133  	}
   134  
   135  	keyspace, shard, err := topoproto.ParseKeyspaceShard(*keyspaceShard)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	var newPrimaryAlias, avoidTabletAlias *topodatapb.TabletAlias
   140  	if *newPrimary != "" {
   141  		newPrimaryAlias, err = topoproto.ParseTabletAlias(*newPrimary)
   142  		if err != nil {
   143  			return err
   144  		}
   145  	}
   146  	if *avoidTablet != "" {
   147  		avoidTabletAlias, err = topoproto.ParseTabletAlias(*avoidTablet)
   148  		if err != nil {
   149  			return err
   150  		}
   151  	}
   152  	return wr.PlannedReparentShard(ctx, keyspace, shard, newPrimaryAlias, avoidTabletAlias, *waitReplicasTimeout)
   153  }
   154  
   155  func commandEmergencyReparentShard(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error {
   156  	if mysqlctl.DisableActiveReparents {
   157  		return fmt.Errorf("active reparent commands disabled (unset the --disable_active_reparents flag to enable)")
   158  	}
   159  
   160  	waitReplicasTimeout := subFlags.Duration("wait_replicas_timeout", topo.RemoteOperationTimeout, "time to wait for replicas to catch up in reparenting")
   161  	keyspaceShard := subFlags.String("keyspace_shard", "", "keyspace/shard of the shard that needs to be reparented")
   162  	newPrimary := subFlags.String("new_primary", "", "optional alias of a tablet that should be the new primary. If not specified, Vitess will select the best candidate")
   163  	preventCrossCellPromotion := subFlags.Bool("prevent_cross_cell_promotion", false, "only promotes a new primary from the same cell as the previous primary")
   164  	ignoreReplicasList := subFlags.String("ignore_replicas", "", "comma-separated list of replica tablet aliases to ignore during emergency reparent")
   165  
   166  	if err := subFlags.Parse(args); err != nil {
   167  		return err
   168  	}
   169  	if subFlags.NArg() == 2 {
   170  		// Legacy syntax: "<keyspace/shard> <tablet alias>".
   171  		if *newPrimary != "" {
   172  			return fmt.Errorf("cannot use legacy syntax and flag --new_primary for action EmergencyReparentShard at the same time")
   173  		}
   174  		*keyspaceShard = subFlags.Arg(0)
   175  		*newPrimary = subFlags.Arg(1)
   176  	} else if subFlags.NArg() != 0 {
   177  		return fmt.Errorf("action EmergencyReparentShard requires --keyspace_shard=<keyspace/shard>")
   178  	}
   179  
   180  	keyspace, shard, err := topoproto.ParseKeyspaceShard(*keyspaceShard)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	var tabletAlias *topodatapb.TabletAlias
   185  	if *newPrimary != "" {
   186  		tabletAlias, err = topoproto.ParseTabletAlias(*newPrimary)
   187  		if err != nil {
   188  			return err
   189  		}
   190  	}
   191  	unreachableReplicas := topoproto.ParseTabletSet(*ignoreReplicasList)
   192  	return wr.EmergencyReparentShard(ctx, keyspace, shard, tabletAlias, *waitReplicasTimeout, unreachableReplicas, *preventCrossCellPromotion)
   193  }
   194  
   195  func commandTabletExternallyReparented(ctx context.Context, wr *wrangler.Wrangler, subFlags *pflag.FlagSet, args []string) error {
   196  	if err := subFlags.Parse(args); err != nil {
   197  		return err
   198  	}
   199  	if subFlags.NArg() != 1 {
   200  		return fmt.Errorf("action TabletExternallyReparented requires <tablet alias>")
   201  	}
   202  
   203  	tabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0))
   204  	if err != nil {
   205  		return err
   206  	}
   207  	return wr.TabletExternallyReparented(ctx, tabletAlias)
   208  }