func getKeyspaceShards(ctx context.Context, topoServ SrvTopoServer, cell, keyspace string, tabletType pb.TabletType) (string, *pb.SrvKeyspace, []*pb.ShardReference, error) { srvKeyspace, err := topoServ.GetSrvKeyspace(ctx, cell, keyspace) if err != nil { return "", nil, nil, vterrors.NewVitessError( vtrpc.ErrorCode_INTERNAL_ERROR, err, "keyspace %v fetch error: %v", keyspace, err, ) } // check if the keyspace has been redirected for this tabletType. for _, sf := range srvKeyspace.ServedFrom { if sf.TabletType == tabletType { keyspace = sf.Keyspace srvKeyspace, err = topoServ.GetSrvKeyspace(ctx, cell, keyspace) if err != nil { return "", nil, nil, vterrors.NewVitessError( vtrpc.ErrorCode_INTERNAL_ERROR, err, "keyspace %v fetch error: %v", keyspace, err, ) } } } partition := topoproto.SrvKeyspaceGetPartition(srvKeyspace, tabletType) if partition == nil { return "", nil, nil, vterrors.NewVitessError( vtrpc.ErrorCode_INTERNAL_ERROR, err, "No partition found for tabletType %v in keyspace %v", strings.ToLower(tabletType.String()), keyspace, ) } return keyspace, srvKeyspace, partition.ShardReferences, nil }
// DbServingGraph returns the ServingGraph for the given cell. func DbServingGraph(ctx context.Context, ts topo.Server, cell string) (servingGraph *ServingGraph) { servingGraph = &ServingGraph{ Cell: cell, Keyspaces: make(map[string]*KeyspaceNodes), } rec := concurrency.AllErrorRecorder{} keyspaces, err := ts.GetSrvKeyspaceNames(ctx, cell) if err != nil { servingGraph.Errors = append(servingGraph.Errors, fmt.Sprintf("GetSrvKeyspaceNames failed: %v", err)) return } wg := sync.WaitGroup{} servingTypes := []pb.TabletType{pb.TabletType_MASTER, pb.TabletType_REPLICA, pb.TabletType_RDONLY} for _, keyspace := range keyspaces { kn := newKeyspaceNodes() servingGraph.Keyspaces[keyspace] = kn wg.Add(1) go func(keyspace string, kn *KeyspaceNodes) { defer wg.Done() ks, err := ts.GetSrvKeyspace(ctx, cell, keyspace) if err != nil { rec.RecordError(fmt.Errorf("GetSrvKeyspace(%v, %v) failed: %v", cell, keyspace, err)) return } for _, sf := range ks.ServedFrom { kn.ServedFrom[strings.ToLower(sf.TabletType.String())] = sf.Keyspace } displayedShards := make(map[string]bool) for _, partitionTabletType := range servingTypes { kp := topoproto.SrvKeyspaceGetPartition(ks, partitionTabletType) if kp == nil { continue } for _, srvShard := range kp.ShardReferences { shard := srvShard.Name if displayedShards[shard] { continue } displayedShards[shard] = true sn := &ShardNodes{ Name: shard, } kn.ShardNodes = append(kn.ShardNodes, sn) wg.Add(1) go func(shard string, sn *ShardNodes) { defer wg.Done() tabletTypes, err := ts.GetSrvTabletTypesPerShard(ctx, cell, keyspace, shard) if err != nil { rec.RecordError(fmt.Errorf("GetSrvTabletTypesPerShard(%v, %v, %v) failed: %v", cell, keyspace, shard, err)) return } for _, tabletType := range tabletTypes { endPoints, _, err := ts.GetEndPoints(ctx, cell, keyspace, shard, tabletType) if err != nil { rec.RecordError(fmt.Errorf("GetEndPoints(%v, %v, %v, %v) failed: %v", cell, keyspace, shard, tabletType, err)) continue } for _, endPoint := range endPoints.Entries { var tabletNode *TabletNodesByType for _, t := range sn.TabletNodes { if t.TabletType == tabletType { tabletNode = t break } } if tabletNode == nil { tabletNode = &TabletNodesByType{ TabletType: tabletType, } sn.TabletNodes = append(sn.TabletNodes, tabletNode) } tabletNode.Nodes = append(tabletNode.Nodes, newTabletNodeFromEndPoint(endPoint, cell)) } } }(shard, sn) } } }(keyspace, kn) } wg.Wait() servingGraph.Errors = rec.ErrorStrings() return }
// rebuildKeyspace 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 rebuildKeyspace(ctx context.Context, log logutil.Logger, ts topo.Server, keyspace string, cells []string, rebuildSrvShards bool) error { log.Infof("rebuildKeyspace %v", keyspace) ki, err := ts.GetKeyspace(ctx, keyspace) if err != nil { return err } var shardCache map[string]*topo.ShardInfo if rebuildSrvShards { shards, err := ts.GetShardNames(ctx, keyspace) if err != nil { return nil } // Rebuild all shards in parallel, save the shards shardCache = make(map[string]*topo.ShardInfo) wg := sync.WaitGroup{} mu := sync.Mutex{} rec := concurrency.FirstErrorRecorder{} for _, shard := range shards { wg.Add(1) go func(shard string) { if shardInfo, err := RebuildShard(ctx, log, ts, keyspace, shard, cells); err != nil { rec.RecordError(fmt.Errorf("RebuildShard failed: %v/%v %v", keyspace, shard, err)) } else { mu.Lock() shardCache[shard] = shardInfo mu.Unlock() } wg.Done() }(shard) } wg.Wait() if rec.HasErrors() { return rec.Error() } } else { shardCache, err = ts.FindAllShardsInKeyspace(ctx, keyspace) if err != nil { return err } } // 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]*topodatapb.SrvKeyspace) findCellsForRebuild(ki, shardCache, cells, srvKeyspaceMap) // Then we add the cells from the keyspaces we might be 'ServedFrom'. for _, ksf := range ki.ServedFroms { servedFromShards, err := ts.FindAllShardsInKeyspace(ctx, ksf.Keyspace) if err != nil { return err } findCellsForRebuild(ki, servedFromShards, cells, srvKeyspaceMap) } // 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) for cell, srvKeyspace := range srvKeyspaceMap { for _, si := range shardCache { servedTypes := si.GetServedTypesPerCell(cell) // for each type this shard is supposed to serve, // add it to srvKeyspace.Partitions for _, tabletType := range servedTypes { partition := topoproto.SrvKeyspaceGetPartition(srvKeyspace, tabletType) if partition == nil { partition = &topodatapb.SrvKeyspace_KeyspacePartition{ ServedType: tabletType, } srvKeyspace.Partitions = append(srvKeyspace.Partitions, partition) } partition.ShardReferences = append(partition.ShardReferences, &topodatapb.ShardReference{ Name: si.ShardName(), KeyRange: si.KeyRange, }) } } if err := orderAndCheckPartitions(cell, srvKeyspace); err != nil { return err } } // and then finally save the keyspace objects for cell, srvKeyspace := range srvKeyspaceMap { log.Infof("updating keyspace serving graph in cell %v for %v", cell, keyspace) if err := ts.UpdateSrvKeyspace(ctx, cell, keyspace, srvKeyspace); err != nil { return fmt.Errorf("writing serving data failed: %v", err) } } return nil }