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 ApplyVSchema to VtctldServer #8113

Merged
merged 11 commits into from
Jun 7, 2021

Conversation

ghost
Copy link

@ghost ghost commented May 12, 2021

@ajm188 am i on the right track here? if so, i'll add tests.
context: https://vitess.slack.com/archives/C0PQY0PTK/p1620754861183700

The goal here is to add the ApplyVSchema api call to the grpc-vtctld service.

Signed-off-by: Alex Charis alex.charis@shopify.com

Checklist

  • Tests were added or are not required
  • Documentation was added or is not required

@ghost
Copy link
Author

ghost commented May 12, 2021

CC: @alexrs

Copy link
Contributor

@ajm188 ajm188 left a comment

Choose a reason for hiding this comment

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

This is definitely on the right track. In addition to the testing (there are many examples of how to use the helpers in server_test.go), you'll also need to add a cli entrypoint, and I think that's where we should include the sql-mode/json-mode behavior of the original vtctl command

@@ -793,6 +793,15 @@ func (s *VtctldServer) GetVSchema(ctx context.Context, req *vtctldatapb.GetVSche
}, nil
}

// ApplyVSchema is part of the vtctlservicepb.VtctldServer interface.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: please try to keep this file sorted. it makes scanning it much easier as it grows ever larger

Copy link
Author

Choose a reason for hiding this comment

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

done

@@ -414,6 +414,14 @@ message GetVSchemaResponse {
vschema.Keyspace v_schema = 1;
}

message ApplyVSchemaRequest {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto on sorting

Copy link
Author

Choose a reason for hiding this comment

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

done

@@ -91,6 +91,8 @@ service Vtctld {
rpc GetTablets(vtctldata.GetTabletsRequest) returns (vtctldata.GetTabletsResponse) {};
// GetVSchema returns the vschema for a keyspace.
rpc GetVSchema(vtctldata.GetVSchemaRequest) returns (vtctldata.GetVSchemaResponse) {};
// ApplyVSchema applies a vschema to a keyspace.
rpc ApplyVSchema(vtctldata.ApplyVSchemaRequest) returns (vtctldata.ApplyVSchemaResponse) {};
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto on sorting

Copy link
Author

Choose a reason for hiding this comment

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

done

@@ -414,6 +414,14 @@ message GetVSchemaResponse {
vschema.Keyspace v_schema = 1;
}

message ApplyVSchemaRequest {
string keyspace = 1;
vschema.Keyspace v_schema = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

This should include a skip_rebuild option to match the original implementation

Copy link
Author

Choose a reason for hiding this comment

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

done

@@ -793,6 +793,15 @@ func (s *VtctldServer) GetVSchema(ctx context.Context, req *vtctldatapb.GetVSche
}, nil
}

// ApplyVSchema is part of the vtctlservicepb.VtctldServer interface.
func (s *VtctldServer) ApplyVSchema(ctx context.Context, req *vtctldatapb.ApplyVSchemaRequest) (*vtctldatapb.ApplyVSchemaResponse, error) {
if err := s.ts.SaveVSchema(ctx, req.Keyspace, req.VSchema); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

looks like you're missing the "keyspace existence" check

Copy link
Author

Choose a reason for hiding this comment

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

done

vschema.Keyspace v_schema = 2;
}

message ApplyVSchemaResponse {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if it will be useful to include the final, saved vschema here (after reading it back out of the topo, similar to how ChangeTabletType works

Copy link
Author

Choose a reason for hiding this comment

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

and similar to how the current vtctlclient command works

Copy link
Author

Choose a reason for hiding this comment

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

done

@ghost
Copy link
Author

ghost commented May 14, 2021

still needs tests and cli entrypoint, but i wanted to get eyes on this again before i take it out of draft.
and i wanted to see if the failing check here in github was a flake or if it failed again.

Copy link
Contributor

@ajm188 ajm188 left a comment

Choose a reason for hiding this comment

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

This is looking great, just some minor feedback that I noticed now, but I think you're good to take it out of draft and work on testing/cli

// ApplyVSchema is part of the vtctlservicepb.VtctldServer interface.
func (s *VtctldServer) ApplyVSchema(ctx context.Context, req *vtctldatapb.ApplyVSchemaRequest) (*vtctldatapb.ApplyVSchemaResponse, error) {
if _, err := s.ts.GetKeyspace(ctx, req.Keyspace); err != nil {
if strings.Contains(err.Error(), "node doesn't exist") {
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be if topo.IsErrType(err, topo.NoNode)

Comment on lines 93 to 187
if !req.SkipRebuild {
if err := s.ts.RebuildSrvVSchema(ctx, nil); err != nil {
return nil, err
}
}
updatedVS, err := s.ts.GetVSchema(ctx, req.Keyspace)
if err != nil {
return nil, err
}
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 wrap these errors to give some extra context of which step failed

@ghost
Copy link
Author

ghost commented Jun 1, 2021

i found some more time to circle back on this.
it still needs tests, but i got the CLI done.
i'll rebase to fix the proto conflicts.

}{}

func commandApplyVSchema(cmd *cobra.Command, args []string) error {
keyspace := cmd.Flags().Arg(0) // validated on the server-side
Copy link
Author

Choose a reason for hiding this comment

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

happy to validate here as well. wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

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

imo we should validate as much server-side as possible, that way the implementation can be shared between vtctldserver and the legacy vtctl commands

@ghost ghost force-pushed the vtctld-applyvschema branch from 74a6c7a to 0c93793 Compare June 1, 2021 18:13
@ghost ghost marked this pull request as ready for review June 1, 2021 18:15
@ghost ghost requested a review from doeg as a code owner June 1, 2021 18:15
@ghost
Copy link
Author

ghost commented Jun 1, 2021

not sure why these are being marked as conflicting, i just rebased and recompiled protos. i'll take another look.

@ajm188
Copy link
Contributor

ajm188 commented Jun 2, 2021

not sure why these are being marked as conflicting, i just rebased and recompiled protos. i'll take another look.

Unfortunately I think I had just merged another vtctld rpc PR, so I am the cause of your merge conflicts this time 😞 sorry about that!

@ghost ghost force-pushed the vtctld-applyvschema branch from 0c93793 to 72e4d67 Compare June 2, 2021 14:38
@ghost
Copy link
Author

ghost commented Jun 2, 2021

yep, another pull and rebase (and proto compile) seems to have gotten rid of the conflicts. thanks

Copy link
Contributor

@ajm188 ajm188 left a comment

Choose a reason for hiding this comment

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

This is looking great. I think we just need to move that sqlparsing/topotools logic from the client to the server and we're good to go. Really sorry that didn't click for me earlier

Comment on lines 77 to 106
if sqlMode {
if applyVSchemaOptions.SQLFile != "" {
sqlBytes, err := ioutil.ReadFile(applyVSchemaOptions.SQLFile)
if err != nil {
return err
}
applyVSchemaOptions.SQL = string(sqlBytes)
}

stmt, err := sqlparser.Parse(applyVSchemaOptions.SQL)
if err != nil {
return fmt.Errorf("error parsing VSchema statement `%s`: %v", applyVSchemaOptions.SQL, err)
}
ddl, ok := stmt.(*sqlparser.AlterVschema)
if !ok {
return fmt.Errorf("error parsing VSchema statement `%s`: not a ddl statement", applyVSchemaOptions.SQL)
}

resp, err := client.GetVSchema(commandCtx, &vtctldatapb.GetVSchemaRequest{
Keyspace: keyspace,
})
if err != nil && !topo.IsErrType(err, topo.NoNode) {
return err
} // otherwise, we keep the empty vschema object from above

vs, err = topotools.ApplyVSchemaDDL(keyspace, resp.VSchema, ddl)
if err != nil {
return err
}

} else { // jsonMode
var schema []byte
if applyVSchemaOptions.VSchemaFile != "" {
schema, err = ioutil.ReadFile(applyVSchemaOptions.VSchemaFile)
if err != nil {
return err
}
} else {
schema = []byte(applyVSchemaOptions.VSchema)
}

vs = &vschemapb.Keyspace{}
err = json2.Unmarshal(schema, vs)
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.

Apologies for not catching this during my first pass, but I think this entire chunk of logic needs to move to the server, as the only way to access this logic is by invoking the client binary (in general my view is that the client commands should do the bare minimum to convert CLI args into a proto message and then invoke the corresponding RPC).

So, I think what we want is:

message ApplyVSchemaRequest {
  // [ other fields elided ]
  vschema.Keyspace vschema = N; // this is the result of `--json` mode on the client, and we apply the vschema blindly
  string sql = N+1; // this is --sql mode, and we do the parsing + topotools call on the server side
}

Copy link
Author

Choose a reason for hiding this comment

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

interesting, i can definitely imagine someone calling the new api with only an sql string rather than a vschema object. so i can see where you're coming from.
i think i liked the idea of keeping the flag validation on the client side up above the call to FinishedParsing.
(though generally i do agree with what you say below about doing as much validation on the server side as possible.)

i do need to keep the file reading parts on the client side though, obviously. so it's not going to be a complete cut+paste job. but i'm definitely happy to shift the bulk of the logic up to the server side.

func (s *VtctldServer) ApplyVSchema(ctx context.Context, req *vtctldatapb.ApplyVSchemaRequest) (*vtctldatapb.ApplyVSchemaResponse, error) {
if _, err := s.ts.GetKeyspace(ctx, req.Keyspace); err != nil {
if topo.IsErrType(err, topo.NoNode) {
return nil, fmt.Errorf("keyspace(%s) doesn't exist, check if the keyspace is initialized", req.Keyspace)
Copy link
Contributor

Choose a reason for hiding this comment

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

I would make this an vterrors.Wrapf(vtrpc.FAILED_PRECONDITION, ...)

Copy link
Author

Choose a reason for hiding this comment

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

not totally sure what you meant here.
Wrap/Wrapf don't take an error code like failed precondition.
New and Errorf do take a code, but those are for making a new error, whereas here i already have an error.

}
updatedVS, err := s.ts.GetVSchema(ctx, req.Keyspace)
if err != nil {
return nil, fmt.Errorf("GetKeyspace(%s) = %w", req.Keyspace, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: wrong error string (probably copy-paste from above?)

}{}

func commandApplyVSchema(cmd *cobra.Command, args []string) error {
keyspace := cmd.Flags().Arg(0) // validated on the server-side
Copy link
Contributor

Choose a reason for hiding this comment

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

imo we should validate as much server-side as possible, that way the implementation can be shared between vtctldserver and the legacy vtctl commands

@@ -33,8 +39,117 @@ var (
Args: cobra.ExactArgs(1),
RunE: commandGetVSchema,
}
// ApplyVSchema makes an ApplyVSchema gRPC call to a vtctld.
ApplyVSchema = &cobra.Command{
Copy link
Contributor

Choose a reason for hiding this comment

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

A net-new thing (that you could not have known!) I'm trying to introduce is:

  • Set DisableFlagsInUseLine: true on every command. This will change the help from {{ .Use }} [flags] to just {{ .Use }} (which is better, because you include the actual named flags in the Use string).
  • Include a .Short help text, which in most cases I just copy from the old vtctl command.

@ghost
Copy link
Author

ghost commented Jun 3, 2021

(in general my view is that the client commands should do the bare minimum to convert CLI args into a proto message and then invoke the corresponding RPC).

do you want the dry run logic on the server side as well, then?
turns out that's the only way to do it

@ghost
Copy link
Author

ghost commented Jun 3, 2021

with the shift of that logic, i've got to expand the testing a bit i think.

Alex Charis added 9 commits June 4, 2021 13:22
Signed-off-by: Alex Charis <alex.charis@shopify.com>
keyspace existence check, and return updated vschema, handle skip_rebuild

Signed-off-by: Alex Charis <alex.charis@shopify.com>
Signed-off-by: Alex Charis <alex.charis@shopify.com>
Signed-off-by: Alex Charis <alex.charis@shopify.com>
Signed-off-by: Alex Charis <alex.charis@shopify.com>
Signed-off-by: Alex Charis <alex.charis@shopify.com>
Signed-off-by: Alex Charis <alex.charis@shopify.com>
Signed-off-by: Alex Charis <alex.charis@shopify.com>
Signed-off-by: Alex Charis <alex.charis@shopify.com>
@ghost ghost force-pushed the vtctld-applyvschema branch from 0a44f79 to 785c3b6 Compare June 4, 2021 15:50
@ghost ghost added the Type: Enhancement Logical improvement (somewhere between a bug and feature) label Jun 4, 2021
@ghost ghost requested a review from ajm188 June 4, 2021 16:28
Signed-off-by: Alex Charis <alex.charis@shopify.com>
Copy link
Contributor

@ajm188 ajm188 left a comment

Choose a reason for hiding this comment

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

some minor changes, then lgtm! thanks for doing this!

vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice"
"vitess.io/vitess/go/vt/proto/vttime"
vttime "vitess.io/vitess/go/vt/proto/vttime"
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't need an alias

@@ -1132,7 +1287,7 @@ func TestDeleteCellsAlias(t *testing.T) {
name string
ts *topo.Server
setup func(ts *topo.Server) error
req *vtctldata.DeleteCellsAliasRequest
req *vtctldatapb.DeleteCellsAliasRequest
Copy link
Contributor

Choose a reason for hiding this comment

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

thanks for catching this one, i must have missed this in another change

Copy link
Contributor

@ajm188 ajm188 left a comment

Choose a reason for hiding this comment

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

whoops, meant to include these comments in my last review

@@ -58,5 +142,14 @@ func commandGetVSchema(cmd *cobra.Command, args []string) error {
}

func init() {
ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.VSchema, "vschema", "", "VSchema")
ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.VSchemaFile, "vschema_file", "", "VSchema File")
Copy link
Contributor

Choose a reason for hiding this comment

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

we're using dashes instead of underscores in the new client

ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.VSchema, "vschema", "", "VSchema")
ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.VSchemaFile, "vschema_file", "", "VSchema File")
ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.SQL, "sql", "", "A VSchema DDL SQL statement, e.g. `alter table t add vindex hash(id)`")
ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.SQLFile, "sql_file", "", "A file containing VSchema DDL SQL")
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.SQL, "sql", "", "A VSchema DDL SQL statement, e.g. `alter table t add vindex hash(id)`")
ApplyVSchema.Flags().StringVar(&applyVSchemaOptions.SQLFile, "sql_file", "", "A file containing VSchema DDL SQL")
ApplyVSchema.Flags().BoolVar(&applyVSchemaOptions.DryRun, "dry-run", false, "If set, do not save the altered vschema, simply echo to console.")
ApplyVSchema.Flags().BoolVar(&applyVSchemaOptions.SkipRebuild, "skip_rebuild", false, "If set, do no rebuild the SrvSchema objects.")
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Signed-off-by: Alex Charis <alex.charis@shopify.com>
@ghost
Copy link
Author

ghost commented Jun 7, 2021

@ajm188 alright, tests passing. are we good to go?

@ajm188 ajm188 merged commit 7dfbc60 into vitessio:main Jun 7, 2021
@ghost ghost deleted the vtctld-applyvschema branch June 7, 2021 18:43
@systay
Copy link
Collaborator

systay commented Jun 8, 2021

Since we are creating the release notes automatically from PRs, I was wondering if we could tweak the title a bit? Should the WIP prefix still be there?

@ajm188 ajm188 changed the title WIP: implement ApplyVSchema in grpc-vtctld [vtctld] Migrate ApplyVSchema to VtctldServer Jun 8, 2021
@ajm188 ajm188 changed the title [vtctld] Migrate ApplyVSchema to VtctldServer [vtctld] Migrate ApplyVSchema to VtctldServer Jun 8, 2021
@ajm188
Copy link
Contributor

ajm188 commented Jun 8, 2021

Nope! I updated it just now, let me know if that's good for the release notes tooling or if I should tweak it differently

@ghost
Copy link
Author

ghost commented Jun 8, 2021

thanks!

ajm188 added a commit to tinyspeck/vitess that referenced this pull request Jul 23, 2021
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
Labels
Component: Cluster management Type: Enhancement Logical improvement (somewhere between a bug and feature)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants