use of io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE 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(...)")));
});
}
Aggregations