Search in sources :

Example 6 with DEFAULT_PARTITION_ID

use of com.linkedin.d2.balancer.util.partitions.DefaultPartitionAccessor.DEFAULT_PARTITION_ID 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;
}
Also used : TrackerClient(com.linkedin.d2.balancer.clients.TrackerClient) URI(java.net.URI)

Example 7 with DEFAULT_PARTITION_ID

use of com.linkedin.d2.balancer.util.partitions.DefaultPartitionAccessor.DEFAULT_PARTITION_ID 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());
        }
    }
}
Also used : TrackerClient(com.linkedin.d2.balancer.clients.TrackerClient)

Example 8 with DEFAULT_PARTITION_ID

use of com.linkedin.d2.balancer.util.partitions.DefaultPartitionAccessor.DEFAULT_PARTITION_ID in project rest.li by linkedin.

the class DegraderLoadBalancerStrategyV2_1 method updateState.

private void updateState(long clusterGenerationId, List<TrackerClient> trackerClients, DegraderLoadBalancerStrategyConfig config) {
    List<TrackerClientUpdater> clientUpdaters = new ArrayList<TrackerClientUpdater>();
    for (TrackerClient client : trackerClients) {
        clientUpdaters.add(new TrackerClientUpdater(client, DEFAULT_PARTITION_ID));
    }
    // doUpdateState has no side effects on _state or trackerClients.
    // all changes to the trackerClients would be recorded in clientUpdaters
    DegraderLoadBalancerState state = doUpdateState(clusterGenerationId, _state, config, clientUpdaters);
    _state = state;
    // only if state update succeeded, do we actually apply the recorded changes to trackerClients
    for (TrackerClientUpdater clientUpdater : clientUpdaters) {
        clientUpdater.update();
    }
}
Also used : TrackerClient(com.linkedin.d2.balancer.clients.TrackerClient) ArrayList(java.util.ArrayList)

Example 9 with DEFAULT_PARTITION_ID

use of com.linkedin.d2.balancer.util.partitions.DefaultPartitionAccessor.DEFAULT_PARTITION_ID 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");
}
Also used : DegraderTrackerClient(com.linkedin.d2.balancer.clients.DegraderTrackerClient) ArrayList(java.util.ArrayList) URIRequest(com.linkedin.d2.balancer.util.URIRequest) DegraderControl(com.linkedin.util.degrader.DegraderControl) URI(java.net.URI) CallCompletion(com.linkedin.util.degrader.CallCompletion) DegraderTrackerClient(com.linkedin.d2.balancer.clients.DegraderTrackerClient) TrackerClient(com.linkedin.d2.balancer.clients.TrackerClient) AtomicLong(java.util.concurrent.atomic.AtomicLong) DegraderTrackerClientImpl(com.linkedin.d2.balancer.clients.DegraderTrackerClientImpl) RequestContext(com.linkedin.r2.message.RequestContext) Test(org.testng.annotations.Test) DegraderTrackerClientTest(com.linkedin.d2.balancer.clients.DegraderTrackerClientTest)

Example 10 with DEFAULT_PARTITION_ID

use of com.linkedin.d2.balancer.util.partitions.DefaultPartitionAccessor.DEFAULT_PARTITION_ID 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");
}
Also used : DegraderTrackerClient(com.linkedin.d2.balancer.clients.DegraderTrackerClient) ArrayList(java.util.ArrayList) URIRequest(com.linkedin.d2.balancer.util.URIRequest) DegraderControl(com.linkedin.util.degrader.DegraderControl) URI(java.net.URI) CallCompletion(com.linkedin.util.degrader.CallCompletion) DegraderTrackerClient(com.linkedin.d2.balancer.clients.DegraderTrackerClient) TrackerClient(com.linkedin.d2.balancer.clients.TrackerClient) AtomicLong(java.util.concurrent.atomic.AtomicLong) DegraderTrackerClientImpl(com.linkedin.d2.balancer.clients.DegraderTrackerClientImpl) RequestContext(com.linkedin.r2.message.RequestContext)

Aggregations

Test (org.testng.annotations.Test)32 TrackerClient (com.linkedin.d2.balancer.clients.TrackerClient)30 URI (java.net.URI)28 ArrayList (java.util.ArrayList)26 DegraderTrackerClient (com.linkedin.d2.balancer.clients.DegraderTrackerClient)21 RequestContext (com.linkedin.r2.message.RequestContext)19 DegraderTrackerClientTest (com.linkedin.d2.balancer.clients.DegraderTrackerClientTest)17 DegraderControl (com.linkedin.util.degrader.DegraderControl)12 HashMap (java.util.HashMap)12 URIRequest (com.linkedin.d2.balancer.util.URIRequest)11 D2RelativeStrategyProperties (com.linkedin.d2.D2RelativeStrategyProperties)10 CallCompletion (com.linkedin.util.degrader.CallCompletion)10 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)9 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)7 AtomicLong (java.util.concurrent.atomic.AtomicLong)7 DegraderTrackerClientImpl (com.linkedin.d2.balancer.clients.DegraderTrackerClientImpl)6 HashSet (java.util.HashSet)6 CountDownLatch (java.util.concurrent.CountDownLatch)6 FutureCallback (com.linkedin.common.callback.FutureCallback)5 None (com.linkedin.common.util.None)5