Search in sources :

Example 11 with ServerToSegmentSetMap

use of com.linkedin.pinot.routing.ServerToSegmentSetMap in project pinot by linkedin.

the class GeneratorBasedRoutingTableBuilder method computeRoutingTableFromExternalView.

@Override
public List<ServerToSegmentSetMap> computeRoutingTableFromExternalView(String tableName, ExternalView externalView, List<InstanceConfig> instanceConfigList) {
    // The default routing table algorithm tries to balance all available segments across all servers, so that each
    // server is hit on every query. This works fine with small clusters (say less than 20 servers) but for larger
    // clusters, this adds up to significant overhead (one request must be enqueued for each server, processed,
    // returned, deserialized, aggregated, etc.).
    //
    // For large clusters, we want to avoid hitting every server, as this also has an adverse effect on client tail
    // latency. This is due to the fact that a query cannot return until it has received a response from each server,
    // and the greater the number of servers that are hit, the more likely it is that one of the servers will be a
    // straggler (eg. due to contention for query processing threads, GC, etc.). We also want to balance the segments
    // within any given routing table so that each server in the routing table has approximately the same number of
    // segments to process.
    //
    // To do so, we have a routing table generator that generates routing tables by picking a random subset of servers.
    // With this set of servers, we check if the set of segments served by these servers is complete. If the set of
    // segments served does not cover all of the segments, we compute the list of missing segments and pick a random
    // server that serves these missing segments until we have complete coverage of all the segments.
    //
    // We then order the segments in ascending number of replicas within our server set, in order to allocate the
    // segments with fewer replicas first. This ensures that segments that are 'easier' to allocate are more likely to
    // end up on a replica with fewer segments.
    //
    // Then, we pick a random replica for each segment, iterating from fewest replicas to most replicas, inversely
    // weighted by the number of segments already assigned to that replica. This ensures that we build a routing table
    // that's as even as possible.
    //
    // The algorithm to generate a routing table is thus:
    // 1. Compute the inverse external view, a mapping of servers to segments
    // 2. For each routing table to generate:
    //   a) Pick TARGET_SERVER_COUNT_PER_QUERY distinct servers
    //   b) Check if the server set covers all the segments; if not, add additional servers until it does.
    //   c) Order the segments in our server set in ascending order of number of replicas present in our server set
    //   d) For each segment, pick a random replica with proper weighting
    //   e) Return that routing table
    //
    // Given that we can generate routing tables at will, we then generate many routing tables and use them to optimize
    // according to two criteria: the variance in workload per server for any individual table as well as the variance
    // in workload per server across all the routing tables. To do so, we generate an initial set of routing tables
    // according to a per-routing table metric and discard the worst routing tables.
    RoutingTableGenerator routingTableGenerator = buildRoutingTableGenerator();
    routingTableGenerator.init(externalView, instanceConfigList);
    PriorityQueue<Pair<Map<String, Set<String>>, Float>> topRoutingTables = new PriorityQueue<>(ROUTING_TABLE_COUNT, new Comparator<Pair<Map<String, Set<String>>, Float>>() {

        @Override
        public int compare(Pair<Map<String, Set<String>>, Float> left, Pair<Map<String, Set<String>>, Float> right) {
            // Float.compare sorts in ascending order and we want a max heap, so we need to return the negative of the comparison
            return -Float.compare(left.getValue(), right.getValue());
        }
    });
    for (int i = 0; i < ROUTING_TABLE_COUNT; i++) {
        topRoutingTables.add(generateRoutingTableWithMetric(routingTableGenerator));
    }
    // Generate routing more tables and keep the ROUTING_TABLE_COUNT top ones
    for (int i = 0; i < (ROUTING_TABLE_GENERATION_COUNT - ROUTING_TABLE_COUNT); ++i) {
        Pair<Map<String, Set<String>>, Float> newRoutingTable = generateRoutingTableWithMetric(routingTableGenerator);
        Pair<Map<String, Set<String>>, Float> worstRoutingTable = topRoutingTables.peek();
        // If the new routing table is better than the worst one, keep it
        if (newRoutingTable.getRight() < worstRoutingTable.getRight()) {
            topRoutingTables.poll();
            topRoutingTables.add(newRoutingTable);
        }
    }
    // Return the best routing tables
    List<ServerToSegmentSetMap> routingTables = new ArrayList<>(topRoutingTables.size());
    while (!topRoutingTables.isEmpty()) {
        Pair<Map<String, Set<String>>, Float> routingTableWithMetric = topRoutingTables.poll();
        routingTables.add(new ServerToSegmentSetMap(routingTableWithMetric.getKey()));
    }
    return routingTables;
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) ArrayList(java.util.ArrayList) ServerToSegmentSetMap(com.linkedin.pinot.routing.ServerToSegmentSetMap) PriorityQueue(java.util.PriorityQueue) HashMap(java.util.HashMap) ServerToSegmentSetMap(com.linkedin.pinot.routing.ServerToSegmentSetMap) Map(java.util.Map) ImmutablePair(org.apache.commons.lang3.tuple.ImmutablePair) Pair(org.apache.commons.lang3.tuple.Pair)

Example 12 with ServerToSegmentSetMap

use of com.linkedin.pinot.routing.ServerToSegmentSetMap in project pinot by linkedin.

the class RandomRoutingTableBuilder method computeRoutingTableFromExternalView.

@Override
public synchronized List<ServerToSegmentSetMap> computeRoutingTableFromExternalView(String tableName, ExternalView externalView, List<InstanceConfig> instanceConfigList) {
    RoutingTableInstancePruner pruner = new RoutingTableInstancePruner(instanceConfigList);
    List<Map<String, Set<String>>> routingTables = new ArrayList<Map<String, Set<String>>>();
    for (int i = 0; i < _numberOfRoutingTables; ++i) {
        routingTables.add(new HashMap<String, Set<String>>());
    }
    String[] segmentSet = externalView.getPartitionSet().toArray(new String[0]);
    for (String segment : segmentSet) {
        Map<String, String> instanceToStateMap = externalView.getStateMap(segment);
        for (String instance : instanceToStateMap.keySet().toArray(new String[0])) {
            if (!instanceToStateMap.get(instance).equals("ONLINE") || pruner.isInactive(instance)) {
                instanceToStateMap.remove(instance);
            }
        }
        if (instanceToStateMap.size() > 0) {
            String[] instances = instanceToStateMap.keySet().toArray(new String[0]);
            Random randomSeed = new Random(System.currentTimeMillis());
            for (int i = 0; i < _numberOfRoutingTables; ++i) {
                String instance = instances[randomSeed.nextInt(instances.length)];
                if (routingTables.get(i).containsKey(instance)) {
                    routingTables.get(i).get(instance).add(segment);
                } else {
                    Set<String> instanceSegmentSet = new HashSet<String>();
                    instanceSegmentSet.add(segment);
                    routingTables.get(i).put(instance, instanceSegmentSet);
                }
            }
        }
    }
    List<ServerToSegmentSetMap> resultRoutingTableList = new ArrayList<ServerToSegmentSetMap>();
    for (int i = 0; i < _numberOfRoutingTables; ++i) {
        resultRoutingTableList.add(new ServerToSegmentSetMap(routingTables.get(i)));
    }
    return resultRoutingTableList;
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) ServerToSegmentSetMap(com.linkedin.pinot.routing.ServerToSegmentSetMap) Random(java.util.Random) ServerToSegmentSetMap(com.linkedin.pinot.routing.ServerToSegmentSetMap) Map(java.util.Map) HashMap(java.util.HashMap) HashSet(java.util.HashSet)

Aggregations

ServerToSegmentSetMap (com.linkedin.pinot.routing.ServerToSegmentSetMap)12 ArrayList (java.util.ArrayList)7 ExternalView (org.apache.helix.model.ExternalView)7 Test (org.testng.annotations.Test)7 InstanceConfig (org.apache.helix.model.InstanceConfig)6 HashMap (java.util.HashMap)5 HashSet (java.util.HashSet)5 Map (java.util.Map)5 Set (java.util.Set)4 LLCSegmentName (com.linkedin.pinot.common.utils.LLCSegmentName)2 Random (java.util.Random)2 BaseConfiguration (org.apache.commons.configuration.BaseConfiguration)2 AbstractTableConfig (com.linkedin.pinot.common.config.AbstractTableConfig)1 HLCSegmentName (com.linkedin.pinot.common.utils.HLCSegmentName)1 SegmentName (com.linkedin.pinot.common.utils.SegmentName)1 HelixExternalViewBasedRouting (com.linkedin.pinot.routing.HelixExternalViewBasedRouting)1 Field (java.lang.reflect.Field)1 List (java.util.List)1 PriorityQueue (java.util.PriorityQueue)1 ImmutablePair (org.apache.commons.lang3.tuple.ImmutablePair)1