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 }