use of com.linkedin.d2.balancer.util.URIRequest in project rest.li by linkedin.
the class DegraderLoadBalancerTest method simulateAndTestOneInterval.
/**
* simulates calling all the clients with the given QPS according to the given interval.
* Then verify that the DegraderLoadBalancerState behaves as expected.
* @param expectedPointsPerClient we'll verify if the points are smaller than the number given here
* @param isCalledWithError
* @param isCalledWithErrorForLoadBalancing
*/
private TrackerClient simulateAndTestOneInterval(long timeInterval, TestClock clock, double qps, List<TrackerClient> clients, DegraderLoadBalancerStrategyAdapter adapter, long clusterGenerationId, Integer expectedPointsPerClient, boolean isExpectingDropCallStrategyForNewState, double expectedClusterOverrideDropRate, long latency, boolean isCalledWithError, boolean isCalledWithErrorForLoadBalancing) {
callClients(latency, qps, clients, clock, timeInterval, isCalledWithError, isCalledWithErrorForLoadBalancing);
//create any random URIRequest because we just need a URI to be hashed to get the point in hash ring anyway
if (clients != null && !clients.isEmpty()) {
URIRequest request = new URIRequest(clients.get(0).getUri());
TrackerClient client = getTrackerClient(adapter, request, new RequestContext(), clusterGenerationId, clients);
Map<URI, Integer> pointsMap = adapter.getPointsMap();
for (TrackerClient trackerClient : clients) {
Integer pointsInTheRing = pointsMap.get(trackerClient.getUri());
assertEquals(pointsInTheRing, expectedPointsPerClient);
}
if (isExpectingDropCallStrategyForNewState) {
assertTrue(adapter.isStrategyCallDrop());
} else {
assertFalse(adapter.isStrategyCallDrop());
}
assertEquals(adapter.getCurrentOverrideDropRate(), expectedClusterOverrideDropRate);
return client;
}
return null;
}
use of com.linkedin.d2.balancer.util.URIRequest in project rest.li by linkedin.
the class RingBasedUriMapper method distributeToHosts.
private <KEY> Map<URI, Set<KEY>> distributeToHosts(Map<Integer, List<URIKeyPair<KEY>>> requestsByParititonId, Map<Integer, Ring<URI>> rings, HashFunction<Request> hashFunction, Map<URI, Integer> hostToPartitionId, Map<Integer, Set<KEY>> unmapped) {
if (hashFunction instanceof RandomHash) {
return distributeToHostNonSticky(requestsByParititonId, rings, hostToPartitionId, unmapped);
}
Map<URI, Set<KEY>> hostToKeySet = new HashMap<>();
for (Map.Entry<Integer, List<URIKeyPair<KEY>>> entry : requestsByParititonId.entrySet()) {
int partitionId = entry.getKey();
for (URIKeyPair<KEY> request : entry.getValue()) {
int hashcode = hashFunction.hash(new URIRequest(request.getRequestUri()));
URI resolvedHost = rings.get(partitionId).get(hashcode);
if (resolvedHost == null) {
// under custom use case, key will be null, in which case we will just return a map from partition id to empty set
// Users should be able to understand what partitions do not have available hosts by examining the keys in "unmapped"
Set<KEY> unmappedKeys = convertURIKeyPairListToKeySet(entry.getValue());
unmapped.computeIfAbsent(entry.getKey(), k -> new HashSet<>()).addAll(unmappedKeys);
break;
} else {
// under custom use case, key will be null, in which case we will just return a map from uri to empty set
hostToPartitionId.putIfAbsent(resolvedHost, entry.getKey());
Set<KEY> newSet = hostToKeySet.computeIfAbsent(resolvedHost, host -> new HashSet<>());
if (request.getKey() != null) {
newSet.add(request.getKey());
}
}
}
}
return hostToKeySet;
}
use of com.linkedin.d2.balancer.util.URIRequest in project rest.li by linkedin.
the class DegraderLoadBalancerTest method testClusterRecovery2TC.
@Test(groups = { "small", "back-end" })
public void testClusterRecovery2TC() {
final int NUM_CHECKS = 5;
final Long TIME_INTERVAL = 5000L;
Map<String, Object> myMap = lbDefaultConfig();
// 1,2,4,8,16,32,64,100% steps, given a 2x recovery step coefficient
int localStepsToFullRecovery = 8;
myMap.put(PropertyKeys.HTTP_LB_INITIAL_RECOVERY_LEVEL, 0.005);
myMap.put(PropertyKeys.HTTP_LB_RING_RAMP_FACTOR, 2d);
myMap.put(PropertyKeys.HTTP_LB_STRATEGY_PROPERTIES_UPDATE_INTERVAL_MS, TIME_INTERVAL);
TestClock clock = new TestClock();
myMap.put(PropertyKeys.CLOCK, clock);
DegraderLoadBalancerStrategyConfig config = DegraderLoadBalancerStrategyConfig.createHttpConfigFromMap(myMap);
DegraderLoadBalancerStrategyV3 strategy = new DegraderLoadBalancerStrategyV3(config, "DegraderLoadBalancerTest", null, DEGRADER_STATE_LISTENER_FACTORIES);
List<DegraderTrackerClient> clients = new ArrayList<>();
URI uri1 = URI.create("http://test.linkedin.com:3242/fdsaf");
URI uri2 = URI.create("http://test.linkedin.com:3243/fdsaf");
URIRequest request = new URIRequest(uri1);
List<CallCompletion> ccList = new ArrayList<>();
CallCompletion cc;
DegraderTrackerClient client1 = new DegraderTrackerClientImpl(uri1, getDefaultPartitionData(1d), new TestLoadBalancerClient(uri1), clock, null);
DegraderTrackerClient client2 = new DegraderTrackerClientImpl(uri2, getDefaultPartitionData(1d), new TestLoadBalancerClient(uri2), clock, null);
clients.add(client1);
clients.add(client2);
// force client1 to be disabled if we encounter errors/high latency
DegraderControl dcClient1Default = client1.getDegraderControl(DEFAULT_PARTITION_ID);
dcClient1Default.setMinCallCount(5);
dcClient1Default.setOverrideMinCallCount(5);
dcClient1Default.setUpStep(1.0);
// force client2 to be disabled if we encounter errors/high latency
DegraderControl dcClient2Default = client2.getDegraderControl(DEFAULT_PARTITION_ID);
dcClient2Default.setOverrideMinCallCount(5);
dcClient2Default.setMinCallCount(5);
dcClient2Default.setUpStep(0.4);
// Have one cycle of successful calls to verify valid tracker clients returned.
// try load balancing on this updateState, need to updateState before forcing the strategy.
TrackerClient resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
strategy.setStrategy(DEFAULT_PARTITION_ID, PartitionDegraderLoadBalancerState.Strategy.LOAD_BALANCE);
resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
assertNotNull(resultTC, "expected non-null trackerclient");
for (int j = 0; j < NUM_CHECKS; j++) {
ccList.add(client1.getCallTracker().startCall());
ccList.add(client2.getCallTracker().startCall());
}
clock.addMs(1);
for (Iterator<CallCompletion> iter = ccList.listIterator(); iter.hasNext(); ) {
cc = iter.next();
cc.endCall();
}
// bump to next interval, and get stats.
clock.addMs(5000);
// try Load balancing on this updateState
strategy.setStrategy(DEFAULT_PARTITION_ID, PartitionDegraderLoadBalancerState.Strategy.LOAD_BALANCE);
resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
assertNotNull(resultTC, "expected non-null trackerclient");
Assert.assertEquals(dcClient1Default.getCurrentComputedDropRate(), 0.0);
Assert.assertEquals(dcClient2Default.getCurrentComputedDropRate(), 0.0);
// now simulate a bad cluster state with high error and high latency
for (int j = 0; j < NUM_CHECKS; j++) {
ccList.add(client1.getCallTracker().startCall());
ccList.add(client2.getCallTracker().startCall());
}
clock.addMs(3500);
for (Iterator<CallCompletion> iter = ccList.listIterator(); iter.hasNext(); ) {
cc = iter.next();
cc.endCallWithError();
}
// go to next interval
clock.addMs(5000);
Assert.assertEquals(dcClient1Default.getCurrentComputedDropRate(), 1.0);
Assert.assertEquals(dcClient2Default.getCurrentComputedDropRate(), 0.4);
// trigger a state update, the returned TrackerClient should be client2
// because client 1 should have gone up to a 1.0 drop rate, and the cluster should
// be unhealthy
strategy.setStrategy(DEFAULT_PARTITION_ID, PartitionDegraderLoadBalancerState.Strategy.LOAD_BALANCE);
resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
assertEquals(resultTC, client2);
// it's current drop rate.
do {
// go to next time interval.
clock.addMs(TIME_INTERVAL);
// adjust the hash ring this time.
strategy.setStrategy(DEFAULT_PARTITION_ID, PartitionDegraderLoadBalancerState.Strategy.LOAD_BALANCE);
resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
localStepsToFullRecovery--;
} while (localStepsToFullRecovery > 0);
assertNotNull(resultTC, "expected non-null trackerclient");
assertTrue(strategy.getState().getPartitionState(DEFAULT_PARTITION_ID).getPointsMap().get(client1.getUri()) == client1.getPartitionWeight(DEFAULT_PARTITION_ID) * config.getPointsPerWeight(), "client1 did not recover to full weight in hash map.");
Assert.assertEquals(dcClient2Default.getCurrentComputedDropRate(), 0.4, "client2 drop rate not as expected");
cc = client1.getCallTracker().startCall();
clock.addMs(10);
cc.endCall();
clock.addMs(TIME_INTERVAL);
resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
assertNotNull(resultTC, "expected non-null trackerclient");
}
use of com.linkedin.d2.balancer.util.URIRequest in project rest.li by linkedin.
the class DegraderLoadBalancerTest method testRegexHashingConsistency.
@Test(groups = { "small", "back-end" })
public void testRegexHashingConsistency() {
final int NUM_SERVERS = 100;
DegraderLoadBalancerStrategyV3 strategy = new DegraderLoadBalancerStrategyV3(new DegraderLoadBalancerStrategyConfig(5000, true, 100, DegraderLoadBalancerStrategyV3.HASH_METHOD_URI_REGEX, Collections.<String, Object>singletonMap(URIRegexHash.KEY_REGEXES, Collections.singletonList("(.*)")), SystemClock.instance(), DegraderLoadBalancerStrategyConfig.DEFAULT_INITIAL_RECOVERY_LEVEL, DegraderLoadBalancerStrategyConfig.DEFAULT_RAMP_FACTOR, DegraderLoadBalancerStrategyConfig.DEFAULT_HIGH_WATER_MARK, DegraderLoadBalancerStrategyConfig.DEFAULT_LOW_WATER_MARK, DegraderLoadBalancerStrategyConfig.DEFAULT_GLOBAL_STEP_UP, DegraderLoadBalancerStrategyConfig.DEFAULT_GLOBAL_STEP_DOWN, DegraderLoadBalancerStrategyConfig.DEFAULT_CLUSTER_MIN_CALL_COUNT_HIGH_WATER_MARK, DegraderLoadBalancerStrategyConfig.DEFAULT_CLUSTER_MIN_CALL_COUNT_LOW_WATER_MARK, DegraderLoadBalancerStrategyConfig.DEFAULT_HASHRING_POINT_CLEANUP_RATE, null, DegraderLoadBalancerStrategyConfig.DEFAULT_NUM_PROBES, DegraderLoadBalancerStrategyConfig.DEFAULT_POINTS_PER_HOST, DegraderLoadBalancerStrategyConfig.DEFAULT_BOUNDED_LOAD_BALANCING_FACTOR, null, DegraderLoadBalancerStrategyConfig.DEFAULT_QUARANTINE_MAXPERCENT, null, null, DegraderLoadBalancerStrategyConfig.DEFAULT_QUARANTINE_METHOD, null, DegraderImpl.DEFAULT_LOW_LATENCY, null, DegraderLoadBalancerStrategyConfig.DEFAULT_LOW_EVENT_EMITTING_INTERVAL, DegraderLoadBalancerStrategyConfig.DEFAULT_HIGH_EVENT_EMITTING_INTERVAL, DegraderLoadBalancerStrategyConfig.DEFAULT_CLUSTER_NAME), "DegraderLoadBalancerTest", null, DEGRADER_STATE_LISTENER_FACTORIES);
List<DegraderTrackerClient> clients = new ArrayList<>(NUM_SERVERS);
for (int i = 0; i < NUM_SERVERS; i++) {
clients.add(getClient(URI.create("http://server" + i + ".testing:9876/foobar")));
}
final int NUM_URIS = 1000;
final int NUM_CHECKS = 10;
final Map<TrackerClient, Integer> serverCounts = new HashMap<>();
for (int i = 0; i < NUM_URIS; i++) {
URIRequest request = new URIRequest("d2://fooService/this/is/a/test/" + i);
TrackerClient lastClient = null;
for (int j = 0; j < NUM_CHECKS; j++) {
TrackerClient client = getTrackerClient(strategy, request, new RequestContext(), 0, clients);
assertNotNull(client);
if (lastClient != null) {
assertEquals(client, lastClient);
}
lastClient = client;
}
Integer count = serverCounts.get(lastClient);
if (count == null) {
count = 0;
}
serverCounts.put(lastClient, count + 1);
}
}
use of com.linkedin.d2.balancer.util.URIRequest in project rest.li by linkedin.
the class DegraderLoadBalancerTest method clusterRecovery1TC.
/**
* helper method to test DegraderLoadBalancerStrategy recovery with 1 TrackerClient.
*
* We want to test DegraderV2 and V3 with 2 different strategies : LoadBalacing and Call Dropping.
* So this method needs to able to handle all 4 permutations.
*
* @param myMap
* @param clock
* @param stepsToFullRecovery
* @param timeInterval
* @param strategy
*/
public void clusterRecovery1TC(Map<String, Object> myMap, TestClock clock, int stepsToFullRecovery, Long timeInterval, DegraderLoadBalancerStrategyAdapter strategy, PartitionDegraderLoadBalancerState.Strategy strategyV3) {
final int NUM_CHECKS = 5;
final Long TIME_INTERVAL = timeInterval;
int localStepsToFullRecovery = stepsToFullRecovery;
DegraderLoadBalancerStrategyConfig config = DegraderLoadBalancerStrategyConfig.createHttpConfigFromMap(myMap);
List<DegraderTrackerClient> clients = new ArrayList<>();
URI uri1 = URI.create("http://test.linkedin.com:3242/fdsaf");
URIRequest request = new URIRequest(uri1);
DegraderTrackerClient client1 = new DegraderTrackerClientImpl(uri1, getDefaultPartitionData(1d), new TestLoadBalancerClient(uri1), clock, null);
clients.add(client1);
// force client1 to be disabled
DegraderControl dcClient1Default = client1.getDegraderControl(DEFAULT_PARTITION_ID);
dcClient1Default.setOverrideMinCallCount(5);
dcClient1Default.setMinCallCount(5);
dcClient1Default.setMaxDropRate(1d);
dcClient1Default.setUpStep(1.0d);
List<CallCompletion> ccList = new ArrayList<>();
CallCompletion cc;
for (int j = 0; j < NUM_CHECKS; j++) {
cc = client1.getCallTracker().startCall();
ccList.add(cc);
}
// add high latency and errors to shut off traffic to this tracker client.
// note: the default values for highError and lowError in the degrader are 1.1,
// which means we don't use errorRates when deciding when to lb/degrade.
// In addition, because we changed to use the
clock.addMs(3500);
// for (int j = 0; j < NUM_CHECKS; j++)
for (Iterator<CallCompletion> iter = ccList.listIterator(); iter.hasNext(); ) {
cc = iter.next();
cc.endCallWithError();
iter.remove();
}
// go to next time interval.
clock.addMs(TIME_INTERVAL);
Assert.assertEquals(dcClient1Default.getCurrentComputedDropRate(), 1.0);
// trigger a state update
TrackerClient resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
if (config.getInitialRecoveryLevel() < 0.01) {
// the returned TrackerClient should be null
assertNull(resultTC, "expected null trackerclient");
// tracker client, so it's time to try it out. We need to enter this code at least once.
do {
// go to next time interval.
clock.addMs(TIME_INTERVAL);
// try adjusting the hash ring on this updateState
if (strategyV3 != null) {
strategy.setStrategyV3(DEFAULT_PARTITION_ID, strategyV3);
} else {
fail("should set strategy (either LoadBalance or Degrader");
}
resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
localStepsToFullRecovery--;
} while (localStepsToFullRecovery > 0);
}
assertNotNull(resultTC, "expected non-null trackerclient");
// make calls to the tracker client to verify that it's on the road to healthy status.
for (int j = 0; j < NUM_CHECKS; j++) {
cc = ((DegraderTrackerClient) resultTC).getCallTracker().startCall();
ccList.add(cc);
}
clock.addMs(10);
for (Iterator<CallCompletion> iter = ccList.listIterator(); iter.hasNext(); ) {
cc = iter.next();
cc.endCall();
iter.remove();
}
// go to next time interval.
clock.addMs(TIME_INTERVAL);
Assert.assertTrue(dcClient1Default.getCurrentComputedDropRate() < 1d);
resultTC = getTrackerClient(strategy, request, new RequestContext(), 1, clients);
assertNotNull(resultTC, "expected non-null trackerclient");
}
Aggregations