Search in sources :

Example 1 with ConnectionFactory

use of io.servicetalk.client.api.ConnectionFactory in project servicetalk by apple.

the class RoundRobinLoadBalancer method selectConnection0.

private Single<C> selectConnection0(Predicate<C> selector) {
    final List<Host<ResolvedAddress, C>> usedHosts = this.usedHosts;
    if (usedHosts.isEmpty()) {
        return usedHosts == CLOSED_LIST ? failedLBClosed(targetResource) : // This is the case when SD has emitted some items but none of the hosts are available.
        failed(StacklessNoAvailableHostException.newInstance("No hosts are available to connect for " + targetResource + ".", RoundRobinLoadBalancer.class, "selectConnection0(...)"));
    }
    // try one loop over hosts and if all are expired, give up
    final int cursor = (indexUpdater.getAndIncrement(this) & Integer.MAX_VALUE) % usedHosts.size();
    final ThreadLocalRandom rnd = ThreadLocalRandom.current();
    Host<ResolvedAddress, C> pickedHost = null;
    for (int i = 0; i < usedHosts.size(); ++i) {
        // for a particular iteration we maintain a local cursor without contention with other requests
        int localCursor = (cursor + i) % usedHosts.size();
        final Host<ResolvedAddress, C> host = usedHosts.get(localCursor);
        assert host != null : "Host can't be null.";
        // Try first to see if an existing connection can be used
        final Object[] connections = host.connState.connections;
        // With small enough search space, attempt all connections.
        // Back off after exploring most of the search space, it gives diminishing returns.
        final int attempts = connections.length < MIN_SEARCH_SPACE ? connections.length : (int) (connections.length * SEARCH_FACTOR);
        for (int j = 0; j < attempts; ++j) {
            @SuppressWarnings("unchecked") final C connection = (C) connections[rnd.nextInt(connections.length)];
            if (selector.test(connection)) {
                return succeeded(connection);
            }
        }
        // Unhealthy hosts have no open connections – that's why we don't fail earlier, the loop will not progress.
        if (host.isActiveAndHealthy()) {
            pickedHost = host;
            break;
        }
    }
    if (pickedHost == null) {
        return failed(StacklessNoAvailableHostException.newInstance("Failed to pick an active host for " + targetResource + ". Either all are busy, expired, or unhealthy: " + usedHosts, RoundRobinLoadBalancer.class, "selectConnection0(...)"));
    }
    // No connection was selected: create a new one.
    final Host<ResolvedAddress, C> host = pickedHost;
    // This LB implementation does not automatically provide TransportObserver. Therefore, we pass "null" here.
    // Users can apply a ConnectionFactoryFilter if they need to override this "null" value with TransportObserver.
    Single<? extends C> establishConnection = connectionFactory.newConnection(host.address, null);
    if (host.healthCheckConfig != null) {
        // Schedule health check before returning
        establishConnection = establishConnection.beforeOnError(t -> host.markUnhealthy(t, connectionFactory));
    }
    return establishConnection.flatMap(newCnx -> {
        // used concurrently and hence a new connection can be rejected by the selector.
        if (!selector.test(newCnx)) {
            // with the fact that select failure does not close a connection.
            return newCnx.closeAsync().concat(failed(StacklessConnectionRejectedException.newInstance("Newly created connection " + newCnx + " for " + targetResource + " was rejected by the selection filter.", RoundRobinLoadBalancer.class, "selectConnection0(...)")));
        }
        if (host.addConnection(newCnx)) {
            return succeeded(newCnx);
        }
        return newCnx.closeAsync().concat(this.usedHosts == CLOSED_LIST ? failedLBClosed(targetResource) : failed(StacklessConnectionRejectedException.newInstance("Failed to add newly created connection " + newCnx + " for " + targetResource + " for " + host, RoundRobinLoadBalancer.class, "selectConnection0(...)")));
    });
}
Also used : Arrays(java.util.Arrays) LoggerFactory(org.slf4j.LoggerFactory) LoadBalancer(io.servicetalk.client.api.LoadBalancer) ServiceDiscovererEvent(io.servicetalk.client.api.ServiceDiscovererEvent) Integer.toHexString(java.lang.Integer.toHexString) Collections.singletonList(java.util.Collections.singletonList) SourceAdapters.fromSource(io.servicetalk.concurrent.api.SourceAdapters.fromSource) UNAVAILABLE(io.servicetalk.client.api.ServiceDiscovererEvent.Status.UNAVAILABLE) Duration(java.time.Duration) Executor(io.servicetalk.concurrent.api.Executor) AtomicReferenceFieldUpdater.newUpdater(java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater) LOAD_BALANCER_NOT_READY_EVENT(io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_NOT_READY_EVENT) EXPIRED(io.servicetalk.client.api.ServiceDiscovererEvent.Status.EXPIRED) AtomicReferenceFieldUpdater(java.util.concurrent.atomic.AtomicReferenceFieldUpdater) Predicate(java.util.function.Predicate) Collections.emptyList(java.util.Collections.emptyList) Collection(java.util.Collection) CompositeCloseable(io.servicetalk.concurrent.api.CompositeCloseable) AsyncCloseables.newCompositeCloseable(io.servicetalk.concurrent.api.AsyncCloseables.newCompositeCloseable) List(java.util.List) Stream(java.util.stream.Stream) ConnectionRejectedException(io.servicetalk.client.api.ConnectionRejectedException) Entry(java.util.Map.Entry) AtomicIntegerFieldUpdater(java.util.concurrent.atomic.AtomicIntegerFieldUpdater) Publisher(io.servicetalk.concurrent.api.Publisher) Processor(io.servicetalk.concurrent.PublisherSource.Processor) SimpleImmutableEntry(java.util.AbstractMap.SimpleImmutableEntry) Function(java.util.function.Function) Subscriber(io.servicetalk.concurrent.PublisherSource.Subscriber) ArrayList(java.util.ArrayList) AVAILABLE(io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE) Single.succeeded(io.servicetalk.concurrent.api.Single.succeeded) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom) Objects.requireNonNull(java.util.Objects.requireNonNull) DelayedCancellable(io.servicetalk.concurrent.internal.DelayedCancellable) AsyncCloseable(io.servicetalk.concurrent.api.AsyncCloseable) Publisher.from(io.servicetalk.concurrent.api.Publisher.from) AtomicIntegerFieldUpdater.newUpdater(java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater) Nullable(javax.annotation.Nullable) ConnectionFactory(io.servicetalk.client.api.ConnectionFactory) Processors.newPublisherProcessorDropHeadOnOverflow(io.servicetalk.concurrent.api.Processors.newPublisherProcessorDropHeadOnOverflow) Logger(org.slf4j.Logger) RetryStrategies.retryWithConstantBackoffFullJitter(io.servicetalk.concurrent.api.RetryStrategies.retryWithConstantBackoffFullJitter) ListenableAsyncCloseable(io.servicetalk.concurrent.api.ListenableAsyncCloseable) Single.defer(io.servicetalk.concurrent.api.Single.defer) FlowControlUtils.addWithOverflowProtection(io.servicetalk.concurrent.internal.FlowControlUtils.addWithOverflowProtection) LoadBalancedConnection(io.servicetalk.client.api.LoadBalancedConnection) Single(io.servicetalk.concurrent.api.Single) Completable(io.servicetalk.concurrent.api.Completable) LOAD_BALANCER_READY_EVENT(io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_READY_EVENT) NoAvailableHostException(io.servicetalk.client.api.NoAvailableHostException) AsyncCloseables.toAsyncCloseable(io.servicetalk.concurrent.api.AsyncCloseables.toAsyncCloseable) Subscription(io.servicetalk.concurrent.PublisherSource.Subscription) SourceAdapters.toSource(io.servicetalk.concurrent.api.SourceAdapters.toSource) ThrowableUtils(io.servicetalk.concurrent.internal.ThrowableUtils) Collectors.toList(java.util.stream.Collectors.toList) Completable.completed(io.servicetalk.concurrent.api.Completable.completed) Single.failed(io.servicetalk.concurrent.api.Single.failed) SequentialCancellable(io.servicetalk.concurrent.internal.SequentialCancellable) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom)

Example 2 with ConnectionFactory

use of io.servicetalk.client.api.ConnectionFactory in project servicetalk by apple.

the class RoundRobinLoadBalancerTest method connectionFactoryErrorPropagation.

@Test
void connectionFactoryErrorPropagation() {
    serviceDiscoveryPublisher.onComplete();
    connectionFactory = new DelegatingConnectionFactory(__ -> failed(DELIBERATE_EXCEPTION));
    lb = defaultLb(connectionFactory);
    sendServiceDiscoveryEvents(upEvent("address-1"));
    ExecutionException ex = assertThrows(ExecutionException.class, () -> awaitIndefinitely(lb.selectConnection(any())));
    assertThat(ex.getCause(), is(instanceOf(DeliberateException.class)));
}
Also used : LegacyTestSingle(io.servicetalk.concurrent.api.LegacyTestSingle) BeforeEach(org.junit.jupiter.api.BeforeEach) Arrays(java.util.Arrays) DEFAULT_TIMEOUT_SECONDS(io.servicetalk.concurrent.internal.TestTimeoutConstants.DEFAULT_TIMEOUT_SECONDS) ServiceDiscovererEvent(io.servicetalk.client.api.ServiceDiscovererEvent) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) UNAVAILABLE(io.servicetalk.client.api.ServiceDiscovererEvent.Status.UNAVAILABLE) Duration(java.time.Duration) Map(java.util.Map) Executor(io.servicetalk.concurrent.api.Executor) Collectors.toSet(java.util.stream.Collectors.toSet) DelegatingExecutor(io.servicetalk.concurrent.api.DelegatingExecutor) CyclicBarrier(java.util.concurrent.CyclicBarrier) EXPIRED(io.servicetalk.client.api.ServiceDiscovererEvent.Status.EXPIRED) ExecutorExtension(io.servicetalk.concurrent.api.ExecutorExtension) DEFAULT_HEALTH_CHECK_FAILED_CONNECTIONS_THRESHOLD(io.servicetalk.loadbalancer.RoundRobinLoadBalancerFactory.DEFAULT_HEALTH_CHECK_FAILED_CONNECTIONS_THRESHOLD) Matchers.notNullValue(org.hamcrest.Matchers.notNullValue) Predicate(java.util.function.Predicate) Matchers.lessThanOrEqualTo(org.hamcrest.Matchers.lessThanOrEqualTo) Collection(java.util.Collection) Set(java.util.Set) DefaultServiceDiscovererEvent(io.servicetalk.client.api.DefaultServiceDiscovererEvent) MILLISECONDS(java.util.concurrent.TimeUnit.MILLISECONDS) CopyOnWriteArraySet(java.util.concurrent.CopyOnWriteArraySet) Collectors(java.util.stream.Collectors) TestSubscription(io.servicetalk.concurrent.api.TestSubscription) Executors(java.util.concurrent.Executors) Test(org.junit.jupiter.api.Test) Matchers.instanceOf(org.hamcrest.Matchers.instanceOf) CountDownLatch(java.util.concurrent.CountDownLatch) List(java.util.List) TestExecutor(io.servicetalk.concurrent.api.TestExecutor) Matchers.contains(org.hamcrest.Matchers.contains) ConnectionRejectedException(io.servicetalk.client.api.ConnectionRejectedException) Matchers.equalTo(org.hamcrest.Matchers.equalTo) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) TransportObserver(io.servicetalk.transport.api.TransportObserver) Matchers.greaterThan(org.hamcrest.Matchers.greaterThan) Matchers.is(org.hamcrest.Matchers.is) Queue(java.util.Queue) ConcurrentLinkedQueue(java.util.concurrent.ConcurrentLinkedQueue) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) Mockito.mock(org.mockito.Mockito.mock) IntStream(java.util.stream.IntStream) Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) Assertions.fail(org.junit.jupiter.api.Assertions.fail) TestPublisher(io.servicetalk.concurrent.api.TestPublisher) Publisher(io.servicetalk.concurrent.api.Publisher) DeliberateException(io.servicetalk.concurrent.internal.DeliberateException) Assertions.assertNull(org.junit.jupiter.api.Assertions.assertNull) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) Subscriber(io.servicetalk.concurrent.PublisherSource.Subscriber) Matchers.hasProperty(org.hamcrest.Matchers.hasProperty) AVAILABLE(io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE) BlockingTestUtils.awaitIndefinitely(io.servicetalk.concurrent.api.BlockingTestUtils.awaitIndefinitely) RegisterExtension(org.junit.jupiter.api.extension.RegisterExtension) Single.succeeded(io.servicetalk.concurrent.api.Single.succeeded) Matchers.hasSize(org.hamcrest.Matchers.hasSize) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) NoSuchElementException(java.util.NoSuchElementException) DELIBERATE_EXCEPTION(io.servicetalk.concurrent.internal.DeliberateException.DELIBERATE_EXCEPTION) UNHEALTHY_HOST_EXCEPTION(io.servicetalk.loadbalancer.RoundRobinLoadBalancerTest.UnhealthyHostConnectionFactory.UNHEALTHY_HOST_EXCEPTION) ExecutorService(java.util.concurrent.ExecutorService) ConnectionFactory(io.servicetalk.client.api.ConnectionFactory) Matchers.empty(org.hamcrest.Matchers.empty) RetryStrategies.retryWithConstantBackoffFullJitter(io.servicetalk.concurrent.api.RetryStrategies.retryWithConstantBackoffFullJitter) ListenableAsyncCloseable(io.servicetalk.concurrent.api.ListenableAsyncCloseable) Single.defer(io.servicetalk.concurrent.api.Single.defer) LoadBalancedConnection(io.servicetalk.client.api.LoadBalancedConnection) Single(io.servicetalk.concurrent.api.Single) Completable(io.servicetalk.concurrent.api.Completable) NoAvailableHostException(io.servicetalk.client.api.NoAvailableHostException) Mockito.when(org.mockito.Mockito.when) LoadBalancerReadyEvent(io.servicetalk.client.api.LoadBalancerReadyEvent) Subscription(io.servicetalk.concurrent.PublisherSource.Subscription) SourceAdapters.toSource(io.servicetalk.concurrent.api.SourceAdapters.toSource) Matchers.both(org.hamcrest.Matchers.both) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) AfterEach(org.junit.jupiter.api.AfterEach) AbstractMap(java.util.AbstractMap) Matchers.emptyIterable(org.hamcrest.Matchers.emptyIterable) Matcher(org.hamcrest.Matcher) Single.failed(io.servicetalk.concurrent.api.Single.failed) TestSingleSubscriber(io.servicetalk.concurrent.test.internal.TestSingleSubscriber) AsyncCloseables.emptyAsyncCloseable(io.servicetalk.concurrent.api.AsyncCloseables.emptyAsyncCloseable) SECONDS(java.util.concurrent.TimeUnit.SECONDS) ExecutionException(java.util.concurrent.ExecutionException) Test(org.junit.jupiter.api.Test)

Aggregations

ConnectionFactory (io.servicetalk.client.api.ConnectionFactory)2 ConnectionRejectedException (io.servicetalk.client.api.ConnectionRejectedException)2 LoadBalancedConnection (io.servicetalk.client.api.LoadBalancedConnection)2 NoAvailableHostException (io.servicetalk.client.api.NoAvailableHostException)2 ServiceDiscovererEvent (io.servicetalk.client.api.ServiceDiscovererEvent)2 AVAILABLE (io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE)2 EXPIRED (io.servicetalk.client.api.ServiceDiscovererEvent.Status.EXPIRED)2 UNAVAILABLE (io.servicetalk.client.api.ServiceDiscovererEvent.Status.UNAVAILABLE)2 Subscriber (io.servicetalk.concurrent.PublisherSource.Subscriber)2 Subscription (io.servicetalk.concurrent.PublisherSource.Subscription)2 Completable (io.servicetalk.concurrent.api.Completable)2 Executor (io.servicetalk.concurrent.api.Executor)2 ListenableAsyncCloseable (io.servicetalk.concurrent.api.ListenableAsyncCloseable)2 Publisher (io.servicetalk.concurrent.api.Publisher)2 RetryStrategies.retryWithConstantBackoffFullJitter (io.servicetalk.concurrent.api.RetryStrategies.retryWithConstantBackoffFullJitter)2 Single (io.servicetalk.concurrent.api.Single)2 Single.defer (io.servicetalk.concurrent.api.Single.defer)2 Single.failed (io.servicetalk.concurrent.api.Single.failed)2 Single.succeeded (io.servicetalk.concurrent.api.Single.succeeded)2 SourceAdapters.toSource (io.servicetalk.concurrent.api.SourceAdapters.toSource)2