Skip to content

Commit

Permalink
Revert "Remove CopySchemaShard"
Browse files Browse the repository at this point in the history
This reverts commit a091042.
  • Loading branch information
mattlord committed Dec 31, 2024
1 parent a091042 commit 6b4251a
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
49 changes: 49 additions & 0 deletions go/cmd/vtctldclient/command/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ For --sql, semi-colons and repeated values may be mixed, for example:
Args: cobra.ExactArgs(1),
RunE: commandApplySchema,
}
CopySchemaShard = &cobra.Command{
Use: "CopySchemaShard [--tables=<table1>,<table2>,...] [--exclude-tables=<table1>,<table2>,...] [--include-views] [--skip-verify] [--wait-replicas-timeout=10s] {<source keyspace/shard> || <source tablet alias>} <destination keyspace/shard>",
Short: "Copies the schema from a source shard's primary (or a specific tablet) to a destination shard. The schema is applied directly on the primary of the destination shard, and it is propagated to the replicas through binlogs.",
DisableFlagsInUseLine: true,
Args: cobra.ExactArgs(2),
RunE: commandCopySchemaShard,
}
// GetSchema makes a GetSchema gRPC call to a vtctld.
GetSchema = &cobra.Command{
Use: "GetSchema [--tables TABLES ...] [--exclude-tables EXCLUDE_TABLES ...] [{--table-names-only | --table-sizes-only}] [--include-views] alias",
Expand Down Expand Up @@ -173,6 +180,47 @@ func commandApplySchema(cmd *cobra.Command, args []string) error {
return nil
}

func commandCopySchemaShard(cmd *cobra.Command, args []string) error {
/*
tables := subFlags.String("tables", "", "Specifies a comma-separated list of tables to copy. Each is either an exact match, or a regular expression of the form /regexp/")
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", true, "Includes views in the output")
skipVerify := subFlags.Bool("skip-verify", false, "Skip verification of source and target schema after copy")
// for backwards compatibility
waitReplicasTimeout := subFlags.Duration("wait_replicas_timeout", grpcvtctldserver.DefaultWaitReplicasTimeout, "The amount of time to wait for replicas to receive the schema change via replication.")
if err := subFlags.Parse(args); err != nil {
return err
}
if subFlags.NArg() != 2 {
return fmt.Errorf("the <source keyspace/shard> and <destination keyspace/shard> arguments are both required for the CopySchemaShard command. Instead of the <source keyspace/shard> argument, you can also specify <tablet alias> which refers to a specific tablet of the shard in the source keyspace")
}
var tableArray []string
if *tables != "" {
tableArray = strings.Split(*tables, ",")
}
var excludeTableArray []string
if *excludeTables != "" {
excludeTableArray = strings.Split(*excludeTables, ",")
}
destKeyspace, destShard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(1))
if err != nil {
return err
}
sourceKeyspace, sourceShard, err := topoproto.ParseKeyspaceShard(subFlags.Arg(0))
if err == nil {
return wr.CopySchemaShardFromShard(ctx, tableArray, excludeTableArray, *includeViews, sourceKeyspace, sourceShard, destKeyspace, destShard, *waitReplicasTimeout, *skipVerify)
}
sourceTabletAlias, err := topoproto.ParseTabletAlias(subFlags.Arg(0))
if err == nil {
return wr.CopySchemaShard(ctx, sourceTabletAlias, tableArray, excludeTableArray, *includeViews, destKeyspace, destShard, *waitReplicasTimeout, *skipVerify)
}
return err
*/
return nil
}

var getSchemaOptions = struct {
Tables []string
ExcludeTables []string
Expand Down Expand Up @@ -375,6 +423,7 @@ func init() {
ApplySchema.Flags().StringArrayVar(&applySchemaOptions.SQL, "sql", nil, "Semicolon-delimited, repeatable SQL commands to apply. Exactly one of --sql|--sql-file is required.")
ApplySchema.Flags().StringVar(&applySchemaOptions.SQLFile, "sql-file", "", "Path to a file containing semicolon-delimited SQL commands to apply. Exactly one of --sql|--sql-file is required.")
ApplySchema.Flags().Int64Var(&applySchemaOptions.BatchSize, "batch-size", 0, "How many queries to batch together. Only applicable when all queries are CREATE TABLE|VIEW")

Root.AddCommand(ApplySchema)

GetSchema.Flags().StringSliceVar(&getSchemaOptions.Tables, "tables", nil, "List of tables to display the schema for. Each is either an exact match, or a regular expression of the form `/regexp/`.")
Expand Down
57 changes: 57 additions & 0 deletions go/test/endtoend/vtgate/schema/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ limitations under the License.
package schema

import (
"encoding/json"
"flag"
"fmt"
"os"
"path"
"reflect"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -106,6 +108,9 @@ func TestSchemaChange(t *testing.T) {
testApplySchemaBatch(t)
testUnsafeAllowForeignKeys(t)
testCreateInvalidView(t)
testCopySchemaShards(t, clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].VttabletProcess.TabletPath, 2)
testCopySchemaShards(t, fmt.Sprintf("%s/0", keyspaceName), 3)
testCopySchemaShardWithDifferentDB(t, 4)
testWithAutoSchemaFromChangeDir(t)
}

Expand Down Expand Up @@ -281,6 +286,58 @@ func checkTablesCount(t *testing.T, tablet *cluster.Vttablet, count int) {
assert.Equal(t, len(queryResult.Rows), count)
}

// testCopySchemaShards tests that schema from source is correctly applied to destination
func testCopySchemaShards(t *testing.T, source string, shard int) {
addNewShard(t, shard)
// InitShardPrimary creates the db, but there shouldn't be any tables yet.
checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0], 0)
checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[1], 0)
// Run the command twice to make sure it's idempotent.
for i := 0; i < 2; i++ {
err := clusterInstance.VtctlclientProcess.ExecuteCommand("CopySchemaShard", source, fmt.Sprintf("%s/%d", keyspaceName, shard))
require.Nil(t, err)
}
// shard2 primary should look the same as the replica we copied from
checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0], totalTableCount)
checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[1], totalTableCount)

matchSchema(t, clusterInstance.Keyspaces[0].Shards[0].Vttablets[0].VttabletProcess.TabletPath, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0].VttabletProcess.TabletPath)
}

// testCopySchemaShardWithDifferentDB if we apply different schema to new shard, it should throw error
func testCopySchemaShardWithDifferentDB(t *testing.T, shard int) {
addNewShard(t, shard)
checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0], 0)
checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[1], 0)
source := fmt.Sprintf("%s/0", keyspaceName)

tabletAlias := clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0].VttabletProcess.TabletPath
schema, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetSchema", tabletAlias)
require.Nil(t, err)

resultMap := make(map[string]any)
err = json.Unmarshal([]byte(schema), &resultMap)
require.Nil(t, err)
dbSchema := reflect.ValueOf(resultMap["database_schema"])
assert.True(t, strings.Contains(dbSchema.String(), "utf8"))

// Change the db charset on the destination shard from utf8 to latin1.
// This will make CopySchemaShard fail during its final diff.
// (The different charset won't be corrected on the destination shard
// because we use "CREATE DATABASE IF NOT EXISTS" and this doesn't fail if
// there are differences in the options e.g. the character set.)
err = clusterInstance.VtctldClientProcess.ExecuteCommand("ExecuteFetchAsDBA", "--json", tabletAlias, "ALTER DATABASE vt_ks CHARACTER SET latin1")
require.Nil(t, err)

output, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("CopySchemaShard", source, fmt.Sprintf("%s/%d", keyspaceName, shard))
require.Error(t, err)
assert.True(t, strings.Contains(output, "schemas are different"))

// shard2 primary should have the same number of tables. Only the db
// character set is different.
checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[shard].Vttablets[0], totalTableCount)
}

// addNewShard adds a new shard dynamically
func addNewShard(t *testing.T, shard int) {
keyspace := &cluster.Keyspace{
Expand Down

0 comments on commit 6b4251a

Please sign in to comment.