diff --git a/go/vt/vtctl/vtctl.go b/go/vt/vtctl/vtctl.go index 30b608f5f45..0224bfa480c 100644 --- a/go/vt/vtctl/vtctl.go +++ b/go/vt/vtctl/vtctl.go @@ -394,7 +394,7 @@ var commands = []commandGroup{ "[-exclude_tables=''] [-include-views] ", "Validates that the master schema matches all of the slaves."}, {"ValidateSchemaKeyspace", commandValidateSchemaKeyspace, - "[-exclude_tables=''] [-include-views] ", + "[-exclude_tables=''] [-include-views] [-skip-no-master] ", "Validates that the master schema from shard 0 matches the schema on all of the other tablets in the keyspace."}, {"ApplySchema", commandApplySchema, "[-allow_long_unavailability] [-wait_slave_timeout=10s] {-sql= || -sql-file=} ", @@ -2256,6 +2256,7 @@ func commandValidateSchemaShard(ctx context.Context, wr *wrangler.Wrangler, subF func commandValidateSchemaKeyspace(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { excludeTables := subFlags.String("exclude_tables", "", "Specifies a comma-separated list of tables to exclude. Each is either an exact match, or a regular expression of the form /regexp/") includeViews := subFlags.Bool("include-views", false, "Includes views in the validation") + skipNoMaster := subFlags.Bool("skip-no-master", false, "Skip shards that don't have master when performing validation") if err := subFlags.Parse(args); err != nil { return err } @@ -2268,7 +2269,7 @@ func commandValidateSchemaKeyspace(ctx context.Context, wr *wrangler.Wrangler, s if *excludeTables != "" { excludeTableArray = strings.Split(*excludeTables, ",") } - return wr.ValidateSchemaKeyspace(ctx, keyspace, excludeTableArray, *includeViews) + return wr.ValidateSchemaKeyspace(ctx, keyspace, excludeTableArray, *includeViews, *skipNoMaster) } func commandApplySchema(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { diff --git a/go/vt/vtctld/vtctld.go b/go/vt/vtctld/vtctld.go index f29967cfdbe..531cb4de544 100644 --- a/go/vt/vtctld/vtctld.go +++ b/go/vt/vtctld/vtctld.go @@ -59,7 +59,7 @@ func InitVtctld(ts *topo.Server) { actionRepo.RegisterKeyspaceAction("ValidateSchemaKeyspace", func(ctx context.Context, wr *wrangler.Wrangler, keyspace string, r *http.Request) (string, error) { - return "", wr.ValidateSchemaKeyspace(ctx, keyspace, nil, false) + return "", wr.ValidateSchemaKeyspace(ctx, keyspace, nil, false, false) }) actionRepo.RegisterKeyspaceAction("ValidateVersionKeyspace", diff --git a/go/vt/wrangler/schema.go b/go/vt/wrangler/schema.go index 9044da86bb6..4bb2c13e6c6 100644 --- a/go/vt/wrangler/schema.go +++ b/go/vt/wrangler/schema.go @@ -186,7 +186,7 @@ func (wr *Wrangler) ValidateSchemaShard(ctx context.Context, keyspace, shard str // ValidateSchemaKeyspace will diff the schema from all the tablets in // the keyspace. -func (wr *Wrangler) ValidateSchemaKeyspace(ctx context.Context, keyspace string, excludeTables []string, includeViews bool) error { +func (wr *Wrangler) ValidateSchemaKeyspace(ctx context.Context, keyspace string, excludeTables []string, includeViews, skipNoMaster bool) error { // find all the shards shards, err := wr.ts.GetShardNames(ctx, keyspace) if err != nil { @@ -202,42 +202,15 @@ func (wr *Wrangler) ValidateSchemaKeyspace(ctx context.Context, keyspace string, return wr.ValidateSchemaShard(ctx, keyspace, shards[0], excludeTables, includeViews) } - // find the reference schema using the first shard's master - si, err := wr.ts.GetShard(ctx, keyspace, shards[0]) - if err != nil { - return fmt.Errorf("GetShard(%v, %v) failed: %v", keyspace, shards[0], err) - } - if !si.HasMaster() { - return fmt.Errorf("no master in shard %v/%v", keyspace, shards[0]) - } - referenceAlias := si.MasterAlias - log.Infof("Gathering schema for reference master %v", topoproto.TabletAliasString(referenceAlias)) - referenceSchema, err := wr.GetSchema(ctx, referenceAlias, nil, excludeTables, includeViews) - if err != nil { - return fmt.Errorf("GetSchema(%v, nil, %v, %v) failed: %v", referenceAlias, excludeTables, includeViews, err) - } + var referenceSchema *tabletmanagerdatapb.SchemaDefinition + var referenceAlias *topodatapb.TabletAlias // then diff with all other tablets everywhere er := concurrency.AllErrorRecorder{} wg := sync.WaitGroup{} - // first diff the slaves in the reference shard 0 - aliases, err := wr.ts.FindAllTabletAliasesInShard(ctx, keyspace, shards[0]) - if err != nil { - return fmt.Errorf("FindAllTabletAliasesInShard(%v, %v) failed: %v", keyspace, shards[0], err) - } - - for _, alias := range aliases { - if topoproto.TabletAliasEqual(alias, si.MasterAlias) { - continue - } - - wg.Add(1) - go wr.diffSchema(ctx, referenceSchema, referenceAlias, alias, excludeTables, includeViews, &wg, &er) - } - // then diffs all tablets in the other shards - for _, shard := range shards[1:] { + for _, shard := range shards[0:] { si, err := wr.ts.GetShard(ctx, keyspace, shard) if err != nil { er.RecordError(fmt.Errorf("GetShard(%v, %v) failed: %v", keyspace, shard, err)) @@ -245,10 +218,21 @@ func (wr *Wrangler) ValidateSchemaKeyspace(ctx context.Context, keyspace string, } if !si.HasMaster() { - er.RecordError(fmt.Errorf("no master in shard %v/%v", keyspace, shard)) + if !skipNoMaster { + er.RecordError(fmt.Errorf("no master in shard %v/%v", keyspace, shard)) + } continue } + if referenceSchema == nil { + referenceAlias = si.MasterAlias + log.Infof("Gathering schema for reference master %v", topoproto.TabletAliasString(referenceAlias)) + referenceSchema, err = wr.GetSchema(ctx, referenceAlias, nil, excludeTables, includeViews) + if err != nil { + return fmt.Errorf("GetSchema(%v, nil, %v, %v) failed: %v", referenceAlias, excludeTables, includeViews, err) + } + } + aliases, err := wr.ts.FindAllTabletAliasesInShard(ctx, keyspace, shard) if err != nil { er.RecordError(fmt.Errorf("FindAllTabletAliasesInShard(%v, %v) failed: %v", keyspace, shard, err)) @@ -256,6 +240,10 @@ func (wr *Wrangler) ValidateSchemaKeyspace(ctx context.Context, keyspace string, } for _, alias := range aliases { + // Don't diff schemas for self + if referenceAlias == alias { + continue + } wg.Add(1) go wr.diffSchema(ctx, referenceSchema, referenceAlias, alias, excludeTables, includeViews, &wg, &er) }