vitess.io/vitess@v0.16.2/go/cmd/vtctldclient/command/shards.go (about) 1 /* 2 Copyright 2021 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 command 18 19 import ( 20 "fmt" 21 "strconv" 22 23 "github.com/spf13/cobra" 24 25 "vitess.io/vitess/go/cmd/vtctldclient/cli" 26 "vitess.io/vitess/go/vt/key" 27 "vitess.io/vitess/go/vt/topo" 28 "vitess.io/vitess/go/vt/topo/topoproto" 29 30 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 31 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 32 ) 33 34 var ( 35 // CreateShard makes a CreateShard gRPC request to a vtctld. 36 CreateShard = &cobra.Command{ 37 Use: "CreateShard [--force|-f] [--include-parent|-p] <keyspace/shard>", 38 Short: "Creates the specified shard in the topology.", 39 DisableFlagsInUseLine: true, 40 Args: cobra.ExactArgs(1), 41 RunE: commandCreateShard, 42 } 43 // DeleteShards makes a DeleteShards gRPC request to a vtctld. 44 DeleteShards = &cobra.Command{ 45 Use: "DeleteShards [--recursive|-r] [--even-if-serving] [--force|-f] <keyspace/shard> [<keyspace/shard> ...]", 46 Short: "Deletes the specified shards from the topology.", 47 Long: `Deletes the specified shards from the topology. 48 49 In recursive mode, it also deletes all tablets belonging to the shard. 50 Otherwise, the shard must be empty (have no tablets) or returns an error for 51 that shard.`, 52 DisableFlagsInUseLine: true, 53 Args: cobra.MinimumNArgs(1), 54 RunE: commandDeleteShards, 55 } 56 // GenerateShardRanges outputs a set of shard ranges assuming a (mostly) 57 // equal distribution of N shards. 58 GenerateShardRanges = &cobra.Command{ 59 Use: "GenerateShardRanges <num_shards>", 60 Short: "Print a set of shard ranges assuming a keyspace with N shards.", 61 DisableFlagsInUseLine: true, 62 Args: cobra.ExactArgs(1), 63 RunE: func(cmd *cobra.Command, args []string) error { 64 n, err := strconv.ParseInt(cmd.Flags().Arg(0), 10, 64) 65 if err != nil { 66 return err 67 } 68 69 cli.FinishedParsing(cmd) 70 71 shards, err := key.GenerateShardRanges(int(n)) 72 if err != nil { 73 return err 74 } 75 76 data, err := cli.MarshalJSON(shards) 77 if err != nil { 78 return err 79 } 80 81 fmt.Printf("%s\n", data) 82 return nil 83 }, 84 Annotations: map[string]string{ 85 skipClientCreationKey: "true", 86 }, 87 } 88 // GetShard makes a GetShard gRPC request to a vtctld. 89 GetShard = &cobra.Command{ 90 Use: "GetShard <keyspace/shard>", 91 Short: "Returns information about a shard in the topology.", 92 DisableFlagsInUseLine: true, 93 Args: cobra.ExactArgs(1), 94 RunE: commandGetShard, 95 } 96 // RemoveShardCell makes a RemoveShardCell gRPC request to a vtctld. 97 RemoveShardCell = &cobra.Command{ 98 Use: "RemoveShardCell [--force|-f] [--recursive|-r] <keyspace/shard> <cell>", 99 Short: "Remove the specified cell from the specified shard's Cells list.", 100 DisableFlagsInUseLine: true, 101 Args: cobra.ExactArgs(2), 102 RunE: commandRemoveShardCell, 103 } 104 // SetShardIsPrimaryServing makes a SetShardIsPrimaryServing gRPC call to a 105 // vtctld. 106 SetShardIsPrimaryServing = &cobra.Command{ 107 Use: "SetShardIsPrimaryServing <keyspace/shard> <true/false>", 108 Short: "Add or remove a shard from serving. This is meant as an emergency function. It does not rebuild any serving graphs; i.e. it does not run `RebuildKeyspaceGraph`.", 109 DisableFlagsInUseLine: true, 110 Args: cobra.ExactArgs(2), 111 RunE: commandSetShardIsPrimaryServing, 112 } 113 // SetShardTabletControl makes a SetShardTabletControl gRPC call to a vtctld. 114 SetShardTabletControl = &cobra.Command{ 115 Use: "SetShardTabletControl [--cells=c1,c2...] [--denied-tables=t1,t2,...] [--remove] [--disable-query-service[=0|false]] <keyspace/shard> <tablet_type>", 116 Short: "Sets the TabletControl record for a shard and tablet type. Only use this for an emergency fix or after a finished MoveTables.", 117 Long: `Sets the TabletControl record for a shard and tablet type. 118 119 Only use this for an emergency fix or after a finished MoveTables. 120 121 Always specify the denied-tables flag for MoveTables, but never for Reshard operations. 122 123 To set the DisableQueryService flag, keep denied-tables empty, and set --disable-query-service 124 to true or false. This is useful to fix Reshard operations gone wrong. 125 126 To change the list of denied tables, specify the --denied-tables parameter with 127 the new list. This is useful to fix tables that are being blocked after a 128 MoveTables operation. 129 130 To remove the ShardTabletControl record entirely, use the --remove flag. This is 131 useful after a MoveTables has finished to remove serving restrictions.`, 132 DisableFlagsInUseLine: true, 133 Args: cobra.ExactArgs(2), 134 RunE: commandSetShardTabletControl, 135 } 136 // ShardReplicationAdd makse a ShardReplicationAdd gRPC request to a vtctld. 137 ShardReplicationAdd = &cobra.Command{ 138 Use: "ShardReplicationAdd <keyspace/shard> <tablet alias>", 139 Short: "Adds an entry to the replication graph in the given cell.", 140 DisableFlagsInUseLine: true, 141 Args: cobra.ExactArgs(2), 142 RunE: commandShardReplicationAdd, 143 Hidden: true, 144 } 145 // ShardReplicationFix makes a ShardReplicationFix gRPC request to a vtctld. 146 ShardReplicationFix = &cobra.Command{ 147 Use: "ShardReplicationFix <cell> <keyspace/shard>", 148 Short: "Walks through a ShardReplication object and fixes the first error encountered.", 149 DisableFlagsInUseLine: true, 150 Args: cobra.ExactArgs(2), 151 RunE: commandShardReplicationFix, 152 } 153 // ShardReplicationPositions makes a ShardReplicationPositions gRPC request 154 // to a vtctld. 155 ShardReplicationPositions = &cobra.Command{ 156 Use: "ShardReplicationPositions <keyspace/shard>", 157 Long: `Shows the replication status of each tablet in the shard graph. 158 Output is sorted by tablet type, then replication position. 159 Use ctrl-C to interrupt the command and see partial results if needed.`, 160 DisableFlagsInUseLine: true, 161 Args: cobra.ExactArgs(1), 162 RunE: commandShardReplicationPositions, 163 } 164 // ShardReplicationRemove makse a ShardReplicationRemove gRPC request to a vtctld. 165 ShardReplicationRemove = &cobra.Command{ 166 Use: "ShardReplicationRemove <keyspace/shard> <tablet alias>", 167 Short: "Removes an entry from the replication graph in the given cell.", 168 DisableFlagsInUseLine: true, 169 Args: cobra.ExactArgs(2), 170 RunE: commandShardReplicationRemove, 171 Hidden: true, 172 } 173 // SourceShardAdd makes a SourceShardAdd gRPC request to a vtctld. 174 SourceShardAdd = &cobra.Command{ 175 Use: "SourceShardAdd [--key-range <keyrange>] [--tables <table1,table2,...> [--tables <table3,...>]...] <keyspace/shard> <uid> <source keyspace/shard>", 176 Short: "Adds the SourceShard record with the provided index for emergencies only. It does not call RefreshState for the shard primary.", 177 DisableFlagsInUseLine: true, 178 Args: cobra.ExactArgs(3), 179 RunE: commandSourceShardAdd, 180 } 181 // SourceShardDelete makes a SourceShardDelete gRPC request to a vtctld. 182 SourceShardDelete = &cobra.Command{ 183 Use: "SourceShardDelete <keyspace/shard> <uid>", 184 Short: "Deletes the SourceShard record with the provided index. This should only be used for emergency cleanup. It does not call RefreshState for the shard primary.", 185 DisableFlagsInUseLine: true, 186 Args: cobra.ExactArgs(2), 187 RunE: commandSourceShardDelete, 188 } 189 190 // ValidateVersionShard makes a ValidateVersionShard gRPC request to a vtctld. 191 ValidateVersionShard = &cobra.Command{ 192 Use: "ValidateVersionShard <keyspace/shard>", 193 Short: "Validates that the version on the primary matches all of the replicas.", 194 DisableFlagsInUseLine: true, 195 Args: cobra.ExactArgs(1), 196 RunE: commandValidateVersionShard, 197 } 198 ) 199 200 var createShardOptions = struct { 201 Force bool 202 IncludeParent bool 203 }{} 204 205 func commandCreateShard(cmd *cobra.Command, args []string) error { 206 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 207 if err != nil { 208 return err 209 } 210 211 cli.FinishedParsing(cmd) 212 213 resp, err := client.CreateShard(commandCtx, &vtctldatapb.CreateShardRequest{ 214 Keyspace: keyspace, 215 ShardName: shard, 216 Force: createShardOptions.Force, 217 IncludeParent: createShardOptions.IncludeParent, 218 }) 219 if err != nil { 220 return err 221 } 222 223 data, err := cli.MarshalJSON(resp) 224 if err != nil { 225 return err 226 } 227 228 fmt.Printf("%s\n", data) 229 230 return nil 231 } 232 233 var deleteShardsOptions = struct { 234 Recursive bool 235 EvenIfServing bool 236 Force bool 237 }{} 238 239 func commandDeleteShards(cmd *cobra.Command, args []string) error { 240 shards, err := cli.ParseKeyspaceShards(cmd.Flags().Args()) 241 if err != nil { 242 return err 243 } 244 245 cli.FinishedParsing(cmd) 246 247 _, err = client.DeleteShards(commandCtx, &vtctldatapb.DeleteShardsRequest{ 248 Shards: shards, 249 EvenIfServing: deleteShardsOptions.EvenIfServing, 250 Recursive: deleteShardsOptions.Recursive, 251 Force: deleteShardsOptions.Force, 252 }) 253 254 if err != nil { 255 return fmt.Errorf("%w: while deleting %d shards; please inspect the topo", err, len(shards)) 256 } 257 258 fmt.Printf("Successfully deleted %d shards\n", len(shards)) 259 260 return nil 261 } 262 263 func commandGetShard(cmd *cobra.Command, args []string) error { 264 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 265 if err != nil { 266 return err 267 } 268 269 cli.FinishedParsing(cmd) 270 271 resp, err := client.GetShard(commandCtx, &vtctldatapb.GetShardRequest{ 272 Keyspace: keyspace, 273 ShardName: shard, 274 }) 275 if err != nil { 276 return err 277 } 278 279 data, err := cli.MarshalJSON(resp.Shard) 280 if err != nil { 281 return err 282 } 283 284 fmt.Printf("%s\n", data) 285 286 return nil 287 } 288 289 var removeShardCellOptions = struct { 290 Force bool 291 Recursive bool 292 }{} 293 294 func commandRemoveShardCell(cmd *cobra.Command, args []string) error { 295 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 296 if err != nil { 297 return err 298 } 299 300 cli.FinishedParsing(cmd) 301 302 cell := cmd.Flags().Arg(1) 303 304 _, err = client.RemoveShardCell(commandCtx, &vtctldatapb.RemoveShardCellRequest{ 305 Keyspace: keyspace, 306 ShardName: shard, 307 Cell: cell, 308 Force: removeShardCellOptions.Force, 309 Recursive: removeShardCellOptions.Recursive, 310 }) 311 312 if err != nil { 313 return err 314 } 315 316 fmt.Printf("Successfully removed cell %v from shard %s/%s\n", cell, keyspace, shard) 317 318 return nil 319 } 320 321 func commandSetShardIsPrimaryServing(cmd *cobra.Command, args []string) error { 322 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 323 if err != nil { 324 return fmt.Errorf("cannot parse keyspace/shard: %w", err) 325 } 326 327 isServing, err := strconv.ParseBool(cmd.Flags().Arg(1)) 328 if err != nil { 329 return fmt.Errorf("cannot parse is_serving as bool: %w", err) 330 } 331 332 cli.FinishedParsing(cmd) 333 334 resp, err := client.SetShardIsPrimaryServing(commandCtx, &vtctldatapb.SetShardIsPrimaryServingRequest{ 335 Keyspace: keyspace, 336 Shard: shard, 337 IsServing: isServing, 338 }) 339 if err != nil { 340 return err 341 } 342 343 data, err := cli.MarshalJSON(resp.Shard) 344 if err != nil { 345 return err 346 } 347 348 fmt.Printf("%s\n", data) 349 return nil 350 } 351 352 var setShardTabletControlOptions = struct { 353 Cells []string 354 DeniedTables []string 355 Remove bool 356 DisableQueryService bool 357 }{} 358 359 func commandSetShardTabletControl(cmd *cobra.Command, args []string) error { 360 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 361 if err != nil { 362 return fmt.Errorf("cannot parse keyspace/shard: %w", err) 363 } 364 365 tabletType, err := topoproto.ParseTabletType(cmd.Flags().Arg(1)) 366 if err != nil { 367 return fmt.Errorf("cannot parse tablet type: %w", err) 368 } 369 370 cli.FinishedParsing(cmd) 371 372 resp, err := client.SetShardTabletControl(commandCtx, &vtctldatapb.SetShardTabletControlRequest{ 373 Keyspace: keyspace, 374 Shard: shard, 375 TabletType: tabletType, 376 Cells: setShardTabletControlOptions.Cells, 377 DeniedTables: setShardTabletControlOptions.DeniedTables, 378 Remove: setShardTabletControlOptions.Remove, 379 DisableQueryService: setShardTabletControlOptions.DisableQueryService, 380 }) 381 if err != nil { 382 return err 383 } 384 385 data, err := cli.MarshalJSON(resp.Shard) 386 if err != nil { 387 return err 388 } 389 390 fmt.Printf("%s\n", data) 391 return nil 392 } 393 394 func commandShardReplicationAdd(cmd *cobra.Command, args []string) error { 395 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 396 if err != nil { 397 return err 398 } 399 400 tabletAlias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(1)) 401 if err != nil { 402 return err 403 } 404 405 cli.FinishedParsing(cmd) 406 407 _, err = client.ShardReplicationAdd(commandCtx, &vtctldatapb.ShardReplicationAddRequest{ 408 Keyspace: keyspace, 409 Shard: shard, 410 TabletAlias: tabletAlias, 411 }) 412 return err 413 } 414 415 func commandShardReplicationFix(cmd *cobra.Command, args []string) error { 416 cell := cmd.Flags().Arg(0) 417 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(1)) 418 if err != nil { 419 return err 420 } 421 422 cli.FinishedParsing(cmd) 423 424 resp, err := client.ShardReplicationFix(commandCtx, &vtctldatapb.ShardReplicationFixRequest{ 425 Keyspace: keyspace, 426 Shard: shard, 427 Cell: cell, 428 }) 429 if err != nil { 430 return err 431 } 432 433 switch resp.Error { 434 case nil: 435 fmt.Println("All nodes in the replication graph are valid.") 436 default: 437 fmt.Printf("%s has been fixed for %s.\n", topoproto.ShardReplicationErrorTypeString(resp.Error.Type), topoproto.TabletAliasString(resp.Error.TabletAlias)) 438 } 439 440 return nil 441 } 442 443 func commandShardReplicationPositions(cmd *cobra.Command, args []string) error { 444 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 445 if err != nil { 446 return err 447 } 448 449 cli.FinishedParsing(cmd) 450 451 resp, err := client.ShardReplicationPositions(commandCtx, &vtctldatapb.ShardReplicationPositionsRequest{ 452 Keyspace: keyspace, 453 Shard: shard, 454 }) 455 if err != nil { 456 return err 457 } 458 459 for _, rt := range cli.SortedReplicatingTablets(resp.TabletMap, resp.ReplicationStatuses) { 460 var line string 461 462 switch rt.Status { 463 case nil: 464 line = cli.MarshalTabletAWK(rt.Tablet) + "<err> <err> <err>" 465 default: 466 line = cli.MarshalTabletAWK(rt.Tablet) + fmt.Sprintf(" %v %v", rt.Status.Position, rt.Status.ReplicationLagSeconds) 467 } 468 469 fmt.Println(line) 470 } 471 472 return nil 473 } 474 475 func commandShardReplicationRemove(cmd *cobra.Command, args []string) error { 476 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 477 if err != nil { 478 return err 479 } 480 481 tabletAlias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(1)) 482 if err != nil { 483 return err 484 } 485 486 cli.FinishedParsing(cmd) 487 488 _, err = client.ShardReplicationRemove(commandCtx, &vtctldatapb.ShardReplicationRemoveRequest{ 489 Keyspace: keyspace, 490 Shard: shard, 491 TabletAlias: tabletAlias, 492 }) 493 return err 494 } 495 496 var sourceShardAddOptions = struct { 497 KeyRangeStr string 498 Tables []string 499 }{} 500 501 func commandSourceShardAdd(cmd *cobra.Command, args []string) error { 502 ks, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 503 if err != nil { 504 return err 505 } 506 507 uid, err := strconv.ParseUint(cmd.Flags().Arg(1), 10, 32) 508 if err != nil { 509 return fmt.Errorf("Failed to parse SourceShard uid: %w", err) // nolint 510 } 511 512 sks, sshard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(2)) 513 if err != nil { 514 return err 515 } 516 517 var kr *topodatapb.KeyRange 518 if sourceShardAddOptions.KeyRangeStr != "" { 519 _, kr, err = topo.ValidateShardName(sourceShardAddOptions.KeyRangeStr) 520 if err != nil { 521 return fmt.Errorf("Invalid keyrange: %w", err) 522 } 523 } 524 525 cli.FinishedParsing(cmd) 526 527 resp, err := client.SourceShardAdd(commandCtx, &vtctldatapb.SourceShardAddRequest{ 528 Keyspace: ks, 529 Shard: shard, 530 Uid: uint32(uid), 531 SourceKeyspace: sks, 532 SourceShard: sshard, 533 KeyRange: kr, 534 Tables: sourceShardAddOptions.Tables, 535 }) 536 if err != nil { 537 return err 538 } 539 540 switch resp.Shard { 541 case nil: 542 fmt.Printf("SourceShard with uid %v already exists for %s/%s, not adding it.\n", uid, ks, shard) 543 default: 544 data, err := cli.MarshalJSON(resp.Shard) 545 if err != nil { 546 return err 547 } 548 549 fmt.Printf("Updated shard record:\n%s\n", data) 550 } 551 552 return nil 553 } 554 555 func commandSourceShardDelete(cmd *cobra.Command, args []string) error { 556 ks, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 557 if err != nil { 558 return err 559 } 560 561 uid, err := strconv.ParseUint(cmd.Flags().Arg(1), 10, 32) 562 if err != nil { 563 return fmt.Errorf("Failed to parse SourceShard uid: %w", err) // nolint 564 } 565 566 cli.FinishedParsing(cmd) 567 568 resp, err := client.SourceShardDelete(commandCtx, &vtctldatapb.SourceShardDeleteRequest{ 569 Keyspace: ks, 570 Shard: shard, 571 Uid: uint32(uid), 572 }) 573 if err != nil { 574 return err 575 } 576 577 switch resp.Shard { 578 case nil: 579 fmt.Printf("No SourceShard with uid %v.\n", uid) 580 default: 581 data, err := cli.MarshalJSON(resp.Shard) 582 if err != nil { 583 return err 584 } 585 586 fmt.Printf("Updated shard record:\n%s\n", data) 587 } 588 return nil 589 } 590 591 func commandValidateVersionShard(cmd *cobra.Command, args []string) error { 592 keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0)) 593 if err != nil { 594 return err 595 } 596 597 cli.FinishedParsing(cmd) 598 599 resp, err := client.ValidateVersionShard(commandCtx, &vtctldatapb.ValidateVersionShardRequest{ 600 Keyspace: keyspace, 601 Shard: shard, 602 }) 603 if err != nil { 604 return err 605 } 606 607 data, err := cli.MarshalJSON(resp) 608 if err != nil { 609 return err 610 } 611 612 fmt.Printf("%s\n", data) 613 return nil 614 } 615 616 func init() { 617 CreateShard.Flags().BoolVarP(&createShardOptions.Force, "force", "f", false, "Overwrite an existing shard record, if one exists.") 618 CreateShard.Flags().BoolVarP(&createShardOptions.IncludeParent, "include-parent", "p", false, "Creates the parent keyspace record if does not already exist.") 619 Root.AddCommand(CreateShard) 620 621 DeleteShards.Flags().BoolVarP(&deleteShardsOptions.Recursive, "recursive", "r", false, "Also delete all tablets belonging to the shard. This is required to delete a non-empty shard.") 622 DeleteShards.Flags().BoolVar(&deleteShardsOptions.EvenIfServing, "even-if-serving", false, "Remove the shard even if it is serving. Use with caution.") 623 DeleteShards.Flags().BoolVarP(&deleteShardsOptions.Force, "force", "f", false, "Remove the shard even if it cannot be locked; this should only be used for cleanup operations.") 624 Root.AddCommand(DeleteShards) 625 626 Root.AddCommand(GetShard) 627 Root.AddCommand(GenerateShardRanges) 628 629 RemoveShardCell.Flags().BoolVarP(&removeShardCellOptions.Force, "force", "f", false, "Proceed even if the cell's topology server cannot be reached. The assumption is that you turned down the entire cell, and just need to update the global topo data.") 630 RemoveShardCell.Flags().BoolVarP(&removeShardCellOptions.Recursive, "recursive", "r", false, "Also delete all tablets in that cell beloning to the specified shard.") 631 Root.AddCommand(RemoveShardCell) 632 633 Root.AddCommand(SetShardIsPrimaryServing) 634 635 SetShardTabletControl.Flags().StringSliceVarP(&setShardTabletControlOptions.Cells, "cells", "c", nil, "Specifies a comma-separated list of cells to update.") 636 SetShardTabletControl.Flags().StringSliceVar(&setShardTabletControlOptions.DeniedTables, "denied-tables", nil, "Specifies a comma-separated list of tables to add to the denylist (for MoveTables). Each table name is either an exact match, or a regular expression of the form '/regexp/'.") 637 SetShardTabletControl.Flags().BoolVarP(&setShardTabletControlOptions.Remove, "remove", "r", false, "Removes the specified cells for MoveTables operations.") 638 SetShardTabletControl.Flags().BoolVar(&setShardTabletControlOptions.DisableQueryService, "disable-query-service", false, "Sets the DisableQueryService flag in the specified cells. This flag requires --denied-tables and --remove to be unset; if either is set, this flag is ignored.") 639 Root.AddCommand(SetShardTabletControl) 640 641 Root.AddCommand(ShardReplicationAdd) 642 Root.AddCommand(ShardReplicationFix) 643 Root.AddCommand(ShardReplicationPositions) 644 Root.AddCommand(ShardReplicationRemove) 645 Root.AddCommand(ValidateVersionShard) 646 647 SourceShardAdd.Flags().StringVar(&sourceShardAddOptions.KeyRangeStr, "key-range", "", "Key range to use for the SourceShard.") 648 SourceShardAdd.Flags().StringSliceVar(&sourceShardAddOptions.Tables, "tables", nil, "Comma-separated lists of tables to replicate (for MoveTables). Each table name is either an exact match, or a regular expression of the form \"/regexp/\".") 649 Root.AddCommand(SourceShardAdd) 650 651 Root.AddCommand(SourceShardDelete) 652 }