func (wr *Wrangler) MigrateServedFrom(keyspace, shard string, servedType topo.TabletType, reverse, skipRebuild bool) error { if servedType == topo.TYPE_MASTER { // we cannot migrate a master back if reverse { return fmt.Errorf("Cannot migrate master back to %v/%v", keyspace, shard) } // we cannot skip rebuild for a master if skipRebuild { return fmt.Errorf("Cannot skip rebuild for master migration on %v/%v", keyspace, shard) } } // read the destination keyspace, check it ki, err := wr.ts.GetKeyspace(keyspace) if err != nil { return err } if len(ki.ServedFrom) == 0 { return fmt.Errorf("Destination keyspace %v is not a vertical split target", keyspace) } // read the destination shard, check it si, err := wr.ts.GetShard(keyspace, shard) if err != nil { return err } if len(si.SourceShards) != 1 || len(si.SourceShards[0].Tables) == 0 { return fmt.Errorf("Destination shard %v/%v is not a vertical split target", keyspace, shard) } // check the migration is valid foundType := false for tt, _ := range ki.ServedFrom { if tt == servedType { foundType = true } } if foundType == reverse { return fmt.Errorf("Supplied type cannot be migrated") } if servedType == topo.TYPE_MASTER && len(ki.ServedFrom) > 1 { return fmt.Errorf("Cannot migrate master into %v/%v until everything else is migrated", keyspace, shard) } // lock the keyspace and shard actionNode := actionnode.MigrateServedFrom(servedType) keyspaceLockPath, err := wr.lockKeyspace(keyspace, actionNode) if err != nil { log.Errorf("Failed to lock destination keyspace %v", keyspace) return err } shardLockPath, err := wr.lockShard(keyspace, shard, actionNode) if err != nil { log.Errorf("Failed to lock destination shard %v/%v", keyspace, shard) wr.unlockKeyspace(keyspace, actionNode, keyspaceLockPath, nil) return err } // record the action error and all unlock errors rec := concurrency.AllErrorRecorder{} // execute the migration rec.RecordError(wr.migrateServedFrom(ki, si, servedType, reverse)) rec.RecordError(wr.unlockShard(keyspace, shard, actionNode, shardLockPath, nil)) rec.RecordError(wr.unlockKeyspace(keyspace, actionNode, keyspaceLockPath, nil)) // rebuild the keyspace serving graph if there was no error if rec.Error() == nil { if skipRebuild { log.Infof("Skipping keyspace rebuild, please run it at earliest convenience") } else { rec.RecordError(wr.RebuildKeyspaceGraph(keyspace, nil, nil)) } } return rec.Error() }
// MigrateServedFrom is used during vertical splits to migrate a // served type from a keyspace to another. func (wr *Wrangler) MigrateServedFrom(ctx context.Context, keyspace, shard string, servedType topodatapb.TabletType, cells []string, reverse bool, filteredReplicationWaitTime time.Duration) error { // read the destination keyspace, check it ki, err := wr.ts.GetKeyspace(ctx, keyspace) if err != nil { return err } if len(ki.ServedFroms) == 0 { return fmt.Errorf("Destination keyspace %v is not a vertical split target", keyspace) } // read the destination shard, check it si, err := wr.ts.GetShard(ctx, keyspace, shard) if err != nil { return err } if len(si.SourceShards) != 1 || len(si.SourceShards[0].Tables) == 0 { return fmt.Errorf("Destination shard %v/%v is not a vertical split target", keyspace, shard) } // check the migration is valid before locking (will also be checked // after locking to be sure) if err := ki.CheckServedFromMigration(servedType, cells, si.SourceShards[0].Keyspace, !reverse); err != nil { return err } // lock the keyspace and shards actionNode := actionnode.MigrateServedFrom(servedType) keyspaceLockPath, err := wr.lockKeyspace(ctx, keyspace, actionNode) if err != nil { wr.Logger().Errorf("Failed to lock destination keyspace %v", keyspace) return err } destinationShardLockPath, err := wr.lockShard(ctx, keyspace, shard, actionNode) if err != nil { wr.Logger().Errorf("Failed to lock destination shard %v/%v", keyspace, shard) wr.unlockKeyspace(ctx, keyspace, actionNode, keyspaceLockPath, nil) return err } sourceKeyspace := si.SourceShards[0].Keyspace sourceShard := si.SourceShards[0].Shard sourceShardLockPath, err := wr.lockShard(ctx, sourceKeyspace, sourceShard, actionNode) if err != nil { wr.Logger().Errorf("Failed to lock source shard %v/%v", sourceKeyspace, sourceShard) wr.unlockShard(ctx, keyspace, shard, actionNode, destinationShardLockPath, nil) wr.unlockKeyspace(ctx, keyspace, actionNode, keyspaceLockPath, nil) return err } // record the action error and all unlock errors rec := concurrency.AllErrorRecorder{} // execute the migration rec.RecordError(wr.migrateServedFrom(ctx, ki, si, servedType, cells, reverse, filteredReplicationWaitTime)) rec.RecordError(wr.unlockShard(ctx, sourceKeyspace, sourceShard, actionNode, sourceShardLockPath, nil)) rec.RecordError(wr.unlockShard(ctx, keyspace, shard, actionNode, destinationShardLockPath, nil)) rec.RecordError(wr.unlockKeyspace(ctx, keyspace, actionNode, keyspaceLockPath, nil)) // rebuild the keyspace serving graph if there was no error if rec.Error() == nil { rec.RecordError(wr.RebuildKeyspaceGraph(ctx, keyspace, cells, false)) } return rec.Error() }