Search in sources :

Example 1 with BackupRequestsClient

use of com.linkedin.d2.balancer.clients.BackupRequestsClient in project rest.li by linkedin.

the class TestBackupRequestsClient method testStatsConsumerRemoveOne.

@Test(dataProvider = "isD2Async")
public void testStatsConsumerRemoveOne(boolean isD2Async) throws Exception {
    AtomicReference<ServiceProperties> serviceProperties = new AtomicReference<>();
    TestBackupRequestsStrategyStatsConsumer statsConsumer = new TestBackupRequestsStrategyStatsConsumer();
    serviceProperties.set(createServiceProperties(null));
    BackupRequestsClient client = createClient(serviceProperties::get, statsConsumer, isD2Async);
    URI uri = URI.create("d2://testService");
    RestRequest restRequest = new RestRequestBuilder(uri).setEntity(CONTENT).build();
    RequestContext requestContext = new RequestContext();
    requestContext.putLocalAttr(R2Constants.OPERATION, "get");
    Future<RestResponse> response = client.restRequest(restRequest, requestContext);
    assertEquals(response.get().getStatus(), 200);
    List<StatsConsumerEvent> events = statsConsumer.getEvents();
    assertEquals(events.size(), 0);
    serviceProperties.set(createServiceProperties(Arrays.asList(createBackupRequestsConfiguration(5, "get"), createBackupRequestsConfiguration(1, "batch_get"))));
    requestContext = new RequestContext();
    requestContext.putLocalAttr(R2Constants.OPERATION, "get");
    response = client.restRequest(restRequest, requestContext);
    assertEquals(response.get().getStatus(), 200);
    events = statsConsumer.getEvents();
    assertEquals(events.size(), 2);
    assertEquals(events.get(0).isEventAdd(), true);
    assertEquals(events.get(0).getService(), SERVICE_NAME);
    assertEquals(events.get(0).getOperation(), "get");
    BackupRequestsStrategyStatsProvider statsProvider1 = events.get(0).getStatsProvider();
    assertNotNull(statsProvider1);
    assertEquals(events.get(1).isEventAdd(), true);
    assertEquals(events.get(1).getService(), SERVICE_NAME);
    assertEquals(events.get(1).getOperation(), "batch_get");
    BackupRequestsStrategyStatsProvider statsProvider2 = events.get(1).getStatsProvider();
    assertNotNull(statsProvider2);
    serviceProperties.set(createServiceProperties(Arrays.asList(createBackupRequestsConfiguration(5, "get"))));
    requestContext = new RequestContext();
    requestContext.putLocalAttr(R2Constants.OPERATION, "get");
    response = client.restRequest(restRequest, requestContext);
    assertEquals(response.get().getStatus(), 200);
    events = statsConsumer.getEvents();
    assertEquals(events.size(), 3);
    assertEquals(events.get(2).isEventAdd(), false);
    assertEquals(events.get(2).getService(), SERVICE_NAME);
    assertEquals(events.get(2).getOperation(), "batch_get");
    BackupRequestsStrategyStatsProvider removedStatsProvider = events.get(2).getStatsProvider();
    assertNotNull(removedStatsProvider);
    assertSame(statsProvider2, removedStatsProvider);
}
Also used : RestResponse(com.linkedin.r2.message.rest.RestResponse) AtomicReference(java.util.concurrent.atomic.AtomicReference) URI(java.net.URI) ServiceProperties(com.linkedin.d2.balancer.properties.ServiceProperties) RestRequest(com.linkedin.r2.message.rest.RestRequest) BackupRequestsStrategyStatsProvider(com.linkedin.d2.backuprequests.BackupRequestsStrategyStatsProvider) RestRequestBuilder(com.linkedin.r2.message.rest.RestRequestBuilder) RequestContext(com.linkedin.r2.message.RequestContext) Test(org.testng.annotations.Test) AfterTest(org.testng.annotations.AfterTest) BeforeTest(org.testng.annotations.BeforeTest)

Example 2 with BackupRequestsClient

use of com.linkedin.d2.balancer.clients.BackupRequestsClient in project rest.li by linkedin.

the class TestBackupRequestsClient method testStatsConsumerLatencyUpdate.

// @Test - Disabled due to flakiness. See SI-3077 to track and resolve this.
public void testStatsConsumerLatencyUpdate() throws Exception {
    AtomicReference<ServiceProperties> serviceProperties = new AtomicReference<>();
    TestBackupRequestsStrategyStatsConsumer statsConsumer = new TestBackupRequestsStrategyStatsConsumer();
    serviceProperties.set(createServiceProperties(null));
    BackupRequestsClient client = createClient(serviceProperties::get, statsConsumer, new ConstantResponseTimeDistribution(1, TimeUnit.NANOSECONDS), false);
    URI uri = URI.create("d2://testService");
    RestRequest restRequest = new RestRequestBuilder(uri).setEntity(CONTENT).build();
    RequestContext requestContext = new RequestContext();
    requestContext.putLocalAttr(R2Constants.OPERATION, "get");
    Future<RestResponse> response = client.restRequest(restRequest, requestContext);
    assertEquals(response.get().getStatus(), 200);
    List<StatsConsumerEvent> events = statsConsumer.getEvents();
    assertEquals(events.size(), 0);
    for (int i = 0; i < Short.MAX_VALUE * 4; i++) {
        requestContext = new RequestContext();
        requestContext.putLocalAttr(R2Constants.OPERATION, "get");
        response = client.restRequest(restRequest, requestContext);
        assertEquals(response.get().getStatus(), 200);
    }
    assertEquals(statsConsumer.getLatencyWithBackup().size(), 0);
    assertEquals(statsConsumer.getLatencyWithoutBackup().size(), 0);
    serviceProperties.set(createServiceProperties(Arrays.asList(createBackupRequestsConfiguration(5, "get"))));
    while (statsConsumer.getLatencyWithoutBackup().size() < 1) {
        requestContext = new RequestContext();
        requestContext.putLocalAttr(R2Constants.OPERATION, "get");
        response = client.restRequest(restRequest, requestContext);
        assertEquals(response.get().getStatus(), 200);
    }
    assertEquals(statsConsumer.getLatencyWithoutBackup().size(), 1);
    assertEquals(statsConsumer.getLatencyWithBackup().size(), 1);
    // allowing 1% imprecision
    long expected = statsConsumer.getLatencyWithoutBackup().get(0).getTotalCount();
    long actual = statsConsumer.getLatencyWithBackup().get(0).getTotalCount();
    assertTrue(actual > expected * .99 && actual < expected * 1.01, "Expected: " + expected + "+-" + (expected * .01) + ", but actual: " + actual);
}
Also used : RestResponse(com.linkedin.r2.message.rest.RestResponse) AtomicReference(java.util.concurrent.atomic.AtomicReference) URI(java.net.URI) ConstantResponseTimeDistribution(com.linkedin.d2.backuprequests.ConstantResponseTimeDistribution) ServiceProperties(com.linkedin.d2.balancer.properties.ServiceProperties) RestRequest(com.linkedin.r2.message.rest.RestRequest) RestRequestBuilder(com.linkedin.r2.message.rest.RestRequestBuilder) RequestContext(com.linkedin.r2.message.RequestContext)

Example 3 with BackupRequestsClient

use of com.linkedin.d2.balancer.clients.BackupRequestsClient in project rest.li by linkedin.

the class D2ClientBuilder method build.

/**
 * @return {@link D2Client} that is not started yet. Call start(Callback) to start it.
 */
public D2Client build() {
    final Map<String, TransportClientFactory> transportClientFactories = (_config.clientFactories == null) ? // if user didn't provide transportClientFactories we'll use default ones
    createDefaultTransportClientFactories() : _config.clientFactories;
    List<ScheduledExecutorService> executorsToShutDown = new ArrayList<>();
    if (_config.startUpExecutorService == null) {
        // creating an executor that when there are no tasks to execute doesn't create any thread.
        _config.startUpExecutorService = Executors.newScheduledThreadPool(0, new NamedThreadFactory("D2 StartupOnlyExecutor"));
        executorsToShutDown.add(_config.startUpExecutorService);
    }
    if (_config._executorService == null) {
        LOG.warn("No executor service passed as argument. Pass it for " + "enhanced monitoring and to have better control over the executor.");
        _config._executorService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("D2 PropertyEventExecutor"));
        executorsToShutDown.add(_config._executorService);
    }
    if (_config.downstreamServicesFetcher == null) {
        _config.downstreamServicesFetcher = new FSBasedDownstreamServicesFetcher(_config.fsBasePath, _config.d2ServicePath);
    }
    if (_config.jmxManager == null) {
        _config.jmxManager = new NoOpJmxManager();
    }
    if (_config.d2ServicePath == null || // checking empty for backward compatibility with ZKFS behavior
    _config.d2ServicePath.isEmpty()) {
        _config.d2ServicePath = ZKFSUtil.SERVICE_PATH;
    }
    final Map<String, LoadBalancerStrategyFactory<? extends LoadBalancerStrategy>> loadBalancerStrategyFactories = createDefaultLoadBalancerStrategyFactories();
    final D2ClientConfig cfg = new D2ClientConfig(_config.zkHosts, _config.zkSessionTimeoutInMs, _config.zkStartupTimeoutInMs, _config.lbWaitTimeout, _config.lbWaitUnit, _config.flagFile, _config.basePath, _config.fsBasePath, _config.componentFactory, transportClientFactories, _config.lbWithFacilitiesFactory, _config.sslContext, _config.sslParameters, _config.isSSLEnabled, _config.shutdownAsynchronously, _config.isSymlinkAware, _config.clientServicesConfig, _config.d2ServicePath, _config.useNewEphemeralStoreWatcher, _config.healthCheckOperations, _config._executorService, _config.retry, _config.restRetryEnabled, _config.streamRetryEnabled, _config.retryLimit, _config.retryUpdateIntervalMs, _config.retryAggregatedIntervalNum, _config.warmUp, _config.warmUpTimeoutSeconds, _config.warmUpConcurrentRequests, _config.downstreamServicesFetcher, _config.backupRequestsEnabled, _config.backupRequestsStrategyStatsConsumer, _config.backupRequestsLatencyNotificationInterval, _config.backupRequestsLatencyNotificationIntervalUnit, _config.enableBackupRequestsClientAsync, _config._backupRequestsExecutorService, _config.eventEmitter, _config.partitionAccessorRegistry, _config.zooKeeperDecorator, _config.enableSaveUriDataOnDisk, loadBalancerStrategyFactories, _config.requestTimeoutHandlerEnabled, _config.sslSessionValidatorFactory, _config.zkConnectionToUseForLB, _config.startUpExecutorService, _config.jmxManager, _config.d2JmxManagerPrefix, _config.zookeeperReadWindowMs, _config.enableRelativeLoadBalancer, _config.deterministicSubsettingMetadataProvider);
    final LoadBalancerWithFacilitiesFactory loadBalancerFactory = (_config.lbWithFacilitiesFactory == null) ? new ZKFSLoadBalancerWithFacilitiesFactory() : _config.lbWithFacilitiesFactory;
    LoadBalancerWithFacilities loadBalancer = loadBalancerFactory.create(cfg);
    D2Client d2Client = new DynamicClient(loadBalancer, loadBalancer, _restOverStream);
    if (_config.requestTimeoutHandlerEnabled) {
        d2Client = new RequestTimeoutClient(d2Client, loadBalancer, _config._executorService);
    }
    if (_config.backupRequestsEnabled) {
        ScheduledExecutorService executor = _config._backupRequestsExecutorService;
        if (executor == null) {
            LOG.warn("Backup Requests Executor not configured, creating one with core pool size equal to: " + Runtime.getRuntime().availableProcessors());
            executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("Backup Requests Executor"));
            executorsToShutDown.add(executor);
        }
        d2Client = new BackupRequestsClient(d2Client, loadBalancer, executor, _config.backupRequestsStrategyStatsConsumer, _config.backupRequestsLatencyNotificationInterval, _config.backupRequestsLatencyNotificationIntervalUnit, _config.enableBackupRequestsClientAsync);
    }
    if (_config.retry) {
        d2Client = new RetryClient(d2Client, loadBalancer, _config.retryLimit, _config.retryUpdateIntervalMs, _config.retryAggregatedIntervalNum, SystemClock.instance(), true, true);
    } else if (_config.restRetryEnabled || _config.streamRetryEnabled) {
        d2Client = new RetryClient(d2Client, loadBalancer, _config.retryLimit, _config.retryUpdateIntervalMs, _config.retryAggregatedIntervalNum, SystemClock.instance(), _config.restRetryEnabled, _config.streamRetryEnabled);
    }
    // is being shut down.
    if (_config.clientFactories != transportClientFactories) {
        d2Client = new TransportClientFactoryAwareD2Client(d2Client, transportClientFactories.values());
    }
    if (executorsToShutDown.size() > 0) {
        d2Client = new ExecutorShutdownAwareD2Client(d2Client, executorsToShutDown);
    }
    return d2Client;
}
Also used : ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) RandomLoadBalancerStrategyFactory(com.linkedin.d2.balancer.strategies.random.RandomLoadBalancerStrategyFactory) RelativeLoadBalancerStrategyFactory(com.linkedin.d2.balancer.strategies.relative.RelativeLoadBalancerStrategyFactory) LoadBalancerStrategyFactory(com.linkedin.d2.balancer.strategies.LoadBalancerStrategyFactory) NamedThreadFactory(com.linkedin.r2.util.NamedThreadFactory) ArrayList(java.util.ArrayList) FSBasedDownstreamServicesFetcher(com.linkedin.d2.balancer.util.downstreams.FSBasedDownstreamServicesFetcher) NoOpJmxManager(com.linkedin.d2.jmx.NoOpJmxManager) RelativeLoadBalancerStrategy(com.linkedin.d2.balancer.strategies.relative.RelativeLoadBalancerStrategy) LoadBalancerStrategy(com.linkedin.d2.balancer.strategies.LoadBalancerStrategy) RequestTimeoutClient(com.linkedin.d2.balancer.clients.RequestTimeoutClient) DynamicClient(com.linkedin.d2.balancer.clients.DynamicClient) BackupRequestsClient(com.linkedin.d2.balancer.clients.BackupRequestsClient) TransportClientFactory(com.linkedin.r2.transport.common.TransportClientFactory) RetryClient(com.linkedin.d2.balancer.clients.RetryClient)

Example 4 with BackupRequestsClient

use of com.linkedin.d2.balancer.clients.BackupRequestsClient in project rest.li by linkedin.

the class TestBackupRequestsClient method createAlwaysBackupClientWithHosts.

private BackupRequestsClient createAlwaysBackupClientWithHosts(List<String> uris, Deque<URI> hostsReceivingRequestList, int responseDelayNano, int backupDelayNano, boolean isD2Async) throws IOException {
    Map<URI, Map<Integer, PartitionData>> partitionDescriptions = new HashMap<>();
    uris.forEach(uri -> partitionDescriptions.put(URI.create(uri), Collections.singletonMap(0, new PartitionData(1))));
    StaticLoadBalancerState LbState = new StaticLoadBalancerState() {

        @Override
        public TrackerClient getClient(String serviceName, URI uri) {
            return new DegraderTrackerClientImpl(uri, partitionDescriptions.get(uri), null, SystemClock.instance(), null) {

                @Override
                public void restRequest(RestRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<RestResponse> callback) {
                    // whenever a trackerClient is used to make request, record down it's hostname
                    hostsReceivingRequestList.add(uri);
                    // delay response to allow backup request to happen
                    _executor.schedule(() -> callback.onResponse(TransportResponseImpl.success(new RestResponseBuilder().build())), responseDelayNano, TimeUnit.NANOSECONDS);
                }

                @Override
                public void streamRequest(StreamRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<StreamResponse> callback) {
                    // whenever a trackerClient is used to make request, record down it's hostname
                    hostsReceivingRequestList.add(uri);
                    if (null != requestContext.getLocalAttr(R2Constants.BACKUP_REQUEST_BUFFERED_BODY)) {
                        callback.onResponse(TransportResponseImpl.success(new StreamResponseBuilder().setHeader(BUFFERED_HEADER, String.valueOf(requestContext.getLocalAttr(R2Constants.BACKUP_REQUEST_BUFFERED_BODY) != null)).build(EntityStreams.emptyStream())));
                        return;
                    }
                    request.getEntityStream().setReader(new DrainReader() {

                        public void onDone() {
                            // delay response to allow backup request to happen
                            _executor.schedule(() -> callback.onResponse(TransportResponseImpl.success(new StreamResponseBuilder().setHeader(BUFFERED_HEADER, String.valueOf(requestContext.getLocalAttr(R2Constants.BACKUP_REQUEST_BUFFERED_BODY) != null)).build(EntityStreams.emptyStream()))), responseDelayNano, TimeUnit.NANOSECONDS);
                        }
                    });
                }
            };
        }
    };
    LbState.TEST_URIS_PARTITIONDESCRIPTIONS.putAll(partitionDescriptions);
    LbState.TEST_SERVICE_BACKUP_REQUEST_PROPERTIES.add(createBackupRequestsConfiguration(5, "get"));
    LbState.refreshDefaultProperties();
    LoadBalancer loadBalancer = new SimpleLoadBalancer(LbState, _executor);
    DynamicClient dynamicClient = new DynamicClient(loadBalancer, null);
    return new BackupRequestsClient(dynamicClient, loadBalancer, _executor, null, 10, TimeUnit.SECONDS, isD2Async) {

        @Override
        Optional<TrackingBackupRequestsStrategy> getStrategyAfterUpdate(final String serviceName, final String operation) {
            // constantly enable backup request after backupDelayNano time.
            BackupRequestsStrategy alwaysBackup = new TestTrackingBackupRequestsStrategy.MockBackupRequestsStrategy(() -> Optional.of((long) backupDelayNano), () -> true);
            return Optional.of(new TrackingBackupRequestsStrategy(alwaysBackup));
        }
    };
}
Also used : TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) StreamResponseBuilder(com.linkedin.r2.message.stream.StreamResponseBuilder) SimpleLoadBalancer(com.linkedin.d2.balancer.simple.SimpleLoadBalancer) HashMap(java.util.HashMap) RestResponseBuilder(com.linkedin.r2.message.rest.RestResponseBuilder) SimpleLoadBalancer(com.linkedin.d2.balancer.simple.SimpleLoadBalancer) LoadBalancer(com.linkedin.d2.balancer.LoadBalancer) ByteString(com.linkedin.data.ByteString) TrackingBackupRequestsStrategy(com.linkedin.d2.backuprequests.TrackingBackupRequestsStrategy) TestTrackingBackupRequestsStrategy(com.linkedin.d2.backuprequests.TestTrackingBackupRequestsStrategy) URI(java.net.URI) DrainReader(com.linkedin.r2.message.stream.entitystream.DrainReader) StreamRequest(com.linkedin.r2.message.stream.StreamRequest) RestRequest(com.linkedin.r2.message.rest.RestRequest) BackupRequestsStrategy(com.linkedin.d2.backuprequests.BackupRequestsStrategy) TrackingBackupRequestsStrategy(com.linkedin.d2.backuprequests.TrackingBackupRequestsStrategy) TestTrackingBackupRequestsStrategy(com.linkedin.d2.backuprequests.TestTrackingBackupRequestsStrategy) PartitionData(com.linkedin.d2.balancer.properties.PartitionData) StaticLoadBalancerState(com.linkedin.d2.balancer.StaticLoadBalancerState) RequestContext(com.linkedin.r2.message.RequestContext) Map(java.util.Map) HashMap(java.util.HashMap)

Example 5 with BackupRequestsClient

use of com.linkedin.d2.balancer.clients.BackupRequestsClient in project rest.li by linkedin.

the class TestBackupRequestsClient method testD2ServiceUnavailable.

@Test(dataProvider = "isD2Async")
public void testD2ServiceUnavailable(boolean isD2Async) throws Exception {
    LoadBalancer loadBalancer = new TestLoadBalancer(new ConstantResponseTimeDistribution(1, TimeUnit.NANOSECONDS), null, new ServiceUnavailableException("", ""));
    TestBackupRequestsStrategyStatsConsumer statsConsumer = new TestBackupRequestsStrategyStatsConsumer();
    BackupRequestsClient client = createClient(statsConsumer, loadBalancer, isD2Async);
    URI uri = URI.create("d2://testService");
    RestRequest restRequest = new RestRequestBuilder(uri).setEntity(CONTENT).build();
    RequestContext requestContext = new RequestContext();
    requestContext.putLocalAttr(R2Constants.OPERATION, "get");
    Future<RestResponse> response = client.restRequest(restRequest, requestContext);
    assertEquals(response.get().getStatus(), 200, "If D2 call fails, we should fallback to request without backup");
    List<StatsConsumerEvent> events = statsConsumer.getEvents();
    assertEquals(events.size(), 0);
}
Also used : RestResponse(com.linkedin.r2.message.rest.RestResponse) SimpleLoadBalancer(com.linkedin.d2.balancer.simple.SimpleLoadBalancer) LoadBalancer(com.linkedin.d2.balancer.LoadBalancer) ServiceUnavailableException(com.linkedin.d2.balancer.ServiceUnavailableException) URI(java.net.URI) ConstantResponseTimeDistribution(com.linkedin.d2.backuprequests.ConstantResponseTimeDistribution) RestRequest(com.linkedin.r2.message.rest.RestRequest) RestRequestBuilder(com.linkedin.r2.message.rest.RestRequestBuilder) RequestContext(com.linkedin.r2.message.RequestContext) Test(org.testng.annotations.Test) AfterTest(org.testng.annotations.AfterTest) BeforeTest(org.testng.annotations.BeforeTest)

Aggregations

RestRequest (com.linkedin.r2.message.rest.RestRequest)8 URI (java.net.URI)8 RequestContext (com.linkedin.r2.message.RequestContext)7 RestRequestBuilder (com.linkedin.r2.message.rest.RestRequestBuilder)7 RestResponse (com.linkedin.r2.message.rest.RestResponse)7 ServiceProperties (com.linkedin.d2.balancer.properties.ServiceProperties)6 AtomicReference (java.util.concurrent.atomic.AtomicReference)6 AfterTest (org.testng.annotations.AfterTest)5 BeforeTest (org.testng.annotations.BeforeTest)5 Test (org.testng.annotations.Test)5 BackupRequestsStrategyStatsProvider (com.linkedin.d2.backuprequests.BackupRequestsStrategyStatsProvider)3 ConstantResponseTimeDistribution (com.linkedin.d2.backuprequests.ConstantResponseTimeDistribution)3 LoadBalancer (com.linkedin.d2.balancer.LoadBalancer)2 ServiceUnavailableException (com.linkedin.d2.balancer.ServiceUnavailableException)2 SimpleLoadBalancer (com.linkedin.d2.balancer.simple.SimpleLoadBalancer)2 JsonParseException (com.fasterxml.jackson.core.JsonParseException)1 JsonMappingException (com.fasterxml.jackson.databind.JsonMappingException)1 BackupRequestsStrategy (com.linkedin.d2.backuprequests.BackupRequestsStrategy)1 EventsArrival (com.linkedin.d2.backuprequests.EventsArrival)1 GaussianResponseTimeDistribution (com.linkedin.d2.backuprequests.GaussianResponseTimeDistribution)1