diff --git a/go/cmd/vtctldclient/command/vreplication/common/utils.go b/go/cmd/vtctldclient/command/vreplication/common/utils.go index a65cf579901..431b1f4e14a 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/utils.go +++ b/go/cmd/vtctldclient/command/vreplication/common/utils.go @@ -287,7 +287,7 @@ func AddCommonSwitchTrafficFlags(cmd *cobra.Command, initializeTargetSequences b cmd.Flags().BoolVar(&SwitchTrafficOptions.DryRun, "dry-run", false, "Print the actions that would be taken and report any known errors that would have occurred.") cmd.Flags().BoolVar(&SwitchTrafficOptions.Force, "force", false, "Force the traffic switch even if some potentially non-critical actions cannot be performed; for example the tablet refresh fails on some tablets in the keyspace. WARNING: this should be used with extreme caution and only in emergency situations!") if initializeTargetSequences { - cmd.Flags().BoolVar(&SwitchTrafficOptions.InitializeTargetSequences, "initialize-target-sequences", false, "When moving tables from an unsharded keyspace to a sharded keyspace, initialize any sequences that are being used on the target when switching writes. If the sequence table is not found AND a value was specified for --global-keyspace, then we will attempt to create the sequence tables in that keyspace if they are not found elsewhere.") + cmd.Flags().BoolVar(&SwitchTrafficOptions.InitializeTargetSequences, "initialize-target-sequences", false, "When moving tables from an unsharded keyspace to a sharded keyspace, initialize any sequences that are being used on the target when switching writes. If the sequence table is not found AND the sequence table reference was fully qualified OR a value was specified for --global-keyspace, then we will attempt to create each sequence table in that keyspace if it doesn't exist.") } } diff --git a/go/cmd/vtctldclient/command/vreplication/movetables/create.go b/go/cmd/vtctldclient/command/vreplication/movetables/create.go index 700d36255c6..c3d1087dbf3 100644 --- a/go/cmd/vtctldclient/command/vreplication/movetables/create.go +++ b/go/cmd/vtctldclient/command/vreplication/movetables/create.go @@ -96,7 +96,7 @@ var ( } createOptions.WorkflowOptions.StripShardedAutoIncrement = vtctldatapb.ShardedAutoIncrementHandling(val) if val == int32(vtctldatapb.ShardedAutoIncrementHandling_REPLACE) && createOptions.WorkflowOptions.GlobalKeyspace == "" { - fmt.Println("WARNING: no global-keyspace value provided so all sequence tables must be created manually before switching traffic") + fmt.Println("WARNING: no global-keyspace value provided so all sequence table references not fully qualified must be created manually before switching traffic") } return nil diff --git a/go/vt/vtctl/workflow/traffic_switcher.go b/go/vt/vtctl/workflow/traffic_switcher.go index dec5d0fcd15..723c7255335 100644 --- a/go/vt/vtctl/workflow/traffic_switcher.go +++ b/go/vt/vtctl/workflow/traffic_switcher.go @@ -30,6 +30,7 @@ import ( "golang.org/x/sync/errgroup" "vitess.io/vitess/go/json2" + "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -76,9 +77,10 @@ const ( // Use pt-osc's naming convention, this format also ensures vstreamer ignores such tables. renameTableTemplate = "_%.59s_old" // limit table name to 64 characters - sqlDeleteWorkflow = "delete from _vt.vreplication where db_name = %s and workflow = %s" - sqlGetMaxSequenceVal = "select max(%a) as maxval from %a.%a" - sqlInitSequenceTable = "insert into %a.%a (id, next_id, cache) values (0, %d, 1000) on duplicate key update next_id = if(next_id < %d, %d, next_id)" + sqlDeleteWorkflow = "delete from _vt.vreplication where db_name = %s and workflow = %s" + sqlGetMaxSequenceVal = "select max(%a) as maxval from %a.%a" + sqlInitSequenceTable = "insert into %a.%a (id, next_id, cache) values (0, %d, 1000) on duplicate key update next_id = if(next_id < %d, %d, next_id)" + sqlCreateSequenceTable = "create table if not exists %a (id int, next_id bigint, cache bigint, primary key(id)) comment 'vitess_sequence'" ) // accessType specifies the type of access for a shard (allow/disallow writes). @@ -1525,8 +1527,10 @@ func (ts *trafficSwitcher) getTargetSequenceMetadata(ctx context.Context) (map[s if err != nil { return nil, err } - stmt := fmt.Sprintf("create table if not exists %s (id int, next_id bigint, cache bigint, primary key(id)) comment 'vitess_sequence'", - tableName) + stmt, err := sqlparser.ParseAndBind(sqlCreateSequenceTable, sqltypes.StringBindVariable(sqlescape.EscapeID(tableName))) + if err != nil { + return nil, err + } _, err = ts.ws.tmc.ApplySchema(ctx, primary.Tablet, &tmutils.SchemaChange{ SQL: stmt, Force: false, @@ -1770,13 +1774,32 @@ func (ts *trafficSwitcher) initializeTargetSequences(ctx context.Context, sequen ) // Now execute this on the primary tablet of the unsharded keyspace // housing the backing table. + initialize: qr, ierr := ts.ws.tmc.ExecuteFetchAsApp(ictx, sequenceTablet.Tablet, true, &tabletmanagerdatapb.ExecuteFetchAsAppRequest{ Query: []byte(query.Query), MaxRows: 1, }) if ierr != nil { - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to initialize the backing sequence table %s.%s: %v", + vterr := vterrors.Errorf(vtrpcpb.Code_INTERNAL, "failed to initialize the backing sequence table %s.%s: %v", sequenceMetadata.backingTableDBName, sequenceMetadata.backingTableName, ierr) + // If the sequence table doesn't exist, let's try and create it, otherwise + // return the error. + if sqlErr, ok := sqlerror.NewSQLErrorFromError(ierr).(*sqlerror.SQLError); !ok || + (sqlErr.Num != sqlerror.ERNoSuchTable && sqlErr.Num != sqlerror.ERBadTable) { + return vterr + } + stmt := sqlparser.BuildParsedQuery(sqlCreateSequenceTable, backingTable) + _, ierr = ts.ws.tmc.ApplySchema(ctx, sequenceTablet.Tablet, &tmutils.SchemaChange{ + SQL: stmt.Query, + Force: false, + AllowReplication: true, + SQLMode: vreplication.SQLMode, + DisableForeignKeyChecks: true, + }) + if ierr != nil { + return vterrors.Wrapf(vterr, "could not create missing sequence table: %v", err) + } + goto initialize } // If we actually updated the backing sequence table, then we need // to tell the primary tablet managing the sequence to refresh/reset