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 }