func (p *DistancePlacement) pickHostsWithFallback(service string, minDistance, maxDistance, minFallback, maxFallback uint16, storeHosts []*common.HostInfo) (*common.HostInfo, error) {
	if hosts, err := p.getHealthyHosts(service); err == nil {
		if maxDistance <= minDistance {
			maxDistance = distance.InfiniteDistance
		}
		if storeHosts != nil {
			if h, e := p.pickHosts(service, hosts, storeHosts, 1, minDistance, maxDistance); e == nil {
				return h[0], nil
			}
			if minFallback < minDistance || maxFallback > maxDistance {
				if maxFallback <= minFallback {
					maxFallback = distance.InfiniteDistance
				}
				if h, e := p.pickHosts(service, hosts, storeHosts, 1, minFallback, maxFallback); e == nil {
					return h[0], nil
				}
			}
		}
		if cnt := len(hosts); cnt >= 1 {
			return hosts[rand.Intn(cnt)], nil
		}
	}
	return &common.HostInfo{}, errNoHosts
}
Helper function to pick hosts with fallback predicates
package controllerhost
import (
	"errors"
	"fmt"
	"math/rand"
	"strconv"
	"strings"
	"github.com/uber-common/bark"
	"github.com/uber/cherami-server/common"
	"github.com/uber/cherami-server/distance"
	"github.com/uber/cherami-server/services/controllerhost/load"
)
var errNoHosts = errors.New("Unable to find healthy hosts")
var errNotEnoughHosts = errors.New("Unable to find enough hosts")
var errNoInputHosts = errors.New("Unable to find healthy input host")
var errNoOutputHosts = errors.New("Unable to find healthy output host")
var errNoStoreHosts = errors.New("Unable to find healthy store hosts")
type Placement interface {
	PickInputHost(storeHosts []*common.HostInfo) (*common.HostInfo, error)
	PickOutputHost(storeHosts []*common.HostInfo) (*common.HostInfo, error)
	PickStoreHosts(count int) ([]*common.HostInfo, error)
}
type DistancePlacement struct {
	context *Context
	distMap distance.Map
}
func NewDistancePlacement(context *Context) (Placement, error) {
	distMap, err := distance.New(context.appConfig.GetControllerConfig().GetTopologyFile(), context.log)
	return &DistancePlacement{
		context: context,
		distMap: distMap,
	}, err
}
func toResources(hosts []*common.HostInfo) []string {
	var resources []string
	for _, host := range hosts {
		resources = append(resources, strings.Split(host.Addr, ":")[0])
	}
	return resources
}
func (p *DistancePlacement) pickHosts(service string, poolHosts, sourceHosts []*common.HostInfo, count int, minDistance, maxDistance uint16) ([]*common.HostInfo, error) {
	if p.distMap == nil {
		return p.pickRandomHosts(poolHosts, count)
	}
	sourceResources := toResources(sourceHosts)
	poolResources := toResources(poolHosts)
	hostPortMap := make(map[string]int)
	for _, host := range poolHosts {
		if hostPort := strings.Split(host.Addr, ":"); len(hostPort) != 2 {
			p.context.log.WithField("hostPort", hostPort).Panic("Invalid host:port")
		} else if port, err := strconv.Atoi(hostPort[1]); err != nil {
			p.context.log.WithField("port", port).Panic("Invalid port")
		} else {
			hostPortMap[hostPort[0]] = port
		}
	}
	resources, err := p.distMap.FindResources(poolResources, sourceResources, "nic", count, minDistance, maxDistance)
	if err != nil {
		return nil, err
	}
	var hosts []*common.HostInfo
	for _, resource := range resources {
		if port, ok := hostPortMap[resource]; !ok {
			p.context.log.WithField("resource", resource).Panic("Invalid resource")
		} else {
			host, e := p.context.rpm.FindHostForAddr(service, fmt.Sprintf("%s:%d", resource, port))
			if e != nil {
				return nil, e
			}
			hosts = append(hosts, host)
		}
	}
	return hosts, nil
}