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);
}
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);
}
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);
}
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());
}
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;
}
Aggregations