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] Migrate topo management RPCs #7395

Merged
merged 30 commits into from
Feb 4, 2021

Conversation

ajm188
Copy link
Contributor

@ajm188 ajm188 commented Jan 27, 2021

Description

Preface

Okay this is a large PR because I couldn't figure out a good way to add RPCs to the proto file in parallel branches without conflicting with those same other branches; in the future (or I can take an afternoon to restructure commits on this one) I could just add a bunch of RPC definitions that panic when called in a single upfront PR and then implement each in a parallel branch. But anyway, onward!

The branch is structured in a series of paired commits, mostly. It goes essentially like:

commit - Add all RPC definitions
foreach rpc
    commit - implement server code and tests
    commit - add CLI entrypoint
end

Occasionally there's an extra commit or two when I needed to touch code outside of grpcvtctldserver and cmd/vtctldclient, but you can mostly view the changes one command at a time by looking at those pairs of commits.


This PR adds a slew of topo-management RPCs to the vtctld server, namely:

  • ChangeTabletType
  • CreateKeyspace
  • CreateShard
  • DeleteKeyspace
  • DeleteShards (in the old vtctl server, this was called DeleteShard but it could delete multiple shards)
  • DeleteTablets (same renaming as above)
  • GetShard
  • RemoveKeyspaceCell
  • RemoveShardCell

In addition, I made some refactors to other parts of vitess to facilitate this change, in particular:

  • Added a helper KeyspaceIDTypeString to package key, similar to topoproto.TabletAliasString but for the topodatapb.KeyspaceIdType enum.
  • Added another helper KeyspaceTypeString to package topoproto, same as above but for the topodatapb.KeyspaceType enum.
    • Note on above 2: I don't know why package key had helpers for some of the stuff in topodatapb, instead of it all being in topoproto, but all the KeyspaceIdType helpers were there, so that's why I added it to package key instead of package topoproto. Happy to just move it if folks feel otherwise.
  • Moved the private isMasterTablet from wrangler to topotools.IsPrimaryTablet so I could reuse the code in both.
  • Extracted an argument parser helper function from vtctl to package topo. I wanted to put this in topoproto, but because topo imports topoproto, and this function needs to import topo, this would have created a circular import.

See it in action

I exercised all the new commands against the local examples, and will include their output here:

ChangeTabletType
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-24T15:42:50Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
❯ vtctldclient --server "localhost:15999" ChangeTabletType zone1-0000000102 replica
- zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
+ zone1-0000000102 commerce 0 replica SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-24T15:42:50Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 replica SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
CreateKeyspace
❯ vtctldclient --server "localhost:15999" CreateKeyspace testkeyspace
Successfully created keyspace testkeyspace. Result:
{
 "name": "testkeyspace",
 "keyspace": {
   "sharding_column_name": "",
   "sharding_column_type": "UNSET",
   "served_froms": [
   ],
   "keyspace_type": "NORMAL",
   "base_keyspace": "",
   "snapshot_time": null
 }
}
❯ vtctldclient --server "localhost:15999" CreateKeyspace --type snapshot --base-keyspace testkeyspace --snapshot-timestamp "2020-12-31T01:02:03.00Z" testsnapshot
Successfully created keyspace testsnapshot. Result:
{
  "name": "testsnapshot",
  "keyspace": {
    "sharding_column_name": "",
    "sharding_column_type": "UNSET",
    "served_froms": [
    ],
    "keyspace_type": "SNAPSHOT",
    "base_keyspace": "testkeyspace",
    "snapshot_time": {
      "seconds": "1609376523",
      "nanoseconds": 0
    }
  }
}
❯ vtctldclient --server "localhost:15999" CreateKeyspace testkeyspace2 --served-from "master:testkeyspace,rdonly:commerce"
Successfully created keyspace testkeyspace2. Result:
{
  "name": "testkeyspace2",
  "keyspace": {
    "sharding_column_name": "",
    "sharding_column_type": "UNSET",
    "served_froms": [
      {
        "tablet_type": "MASTER",
        "cells": [
        ],
        "keyspace": "testkeyspace"
      },
      {
        "tablet_type": "RDONLY",
        "cells": [
        ],
        "keyspace": "commerce"
      }
    ],
    "keyspace_type": "NORMAL",
    "base_keyspace": "",
    "snapshot_time": null
  }
}
GetShard
❯ vtctldclient --server "localhost:15999" GetShard commerce/-
E0125 11:03:51.136664   14959 main.go:42] rpc error: code = Unknown desc = node doesn't exist: /vitess/global/keyspaces/commerce/shards/-/Shard
❯ vtctldclient --server "localhost:15999" GetShard commerce/0
{
  "keyspace": "commerce",
  "name": "0",
  "shard": {
    "master_alias": {
      "cell": "zone1",
      "uid": 100
    },
    "master_term_start_time": {
      "seconds": "1611590612",
      "nanoseconds": 941055000
    },
    "key_range": null,
    "served_types": [
    ],
    "source_shards": [
    ],
    "tablet_controls": [
    ],
    "is_master_serving": true
  }
}
RemoveShardCell
❯ ./101_initial_cluster.sh
❯ ./201_customer_tablets.sh
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-25T23:15:11Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
zone1-0000000200 customer 0 master SFO-M-AMASON02:15200 SFO-M-AMASON02:17200 [] 2021-01-25T23:21:39Z
zone1-0000000201 customer 0 replica SFO-M-AMASON02:15201 SFO-M-AMASON02:17201 [] <null>
zone1-0000000202 customer 0 rdonly SFO-M-AMASON02:15202 SFO-M-AMASON02:17202 [] <null>
❯ vtctlclient -server "localhost:15999" DeleteTablet -allow_master zone1-0000000200
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-25T23:15:11Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
zone1-0000000201 customer 0 replica SFO-M-AMASON02:15201 SFO-M-AMASON02:17201 [] <null>
zone1-0000000202 customer 0 rdonly SFO-M-AMASON02:15202 SFO-M-AMASON02:17202 [] <null>
❯ vtctldclient --server "localhost:15999" RemoveShardCell customer/0 zone1
E0125 18:22:29.837635   40277 main.go:42] rpc error: code = Unknown desc = cell zone1 has 2 possible tablets in replication graph
❯ vtctldclient --server "localhost:15999" RemoveShardCell customer/0 zone1 --recursive
Successfully removed cell zone1 from shard customer/0
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-25T23:15:11Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
RemoveKeyspaceCell
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-26T01:28:23Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
zone1-0000000200 customer 0 master SFO-M-AMASON02:15200 SFO-M-AMASON02:17200 [] 2021-01-26T01:29:14Z
zone1-0000000201 customer 0 replica SFO-M-AMASON02:15201 SFO-M-AMASON02:17201 [] <null>
zone1-0000000202 customer 0 rdonly SFO-M-AMASON02:15202 SFO-M-AMASON02:17202 [] <null>
❯ vtctlclient -server "localhost:15999" deletetablet -allow_master zone1-0000000200
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-26T01:28:23Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
zone1-0000000201 customer 0 replica SFO-M-AMASON02:15201 SFO-M-AMASON02:17201 [] <null>
zone1-0000000202 customer 0 rdonly SFO-M-AMASON02:15202 SFO-M-AMASON02:17202 [] <null>
❯ vtctldclient --server "localhost:15999" RemoveKeyspaceCell customer zone1 --recursive
Successfully removed keyspace customer from cell zone1
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-26T01:28:23Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
CreateShard
❯ vtctldclient --server "localhost:15999" CreateShard commerce/-40
{
  "keyspace": {
    "name": "commerce",
    "keyspace": {
      "sharding_column_name": "",
      "sharding_column_type": "UNSET",
      "served_froms": [
      ],
      "keyspace_type": "NORMAL",
      "base_keyspace": "",
      "snapshot_time": null
    }
  },
  "shard": {
    "keyspace": "commerce",
    "name": "-40",
    "shard": {
      "master_alias": null,
      "master_term_start_time": null,
      "key_range": {
        "start": null,
        "end": "QA=="
      },
      "served_types": [
      ],
      "source_shards": [
      ],
      "tablet_controls": [
      ],
      "is_master_serving": false
    }
  },
  "shard_already_exists": false
}
❯ vtctldclient --server "localhost:15999" CreateShard testkeyspace/- -p
{
  "keyspace": {
    "name": "testkeyspace",
    "keyspace": {
      "sharding_column_name": "",
      "sharding_column_type": "UNSET",
      "served_froms": [
      ],
      "keyspace_type": "NORMAL",
      "base_keyspace": "",
      "snapshot_time": null
    }
  },
  "shard": {
    "keyspace": "testkeyspace",
    "name": "-",
    "shard": {
      "master_alias": null,
      "master_term_start_time": null,
      "key_range": {
        "start": null,
        "end": null
      },
      "served_types": [
      ],
      "source_shards": [
      ],
      "tablet_controls": [
      ],
      "is_master_serving": true
    }
  },
  "shard_already_exists": false
}
❯ vtctldclient --server "localhost:15999" CreateShard testkeyspace/- -pf
{
  "keyspace": {
    "name": "testkeyspace",
    "keyspace": {
      "sharding_column_name": "",
      "sharding_column_type": "UNSET",
      "served_froms": [
      ],
      "keyspace_type": "NORMAL",
      "base_keyspace": "",
      "snapshot_time": null
    }
  },
  "shard": {
    "keyspace": "testkeyspace",
    "name": "-",
    "shard": {
      "master_alias": null,
      "master_term_start_time": null,
      "key_range": {
        "start": null,
        "end": null
      },
      "served_types": [
      ],
      "source_shards": [
      ],
      "tablet_controls": [
      ],
      "is_master_serving": true
    }
  },
  "shard_already_exists": true
}
DeleteTablets
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-26T14:54:12Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
❯ vtctldclient --server "localhost:15999" DeleteTablets zone1-0000000101 zone1-0000000102
Successfully deleted 2 tablets
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-26T14:54:12Z
❯ vtctldclient --server "localhost:15999" DeleteTablets zone1-0000000100
Error: rpc error: code = Unknown desc = cannot delete tablet zone1-0000000100 as it is a master, pass AllowPrimary = true: while deleting 1 tablets; please inspect the topo
❯ vtctldclient --server "localhost:15999" DeleteTablets zone1-0000000100 -p
Successfully deleted 1 tablets
❯ vtctldclient --server "localhost:15999" GetTablets
DeleteShards
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-27T03:14:46Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
❯ vtctldclient --server "localhost:15999" DeleteShards -rf commerce/0
Successfully deleted 1 shards
❯ vtctldclient --server "localhost:15999" GetTablets
DeleteKeyspace
❯ vtctldclient --server "localhost:15999" GetTablets
zone1-0000000100 commerce 0 master SFO-M-AMASON02:15100 SFO-M-AMASON02:17100 [] 2021-01-27T12:57:20Z
zone1-0000000101 commerce 0 replica SFO-M-AMASON02:15101 SFO-M-AMASON02:17101 [] <null>
zone1-0000000102 commerce 0 rdonly SFO-M-AMASON02:15102 SFO-M-AMASON02:17102 [] <null>
❯ vtctldclient --server "localhost:15999" GetKeyspaces
[
  {
    "name": "commerce",
    "keyspace": {}
  }
]
❯ vtctldclient --server "localhost:15999" DeleteKeyspace commerce
Error: DeleteKeyspace(commerce) error: rpc error: code = Unknown desc = keyspace commerce still has 1 shards; use Recursive=true or remove them manually; please check the topo
❯ vtctldclient --server "localhost:15999" DeleteKeyspace -r commerce
Successfully deleted keyspace commerce.
❯ vtctldclient --server "localhost:15999" GetKeyspaces
null
❯ vtctldclient --server "localhost:15999" GetTablets

Related Issue(s)

Checklist

  • Should this PR be backported? No
  • Tests were added or are not required oh boy were they ever
  • Documentation was added or is not required

Deployment Notes

Impacted Areas in Vitess

Components that this PR will affect:

  • Query Serving
  • VReplication
  • Cluster Management
  • Build/CI
  • VTAdmin

Signed-off-by: Andrew Mason <amason@slack-corp.com>
… add tests

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
…rser from vtctl

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
…veKeyspaceCell`

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Also some changes to `RemoveShardCell` tests

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
…om wrangler

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
…d home for it

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
@ajm188 ajm188 requested a review from sougou as a code owner January 27, 2021 13:33
Copy link
Contributor

@doeg doeg left a comment

Choose a reason for hiding this comment

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

I'm still reviewing this! Had a couple of preliminary questions to make sure I'm interpreting this vs. the docs correctly. :)

bool was_dry_run = 3;
}

message CreateKeyspaceRequest {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious -- CreateKeyspace docs don't mention a few of these params. Do you think this is usual doc drift or are there $reasons they aren't included? (Or am I looking at the wrong thing entirely?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, my broad answer on this is that these are similar to but distinctly not the vtctl commands, and that includes the CLI definitions (and, those docs don't actually list all the flags you can actually use in the current version). So exact matching is not a requirement, and I figured this was a good opportunity to improve on the existing CLI options rather than just carry them over.


// ServedFroms specifies a set of db_type:keyspace pairs used to serve
// traffic for the keyspace.
repeated topodata.Keyspace.ServedFrom served_froms = 6;
Copy link
Contributor

Choose a reason for hiding this comment

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

On served_froms specifically, the docs list served_from 🤔 are they supposed to be the same?

Copy link
Contributor

@setassociative setassociative Feb 2, 2021

Choose a reason for hiding this comment

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

I think I can take this one -- the existing docs list the arg as -served_from=tablettype1:ks1,tablettype2:ks2,...

but served_from takes a list and gets mapped into ServedFroms for the actual call if more than one is provided (code ref):

        if len(servedFrom) > 0 {
                for name, value := range servedFrom {
                        tt, err := parseServingTabletType3(name)
                        if err != nil {
                                return err
                        }
                        ki.ServedFroms = append(ki.ServedFroms, &topodatapb.Keyspace_ServedFrom{
                                TabletType: tt,
                                Keyspace:   value,
                        })
                }
        }

This is formalizing that CLI behavior into a request object.

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Copy link
Contributor

@doeg doeg left a comment

Choose a reason for hiding this comment

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

Ok I read this whole thing and, first of all: this is amazing. An immense amount of work + as usual you are so very thorough.

Standard caveat I am but a golang/Vitess novice, so definitely get a review from a Vitess-knower! (I'm going to comment instead of approve for that reason.) :)

From my perspective though, this is all very approachable and easy to follow. Your comments + tests are 🏆 so good. 🏆 It is a pleasure to learn from you. 😎

func commandCreateKeyspace(cmd *cobra.Command, args []string) error {
name := cmd.Flags().Arg(0)

switch topodatapb.KeyspaceType(createKeyspaceOptions.KeyspaceType) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Babby golang question -- is this switch construction equivalent to:

kst := topodatapb.KeyspaceType(createKeyspaceOptions.KeyspaceType)
if (kst != topodatapb.KeyspaceType_NORMAL || kst != topodatapb.KeyspaceType_SNAPSHOT) {
  return errors.New("invalid keyspace type passed to --type")
}

I like it! It's nice that it avoids the need for a kst variable (as above). If there are other advantages over an if, I'm curious to know them!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, those are exactly equivalent! I think also you could remove the kst variable and call the function both times, and the compiler should (🤞) be smart enough to simplify the expression and store the result of the first call.

If there are other advantages over an if, I'm curious to know them!

I think it's mainly a style convention! I've come to find it more readable, and I think it's generally preferred in Go to write switch statements over long if-else-if-else chains, which is hinted at, but not explicitly stated in effective go.

@@ -103,6 +103,44 @@ func CheckOwnership(oldTablet, newTablet *topodatapb.Tablet) error {
return nil
}

// IsPrimaryTablet is a helper function to determine whether the current tablet
Copy link
Contributor

Choose a reason for hiding this comment

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

These comments are so good ❤️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! 😊

@@ -185,7 +195,7 @@ func KeyRangeEqual(left, right *topodatapb.KeyRange) bool {
bytes.Equal(left.End, right.End)
}

// KeyRangeStartEqual returns true if right's keyrange start is _after_ left's start
// KeyRangeStartSmaller returns true if right's keyrange start is _after_ left's start
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch :3

Copy link
Contributor Author

Choose a reason for hiding this comment

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

githook linter caught it for me 😀

…tation

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Copy link
Contributor

@setassociative setassociative left a comment

Choose a reason for hiding this comment

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

I want to sit down with a real monitor and A/B the new implementations from the ones they're based on but I don't expect anything surprising. I had a few nit picks but nothing that is actually concerning or suggests major changes, I think.


t, err := time.Parse(time.RFC3339, createKeyspaceOptions.SnapshotTimestamp)
if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

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

TIOLI: indicate which flag failed to parse time here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

func commandDeleteKeyspace(cmd *cobra.Command, args []string) error {
ks := cmd.Flags().Arg(0)

_, err := client.DeleteKeyspace(commandCtx, &vtctldatapb.DeleteKeyspaceRequest{
Copy link
Contributor

Choose a reason for hiding this comment

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

general pedantry: for these cases where an empty result is returned I can't decide if it makes sense to go ahead and print the empty result so that we don't have to remember to update the client (which we will forget) if they have some payload in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a great point. I started down the following compromise:

resp, err := client.DeleteKeyspace(...)
if err != nil {
    return fmt.Errorf("...", ks, err)
}

defer func() { fmt.Fprintf(os.Stderr, "Successfully deleted keyspace %v.\n", ks }()

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

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

return nil

Which produces:

❯ vtctldclient --server "localhost:15999" DeleteKeyspace -r commerce
{

}
Successfully deleted keyspace commerce.

And I kinda wish I got back bytes that let me do something like if len(data) > 0 { fmt.Printf("%s\n", data) } but I'm also not sure how to do that without losing the pretty printing (and also the curly braces for the empty-but-non-null object).

What we could do now that I'm thinking this through is expand cli.MarshalJSON's proto.Message case to protoreflect on the message and return an empty slice if there are no real fields in the message. However. That only works if we're using google.golang.org/protobuf/proto, which provides a protoreflect package that works with the proto.Message type defined in that module, but does not work with the types in github.com/golang/protobuf/proto, because the former is technically a v2 of the latter and they didn't keep backwards compatibility on this one.

So I think either of these has to be Good Enough, but I'm also curious if you have other ideas after reading all this:

  1. The "Really Hope We Don't Forget" approach. We could also add something to the PR template, but that feels a bit heavy-handed since it will not apply to something like 99% of PRs.
  2. Accept that these commands will print an empty object over 3 lines ([]byte("{\n\n}"), plus the newline from fmt.Printf("%s\n", data))

// the true primary. This can occur if someone issues a DeleteTablet while
// the system is in transition (a reparenting event is in progress and parts of
// the topo have not yet been updated).
func IsPrimaryTablet(ctx context.Context, ts *topo.Server, ti *topo.TabletInfo) (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should confirm that the method here (walking MasterTermStartTime) is a universally correct way to make this check. How is MTST set / how is it coordinated with the MTST of the shard. When doing PRS/ERS/TER topo changes how are those values updated?

As written i don't see this being problematic since it's only called from deleteTablet but I could imagine it eventually being a private method here for deleteTablet if there are concerns about exposing it more widely. @deepthi is likel the authoritative wourd here.

But, tbh, I think it's probably fine as is and a useful function to have around!

Copy link
Contributor Author

@ajm188 ajm188 Feb 2, 2021

Choose a reason for hiding this comment

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

This is an extraction of isMasterTablet, which now calls this

Copy link
Member

@deepthi deepthi Feb 2, 2021

Choose a reason for hiding this comment

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

@setassociative we went through those scenarios while writing the original version, so it should be fine.
I think the concern about public vs private function is valid, but that can be addressed in the PR that replaces the legacy command implementations with calls to VtctldServer().Foo().

return nil, err
}

changedTablet, _ := s.ts.GetTablet(ctx, req.TabletAlias)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd at least catch the error and log ... something? getting a Nil back for after tablet on success is a bit weird and we'll want some kind trail if we hit it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

}

// CreateKeyspace is part of the vtctlservicepb.VtctldServer interface.
func (s *VtctldServer) CreateKeyspace(ctx context.Context, req *vtctldatapb.CreateKeyspaceRequest) (*vtctldatapb.CreateKeyspaceResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I understand these implementations are intended as a leg over from having the implementation tied up in vtctl/vtctl.go::commandCreateKeyspace etc but it feels odd that many of these implementations are so heavily duplicated and could result in drift. How do we plan on preventing that (rapid deprecation+removal of legacy impl is a totally valid answer).

Even so one thing I'd like us to consider is how we might want to detach the command functionality from the transport? E.g. what does it look like if we also wanted a REST API or something that kicked off create keyspace etc. (Viable answer here is : we only ever care about grpc but if that changes the req/resp objects provide a clear way to introduce a layer of indirection so we don't have to duplicate code again).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but it feels odd that many of these implementations are so heavily duplicated and could result in drift. How do we plan on preventing that (rapid deprecation+removal of legacy impl is a totally valid answer).

Totally. I've stuck a VtctldServer inside of the wrangler, which powers the legacy implementation. My plan here is to make another pass after this PR and replace all the logic with a call to wr.VtctldServer().CreateKeyspace(...) and so on, and then munge those responses back into the legacy output format.

I didn't do this in this PR mainly because this branch was already gigantic, and it'll let me just focus on making sure I don't accidentally change the legacy output, which I've already done once.

one thing I'd like us to consider is how we might want to detach the command functionality from the transport?

This one is really interesting. My answer for the legacy CLI is still to deprecate+remove. But, more abstractly about the REST case:

I'm not sure how we can guarantee client/server type safety without adding something like jsonschema to the mix, but ignoring that, I would probably use the grpc api as the de facto standard, and add adapters for REST/whatever, so we would:

  1. Add a global flag to vtctldclient that says which transport to use. Add any extra flags needed to set up those transports (like the grpc_static_auth_creds flag for the grpc transport)
  2. Create package httpvtctldclient in the same style as package grpcvtctldclient. This is where we'll translate the proto message objects into an HTTP request, and then the HTTP response back into a proto message. Similar for any other transports we want to support.
  3. In Root.PersistentPreRunE, instantiate the correct client (grpcvtctldclient.New(...), httpvtctldclient.New(...), etc), everything about the individual command implementations is unchanged.

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Signed-off-by: Andrew Mason <amason@slack-corp.com>
Copy link
Contributor

@rohit-nayak-ps rohit-nayak-ps left a comment

Choose a reason for hiding this comment

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

lgtm
a couple of trivial nits. will merge once addressed

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

// StringMapValue augements flagutil.StringMapValue so it can be used as a
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: should be augments

Copy link
Contributor Author

Choose a reason for hiding this comment

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


// KeyspaceIDTypeFlag adds the pflag.Value interface to a
// topodatapb.KeyspaceIdType.
type KeyspaceIDTypeFlag topodatapb.KeyspaceIdType
Copy link
Contributor

Choose a reason for hiding this comment

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

where is this used? didn't see a reference in this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -0,0 +1,43 @@
/*
Copyright 20201 The Vitess Authors.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: 20201=>2021

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Copy link
Contributor

@rohit-nayak-ps rohit-nayak-ps left a comment

Choose a reason for hiding this comment

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

lgtm

@rohit-nayak-ps rohit-nayak-ps merged commit f03ce84 into vitessio:master Feb 4, 2021
@askdba askdba added this to the v10.0 milestone Feb 8, 2021
ajm188 pushed a commit to tinyspeck/vitess that referenced this pull request Feb 11, 2021
@ajm188 ajm188 deleted the am_vtctld_manage_topo branch March 4, 2021 16:32
setassociative pushed a commit to tinyspeck/vitess that referenced this pull request Mar 11, 2021
[vtctld] Migrate topo management RPCs

Signed-off-by: Richard Bailey <rbailey@slack-corp.com>
ajm188 pushed a commit to tinyspeck/vitess that referenced this pull request Apr 6, 2021
[vtctld] Migrate topo management RPCs

Signed-off-by: Andrew Mason <amason@slack-corp.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate topo management commands to VtctldServer
6 participants