vitess.io/vitess@v0.16.2/go/vt/wrangler/switcher_dry_run.go (about)

     1  /*
     2  Copyright 2020 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package wrangler
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sort"
    23  	"strings"
    24  	"time"
    25  
    26  	"vitess.io/vitess/go/mysql"
    27  	"vitess.io/vitess/go/vt/vtctl/workflow"
    28  
    29  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    30  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    31  )
    32  
    33  var _ iswitcher = (*switcherDryRun)(nil)
    34  
    35  type switcherDryRun struct {
    36  	drLog *LogRecorder
    37  	ts    *trafficSwitcher
    38  }
    39  
    40  func (dr *switcherDryRun) addParticipatingTablesToKeyspace(ctx context.Context, keyspace, tableSpecs string) error {
    41  	dr.drLog.Log("All source tables will be added to the target keyspace vschema")
    42  	return nil
    43  }
    44  
    45  func (dr *switcherDryRun) deleteRoutingRules(ctx context.Context) error {
    46  	dr.drLog.Log("Routing rules for participating tables will be deleted")
    47  	return nil
    48  }
    49  
    50  func (dr *switcherDryRun) deleteShardRoutingRules(ctx context.Context) error {
    51  	if dr.ts.isPartialMigration {
    52  		dr.drLog.Log("Shard routing rules for participating shards will be deleted")
    53  	}
    54  	return nil
    55  }
    56  
    57  func (dr *switcherDryRun) switchShardReads(ctx context.Context, cells []string, servedTypes []topodatapb.TabletType, direction workflow.TrafficSwitchDirection) error {
    58  	sourceShards := make([]string, 0)
    59  	targetShards := make([]string, 0)
    60  	for _, source := range dr.ts.Sources() {
    61  		sourceShards = append(sourceShards, source.GetShard().ShardName())
    62  	}
    63  	for _, target := range dr.ts.Targets() {
    64  		targetShards = append(targetShards, target.GetShard().ShardName())
    65  	}
    66  	sort.Strings(sourceShards)
    67  	sort.Strings(targetShards)
    68  	if direction == workflow.DirectionForward {
    69  		dr.drLog.Log(fmt.Sprintf("Switch reads from keyspace %s to keyspace %s for shards %s to shards %s",
    70  			dr.ts.SourceKeyspaceName(), dr.ts.TargetKeyspaceName(), strings.Join(sourceShards, ","), strings.Join(targetShards, ",")))
    71  	} else {
    72  		dr.drLog.Log(fmt.Sprintf("Switch reads from keyspace %s to keyspace %s for shards %s to shards %s",
    73  			dr.ts.TargetKeyspaceName(), dr.ts.SourceKeyspaceName(), strings.Join(targetShards, ","), strings.Join(sourceShards, ",")))
    74  	}
    75  	return nil
    76  }
    77  
    78  func (dr *switcherDryRun) switchTableReads(ctx context.Context, cells []string, servedTypes []topodatapb.TabletType, direction workflow.TrafficSwitchDirection) error {
    79  	ks := dr.ts.TargetKeyspaceName()
    80  	if direction == workflow.DirectionBackward {
    81  		ks = dr.ts.SourceKeyspaceName()
    82  	}
    83  	var tabletTypes []string
    84  	for _, servedType := range servedTypes {
    85  		tabletTypes = append(tabletTypes, servedType.String())
    86  	}
    87  	tables := strings.Join(dr.ts.Tables(), ",")
    88  	dr.drLog.Log(fmt.Sprintf("Switch reads for tables [%s] to keyspace %s for tablet types [%s]",
    89  		tables, ks, strings.Join(tabletTypes, ",")))
    90  	dr.drLog.Log(fmt.Sprintf("Routing rules for tables [%s] will be updated", tables))
    91  	return nil
    92  }
    93  
    94  func (dr *switcherDryRun) createJournals(ctx context.Context, sourceWorkflows []string) error {
    95  	dr.drLog.Log("Create journal entries on source databases")
    96  	if len(sourceWorkflows) > 0 {
    97  		dr.drLog.Log("Source workflows found: ")
    98  		dr.drLog.LogSlice(sourceWorkflows)
    99  	}
   100  	return nil
   101  }
   102  
   103  func (dr *switcherDryRun) allowTargetWrites(ctx context.Context) error {
   104  	dr.drLog.Log(fmt.Sprintf("Enable writes on keyspace %s tables [%s]", dr.ts.TargetKeyspaceName(), strings.Join(dr.ts.Tables(), ",")))
   105  	return nil
   106  }
   107  
   108  func (dr *switcherDryRun) changeRouting(ctx context.Context) error {
   109  	dr.drLog.Log(fmt.Sprintf("Switch routing from keyspace %s to keyspace %s", dr.ts.SourceKeyspaceName(), dr.ts.TargetKeyspaceName()))
   110  	var deleteLogs, addLogs []string
   111  	if dr.ts.MigrationType() == binlogdatapb.MigrationType_TABLES {
   112  		tables := strings.Join(dr.ts.Tables(), ",")
   113  		dr.drLog.Log(fmt.Sprintf("Routing rules for tables [%s] will be updated", tables))
   114  		return nil
   115  	}
   116  	deleteLogs = nil
   117  	addLogs = nil
   118  	for _, source := range dr.ts.Sources() {
   119  		deleteLogs = append(deleteLogs, fmt.Sprintf("\tShard %s, Tablet %d", source.GetShard().ShardName(), source.GetShard().PrimaryAlias.Uid))
   120  	}
   121  	for _, target := range dr.ts.Targets() {
   122  		addLogs = append(addLogs, fmt.Sprintf("\tShard %s, Tablet %d", target.GetShard().ShardName(), target.GetShard().PrimaryAlias.Uid))
   123  	}
   124  	if len(deleteLogs) > 0 {
   125  		dr.drLog.Log("IsPrimaryServing will be set to false for:")
   126  		dr.drLog.LogSlice(deleteLogs)
   127  		dr.drLog.Log("IsPrimaryServing will be set to true for:")
   128  		dr.drLog.LogSlice(addLogs)
   129  	}
   130  	return nil
   131  }
   132  
   133  func (dr *switcherDryRun) streamMigraterfinalize(ctx context.Context, ts *trafficSwitcher, workflows []string) error {
   134  	dr.drLog.Log("Switch writes completed, freeze and delete vreplication streams on:")
   135  	logs := make([]string, 0)
   136  	for _, t := range ts.Targets() {
   137  		logs = append(logs, fmt.Sprintf("\ttablet %d", t.GetPrimary().Alias.Uid))
   138  	}
   139  	dr.drLog.LogSlice(logs)
   140  	return nil
   141  }
   142  
   143  func (dr *switcherDryRun) startReverseVReplication(ctx context.Context) error {
   144  	dr.drLog.Log("Start reverse replication streams on:")
   145  	logs := make([]string, 0)
   146  	for _, t := range dr.ts.Sources() {
   147  		logs = append(logs, fmt.Sprintf("\ttablet %d", t.GetPrimary().Alias.Uid))
   148  	}
   149  	dr.drLog.LogSlice(logs)
   150  	return nil
   151  }
   152  
   153  func (dr *switcherDryRun) createReverseVReplication(ctx context.Context) error {
   154  	dr.drLog.Log(fmt.Sprintf("Create reverse replication workflow %s", dr.ts.ReverseWorkflowName()))
   155  	return nil
   156  }
   157  
   158  func (dr *switcherDryRun) migrateStreams(ctx context.Context, sm *workflow.StreamMigrator) error {
   159  	templates := sm.Templates()
   160  
   161  	if len(templates) == 0 {
   162  		return nil
   163  	}
   164  	logs := make([]string, 0)
   165  
   166  	dr.drLog.Log(fmt.Sprintf("Migrate streams to %s:", dr.ts.TargetKeyspaceName()))
   167  	for key, streams := range sm.Streams() {
   168  		for _, stream := range streams {
   169  			logs = append(logs, fmt.Sprintf("\tShard %s Id %d, Workflow %s, Pos %s, BinLogSource %v", key, stream.ID, stream.Workflow, mysql.EncodePosition(stream.Position), stream.BinlogSource))
   170  		}
   171  	}
   172  	if len(logs) > 0 {
   173  		dr.drLog.Log("Source streams will be migrated:")
   174  		dr.drLog.LogSlice(logs)
   175  		logs = nil
   176  	}
   177  	for _, target := range dr.ts.Targets() {
   178  		tabletStreams := templates
   179  		for _, vrs := range tabletStreams {
   180  			logs = append(logs, fmt.Sprintf("\t Keyspace %s, Shard %s, Tablet %d, Workflow %s, Id %d, Pos %v, BinLogSource %s",
   181  				vrs.BinlogSource.Keyspace, vrs.BinlogSource.Shard, target.GetPrimary().Alias.Uid, vrs.Workflow, vrs.ID, mysql.EncodePosition(vrs.Position), vrs.BinlogSource))
   182  		}
   183  	}
   184  	if len(logs) > 0 {
   185  		dr.drLog.Log("Target streams will be created (as stopped):")
   186  		dr.drLog.LogSlice(logs)
   187  	}
   188  	return nil
   189  }
   190  
   191  func (dr *switcherDryRun) waitForCatchup(ctx context.Context, filteredReplicationWaitTime time.Duration) error {
   192  	dr.drLog.Log(fmt.Sprintf("Wait for VReplication on stopped streams to catchup for up to %v", filteredReplicationWaitTime))
   193  	return nil
   194  }
   195  
   196  func (dr *switcherDryRun) stopSourceWrites(ctx context.Context) error {
   197  	logs := make([]string, 0)
   198  	for _, source := range dr.ts.Sources() {
   199  		position, _ := dr.ts.TabletManagerClient().PrimaryPosition(ctx, source.GetPrimary().Tablet)
   200  		logs = append(logs, fmt.Sprintf("\tKeyspace %s, Shard %s at Position %s", dr.ts.SourceKeyspaceName(), source.GetShard().ShardName(), position))
   201  	}
   202  	if len(logs) > 0 {
   203  		dr.drLog.Log(fmt.Sprintf("Stop writes on keyspace %s, tables [%s]:", dr.ts.SourceKeyspaceName(), strings.Join(dr.ts.Tables(), ",")))
   204  		dr.drLog.LogSlice(logs)
   205  	}
   206  	return nil
   207  }
   208  
   209  func (dr *switcherDryRun) stopStreams(ctx context.Context, sm *workflow.StreamMigrator) ([]string, error) {
   210  	logs := make([]string, 0)
   211  	for _, streams := range sm.Streams() {
   212  		for _, stream := range streams {
   213  			logs = append(logs, fmt.Sprintf("\tId %d Keyspace %s Shard %s Rules %s at Position %v",
   214  				stream.ID, stream.BinlogSource.Keyspace, stream.BinlogSource.Shard, stream.BinlogSource.Filter, stream.Position))
   215  		}
   216  	}
   217  	if len(logs) > 0 {
   218  		dr.drLog.Log(fmt.Sprintf("Stop streams on keyspace %s", dr.ts.SourceKeyspaceName()))
   219  		dr.drLog.LogSlice(logs)
   220  	}
   221  	return nil, nil
   222  }
   223  
   224  func (dr *switcherDryRun) cancelMigration(ctx context.Context, sm *workflow.StreamMigrator) {
   225  	dr.drLog.Log("Cancel stream migrations as requested")
   226  }
   227  
   228  func (dr *switcherDryRun) lockKeyspace(ctx context.Context, keyspace, _ string) (context.Context, func(*error), error) {
   229  	dr.drLog.Log(fmt.Sprintf("Lock keyspace %s", keyspace))
   230  	return ctx, func(e *error) {
   231  		dr.drLog.Log(fmt.Sprintf("Unlock keyspace %s", keyspace))
   232  	}, nil
   233  }
   234  
   235  func (dr *switcherDryRun) removeSourceTables(ctx context.Context, removalType workflow.TableRemovalType) error {
   236  	logs := make([]string, 0)
   237  	for _, source := range dr.ts.Sources() {
   238  		for _, tableName := range dr.ts.Tables() {
   239  			logs = append(logs, fmt.Sprintf("\tKeyspace %s Shard %s DbName %s Tablet %d Table %s",
   240  				source.GetPrimary().Keyspace, source.GetPrimary().Shard, source.GetPrimary().DbName(), source.GetPrimary().Alias.Uid, tableName))
   241  		}
   242  	}
   243  	action := "Dropping"
   244  	if removalType == workflow.RenameTable {
   245  		action = "Renaming"
   246  	}
   247  	if len(logs) > 0 {
   248  		dr.drLog.Log(fmt.Sprintf("%s these tables from the database and removing them from the vschema for keyspace %s:",
   249  			action, dr.ts.SourceKeyspaceName()))
   250  		dr.drLog.LogSlice(logs)
   251  	}
   252  	return nil
   253  }
   254  
   255  func (dr *switcherDryRun) dropSourceShards(ctx context.Context) error {
   256  	logs := make([]string, 0)
   257  	tabletsList := make(map[string][]string)
   258  	for _, si := range dr.ts.SourceShards() {
   259  		tabletAliases, err := dr.ts.TopoServer().FindAllTabletAliasesInShard(ctx, si.Keyspace(), si.ShardName())
   260  		if err != nil {
   261  			return err
   262  		}
   263  		tabletsList[si.ShardName()] = make([]string, 0)
   264  		for _, t := range tabletAliases {
   265  			tabletsList[si.ShardName()] = append(tabletsList[si.ShardName()], fmt.Sprintf("\t\t%d", t.Uid))
   266  		}
   267  		sort.Strings(tabletsList[si.ShardName()])
   268  		logs = append(logs, fmt.Sprintf("\tCell %s Keyspace %s Shard\n%s",
   269  			si.Shard.PrimaryAlias.Cell, si.Keyspace(), si.ShardName()), strings.Join(tabletsList[si.ShardName()], "\n"))
   270  	}
   271  	if len(logs) > 0 {
   272  		dr.drLog.Log("Deleting following shards (and all related tablets):")
   273  		dr.drLog.LogSlice(logs)
   274  	}
   275  
   276  	return nil
   277  }
   278  
   279  func (dr *switcherDryRun) validateWorkflowHasCompleted(ctx context.Context) error {
   280  	return doValidateWorkflowHasCompleted(ctx, dr.ts)
   281  }
   282  
   283  func (dr *switcherDryRun) dropTargetVReplicationStreams(ctx context.Context) error {
   284  	dr.drLog.Log("Delete vreplication streams on target:")
   285  	logs := make([]string, 0)
   286  	for _, t := range dr.ts.Targets() {
   287  		logs = append(logs, fmt.Sprintf("\tKeyspace %s Shard %s Workflow %s DbName %s Tablet %d",
   288  			t.GetShard().Keyspace(), t.GetShard().ShardName(), dr.ts.WorkflowName(), t.GetPrimary().DbName(), t.GetPrimary().Alias.Uid))
   289  	}
   290  	dr.drLog.LogSlice(logs)
   291  	return nil
   292  }
   293  
   294  func (dr *switcherDryRun) dropSourceReverseVReplicationStreams(ctx context.Context) error {
   295  	dr.drLog.Log("Delete reverse vreplication streams on source:")
   296  	logs := make([]string, 0)
   297  	for _, t := range dr.ts.Sources() {
   298  		logs = append(logs, fmt.Sprintf("\tKeyspace %s Shard %s Workflow %s DbName %s Tablet %d",
   299  			t.GetShard().Keyspace(), t.GetShard().ShardName(), workflow.ReverseWorkflowName(dr.ts.WorkflowName()), t.GetPrimary().DbName(), t.GetPrimary().Alias.Uid))
   300  	}
   301  	dr.drLog.LogSlice(logs)
   302  	return nil
   303  }
   304  
   305  func (dr *switcherDryRun) freezeTargetVReplication(ctx context.Context) error {
   306  	logs := make([]string, 0)
   307  	for _, target := range dr.ts.Targets() {
   308  		logs = append(logs, fmt.Sprintf("\tKeyspace %s, Shard %s, Tablet %d, Workflow %s, DbName %s",
   309  			target.GetPrimary().Keyspace, target.GetPrimary().Shard, target.GetPrimary().Alias.Uid, dr.ts.WorkflowName(), target.GetPrimary().DbName()))
   310  	}
   311  	if len(logs) > 0 {
   312  		dr.drLog.Log("Mark vreplication streams frozen on:")
   313  		dr.drLog.LogSlice(logs)
   314  	}
   315  	return nil
   316  }
   317  
   318  func (dr *switcherDryRun) dropSourceDeniedTables(ctx context.Context) error {
   319  	logs := make([]string, 0)
   320  	for _, si := range dr.ts.SourceShards() {
   321  		logs = append(logs, fmt.Sprintf("\tKeyspace %s Shard %s Tablet %d", si.Keyspace(), si.ShardName(), si.PrimaryAlias.Uid))
   322  	}
   323  	if len(logs) > 0 {
   324  		dr.drLog.Log(fmt.Sprintf("Denied tables [%s] will be removed from:", strings.Join(dr.ts.Tables(), ",")))
   325  		dr.drLog.LogSlice(logs)
   326  	}
   327  	return nil
   328  }
   329  
   330  func (dr *switcherDryRun) logs() *[]string {
   331  	return &dr.drLog.logs
   332  }
   333  
   334  func (dr *switcherDryRun) removeTargetTables(ctx context.Context) error {
   335  	logs := make([]string, 0)
   336  	for _, target := range dr.ts.Targets() {
   337  		for _, tableName := range dr.ts.Tables() {
   338  			logs = append(logs, fmt.Sprintf("\tKeyspace %s Shard %s DbName %s Tablet %d Table %s",
   339  				target.GetPrimary().Keyspace, target.GetPrimary().Shard, target.GetPrimary().DbName(), target.GetPrimary().Alias.Uid, tableName))
   340  		}
   341  	}
   342  	if len(logs) > 0 {
   343  		dr.drLog.Log(fmt.Sprintf("Dropping these tables from the database and removing from the vschema for keyspace %s:",
   344  			dr.ts.TargetKeyspaceName()))
   345  		dr.drLog.LogSlice(logs)
   346  	}
   347  	return nil
   348  }
   349  
   350  func (dr *switcherDryRun) dropTargetShards(ctx context.Context) error {
   351  	logs := make([]string, 0)
   352  	tabletsList := make(map[string][]string)
   353  	for _, si := range dr.ts.TargetShards() {
   354  		tabletAliases, err := dr.ts.TopoServer().FindAllTabletAliasesInShard(ctx, si.Keyspace(), si.ShardName())
   355  		if err != nil {
   356  			return err
   357  		}
   358  		tabletsList[si.ShardName()] = make([]string, 0)
   359  		for _, t := range tabletAliases {
   360  			tabletsList[si.ShardName()] = append(tabletsList[si.ShardName()], fmt.Sprintf("\t\t%d", t.Uid))
   361  		}
   362  		sort.Strings(tabletsList[si.ShardName()])
   363  		logs = append(logs, fmt.Sprintf("\tCell %s Keyspace %s Shard\n%s",
   364  			si.Shard.PrimaryAlias.Cell, si.Keyspace(), si.ShardName()), strings.Join(tabletsList[si.ShardName()], "\n"))
   365  	}
   366  	if len(logs) > 0 {
   367  		dr.drLog.Log("Deleting following shards (and all related tablets):")
   368  		dr.drLog.LogSlice(logs)
   369  	}
   370  
   371  	return nil
   372  }