// This maps a list of keyranges to shard names. func resolveKeyRangeToShards(topoServer SrvTopoServer, cell, keyspace string, tabletType topo.TabletType, kr key.KeyRange) ([]string, error) { srvKeyspace, err := topoServer.GetSrvKeyspace(cell, keyspace) if err != nil { return nil, fmt.Errorf("Error in reading the keyspace %v", err) } tabletTypePartition, ok := srvKeyspace.Partitions[tabletType] if !ok { return nil, fmt.Errorf("No shards available for tablet type '%v' in keyspace '%v'", tabletType, keyspace) } topo.SrvShardArray(tabletTypePartition.Shards).Sort() shards := make([]string, 0, 1) if !kr.IsPartial() { for j := 0; j < len(tabletTypePartition.Shards); j++ { shards = append(shards, tabletTypePartition.Shards[j].ShardName()) } return shards, nil } for j := 0; j < len(tabletTypePartition.Shards); j++ { shard := tabletTypePartition.Shards[j] if key.KeyRangesIntersect(kr, shard.KeyRange) { shards = append(shards, shard.ShardName()) } if kr.End != key.MaxKey && kr.End < shard.KeyRange.Start { break } } return shards, nil }
// checkPartitions will check the partition list is correct. // (it will also set Shards, but that should go away soon). func (wr *Wrangler) checkPartitions(cell string, srvKeyspace *topo.SrvKeyspace) error { // now check them all first := true for tabletType, partition := range srvKeyspace.Partitions { topo.SrvShardArray(partition.Shards).Sort() // check the first Start is MinKey, the last End is MaxKey, // and the values in between match: End[i] == Start[i+1] if partition.Shards[0].KeyRange.Start != key.MinKey { return fmt.Errorf("keyspace partition for %v in cell %v does not start with %v", tabletType, cell, key.MinKey) } if partition.Shards[len(partition.Shards)-1].KeyRange.End != key.MaxKey { return fmt.Errorf("keyspace partition for %v in cell %v does not end with %v", tabletType, cell, key.MaxKey) } for i := range partition.Shards[0 : len(partition.Shards)-1] { if partition.Shards[i].KeyRange.End != partition.Shards[i+1].KeyRange.Start { return fmt.Errorf("non-contiguous KeyRange values for %v in cell %v at shard %v to %v: %v != %v", tabletType, cell, i, i+1, partition.Shards[i].KeyRange.End.Hex(), partition.Shards[i+1].KeyRange.Start.Hex()) } } // backfill Shards if first { first = false srvKeyspace.Shards = partition.Shards } } return nil }
// This maps a list of keyranges to shard names. func resolveKeyRangeToShards(allShards []topo.SrvShard, kr key.KeyRange) ([]string, error) { shards := make([]string, 0, 1) topo.SrvShardArray(allShards).Sort() if !kr.IsPartial() { for j := 0; j < len(allShards); j++ { shards = append(shards, allShards[j].ShardName()) } return shards, nil } for j := 0; j < len(allShards); j++ { shard := allShards[j] if key.KeyRangesIntersect(kr, shard.KeyRange) { shards = append(shards, shard.ShardName()) } if kr.End != key.MaxKey && kr.End < shard.KeyRange.Start { break } } return shards, nil }
// This function should only be used with an action lock on the keyspace // - otherwise the consistency of the serving graph data can't be // guaranteed. // // Take data from the global keyspace and rebuild the local serving // copies in each cell. func (wr *Wrangler) rebuildKeyspace(keyspace string, cells []string) error { log.Infof("rebuildKeyspace %v", keyspace) ki, err := wr.ts.GetKeyspace(keyspace) if err != nil { // Temporary change: we try to keep going even if node // doesn't exist if err != topo.ErrNoNode { return err } ki = topo.NewKeyspaceInfo(keyspace, &topo.Keyspace{}) } shards, err := wr.ts.GetShardNames(keyspace) if err != nil { return err } // Rebuild all shards in parallel. wg := sync.WaitGroup{} er := concurrency.FirstErrorRecorder{} for _, shard := range shards { wg.Add(1) go func(shard string) { if err := wr.RebuildShardGraph(keyspace, shard, cells); err != nil { er.RecordError(fmt.Errorf("RebuildShardGraph failed: %v/%v %v", keyspace, shard, err)) } wg.Done() }(shard) } wg.Wait() if er.HasErrors() { return er.Error() } // Scan the first shard to discover which cells need local serving data. aliases, err := topo.FindAllTabletAliasesInShard(wr.ts, keyspace, shards[0]) if err != nil { return err } // srvKeyspaceMap is a map: // key: local keyspace {cell,keyspace} // value: topo.SrvKeyspace object being built srvKeyspaceMap := make(map[cellKeyspace]*topo.SrvKeyspace) for _, alias := range aliases { keyspaceLocation := cellKeyspace{alias.Cell, keyspace} if _, ok := srvKeyspaceMap[keyspaceLocation]; !ok { // before adding keyspaceLocation to the map of // of KeyspaceByPath, we check this is a // serving tablet. No serving tablet in shard // 0 means we're not rebuilding the serving // graph in that cell. This is somewhat // expensive, but we only do it on all the // non-serving tablets in a shard before we // find a serving tablet. ti, err := wr.ts.GetTablet(alias) if err != nil { return err } if !ti.IsInServingGraph() { continue } srvKeyspaceMap[keyspaceLocation] = &topo.SrvKeyspace{ Shards: make([]topo.SrvShard, 0, 16), ShardingColumnName: ki.ShardingColumnName, ShardingColumnType: ki.ShardingColumnType, ServedFrom: ki.ServedFrom, } } } // for each entry in the srvKeyspaceMap map, we do the following: // - read the ShardInfo structures for each shard // - compute the union of the db types (replica, master, ...) // - sort the shards in the list by range // - check the ranges are compatible (no hole, covers everything) for ck, srvKeyspace := range srvKeyspaceMap { keyspaceDbTypes := make(map[topo.TabletType]bool) srvKeyspace.Partitions = make(map[topo.TabletType]*topo.KeyspacePartition) for _, shard := range shards { srvShard, err := wr.ts.GetSrvShard(ck.cell, ck.keyspace, shard) if err != nil { return err } for _, tabletType := range srvShard.TabletTypes { keyspaceDbTypes[tabletType] = true } // for each type this shard is supposed to serve, // add it to srvKeyspace.Partitions for _, tabletType := range srvShard.ServedTypes { if _, ok := srvKeyspace.Partitions[tabletType]; !ok { srvKeyspace.Partitions[tabletType] = &topo.KeyspacePartition{ Shards: make([]topo.SrvShard, 0)} } srvKeyspace.Partitions[tabletType].Shards = append(srvKeyspace.Partitions[tabletType].Shards, *srvShard) } } srvKeyspace.TabletTypes = make([]topo.TabletType, 0, len(keyspaceDbTypes)) for dbType := range keyspaceDbTypes { srvKeyspace.TabletTypes = append(srvKeyspace.TabletTypes, dbType) } first := true for tabletType, partition := range srvKeyspace.Partitions { topo.SrvShardArray(partition.Shards).Sort() // check the first Start is MinKey, the last End is MaxKey, // and the values in between match: End[i] == Start[i+1] if partition.Shards[0].KeyRange.Start != key.MinKey { return fmt.Errorf("Keyspace partition for %v does not start with %v", tabletType, key.MinKey) } if partition.Shards[len(partition.Shards)-1].KeyRange.End != key.MaxKey { return fmt.Errorf("Keyspace partition for %v does not end with %v", tabletType, key.MaxKey) } for i := range partition.Shards[0 : len(partition.Shards)-1] { if partition.Shards[i].KeyRange.End != partition.Shards[i+1].KeyRange.Start { return fmt.Errorf("Non-contiguous KeyRange values for %v at shard %v to %v: %v != %v", tabletType, i, i+1, partition.Shards[i].KeyRange.End.Hex(), partition.Shards[i+1].KeyRange.Start.Hex()) } } // backfill Shards if first { first = false srvKeyspace.Shards = partition.Shards } } } // and then finally save the keyspace objects for ck, srvKeyspace := range srvKeyspaceMap { if err := wr.ts.UpdateSrvKeyspace(ck.cell, ck.keyspace, srvKeyspace); err != nil { return fmt.Errorf("writing serving data failed: %v", err) } } return nil }
// This function should only be used with an action lock on the keyspace // - otherwise the consistency of the serving graph data can't be // guaranteed. // // Take data from the global keyspace and rebuild the local serving // copies in each cell. func (wr *Wrangler) rebuildKeyspace(keyspace string, cells []string) error { relog.Info("rebuildKeyspace %v", keyspace) shards, err := wr.ts.GetShardNames(keyspace) if err != nil { return err } // Rebuild all shards in parallel. wg := sync.WaitGroup{} er := concurrency.FirstErrorRecorder{} for _, shard := range shards { wg.Add(1) go func(shard string) { if err := wr.RebuildShardGraph(keyspace, shard, cells); err != nil { er.RecordError(fmt.Errorf("RebuildShardGraph failed: %v/%v %v", keyspace, shard, err)) } wg.Done() }(shard) } wg.Wait() if er.HasErrors() { return er.Error() } // Scan the first shard to discover which cells need local serving data. aliases, err := topo.FindAllTabletAliasesInShard(wr.ts, keyspace, shards[0]) if err != nil { return err } // srvKeyspaceByPath is a map: // key: local keyspace {cell,keyspace} // value: topo.SrvKeyspace object being built srvKeyspaceByPath := make(map[cellKeyspace]*topo.SrvKeyspace) for _, alias := range aliases { keyspaceLocation := cellKeyspace{alias.Cell, keyspace} if _, ok := srvKeyspaceByPath[keyspaceLocation]; !ok { // before adding keyspaceLocation to the map of // of KeyspaceByPath, we check this is a // serving tablet. No serving tablet in shard // 0 means we're not rebuilding the serving // graph in that cell. This is somewhat // expensive, but we only do it on all the // non-serving tablets in a shard before we // find a serving tablet. ti, err := wr.ts.GetTablet(alias) if err != nil { return err } if !ti.IsServingType() { continue } srvKeyspaceByPath[keyspaceLocation] = &topo.SrvKeyspace{Shards: make([]topo.SrvShard, 0, 16)} } } // for each entry in the srvKeyspaceByPath map, we do the following: // - read the ShardInfo structures for each shard // - prune the AddrsByType field, result would be too big // - compute the union of the db types (replica, master, ...) // - sort the shards in the list by range // - check the ranges are compatible (no hole, covers everything) for srvPath, srvKeyspace := range srvKeyspaceByPath { keyspaceDbTypes := make(map[topo.TabletType]bool) for _, shard := range shards { srvShard, err := wr.ts.GetSrvShard(srvPath.cell, srvPath.keyspace, shard) if err != nil { return err } for dbType, _ := range srvShard.AddrsByType { keyspaceDbTypes[topo.TabletType(dbType)] = true } // Prune addrs, this is unnecessarily expensive right now. It is easier to // load on-demand since we have to do that anyway on a reconnect. srvShard.AddrsByType = nil srvKeyspace.Shards = append(srvKeyspace.Shards, *srvShard) } tabletTypes := make([]topo.TabletType, 0, len(keyspaceDbTypes)) for dbType, _ := range keyspaceDbTypes { tabletTypes = append(tabletTypes, dbType) } srvKeyspace.TabletTypes = tabletTypes // FIXME(msolomon) currently this only works when the shards are range-based topo.SrvShardArray(srvKeyspace.Shards).Sort() // check the first Start is MinKey, the last End is MaxKey, // and the values in between match: End[i] == Start[i+1] if srvKeyspace.Shards[0].KeyRange.Start != key.MinKey { return fmt.Errorf("Keyspace does not start with %v", key.MinKey) } if srvKeyspace.Shards[len(srvKeyspace.Shards)-1].KeyRange.End != key.MaxKey { return fmt.Errorf("Keyspace does not end with %v", key.MaxKey) } for i, _ := range srvKeyspace.Shards[0 : len(srvKeyspace.Shards)-1] { if srvKeyspace.Shards[i].KeyRange.End != srvKeyspace.Shards[i+1].KeyRange.Start { return fmt.Errorf("Non-contiguous KeyRange values at shard %v to %v: %v != %v", i, i+1, srvKeyspace.Shards[i].KeyRange.End.Hex(), srvKeyspace.Shards[i+1].KeyRange.Start.Hex()) } } } // and then finally save the keyspace objects for srvPath, srvKeyspace := range srvKeyspaceByPath { if err := wr.ts.UpdateSrvKeyspace(srvPath.cell, srvPath.keyspace, srvKeyspace); err != nil { return fmt.Errorf("writing serving data failed: %v", err) } } return nil }
// This function should only be used with an action lock on the keyspace // - otherwise the consistency of the serving graph data can't be // guaranteed. // // Take data from the global keyspace and rebuild the local serving // copies in each cell. func (wr *Wrangler) rebuildKeyspace(keyspace string, cells []string, shardCache map[string]*topo.ShardInfo) error { wr.logger.Infof("rebuildKeyspace %v", keyspace) ki, err := wr.ts.GetKeyspace(keyspace) if err != nil { return err } shards, err := wr.ts.GetShardNames(keyspace) if err != nil { return err } // Rebuild all shards in parallel. wg := sync.WaitGroup{} er := concurrency.FirstErrorRecorder{} for _, shard := range shards { wg.Add(1) go func(shard string) { if err := wr.RebuildShardGraph(keyspace, shard, cells); err != nil { er.RecordError(fmt.Errorf("RebuildShardGraph failed: %v/%v %v", keyspace, shard, err)) } wg.Done() }(shard) } wg.Wait() if er.HasErrors() { return er.Error() } // Build the list of cells to work on: we get the union // of all the Cells of all the Shards, limited to the provided cells. // // srvKeyspaceMap is a map: // key: cell // value: topo.SrvKeyspace object being built srvKeyspaceMap := make(map[string]*topo.SrvKeyspace) if err := wr.findCellsForRebuild(ki, keyspace, shards, cells, srvKeyspaceMap); err != nil { return err } // Then we add the cells from the keyspaces we might be 'ServedFrom'. for _, servedFrom := range ki.ServedFrom { servedFromShards, err := wr.ts.GetShardNames(servedFrom) if err != nil { return err } if err := wr.findCellsForRebuild(ki, servedFrom, servedFromShards, cells, srvKeyspaceMap); err != nil { return err } } // for each entry in the srvKeyspaceMap map, we do the following: // - read the SrvShard structures for each shard / cell // - if not present, build an empty one from global Shard // - compute the union of the db types (replica, master, ...) // - sort the shards in the list by range // - check the ranges are compatible (no hole, covers everything) if shardCache == nil { shardCache = make(map[string]*topo.ShardInfo) } for cell, srvKeyspace := range srvKeyspaceMap { keyspaceDbTypes := make(map[topo.TabletType]bool) srvKeyspace.Partitions = make(map[topo.TabletType]*topo.KeyspacePartition) for _, shard := range shards { srvShard, err := wr.ts.GetSrvShard(cell, keyspace, shard) switch err { case nil: // we keep going case topo.ErrNoNode: wr.logger.Infof("Cell %v for %v/%v has no SvrShard, using Shard data with no TabletTypes instead", cell, keyspace, shard) si, ok := shardCache[shard] if !ok { si, err = wr.ts.GetShard(keyspace, shard) if err != nil { return fmt.Errorf("GetShard(%v, %v) (backup for GetSrvShard in cell %v) failed: %v", keyspace, shard, cell, err) } shardCache[shard] = si } srvShard = &topo.SrvShard{ Name: si.ShardName(), KeyRange: si.KeyRange, ServedTypes: si.ServedTypes, MasterCell: si.MasterAlias.Cell, } default: return err } for _, tabletType := range srvShard.TabletTypes { keyspaceDbTypes[tabletType] = true } // for each type this shard is supposed to serve, // add it to srvKeyspace.Partitions for _, tabletType := range srvShard.ServedTypes { if _, ok := srvKeyspace.Partitions[tabletType]; !ok { srvKeyspace.Partitions[tabletType] = &topo.KeyspacePartition{ Shards: make([]topo.SrvShard, 0)} } srvKeyspace.Partitions[tabletType].Shards = append(srvKeyspace.Partitions[tabletType].Shards, *srvShard) } } srvKeyspace.TabletTypes = make([]topo.TabletType, 0, len(keyspaceDbTypes)) for dbType := range keyspaceDbTypes { srvKeyspace.TabletTypes = append(srvKeyspace.TabletTypes, dbType) } first := true for tabletType, partition := range srvKeyspace.Partitions { topo.SrvShardArray(partition.Shards).Sort() // check the first Start is MinKey, the last End is MaxKey, // and the values in between match: End[i] == Start[i+1] if partition.Shards[0].KeyRange.Start != key.MinKey { return fmt.Errorf("keyspace partition for %v in cell %v does not start with %v", tabletType, cell, key.MinKey) } if partition.Shards[len(partition.Shards)-1].KeyRange.End != key.MaxKey { return fmt.Errorf("keyspace partition for %v in cell %v does not end with %v", tabletType, cell, key.MaxKey) } for i := range partition.Shards[0 : len(partition.Shards)-1] { if partition.Shards[i].KeyRange.End != partition.Shards[i+1].KeyRange.Start { return fmt.Errorf("non-contiguous KeyRange values for %v in cell %v at shard %v to %v: %v != %v", tabletType, cell, i, i+1, partition.Shards[i].KeyRange.End.Hex(), partition.Shards[i+1].KeyRange.Start.Hex()) } } // backfill Shards if first { first = false srvKeyspace.Shards = partition.Shards } } } // and then finally save the keyspace objects for cell, srvKeyspace := range srvKeyspaceMap { wr.logger.Infof("updating keyspace serving graph in cell %v for %v", cell, keyspace) if err := wr.ts.UpdateSrvKeyspace(cell, keyspace, srvKeyspace); err != nil { return fmt.Errorf("writing serving data failed: %v", err) } } return nil }
func (wr *Wrangler) rebuildKeyspaceWithServedTypes(shards []string, srvKeyspaceMap map[cellKeyspace]*topo.SrvKeyspace) error { // for each entry in the srvKeyspaceMap map, we do the following: // - read the ShardInfo structures for each shard // - prune the AddrsByType field, result would be too big // - compute the union of the db types (replica, master, ...) // - sort the shards in the list by range // - check the ranges are compatible (no hole, covers everything) for ck, srvKeyspace := range srvKeyspaceMap { keyspaceDbTypes := make(map[topo.TabletType]bool) srvKeyspace.Partitions = make(map[topo.TabletType]*topo.KeyspacePartition) for _, shard := range shards { srvShard, err := wr.ts.GetSrvShard(ck.cell, ck.keyspace, shard) if err != nil { return err } for dbType, _ := range srvShard.AddrsByType { keyspaceDbTypes[topo.TabletType(dbType)] = true } // Prune addrs, this is unnecessarily expensive right // now. It is easier to load on-demand since we have // to do that anyway on a reconnect. srvShard.AddrsByType = nil // for each type this shard is supposed to serve, // add it to srvKeyspace.Partitions for _, tabletType := range srvShard.ServedTypes { if _, ok := srvKeyspace.Partitions[tabletType]; !ok { srvKeyspace.Partitions[tabletType] = &topo.KeyspacePartition{ Shards: make([]topo.SrvShard, 0)} } srvKeyspace.Partitions[tabletType].Shards = append(srvKeyspace.Partitions[tabletType].Shards, *srvShard) } } srvKeyspace.TabletTypes = make([]topo.TabletType, 0, len(keyspaceDbTypes)) for dbType, _ := range keyspaceDbTypes { srvKeyspace.TabletTypes = append(srvKeyspace.TabletTypes, dbType) } first := true for tabletType, partition := range srvKeyspace.Partitions { topo.SrvShardArray(partition.Shards).Sort() // check the first Start is MinKey, the last End is MaxKey, // and the values in between match: End[i] == Start[i+1] if partition.Shards[0].KeyRange.Start != key.MinKey { return fmt.Errorf("Keyspace partition for %v does not start with %v", tabletType, key.MinKey) } if partition.Shards[len(partition.Shards)-1].KeyRange.End != key.MaxKey { return fmt.Errorf("Keyspace partition for %v does not end with %v", tabletType, key.MaxKey) } for i, _ := range partition.Shards[0 : len(partition.Shards)-1] { if partition.Shards[i].KeyRange.End != partition.Shards[i+1].KeyRange.Start { return fmt.Errorf("Non-contiguous KeyRange values for %v at shard %v to %v: %v != %v", tabletType, i, i+1, partition.Shards[i].KeyRange.End.Hex(), partition.Shards[i+1].KeyRange.Start.Hex()) } } // backfill Shards if first { first = false srvKeyspace.Shards = partition.Shards } } } // and then finally save the keyspace objects for ck, srvKeyspace := range srvKeyspaceMap { if err := wr.ts.UpdateSrvKeyspace(ck.cell, ck.keyspace, srvKeyspace); err != nil { return fmt.Errorf("writing serving data failed: %v", err) } } return nil }