/// <summary>
        /// Retrieves the next reachable <see cref="RemoteHost"/>.
        /// </summary>
        /// <param name="affinityToken">
        /// A string to generate a consistent affinity to a specific host within the set of available hosts.
        /// Identical strings will return the same host for a given pool of reachable hosts. A request ID is usually provided.
        /// </param>
        /// <returns>A reachable <see cref="RemoteHost"/>.</returns>
        /// <exception cref="EnvironmentException">Thrown when there is no reachable <see cref="RemoteHost"/> available.</exception>
        public IEndPointHandle GetNextHost(string affinityToken = null)
        {
            LastEndpointRequest = DateTime.UtcNow;

            var hostOverride = TracingContext.GetHostOverride(DeploymentIdentifier.ServiceName);

            if (hostOverride != null)
            {
                return(new OverriddenRemoteHost(DeploymentIdentifier.ServiceName, hostOverride.Hostname, hostOverride.Port ?? GetConfig().DefaultPort));
            }

            lock (_lock)
            {
                Health.Activate();

                if (ReachableHosts.Count == 0)
                {
                    var lastExceptionEndPoint = UnreachableHosts.FirstOrDefault();

                    // TODO: Exception throwing code should be in this class, not in another.
                    throw DiscoverySource.AllEndpointsUnreachable(EndPointsResult, lastExceptionEndPoint?.LastException, lastExceptionEndPoint == null ? null : $"{lastExceptionEndPoint.HostName}:{lastExceptionEndPoint.Port}", string.Join(", ", UnreachableHosts));
                }

                Counter++;

                ulong hostId = affinityToken == null ? Counter : (ulong)affinityToken.GetHashCode();

                return(ReachableHosts[(int)(hostId % (ulong)ReachableHosts.Count)]);
            }
        }
        private ValueTask <HealthCheckResult> CheckHealth()
        {
            var config = GetConfig();

            if (IsHealthCheckSuppressed(config))
            {
                return(new ValueTask <HealthCheckResult>(HealthCheckResult.Healthy($"Health check suppressed because service was not in use for more than {config.SuppressHealthCheckAfterServiceUnused.TotalSeconds} seconds.")));
            }

            int       reachableCount;
            int       unreachableCount;
            Exception exception;

            string[] unreachableHosts;

            lock (_lock)
            {
                reachableCount   = ReachableHosts.Count;
                unreachableHosts = UnreachableHosts.Select(x => $"{x.HostName}:{x.Port}").ToArray();
                unreachableCount = unreachableHosts.Length;
                exception        = UnreachableHosts.FirstOrDefault()?.LastException;
            }

            if (reachableCount == 0)
            {
                return(new ValueTask <HealthCheckResult>(HealthCheckResult.Unhealthy($"All of the {unreachableCount} hosts are unreachable: " +
                                                                                     $"{string.Join(",", unreachableHosts)}. Last exception: {HealthMonitor.GetMessages(exception)}")));
            }
            else
            {
                if (unreachableCount > 0)
                {
                    return(new ValueTask <HealthCheckResult>(HealthCheckResult.Unhealthy($"The following {unreachableCount} hosts " +
                                                                                         $"(out of {unreachableCount + reachableCount}) are unreachable: {string.Join(",", unreachableHosts)}. " +
                                                                                         $"Last exception: {HealthMonitor.GetMessages(exception)}")));
                }
                else
                {
                    return(new ValueTask <HealthCheckResult>(HealthCheckResult.Healthy($"All {reachableCount} hosts are reachable.")));
                }
            }
        }