/// <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)]);
            }
        }