-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Conversation
CC: @alexrs |
There was a problem hiding this 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. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
proto/vtctldata.proto
Outdated
@@ -414,6 +414,14 @@ message GetVSchemaResponse { | |||
vschema.Keyspace v_schema = 1; | |||
} | |||
|
|||
message ApplyVSchemaRequest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto on sorting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
proto/vtctlservice.proto
Outdated
@@ -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) {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto on sorting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
proto/vtctldata.proto
Outdated
@@ -414,6 +414,14 @@ message GetVSchemaResponse { | |||
vschema.Keyspace v_schema = 1; | |||
} | |||
|
|||
message ApplyVSchemaRequest { | |||
string keyspace = 1; | |||
vschema.Keyspace v_schema = 2; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
proto/vtctldata.proto
Outdated
vschema.Keyspace v_schema = 2; | ||
} | ||
|
||
message ApplyVSchemaResponse { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
still needs tests and cli entrypoint, but i wanted to get eyes on this again before i take it out of draft. |
There was a problem hiding this 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") { |
There was a problem hiding this comment.
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)
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 | ||
} |
There was a problem hiding this comment.
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
i found some more time to circle back on this. |
}{} | ||
|
||
func commandApplyVSchema(cmd *cobra.Command, args []string) error { | ||
keyspace := cmd.Flags().Arg(0) // validated on the server-side |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
74a6c7a
to
0c93793
Compare
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! |
0c93793
to
72e4d67
Compare
yep, another pull and rebase (and proto compile) seems to have gotten rid of the conflicts. thanks |
There was a problem hiding this 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
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 | ||
} | ||
} |
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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, ...)
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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{ |
There was a problem hiding this comment.
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.
|
with the shift of that logic, i've got to expand the testing a bit i think. |
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>
0a44f79
to
785c3b6
Compare
Signed-off-by: Alex Charis <alex.charis@shopify.com>
There was a problem hiding this 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" |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this 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") |
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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.") |
There was a problem hiding this comment.
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>
@ajm188 alright, tests passing. are we good to go? |
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? |
ApplyVSchema
to VtctldServer
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 |
thanks! |
Signed-off-by: Andrew Mason <amason@slack-corp.com>
@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