func (wr *Wrangler) applySchemaShardComplex(statusArray []*TabletStatus, shardInfo *topo.ShardInfo, preflight *myproto.SchemaChangeResult, masterTabletAlias topo.TabletAlias, change string, newParentTabletAlias topo.TabletAlias, force bool) (*myproto.SchemaChangeResult, error) { // apply the schema change to all replica / slave tablets for _, status := range statusArray { // if already applied, we skip this guy diffs := myproto.DiffSchemaToArray("after", preflight.AfterSchema, status.ti.Alias.String(), status.beforeSchema) if len(diffs) == 0 { log.Infof("Tablet %v already has the AfterSchema, skipping", status.ti.Alias) continue } // make sure the before schema matches diffs = myproto.DiffSchemaToArray("master", preflight.BeforeSchema, status.ti.Alias.String(), status.beforeSchema) if len(diffs) > 0 { if force { log.Warningf("Tablet %v has inconsistent schema, ignoring: %v", status.ti.Alias, strings.Join(diffs, "\n")) } else { return nil, fmt.Errorf("Tablet %v has inconsistent schema: %v", status.ti.Alias, strings.Join(diffs, "\n")) } } // take this guy out of the serving graph if necessary ti, err := wr.ts.GetTablet(status.ti.Alias) if err != nil { return nil, err } typeChangeRequired := ti.Tablet.IsInServingGraph() if typeChangeRequired { // note we want to update the serving graph there err = wr.changeTypeInternal(ti.Alias, topo.TYPE_SCHEMA_UPGRADE) if err != nil { return nil, err } } // apply the schema change log.Infof("Applying schema change to slave %v in complex mode", status.ti.Alias) sc := &myproto.SchemaChange{Sql: change, Force: force, AllowReplication: false, BeforeSchema: preflight.BeforeSchema, AfterSchema: preflight.AfterSchema} _, err = wr.ApplySchema(status.ti.Alias, sc) if err != nil { return nil, err } // put this guy back into the serving graph if typeChangeRequired { err = wr.changeTypeInternal(ti.Alias, ti.Tablet.Type) if err != nil { return nil, err } } } // if newParentTabletAlias is passed in, use that as the new master if !newParentTabletAlias.IsZero() { log.Infof("Reparenting with new master set to %v", newParentTabletAlias) tabletMap, err := topo.GetTabletMapForShard(context.TODO(), wr.ts, shardInfo.Keyspace(), shardInfo.ShardName()) if err != nil { return nil, err } slaveTabletMap, masterTabletMap := topotools.SortedTabletMap(tabletMap) newMasterTablet, err := wr.ts.GetTablet(newParentTabletAlias) if err != nil { return nil, err } // Create reusable Reparent event with available info ev := &events.Reparent{ ShardInfo: *shardInfo, NewMaster: *newMasterTablet.Tablet, } if oldMasterTablet, ok := tabletMap[shardInfo.MasterAlias]; ok { ev.OldMaster = *oldMasterTablet.Tablet } err = wr.reparentShardGraceful(ev, shardInfo, slaveTabletMap, masterTabletMap, newMasterTablet /*leaveMasterReadOnly*/, false) if err != nil { return nil, err } // Here we would apply the schema change to the old // master, but after a reparent it's in Scrap state, // so no need to. When/if reparent leaves the // original master in a different state (like replica // or rdonly), then we should apply the schema there // too. log.Infof("Skipping schema change on old master %v in complex mode, it's been Scrapped", masterTabletAlias) } return &myproto.SchemaChangeResult{BeforeSchema: preflight.BeforeSchema, AfterSchema: preflight.AfterSchema}, nil }