@Override protected Map<String, String> getPartitionStateMap(ExternalView state) { Map<String, String> partitionState = new HashMap<>(); for (String partition : state.getPartitionSet()) { Map<String, String> instanceStateMap = state.getStateMap(partition); if (instanceStateMap.containsKey(_instanceName)) { partitionState.put(partition, instanceStateMap.get(_instanceName)); } } return partitionState; } }
/** * Get a set of offline instance from the external view of the resource. * * @param resourceExternalView External view of the resource * @return Set of string instance names of the offline instances in the external view. */ public static Set<String> getOfflineInstanceFromExternalView(ExternalView resourceExternalView) { Set<String> instanceSet = new HashSet<String>(); for (String partition : resourceExternalView.getPartitionSet()) { Map<String, String> stateMap = resourceExternalView.getStateMap(partition); for (String instance : stateMap.keySet()) { if (stateMap.get(instance).equalsIgnoreCase(OFFLINE)) { instanceSet.add(instance); } } } return instanceSet; }
/** * Returns the set of online instances from external view. * * @param resourceExternalView External view for the resource. * @return Set<String> of online instances in the external view for the resource. */ public static Set<String> getOnlineInstanceFromExternalView(ExternalView resourceExternalView) { Set<String> instanceSet = new HashSet<String>(); if (resourceExternalView != null) { for (String partition : resourceExternalView.getPartitionSet()) { Map<String, String> stateMap = resourceExternalView.getStateMap(partition); for (String instance : stateMap.keySet()) { if (stateMap.get(instance).equalsIgnoreCase(ONLINE)) { instanceSet.add(instance); } } } } return instanceSet; }
private void updateInstanceConfigsMapFromExternalView(Map<String, InstanceConfig> relevantInstanceConfigs, List<InstanceConfig> instanceConfigs, ExternalView externalView) { Set<String> relevantInstanceNames = new HashSet<>(); // Gather all the instance names contained in the external view for (String partitionName : externalView.getPartitionSet()) { relevantInstanceNames.addAll(externalView.getStateMap(partitionName).keySet()); } // Update the relevant instance config map with the instance configs given for (InstanceConfig instanceConfig : instanceConfigs) { if (relevantInstanceNames.contains(instanceConfig.getInstanceName())) { relevantInstanceConfigs.put(instanceConfig.getInstanceName(), instanceConfig); } } }
private boolean noOnlineResources(HelixManager spectatorManager, Set<String> resources) { Iterator<String> iterator = resources.iterator(); while (iterator.hasNext()) { String resourceName = iterator.next(); ExternalView externalView = spectatorManager.getClusterManagmentTool().getResourceExternalView(_helixClusterName, resourceName); if (externalView == null) { iterator.remove(); continue; } for (String partition : externalView.getPartitionSet()) { Map<String, String> instanceStateMap = externalView.getStateMap(partition); if (instanceStateMap.containsKey(_instanceId)) { if ("ONLINE".equals(instanceStateMap.get(_instanceId))) { return false; } } } iterator.remove(); } return true; }
private boolean isLargeCluster(ExternalView externalView) { // Check if the number of replicas is sufficient to treat it as a large cluster final String helixReplicaCount = externalView.getRecord().getSimpleField("REPLICAS"); final int replicaCount; try { replicaCount = Integer.parseInt(helixReplicaCount); } catch (Exception e) { LOGGER.warn("Failed to parse the replica count ({}) from external view of table {}", helixReplicaCount, externalView.getResourceName()); return false; } if (replicaCount < _minReplicaCountForLargeCluster) { return false; } // Check if the server count is high enough to count as a large cluster final Set<String> instanceSet = new HashSet<>(); for (String partition : externalView.getPartitionSet()) { instanceSet.addAll(externalView.getStateMap(partition).keySet()); } return _minServerCountForLargeCluster <= instanceSet.size(); }
@Override protected Map<String, List<String>> computeSegmentToServersMapFromExternalView(ExternalView externalView, List<InstanceConfig> instanceConfigs) { Map<String, List<String>> segmentToServersMap = new HashMap<>(); RoutingTableInstancePruner instancePruner = new RoutingTableInstancePruner(instanceConfigs); for (String segmentName : externalView.getPartitionSet()) { List<String> servers = new ArrayList<>(); for (Map.Entry<String, String> entry : externalView.getStateMap(segmentName).entrySet()) { String serverName = entry.getKey(); if (entry.getValue().equals(CommonConstants.Helix.StateModel.SegmentOnlineOfflineStateModel.ONLINE) && !instancePruner.isInactive(serverName) && SegmentName.isHighLevelConsumerSegmentName(segmentName)) { servers.add(serverName); } } if (servers.size() != 0) { segmentToServersMap.put(segmentName, servers); } else { handleNoServingHost(segmentName); } } return segmentToServersMap; }
/** * Given an external view and a list of instance configs, computes the mapping of segment to servers. The mapping * will be cached if we use dynamic routing. By default, this will check ONLINE segments and active servers. * * @param externalView an external view * @param instanceConfigs a list of instance config * @return a mapping of segment to servers */ protected Map<String, List<String>> computeSegmentToServersMapFromExternalView(ExternalView externalView, List<InstanceConfig> instanceConfigs) { Map<String, List<String>> segmentToServersMap = new HashMap<>(); RoutingTableInstancePruner instancePruner = new RoutingTableInstancePruner(instanceConfigs); for (String segmentName : externalView.getPartitionSet()) { // List of servers that are active and are serving the segment List<String> servers = new ArrayList<>(); for (Map.Entry<String, String> entry : externalView.getStateMap(segmentName).entrySet()) { String serverName = entry.getKey(); if (entry.getValue().equals(CommonConstants.Helix.StateModel.SegmentOnlineOfflineStateModel.ONLINE) && !instancePruner.isInactive(serverName)) { servers.add(serverName); } } if (!servers.isEmpty()) { segmentToServersMap.put(segmentName, servers); } else { handleNoServingHost(segmentName); } } return segmentToServersMap; }
@Override public boolean isRoutingTableValid(Map<String, List<String>> routingTable, ExternalView externalView, List<InstanceConfig> instanceConfigs) { Set<String> unassignedSegments = new HashSet<>(); unassignedSegments.addAll(externalView.getPartitionSet()); for (List<String> segmentsForServer : routingTable.values()) { if (!unassignedSegments.containsAll(segmentsForServer)) { // A segment is already assigned to another server and/or doesn't exist in external view return false; } unassignedSegments.removeAll(segmentsForServer); } return unassignedSegments.isEmpty(); } }, "Routing table should contain all segments exactly once");
@Override public Boolean call() throws Exception { return _helixAdmin.getResourceExternalView(HELIX_CLUSTER_NAME, DINING_TABLE_NAME).getPartitionSet().size() == SEGMENT_COUNT; } }, 30000L);
LLCUtils.sortSegmentsByStreamPartition(externalView.getPartitionSet());
private List<InstanceConfig> getInstanceConfigs(ExternalView externalView) { List<InstanceConfig> instanceConfigs = new ArrayList<>(); Set<String> instances = new HashSet<>(); // Collect all unique instances for (String partitionName : externalView.getPartitionSet()) { for (String instance : externalView.getStateMap(partitionName).keySet()) { if (!instances.contains(instance)) { instanceConfigs.add(new InstanceConfig(instance)); instances.add(instance); } } } return instanceConfigs; }
List<InstanceConfig> instanceConfigs) { Set<String> segmentSet = externalView.getPartitionSet(); for (String segmentName : segmentSet) { SegmentZKMetadata segmentZKMetadata = _segmentToZkMetadataMapping.get(segmentName); LLCUtils.sortSegmentsByStreamPartition(externalView.getPartitionSet());
List<InstanceConfig> instanceConfigs) { RoutingTableInstancePruner instancePruner = new RoutingTableInstancePruner(instanceConfigs); Set<String> segmentSet = externalView.getPartitionSet();
@Override public void computeOnExternalViewChange(String tableName, ExternalView externalView, List<InstanceConfig> instanceConfigs) { Set<String> segmentSet = externalView.getPartitionSet(); for (String segmentName : segmentSet) { if (SegmentName.isHighLevelConsumerSegmentName(segmentName)) { _hasHLC = true; } if (SegmentName.isLowLevelConsumerSegmentName(segmentName)) { _hasLLC = true; } } if (_hasHLC) { _realtimeHLCRoutingTableBuilder.computeOnExternalViewChange(tableName, externalView, instanceConfigs); } if (_hasLLC) { _realtimeLLCRoutingTableBuilder.computeOnExternalViewChange(tableName, externalView, instanceConfigs); } }
Set<String> offlineSegmentsServing = externalView.getPartitionSet(); if (offlineSegmentsServing.isEmpty()) { LOGGER.info("Skipping updating time boundary service for table '{}' with no offline segments.", tableName);
@Test public void testHelixExternalViewBasedRoutingTable() throws Exception { URL resourceUrl = getClass().getClassLoader().getResource("SampleExternalView.json"); Assert.assertNotNull(resourceUrl); String fileName = resourceUrl.getFile(); byte[] externalViewBytes = IOUtils.toByteArray(new FileInputStream(fileName)); ExternalView externalView = new ExternalView((ZNRecord) new ZNRecordSerializer().deserialize(externalViewBytes)); String tableName = externalView.getResourceName(); List<InstanceConfig> instanceConfigs = getInstanceConfigs(externalView); int numSegmentsInEV = externalView.getPartitionSet().size(); int numServersInEV = instanceConfigs.size(); HelixExternalViewBasedRouting routing = new HelixExternalViewBasedRouting(null, null, new BaseConfiguration()); routing.markDataResourceOnline(generateTableConfig(tableName), externalView, instanceConfigs); for (int i = 0; i < NUM_ROUNDS; i++) { Map<String, List<String>> routingTable = routing.getRoutingTable(new RoutingTableLookupRequest(tableName)); Assert.assertEquals(routingTable.size(), numServersInEV); int numSegments = 0; for (List<String> segmentsForServer : routingTable.values()) { int numSegmentsForServer = segmentsForServer.size(); Assert.assertTrue( numSegmentsForServer >= MIN_NUM_SEGMENTS_PER_SERVER && numSegmentsForServer <= MAX_NUM_SEGMENTS_PER_SERVER); numSegments += numSegmentsForServer; } Assert.assertEquals(numSegments, numSegmentsInEV); } }
@Test public void testDynamicRouting() throws Exception { String tableNameWithType = "testTable_OFFLINE"; BalancedRandomRoutingTableBuilder routingTableBuilder = new BalancedRandomRoutingTableBuilder(); TableConfig tableConfig = RoutingTableBuilderTestUtil.getDynamicComputingTableConfig(tableNameWithType); routingTableBuilder.init(new BaseConfiguration(), tableConfig, null, null); // Create external view ExternalView externalView = getDummyExternalView(); // Create instance configs List<InstanceConfig> instanceConfigList = getDummyInstanceConfigs(); // Build routing table routingTableBuilder.computeOnExternalViewChange("dummy", externalView, instanceConfigList); RoutingTableLookupRequest request = new RoutingTableLookupRequest(tableNameWithType); Map<String, List<String>> routingTable = routingTableBuilder.getRoutingTable(request, null); Set<String> segmentsInRoutingTable = new HashSet<>(); for (List<String> segments : routingTable.values()) { segmentsInRoutingTable.addAll(segments); } // Check that we picked all segments from the table Set<String> expectedSegments = externalView.getPartitionSet(); Assert.assertEquals(segmentsInRoutingTable, expectedSegments); }
Assert.assertEquals(externalView.getPartitionSet().size(), SEGMENT_COUNT); tableArray = brokerRoutingTableBuilderMap.keySet().toArray(); Arrays.sort(tableArray);
Assert.assertEquals(externalView.getPartitionSet().size(), 5);