use of com.linkedin.d2.balancer.util.hashing.Ring in project rest.li by linkedin.
the class DegraderLoadBalancerStrategyV2_1 method getTrackerClient.
@Override
public TrackerClient getTrackerClient(Request request, RequestContext requestContext, long clusterGenerationId, int partitionId, List<TrackerClient> trackerClients) {
if (partitionId != DEFAULT_PARTITION_ID) {
throw new UnsupportedOperationException("Trying to access partition: " + partitionId + "on an unpartitioned cluster");
}
debug(_log, "getTrackerClient with generation id ", clusterGenerationId, " on tracker clients: ", clusterGenerationId);
if (trackerClients == null || trackerClients.size() == 0) {
warn(_log, "getTrackerClient called with null/empty trackerClients, so returning null");
return null;
}
// only one thread will be allowed to enter updateState.
checkUpdateState(clusterGenerationId, trackerClients);
URI targetHostUri = KeyMapper.TargetHostHints.getRequestContextTargetHost(requestContext);
Set<URI> excludedUris = ExcludedHostHints.getRequestContextExcludedHosts(requestContext);
URI hostHeaderUri = targetHostUri;
//no valid target host header was found in the request
if (targetHostUri == null) {
// Compute the hash code
int hashCode = _hashFunction.hash(request);
// we operate only on URIs to ensure that we never hold on to an old tracker client
// that the cluster manager has removed
Ring<URI> ring = _state.getRing();
Iterator<URI> iterator = ring.getIterator(hashCode);
while (iterator.hasNext() && targetHostUri == null) {
URI uri = iterator.next();
if (excludedUris == null || !excludedUris.contains(uri)) {
targetHostUri = uri;
}
}
ExcludedHostHints.addRequestContextExcludedHost(requestContext, targetHostUri);
} else {
debug(_log, "Degrader honoring target host header in request, skipping hashing. URI: " + targetHostUri.toString());
}
TrackerClient client = null;
if (targetHostUri != null) {
// consistent hash ring! Therefore, this linear scan is the best we can do.
for (TrackerClient trackerClient : trackerClients) {
if (trackerClient.getUri().equals(targetHostUri)) {
client = trackerClient;
break;
}
}
if (client == null) {
warn(_log, "No client found for " + targetHostUri + (hostHeaderUri == null ? ", degrader load balancer state is inconsistent with cluster manager" : ", target host specified is no longer part of cluster"));
}
} else {
warn(_log, "unable to find a URI to use");
}
boolean dropCall = client == null;
if (!dropCall) {
dropCall = client.getDegrader(DEFAULT_PARTITION_ID).checkDrop();
if (dropCall) {
warn(_log, "client's degrader is dropping call for: ", client);
} else {
debug(_log, "returning client: ", client);
}
}
return (!dropCall) ? client : null;
}
use of com.linkedin.d2.balancer.util.hashing.Ring in project rest.li by linkedin.
the class DegraderLoadBalancerStrategyV2_1 method overrideMinCallCount.
/**
* Both the drop in hash ring points and the global drop rate influence the minimum call count
* that we should see to qualify for a state update. Currently, both factors are equally weighed,
* and multiplied together to come up with a scale factor. With this scheme, if either factor is
* zero, then the overrideMinCallCount will be set to 1. If both factors are at half weight, then
* the overall weight will be .5 * .5 = .25 of the original minCallCount.
*
* @param newOverrideDropRate
* @param trackerClientUpdaters
* @param pointsMap
* @param pointsPerWeight
*/
public static void overrideMinCallCount(double newOverrideDropRate, List<TrackerClientUpdater> trackerClientUpdaters, Map<URI, Integer> pointsMap, int pointsPerWeight) {
for (TrackerClientUpdater clientUpdater : trackerClientUpdaters) {
TrackerClient client = clientUpdater.getTrackerClient();
int currentOverrideMinCallCount = client.getDegraderControl(DEFAULT_PARTITION_ID).getOverrideMinCallCount();
double hashFactor = pointsMap.get(client.getUri()) / pointsPerWeight;
double transmitFactor = 1.0 - newOverrideDropRate;
int newOverrideMinCallCount = (int) Math.max(Math.round(client.getDegraderControl(DEFAULT_PARTITION_ID).getMinCallCount() * hashFactor * transmitFactor), 1);
if (newOverrideMinCallCount != currentOverrideMinCallCount) {
clientUpdater.setOverrideMinCallCount(newOverrideMinCallCount);
warn(_log, "overriding Min Call Count to ", newOverrideMinCallCount, " for client: ", client.getUri());
}
}
}
use of com.linkedin.d2.balancer.util.hashing.Ring in project rest.li by linkedin.
the class SimpleLoadBalancerTest method testGetPartitionInfoOrdering.
/**
* This tests the getPartitionInfo() when given a collection of keys (actually a test for KeyMapper.mapKeysV3()).
*/
@Test
public void testGetPartitionInfoOrdering() throws Exception {
String serviceName = "articles";
String clusterName = "cluster";
String path = "path";
String strategyName = "degrader";
// setup 3 partitions. Partition 1 and Partition 2 both have server1 - server3. Partition 3 only has server1.
Map<URI, Map<Integer, PartitionData>> partitionDescriptions = new HashMap<URI, Map<Integer, PartitionData>>();
final URI server1 = new URI("http://foo1.com");
Map<Integer, PartitionData> server1Data = new HashMap<Integer, PartitionData>();
server1Data.put(1, new PartitionData(1.0));
server1Data.put(2, new PartitionData(1.0));
server1Data.put(3, new PartitionData(1.0));
partitionDescriptions.put(server1, server1Data);
final URI server2 = new URI("http://foo2.com");
Map<Integer, PartitionData> server2Data = new HashMap<Integer, PartitionData>();
server2Data.put(1, new PartitionData(1.0));
server2Data.put(2, new PartitionData(1.0));
partitionDescriptions.put(server2, server2Data);
final URI server3 = new URI("http://foo3.com");
Map<Integer, PartitionData> server3Data = new HashMap<Integer, PartitionData>();
server3Data.put(1, new PartitionData(1.0));
server3Data.put(2, new PartitionData(1.0));
partitionDescriptions.put(server3, server3Data);
//setup strategy which involves tweaking the hash ring to get partitionId -> URI host
List<LoadBalancerState.SchemeStrategyPair> orderedStrategies = new ArrayList<LoadBalancerState.SchemeStrategyPair>();
LoadBalancerStrategy strategy = new TestLoadBalancerStrategy(partitionDescriptions);
orderedStrategies.add(new LoadBalancerState.SchemeStrategyPair("http", strategy));
//setup the partition accessor which can only map keys from 1 - 3.
PartitionAccessor accessor = new TestPartitionAccessor();
URI serviceURI = new URI("d2://" + serviceName);
SimpleLoadBalancer balancer = new SimpleLoadBalancer(new PartitionedLoadBalancerTestState(clusterName, serviceName, path, strategyName, partitionDescriptions, orderedStrategies, accessor));
List<Integer> keys = new ArrayList<Integer>();
keys.add(1);
keys.add(2);
keys.add(3);
keys.add(123);
HostToKeyMapper<Integer> result = balancer.getPartitionInformation(serviceURI, keys, 3, 123);
Assert.assertEquals(result.getLimitHostPerPartition(), 3);
Assert.assertEquals(1, result.getUnmappedKeys().size());
Assert.assertEquals(123, (int) result.getUnmappedKeys().iterator().next().getKey());
//partition 0 should be null
Assert.assertNull(result.getPartitionInfoMap().get(0));
// results for partition 1 should contain server1, server2 and server3
KeysAndHosts<Integer> keysAndHosts1 = result.getPartitionInfoMap().get(1);
Assert.assertTrue(keysAndHosts1.getKeys().size() == 1);
Assert.assertTrue(keysAndHosts1.getKeys().iterator().next() == 1);
List<URI> ordering1 = keysAndHosts1.getHosts();
// results for partition 2 should be the same as partition1.
KeysAndHosts<Integer> keysAndHosts2 = result.getPartitionInfoMap().get(2);
Assert.assertTrue(keysAndHosts2.getKeys().size() == 1);
Assert.assertTrue(keysAndHosts2.getKeys().iterator().next() == 2);
List<URI> ordering2 = keysAndHosts2.getHosts();
//for partition 3
KeysAndHosts<Integer> keysAndHosts3 = result.getPartitionInfoMap().get(3);
Assert.assertTrue(keysAndHosts3.getKeys().size() == 1);
Assert.assertTrue(keysAndHosts3.getKeys().iterator().next() == 3);
List<URI> ordering3 = keysAndHosts3.getHosts();
// Just compare the size and contents of the list, not the ordering.
Assert.assertTrue(ordering1.size() == 3);
List<URI> allServers = new ArrayList<>();
allServers.add(server1);
allServers.add(server2);
allServers.add(server3);
Assert.assertTrue(ordering1.containsAll(allServers));
Assert.assertTrue(ordering2.containsAll(allServers));
Assert.assertEquals(ordering1, ordering2);
Assert.assertEquals(ordering3.get(0), server1);
Assert.assertTrue(result.getPartitionsWithoutEnoughHosts().containsKey(3));
Assert.assertEquals((int) result.getPartitionsWithoutEnoughHosts().get(3), 2);
}
use of com.linkedin.d2.balancer.util.hashing.Ring in project rest.li by linkedin.
the class SimpleLoadBalancerTest method testGetAllPartitionMultipleHostsOrdering.
/**
* This tests the getPartitionInfo() when keys are null (actually a test for KeyMapper.getAllPartitionMultipleHosts()).
*/
@Test
public void testGetAllPartitionMultipleHostsOrdering() throws Exception {
String serviceName = "articles";
String clusterName = "cluster";
String path = "path";
String strategyName = "degrader";
//setup partition
Map<URI, Map<Integer, PartitionData>> partitionDescriptions = new HashMap<URI, Map<Integer, PartitionData>>();
final URI server1 = new URI("http://foo1.com");
Map<Integer, PartitionData> server1Data = new HashMap<Integer, PartitionData>();
server1Data.put(1, new PartitionData(1.0));
server1Data.put(2, new PartitionData(1.0));
server1Data.put(3, new PartitionData(1.0));
partitionDescriptions.put(server1, server1Data);
final URI server2 = new URI("http://foo2.com");
Map<Integer, PartitionData> server2Data = new HashMap<Integer, PartitionData>();
server2Data.put(1, new PartitionData(1.0));
server2Data.put(2, new PartitionData(1.0));
//server2Data.put(3, new PartitionData(1.0));
partitionDescriptions.put(server2, server2Data);
final URI server3 = new URI("http://foo3.com");
Map<Integer, PartitionData> server3Data = new HashMap<Integer, PartitionData>();
server3Data.put(1, new PartitionData(1.0));
server3Data.put(2, new PartitionData(1.0));
//server3Data.put(3, new PartitionData(1.0));
partitionDescriptions.put(server3, server3Data);
//setup strategy which involves tweaking the hash ring to get partitionId -> URI host
List<LoadBalancerState.SchemeStrategyPair> orderedStrategies = new ArrayList<LoadBalancerState.SchemeStrategyPair>();
LoadBalancerStrategy strategy = new TestLoadBalancerStrategy(partitionDescriptions);
orderedStrategies.add(new LoadBalancerState.SchemeStrategyPair("http", strategy));
//setup the partition accessor which is used to get partitionId -> keys
PartitionAccessor accessor = new TestPartitionAccessor();
URI serviceURI = new URI("d2://" + serviceName);
SimpleLoadBalancer balancer = new SimpleLoadBalancer(new PartitionedLoadBalancerTestState(clusterName, serviceName, path, strategyName, partitionDescriptions, orderedStrategies, accessor));
HostToKeyMapper<URI> result = balancer.getPartitionInformation(serviceURI, null, 3, 123);
Assert.assertEquals(result.getPartitionInfoMap().size(), 4);
Assert.assertEquals(4, result.getPartitionCount());
// partition 0 should be empty
Assert.assertTrue(result.getPartitionInfoMap().get(0).getHosts().isEmpty());
// partition 1 should have server1, server2 and server3.
List<URI> ordering1 = result.getPartitionInfoMap().get(1).getHosts();
List<URI> allServers = new ArrayList<>();
allServers.add(server1);
allServers.add(server2);
allServers.add(server3);
Assert.assertTrue(ordering1.size() == 3);
Assert.assertTrue(ordering1.containsAll(allServers));
// partition 2 should be the same as partition 1
List<URI> ordering2 = result.getPartitionInfoMap().get(2).getHosts();
Assert.assertEquals(ordering1, ordering2);
// partition 3 should only contain server1
List<URI> ordering3 = result.getPartitionInfoMap().get(3).getHosts();
Assert.assertEquals(ordering3.get(0), server1);
// partition 0 and partition 3 should not have enough hosts: lacking 3 and 2 respectively.
Assert.assertTrue(result.getPartitionsWithoutEnoughHosts().containsKey(3));
Assert.assertTrue(result.getPartitionsWithoutEnoughHosts().containsKey(0));
Assert.assertEquals((int) result.getPartitionsWithoutEnoughHosts().get(3), 2);
Assert.assertEquals((int) result.getPartitionsWithoutEnoughHosts().get(0), 3);
}
use of com.linkedin.d2.balancer.util.hashing.Ring in project rest.li by linkedin.
the class DegraderLoadBalancerTest method testInconsistentHashAndTrackerclients.
@Test(groups = { "small", "back-end" }, dataProvider = "consistentHashAlgorithms")
public void testInconsistentHashAndTrackerclients(String consistentHashAlgorithm) throws URISyntaxException, InterruptedException {
// check if the inconsistent Hash ring and trackerlients can be handled
TestClock clock = new TestClock();
Map<String, Object> myMap = new HashMap<String, Object>();
myMap.put(PropertyKeys.CLOCK, clock);
myMap.put(PropertyKeys.HTTP_LB_CONSISTENT_HASH_ALGORITHM, consistentHashAlgorithm);
DegraderLoadBalancerStrategyConfig config = DegraderLoadBalancerStrategyConfig.createHttpConfigFromMap(myMap);
DegraderLoadBalancerStrategyV3 strategy = new DegraderLoadBalancerStrategyV3(config, "DegraderLoadBalancerTest", null);
List<TrackerClient> clients = new ArrayList<TrackerClient>();
clients.add(getClient(URI.create("http://test.linkedin.com:3242/fdsaf"), clock));
clients.add(getClient(URI.create("http://test.linkedin.com:3243/fdsaf"), clock));
TrackerClient chosen = getTrackerClient(strategy, null, new RequestContext(), 0, clients);
assertNotNull(chosen);
// remove the client from the list, now the ring and the trackerClient list are inconsistent
clients.remove(chosen);
assertNotNull(getTrackerClient(strategy, null, new RequestContext(), 0, clients));
// update the hash ring we should get the results as well
assertNotNull(getTrackerClient(strategy, null, new RequestContext(), 1, clients));
}
Aggregations