Search in sources :

Example 81 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project pinot by linkedin.

the class LargeClusterRoutingTableBuilderTest method testRoutingTableExcludesDisabledAndRebootingInstances.

@Test
public void testRoutingTableExcludesDisabledAndRebootingInstances() {
    final String tableName = "fakeTable_OFFLINE";
    final int segmentCount = 100;
    final int replicationFactor = 6;
    final int instanceCount = 50;
    ExternalView externalView = createExternalView(tableName, segmentCount, replicationFactor, instanceCount);
    List<InstanceConfig> instanceConfigs = createInstanceConfigs(instanceCount);
    final InstanceConfig disabledHelixInstance = instanceConfigs.get(0);
    final String disabledHelixInstanceName = disabledHelixInstance.getInstanceName();
    disabledHelixInstance.setInstanceEnabled(false);
    final InstanceConfig shuttingDownInstance = instanceConfigs.get(1);
    final String shuttingDownInstanceName = shuttingDownInstance.getInstanceName();
    shuttingDownInstance.getRecord().setSimpleField(CommonConstants.Helix.IS_SHUTDOWN_IN_PROGRESS, Boolean.toString(true));
    validateAssertionForOneRoutingTable(new RoutingTableValidator() {

        @Override
        public boolean isRoutingTableValid(ServerToSegmentSetMap routingTable, ExternalView externalView, List<InstanceConfig> instanceConfigs) {
            for (String server : routingTable.getServerSet()) {
                // These servers should not appear in the routing table
                if (server.equals(disabledHelixInstanceName) || server.equals(shuttingDownInstanceName)) {
                    return false;
                }
            }
            return true;
        }
    }, "Routing table should not contain disabled instances", externalView, instanceConfigs, tableName);
}
Also used : ExternalView(org.apache.helix.model.ExternalView) InstanceConfig(org.apache.helix.model.InstanceConfig) ServerToSegmentSetMap(com.linkedin.pinot.routing.ServerToSegmentSetMap) Test(org.testng.annotations.Test)

Example 82 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project pinot by linkedin.

the class LargeClusterRoutingTableBuilderTest method testRoutingTableServerLoadIsRelativelyEqual.

@Test
public void testRoutingTableServerLoadIsRelativelyEqual() {
    final String tableName = "fakeTable_OFFLINE";
    final int segmentCount = 300;
    final int replicationFactor = 10;
    final int instanceCount = 50;
    ExternalView externalView = createExternalView(tableName, segmentCount, replicationFactor, instanceCount);
    List<InstanceConfig> instanceConfigs = createInstanceConfigs(instanceCount);
    List<ServerToSegmentSetMap> routingTables = _largeClusterRoutingTableBuilder.computeRoutingTableFromExternalView(tableName, externalView, instanceConfigs);
    Map<String, Integer> segmentCountPerServer = new HashMap<>();
    // Count number of segments assigned per server
    for (ServerToSegmentSetMap routingTable : routingTables) {
        for (String server : routingTable.getServerSet()) {
            Integer segmentCountForServer = segmentCountPerServer.get(server);
            if (segmentCountForServer == null) {
                segmentCountForServer = 0;
            }
            segmentCountForServer += routingTable.getSegmentSet(server).size();
            segmentCountPerServer.put(server, segmentCountForServer);
        }
    }
    int minNumberOfSegmentsAssignedPerServer = Integer.MAX_VALUE;
    int maxNumberOfSegmentsAssignedPerServer = 0;
    for (Integer segmentCountForServer : segmentCountPerServer.values()) {
        if (segmentCountForServer < minNumberOfSegmentsAssignedPerServer) {
            minNumberOfSegmentsAssignedPerServer = segmentCountForServer;
        }
        if (maxNumberOfSegmentsAssignedPerServer < segmentCountForServer) {
            maxNumberOfSegmentsAssignedPerServer = segmentCountForServer;
        }
    }
    assertTrue(maxNumberOfSegmentsAssignedPerServer < minNumberOfSegmentsAssignedPerServer * 1.5, "At least one server has more than 150% of the load of the least loaded server, minNumberOfSegmentsAssignedPerServer = " + minNumberOfSegmentsAssignedPerServer + " maxNumberOfSegmentsAssignedPerServer = " + maxNumberOfSegmentsAssignedPerServer + " RANDOM_SEED = " + RANDOM_SEED);
}
Also used : ExternalView(org.apache.helix.model.ExternalView) InstanceConfig(org.apache.helix.model.InstanceConfig) HashMap(java.util.HashMap) ServerToSegmentSetMap(com.linkedin.pinot.routing.ServerToSegmentSetMap) Test(org.testng.annotations.Test)

Example 83 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project pinot by linkedin.

the class HelixExternalViewBasedRouting method buildRoutingTable.

private void buildRoutingTable(String tableName, ExternalView externalView, List<InstanceConfig> instanceConfigs) {
    // Save the current version number of the external view to avoid unnecessary routing table updates
    int externalViewRecordVersion = externalView.getRecord().getVersion();
    _lastKnownExternalViewVersionMap.put(tableName, externalViewRecordVersion);
    RoutingTableBuilder routingTableBuilder;
    CommonConstants.Helix.TableType tableType = TableNameBuilder.getTableTypeFromTableName(tableName);
    // Pick the appropriate routing table builder based on the table type
    if (CommonConstants.Helix.TableType.REALTIME.equals(tableType)) {
        routingTableBuilder = _realtimeHLCRoutingTableBuilder;
    } else {
        if (isLargeCluster(externalView)) {
            routingTableBuilder = _largeClusterRoutingTableBuilder;
        } else {
            routingTableBuilder = _smallClusterRoutingTableBuilder;
        }
    }
    LOGGER.info("Trying to compute routing table for table {} using {}", tableName, routingTableBuilder);
    long startTimeMillis = System.currentTimeMillis();
    try {
        Map<String, InstanceConfig> relevantInstanceConfigs = new HashMap<>();
        // Build a list of routing tables
        List<ServerToSegmentSetMap> serverToSegmentSetMap = routingTableBuilder.computeRoutingTableFromExternalView(tableName, externalView, instanceConfigs);
        // Keep track of the instance configs that are used in that routing table
        updateInstanceConfigsMapFromRoutingTables(relevantInstanceConfigs, instanceConfigs, serverToSegmentSetMap);
        _brokerRoutingTable.put(tableName, serverToSegmentSetMap);
        // If this is a realtime table, also build a LLC routing table
        if (CommonConstants.Helix.TableType.REALTIME.equals(tableType)) {
            _routingTableSelector.registerTable(tableName);
            try {
                // Build the routing table
                List<ServerToSegmentSetMap> llcserverToSegmentSetMap = _realtimeLLCRoutingTableBuilder.computeRoutingTableFromExternalView(tableName, externalView, instanceConfigs);
                // Keep track of the instance configs that are used in that routing table
                updateInstanceConfigsMapFromRoutingTables(relevantInstanceConfigs, instanceConfigs, llcserverToSegmentSetMap);
                _llcBrokerRoutingTable.put(tableName, llcserverToSegmentSetMap);
            } catch (Exception e) {
                LOGGER.error("Failed to compute LLC routing table for {}. Ignoring", tableName, e);
            }
        }
        // Save the instance configs used so that we can avoid unnecessary routing table updates later
        _lastKnownInstanceConfigsForTable.put(tableName, relevantInstanceConfigs);
        for (InstanceConfig instanceConfig : relevantInstanceConfigs.values()) {
            _lastKnownInstanceConfigs.put(instanceConfig.getInstanceName(), instanceConfig);
        }
        // Ensure this table is registered with all relevant instances
        for (String instanceName : relevantInstanceConfigs.keySet()) {
            Set<String> tablesForCurrentInstance = _tablesForInstance.get(instanceName);
            // Ensure there is a table set for this instance
            if (tablesForCurrentInstance == null) {
                synchronized (_tablesForInstance) {
                    if (!_tablesForInstance.containsKey(instanceName)) {
                        tablesForCurrentInstance = Sets.newConcurrentHashSet();
                        _tablesForInstance.put(instanceName, tablesForCurrentInstance);
                    } else {
                        // Another thread has created a table set for this instance, use it
                        tablesForCurrentInstance = _tablesForInstance.get(instanceName);
                    }
                }
            }
            // Add the table to the set of tables for this instance
            tablesForCurrentInstance.add(tableName);
        }
    } catch (Exception e) {
        _brokerMetrics.addMeteredTableValue(tableName, BrokerMeter.ROUTING_TABLE_REBUILD_FAILURES, 1L);
        LOGGER.error("Failed to compute/update the routing table", e);
        // Mark the routing table as needing a rebuild
        _lastKnownExternalViewVersionMap.put(tableName, INVALID_EXTERNAL_VIEW_VERSION);
    }
    try {
        // We need to compute the time boundary only in two situations:
        // 1) We're adding/updating an offline table and there's a realtime table that we're serving
        // 2) We're adding a new realtime table and there's already an offline table, in which case we need to update the
        //    time boundary for the existing offline table
        String tableForTimeBoundaryUpdate = null;
        ExternalView externalViewForTimeBoundaryUpdate = null;
        if (tableType == CommonConstants.Helix.TableType.OFFLINE) {
            // Does a realtime table exist?
            String realtimeTableName = TableNameBuilder.REALTIME_TABLE_NAME_BUILDER.forTable(TableNameBuilder.extractRawTableName(tableName));
            if (_brokerRoutingTable.containsKey(realtimeTableName)) {
                tableForTimeBoundaryUpdate = tableName;
                externalViewForTimeBoundaryUpdate = externalView;
            }
        }
        if (tableType == CommonConstants.Helix.TableType.REALTIME) {
            // Does an offline table exist?
            String offlineTableName = TableNameBuilder.OFFLINE_TABLE_NAME_BUILDER.forTable(TableNameBuilder.extractRawTableName(tableName));
            if (_brokerRoutingTable.containsKey(offlineTableName)) {
                // Is there no time boundary?
                if (_timeBoundaryService.getTimeBoundaryInfoFor(offlineTableName) == null) {
                    tableForTimeBoundaryUpdate = offlineTableName;
                    externalViewForTimeBoundaryUpdate = fetchExternalView(offlineTableName);
                }
            }
        }
        if (tableForTimeBoundaryUpdate != null) {
            updateTimeBoundary(tableForTimeBoundaryUpdate, externalViewForTimeBoundaryUpdate);
        } else {
            LOGGER.info("No need to update time boundary for table {}", tableName);
        }
    } catch (Exception e) {
        LOGGER.error("Failed to update the TimeBoundaryService", e);
    }
    long updateTime = System.currentTimeMillis() - startTimeMillis;
    if (_brokerMetrics != null) {
        _brokerMetrics.addTimedValue(BrokerTimer.ROUTING_TABLE_UPDATE_TIME, updateTime, TimeUnit.MILLISECONDS);
    }
    LOGGER.info("Routing table update for table {} completed in {} ms", tableName, updateTime);
}
Also used : BalancedRandomRoutingTableBuilder(com.linkedin.pinot.routing.builder.BalancedRandomRoutingTableBuilder) LargeClusterRoutingTableBuilder(com.linkedin.pinot.routing.builder.LargeClusterRoutingTableBuilder) KafkaHighLevelConsumerBasedRoutingTableBuilder(com.linkedin.pinot.routing.builder.KafkaHighLevelConsumerBasedRoutingTableBuilder) RoutingTableBuilder(com.linkedin.pinot.routing.builder.RoutingTableBuilder) KafkaLowLevelConsumerRoutingTableBuilder(com.linkedin.pinot.routing.builder.KafkaLowLevelConsumerRoutingTableBuilder) ExternalView(org.apache.helix.model.ExternalView) HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) InstanceConfig(org.apache.helix.model.InstanceConfig)

Example 84 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project pinot by linkedin.

the class HelixExternalViewBasedRouting method processExternalViewChange.

public void processExternalViewChange() {
    long startTime = System.currentTimeMillis();
    // Get list of tables that we're serving
    List<String> tablesServed = new ArrayList<>(_lastKnownExternalViewVersionMap.keySet());
    if (tablesServed.isEmpty()) {
        return;
    }
    // Build list of external views to fetch
    HelixDataAccessor helixDataAccessor = _helixManager.getHelixDataAccessor();
    PropertyKey.Builder propertyKeyBuilder = helixDataAccessor.keyBuilder();
    List<String> externalViewPaths = new ArrayList<>(tablesServed.size());
    for (String tableName : tablesServed) {
        PropertyKey propertyKey = propertyKeyBuilder.externalView(tableName);
        externalViewPaths.add(propertyKey.getPath());
    }
    // Get znode stats for all tables that we're serving
    long statStartTime = System.currentTimeMillis();
    Stat[] externalViewStats = helixDataAccessor.getBaseDataAccessor().getStats(externalViewPaths, AccessOption.PERSISTENT);
    long statEndTime = System.currentTimeMillis();
    // Make a list of external views that changed
    List<String> tablesThatChanged = new ArrayList<>();
    long evCheckStartTime = System.currentTimeMillis();
    for (int i = 0; i < externalViewStats.length; i++) {
        Stat externalViewStat = externalViewStats[i];
        if (externalViewStat != null) {
            String currentTableName = tablesServed.get(i);
            int currentExternalViewVersion = externalViewStat.getVersion();
            int lastKnownExternalViewVersion = _lastKnownExternalViewVersionMap.get(currentTableName);
            if (lastKnownExternalViewVersion != currentExternalViewVersion) {
                tablesThatChanged.add(currentTableName);
            }
        }
    }
    long evCheckEndTime = System.currentTimeMillis();
    // Fetch the instance configs and update the routing tables for the tables that changed
    long icFetchTime = 0;
    long rebuildStartTime = System.currentTimeMillis();
    if (!tablesThatChanged.isEmpty()) {
        // Fetch instance configs
        long icFetchStart = System.currentTimeMillis();
        List<InstanceConfig> instanceConfigs = helixDataAccessor.getChildValues(propertyKeyBuilder.instanceConfigs());
        long icFetchEnd = System.currentTimeMillis();
        icFetchTime = icFetchEnd - icFetchStart;
        for (String tableThatChanged : tablesThatChanged) {
            // We ignore the external views given by Helix on external view change and fetch the latest version as our
            // version of Helix (0.6.5) does not batch external view change messages.
            ExternalView externalView = helixDataAccessor.getProperty(propertyKeyBuilder.externalView(tableThatChanged));
            buildRoutingTable(tableThatChanged, externalView, instanceConfigs);
        }
    }
    long rebuildEndTime = System.currentTimeMillis();
    long endTime = System.currentTimeMillis();
    LOGGER.info("Processed external view change in {} ms (stat {} ms, EV check {} ms, IC fetch {} ms, rebuild {} ms), routing tables rebuilt for tables {}, {} / {} routing tables rebuilt", (endTime - startTime), (statEndTime - statStartTime), (evCheckEndTime - evCheckStartTime), icFetchTime, (rebuildEndTime - rebuildStartTime), tablesThatChanged, tablesThatChanged.size(), tablesServed.size());
}
Also used : ExternalView(org.apache.helix.model.ExternalView) ArrayList(java.util.ArrayList) HelixDataAccessor(org.apache.helix.HelixDataAccessor) Stat(org.apache.zookeeper.data.Stat) InstanceConfig(org.apache.helix.model.InstanceConfig) PropertyKey(org.apache.helix.PropertyKey)

Example 85 with InstanceConfig

use of org.apache.helix.model.InstanceConfig in project pinot by linkedin.

the class RandomRoutingTableTest method getInstanceConfigs.

/**
   * Returns a list of configs containing all instances in the external view.
   * @param externalView From which to extract the instance list from.
   * @return Instance Config list
   */
private List<InstanceConfig> getInstanceConfigs(ExternalView externalView) {
    List<InstanceConfig> instanceConfigList = new ArrayList<>();
    Set<String> instanceSet = new HashSet<>();
    // Collect all unique instances
    for (String partitionName : externalView.getPartitionSet()) {
        for (String instance : externalView.getStateMap(partitionName).keySet()) {
            if (!instanceSet.contains(instance)) {
                instanceConfigList.add(new InstanceConfig(instance));
                instanceSet.add(instance);
            }
        }
    }
    return instanceConfigList;
}
Also used : InstanceConfig(org.apache.helix.model.InstanceConfig) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet)

Aggregations

InstanceConfig (org.apache.helix.model.InstanceConfig)149 ArrayList (java.util.ArrayList)40 Test (org.testng.annotations.Test)35 HashMap (java.util.HashMap)32 HashSet (java.util.HashSet)28 ZNRecord (org.apache.helix.ZNRecord)26 IdealState (org.apache.helix.model.IdealState)24 ExternalView (org.apache.helix.model.ExternalView)23 Map (java.util.Map)21 HelixException (org.apache.helix.HelixException)21 HelixAdmin (org.apache.helix.HelixAdmin)20 List (java.util.List)19 ZKHelixAdmin (org.apache.helix.manager.zk.ZKHelixAdmin)19 HelixDataAccessor (org.apache.helix.HelixDataAccessor)17 ClusterMapConfig (com.github.ambry.config.ClusterMapConfig)15 Test (org.junit.Test)15 Set (java.util.Set)13 VerifiableProperties (com.github.ambry.config.VerifiableProperties)12 IOException (java.io.IOException)12 ZNRecord (org.apache.helix.zookeeper.datamodel.ZNRecord)12