// 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) (err 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) sourceKeyspace := si.SourceShards[0].Keyspace if err := ki.CheckServedFromMigration(servedType, cells, sourceKeyspace, !reverse); err != nil { return err } // lock the keyspaces, source first. ctx, unlock, lockErr := wr.ts.LockKeyspace(ctx, sourceKeyspace, fmt.Sprintf("MigrateServedFrom(%v)", servedType)) if lockErr != nil { return lockErr } defer unlock(&err) ctx, unlock, lockErr = wr.ts.LockKeyspace(ctx, keyspace, fmt.Sprintf("MigrateServedFrom(%v)", servedType)) if lockErr != nil { return lockErr } defer unlock(&err) // execute the migration err = wr.migrateServedFromLocked(ctx, ki, si, servedType, cells, reverse, filteredReplicationWaitTime) // rebuild the keyspace serving graph if there was no error if err == nil { err = topotools.RebuildKeyspaceLocked(ctx, wr.logger, wr.ts, keyspace, cells) } return err }
// MigrateServedTypes is used during horizontal splits to migrate a // served type from a list of shards to another. func (wr *Wrangler) MigrateServedTypes(ctx context.Context, keyspace, shard string, cells []string, servedType topodatapb.TabletType, reverse, skipReFreshState bool, filteredReplicationWaitTime time.Duration) (err error) { // check input parameters if servedType == topodatapb.TabletType_MASTER { // we cannot migrate a master back, since when master migration // is done, the source shards are dead if reverse { return fmt.Errorf("Cannot migrate master back to %v/%v", keyspace, shard) } // we cannot skip refresh state for a master if skipReFreshState { return fmt.Errorf("Cannot skip refresh state for master migration on %v/%v", keyspace, shard) } } // lock the keyspace ctx, unlock, lockErr := wr.ts.LockKeyspace(ctx, keyspace, fmt.Sprintf("MigrateServedTypes(%v)", servedType)) if lockErr != nil { return lockErr } defer unlock(&err) // find overlapping shards in this keyspace wr.Logger().Infof("Finding the overlapping shards in keyspace %v", keyspace) osList, err := topotools.FindOverlappingShards(ctx, wr.ts, keyspace) if err != nil { return fmt.Errorf("FindOverlappingShards failed: %v", err) } // find our shard in there os := topotools.OverlappingShardsForShard(osList, shard) if os == nil { return fmt.Errorf("Shard %v is not involved in any overlapping shards", shard) } // find which list is which: the sources have no source // shards, the destination have source shards. We check the // first entry in the lists, then just check they're // consistent var sourceShards []*topo.ShardInfo var destinationShards []*topo.ShardInfo if len(os.Left[0].SourceShards) == 0 { sourceShards = os.Left destinationShards = os.Right } else { sourceShards = os.Right destinationShards = os.Left } // Verify the sources has the type we're migrating (or not if reverse) for _, si := range sourceShards { if err := si.CheckServedTypesMigration(servedType, cells, !reverse); err != nil { return err } } // Verify the destinations do not have the type we're // migrating (or do if reverse) for _, si := range destinationShards { if err := si.CheckServedTypesMigration(servedType, cells, reverse); err != nil { return err } } // execute the migration if err = wr.migrateServedTypesLocked(ctx, keyspace, sourceShards, destinationShards, cells, servedType, reverse, filteredReplicationWaitTime); err != nil { return err } // rebuild the keyspace serving graph now that there is no error if err = topotools.RebuildKeyspaceLocked(ctx, wr.logger, wr.ts, keyspace, cells); err != nil { return err } // Send a refresh to the tablets we just disabled, iff: // - we're not migrating a master // - we don't have any errors // - we're not told to skip the refresh if servedType != topodatapb.TabletType_MASTER && !skipReFreshState { rec := concurrency.AllErrorRecorder{} var refreshShards []*topo.ShardInfo if reverse { // For a backwards migration, we just disabled query service on the destination shards refreshShards = destinationShards } else { // For a forwards migration, we just disabled query service on the source shards refreshShards = sourceShards } for _, si := range refreshShards { rec.RecordError(wr.RefreshTabletsByShard(ctx, si, servedType, cells)) } return rec.Error() } return nil }
// MigrateServedTypes is used during horizontal splits to migrate a // served type from a list of shards to another. func (wr *Wrangler) MigrateServedTypes(ctx context.Context, keyspace, shard string, cells []string, servedType topodatapb.TabletType, reverse, skipReFreshState bool, filteredReplicationWaitTime time.Duration) (err error) { // check input parameters if servedType == topodatapb.TabletType_MASTER { // we cannot migrate a master back, since when master migration // is done, the source shards are dead if reverse { return fmt.Errorf("Cannot migrate master back to %v/%v", keyspace, shard) } // we cannot skip refresh state for a master if skipReFreshState { return fmt.Errorf("Cannot skip refresh state for master migration on %v/%v", keyspace, shard) } } // lock the keyspace ctx, unlock, lockErr := wr.ts.LockKeyspace(ctx, keyspace, fmt.Sprintf("MigrateServedTypes(%v)", servedType)) if lockErr != nil { return lockErr } defer unlock(&err) // find overlapping shards in this keyspace wr.Logger().Infof("Finding the overlapping shards in keyspace %v", keyspace) osList, err := topotools.FindOverlappingShards(ctx, wr.ts, keyspace) if err != nil { return fmt.Errorf("FindOverlappingShards failed: %v", err) } // find our shard in there os := topotools.OverlappingShardsForShard(osList, shard) if os == nil { return fmt.Errorf("Shard %v is not involved in any overlapping shards", shard) } // find which list is which: the sources have no source // shards, the destination have source shards. We check the // first entry in the lists, then just check they're // consistent var sourceShards []*topo.ShardInfo var destinationShards []*topo.ShardInfo if len(os.Left[0].SourceShards) == 0 { if len(os.Right[0].SourceShards) == 0 { return fmt.Errorf("neither Shard '%v' nor Shard '%v' have a 'SourceShards' entry. Did you successfully run vtworker SplitClone before? Or did you already migrate the MASTER type?", os.Left[0].ShardName(), os.Right[0].ShardName()) } sourceShards = os.Left destinationShards = os.Right } else { sourceShards = os.Right destinationShards = os.Left } // Verify the sources has the type we're migrating (or not if reverse) for _, si := range sourceShards { if err := si.CheckServedTypesMigration(servedType, cells, !reverse); err != nil { return err } } // Verify the destinations do not have the type we're // migrating (or do if reverse) for _, si := range destinationShards { if err := si.CheckServedTypesMigration(servedType, cells, reverse); err != nil { return err } } // execute the migration if err = wr.migrateServedTypesLocked(ctx, keyspace, sourceShards, destinationShards, cells, servedType, reverse, filteredReplicationWaitTime); err != nil { return err } // rebuild the keyspace serving graph now that there is no error if err = topotools.RebuildKeyspaceLocked(ctx, wr.logger, wr.ts, keyspace, cells); err != nil { return err } // Send a refresh to the tablets we just disabled, iff: // - we're not migrating a master // - we don't have any errors // - we're not told to skip the refresh if servedType != topodatapb.TabletType_MASTER && !skipReFreshState { rec := concurrency.AllErrorRecorder{} var refreshShards []*topo.ShardInfo if reverse { // For a backwards migration, we just disabled query service on the destination shards refreshShards = destinationShards } else { // For a forwards migration, we just disabled query service on the source shards refreshShards = sourceShards } // TODO(b/26388813): Integrate vtctl WaitForDrain here instead of just sleeping. var waitForDrainSleep time.Duration switch servedType { case topodatapb.TabletType_RDONLY: waitForDrainSleep = *waitForDrainSleepRdonly case topodatapb.TabletType_REPLICA: waitForDrainSleep = *waitForDrainSleepReplica default: wr.Logger().Warningf("invalid TabletType: %v for MigrateServedTypes command", servedType) } wr.Logger().Infof("WaitForDrain: Sleeping for %.0f seconds before shutting down query service on old tablets...", waitForDrainSleep.Seconds()) time.Sleep(waitForDrainSleep) wr.Logger().Infof("WaitForDrain: Sleeping finished. Shutting down queryservice on old tablets now.") for _, si := range refreshShards { rec.RecordError(wr.RefreshTabletsByShard(ctx, si, []topodatapb.TabletType{servedType}, cells)) } return rec.Error() } return nil }