Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vtctld] Add remaining reparent commands to VtctldServer #7536

Merged
merged 16 commits into from
Feb 25, 2021
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
199 changes: 198 additions & 1 deletion go/cmd/vtctldclient/internal/command/reparents.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,114 @@ limitations under the License.
package command

import (
"fmt"
"time"

"github.com/spf13/cobra"

"vitess.io/vitess/go/cmd/vtctldclient/cli"
"vitess.io/vitess/go/protoutil"
"vitess.io/vitess/go/vt/log"
"vitess.io/vitess/go/vt/logutil"
"vitess.io/vitess/go/vt/topo"
"vitess.io/vitess/go/vt/topo/topoproto"

topodatapb "vitess.io/vitess/go/vt/proto/topodata"
vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
)

var (
// EmergencyReparentShard makes an EmergencyReparent gRPC call to a vtctld.
EmergencyReparentShard = &cobra.Command{
Use: "EmergencyReparentShard <keyspace/shard>",
Args: cobra.ExactArgs(1),
Long: "Reparents the shard to the new primary. Assumes the old primary is dead and not responding",
RunE: commandEmergencyReparentShard,
}
// InitShardPrimary makes an InitShardPrimary gRPC call to a vtctld.
InitShardPrimary = &cobra.Command{
Use: "InitShardPrimary",
Use: "InitShardPrimary <keyspace/shard> <primary alias>",
Args: cobra.ExactArgs(2),
RunE: commandInitShardPrimary,
}
// PlannedReparentShard makes a PlannedReparentShard gRPC call to a vtctld.
PlannedReparentShard = &cobra.Command{
Use: "PlannedReparentShard <keyspace/shard>",
Args: cobra.ExactArgs(1),
Long: "string",
RunE: commandPlannedReparentShard,
}
// ReparentTablet makes a ReparentTablet gRPC call to a vtctld.
ReparentTablet = &cobra.Command{
Use: "ReparentTablet <alias>",
Long: "Reparent a tablet to the current primary in the shard. This only works if the current replica position " +
"matches the last known reparent action.",
Args: cobra.ExactArgs(1),
RunE: commandReparentTablet,
}
// TabletExternallyReparented makes a TabletExternallyReparented gRPC call
// to a vtctld.
TabletExternallyReparented = &cobra.Command{
Use: "TabletExternallyReparented <alias>",
Args: cobra.ExactArgs(1),
RunE: commandTabletExternallyReparented,
}
)

var emergencyReparentShardOptions = struct {
Force bool
WaitReplicasTimeout time.Duration
NewPrimaryAliasStr string
IgnoreReplicaAliasStrList []string
}{}

func commandEmergencyReparentShard(cmd *cobra.Command, args []string) error {
keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
if err != nil {
return err
}

var (
newPrimaryAlias *topodatapb.TabletAlias
ignoreReplicaAliases = make([]*topodatapb.TabletAlias, len(emergencyReparentShardOptions.IgnoreReplicaAliasStrList))
)

if emergencyReparentShardOptions.NewPrimaryAliasStr != "" {
newPrimaryAlias, err = topoproto.ParseTabletAlias(emergencyReparentShardOptions.NewPrimaryAliasStr)
if err != nil {
return err
}
}

for i, aliasStr := range emergencyReparentShardOptions.IgnoreReplicaAliasStrList {
alias, err := topoproto.ParseTabletAlias(aliasStr)
if err != nil {
return err
}

ignoreReplicaAliases[i] = alias
}

cli.FinishedParsing(cmd)

resp, err := client.EmergencyReparentShard(commandCtx, &vtctldatapb.EmergencyReparentShardRequest{
Keyspace: keyspace,
Shard: shard,
NewPrimary: newPrimaryAlias,
IgnoreReplicas: ignoreReplicaAliases,
WaitReplicasTimeout: protoutil.DurationToProto(emergencyReparentShardOptions.WaitReplicasTimeout),
})
if err != nil {
return err
}

for _, event := range resp.Events {
fmt.Println(logutil.EventString(event))
}

return nil
}

var initShardPrimaryOptions = struct {
WaitReplicasTimeout time.Duration
Force bool
Expand Down Expand Up @@ -71,8 +158,118 @@ func commandInitShardPrimary(cmd *cobra.Command, args []string) error {
return err
}

var plannedReparentShardOptions = struct {
NewPrimaryAliasStr string
AvoidPrimaryAliasStr string
WaitReplicasTimeout time.Duration
}{}

func commandPlannedReparentShard(cmd *cobra.Command, args []string) error {
keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
if err != nil {
return err
}

var (
newPrimaryAlias *topodatapb.TabletAlias
avoidPrimaryAlias *topodatapb.TabletAlias
)

if plannedReparentShardOptions.NewPrimaryAliasStr != "" {
newPrimaryAlias, err = topoproto.ParseTabletAlias(plannedReparentShardOptions.NewPrimaryAliasStr)
if err != nil {
return err
}
}

if plannedReparentShardOptions.AvoidPrimaryAliasStr != "" {
avoidPrimaryAlias, err = topoproto.ParseTabletAlias(plannedReparentShardOptions.AvoidPrimaryAliasStr)
if err != nil {
return err
}
}

cli.FinishedParsing(cmd)

resp, err := client.PlannedReparentShard(commandCtx, &vtctldatapb.PlannedReparentShardRequest{
Keyspace: keyspace,
Shard: shard,
NewPrimary: newPrimaryAlias,
AvoidPrimary: avoidPrimaryAlias,
WaitReplicasTimeout: protoutil.DurationToProto(plannedReparentShardOptions.WaitReplicasTimeout),
})
if err != nil {
return err
}

for _, event := range resp.Events {
fmt.Println(logutil.EventString(event))
}

return nil
}

func commandReparentTablet(cmd *cobra.Command, args []string) error {
alias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(0))
if err != nil {
return err
}

resp, err := client.ReparentTablet(commandCtx, &vtctldatapb.ReparentTabletRequest{
Tablet: alias,
})
if err != nil {
return err
}

data, err := cli.MarshalJSON(resp)
if err != nil {
return err
}

fmt.Printf("%s\n", data)

return nil
}

func commandTabletExternallyReparented(cmd *cobra.Command, args []string) error {
alias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(0))
if err != nil {
return err
}

resp, err := client.TabletExternallyReparented(commandCtx, &vtctldatapb.TabletExternallyReparentedRequest{
Tablet: alias,
})
if err != nil {
return err
}

data, err := cli.MarshalJSON(resp)
if err != nil {
return err
}

fmt.Printf("%s\n", data)

return nil
}

func init() {
EmergencyReparentShard.Flags().DurationVar(&emergencyReparentShardOptions.WaitReplicasTimeout, "wait-replicas-timeout", *topo.RemoteOperationTimeout, "Time to wait for replicas to catch up in reparenting.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the old flags use underscore (_). Is it a cobra convention to use dash (-)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a GNU convention [1] (and therefore more common among most CLIs), so I've been doing it for all the vtctldclient options (except the global action_timeout, which I copied from vtctlclient and for some reason didn't change at the time). Happy to back this out in a separate change (since I'll have to update a bunch of flags) if Vitess prefers to enforce underscores everywhere.

[1]: Stack Overflow, which provides some additional reasoning as well as linking to the GNU style guide: https://stackoverflow.com/a/50537232

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see.. let us follow the convention.

EmergencyReparentShard.Flags().StringVar(&emergencyReparentShardOptions.NewPrimaryAliasStr, "new-primary", "", "Alias of a tablet that should be the new primary. If not specified, the vtctld will select the best candidate to promote.")
EmergencyReparentShard.Flags().StringSliceVarP(&emergencyReparentShardOptions.IgnoreReplicaAliasStrList, "ignore-replicas", "i", nil, "Comma-separated, repeated list of replica tablet aliases to ignore during the emergency reparent.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
EmergencyReparentShard.Flags().StringSliceVarP(&emergencyReparentShardOptions.IgnoreReplicaAliasStrList, "ignore-replicas", "i", nil, "Comma-separated, repeated list of replica tablet aliases to ignore during the emergency reparent.")
EmergencyReparentShard.Flags().StringSliceVarP(&emergencyReparentShardOptions.IgnoreReplicaAliasStrList, "ignore-replicas", "", nil, "Comma-separated, repeated list of replica tablet aliases to ignore during the emergency reparent.")

I assume this is a typo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why "Comma-separated, repeated list"? Why not just "Comma-separated list"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's repeated in the sense that you can specify the flag multiple times, so both -i "${alias1},${alias2}" and -i ${alias1} -i ${alias2} will work (as well as -i "${alias1},${alias2}" -i ${alias3}). I'm not sure of a concise way to word that, and what I have here is definitely not straightforward, so I'm very open to suggestions!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get it. Fine for right now, maybe we can revisit later.

Root.AddCommand(EmergencyReparentShard)

InitShardPrimary.Flags().DurationVar(&initShardPrimaryOptions.WaitReplicasTimeout, "wait-replicas-timeout", 30*time.Second, "time to wait for replicas to catch up in reparenting")
InitShardPrimary.Flags().BoolVar(&initShardPrimaryOptions.Force, "force", false, "will force the reparent even if the provided tablet is not a master or the shard master")
Root.AddCommand(InitShardPrimary)

PlannedReparentShard.Flags().DurationVar(&plannedReparentShardOptions.WaitReplicasTimeout, "wait-replicas-timeout", *topo.RemoteOperationTimeout, "Time to wait for replicas to catch up on replication both before and after reparenting.")
PlannedReparentShard.Flags().StringVar(&plannedReparentShardOptions.NewPrimaryAliasStr, "new-primary", "", "Alias of a tablet that should be the new primary.")
PlannedReparentShard.Flags().StringVar(&plannedReparentShardOptions.AvoidPrimaryAliasStr, "avoid-primary", "", "Alias of a tablet that should not be the primary; i.e. \"reparent to any other tablet if this one is the primary\".")
Root.AddCommand(PlannedReparentShard)

Root.AddCommand(ReparentTablet)
Root.AddCommand(TabletExternallyReparented)
}
Loading