Repository: caddyserver/caddy
File: modules/caddyhttp/reverseproxy/selectionpolicies.go (Go)

Code before:
func hostByHashing(pool []*Upstream, s string) *Upstream {
	// Highest Random Weight (HRW, or "Rendezvous") hashing,
	// guarantees stability when the list of upstreams changes;
	// see https://medium.com/i0exception/rendezvous-hashing-8c00e2fb58b0,
	// https://randorithms.com/2020/12/26/rendezvous-hashing.html,
	// and https://en.wikipedia.org/wiki/Rendezvous_hashing.
	var highestHash uint32
	var upstream *Upstream
	for _, up := range pool {
		if !up.Available() {
			continue
		}
		h := hash(up.String() + s) // important to hash key and server together
		if h > highestHash {
			highestHash = h
			upstream = up
		}
	}
	return upstream
}

// hash calculates a fast hash based on s.
func hash(s string) uint32 {
	h := xxhash.New()
	_, _ = h.Write([]byte(s))
	return uint32(h.Sum64())
}

func loadFallbackPolicy(d *caddyfile.Dispenser) (json.RawMessage, error) {
	name := d.Val()
	modID := "http.reverse_proxy.selection_policies." + name
	unm, err := caddyfile.UnmarshalModule(d, modID)
	if err != nil {
		return nil, err
	}
	sel, ok := unm.(Selector)
	if !ok {
		return nil, d.Errf("module %s (%T) is not a reverseproxy.Selector", modID, unm)
	}
	return caddyconfig.JSONModuleObject(sel, "policy", name, nil), nil
}

// Interface guards
var (
	_ Selector = (*RandomSelection)(nil)
	_ Selector = (*RandomChoiceSelection)(nil)
	_ Selector = (*LeastConnSelection)(nil)
	_ Selector = (*RoundRobinSelection)(nil)
	_ Selector = (*WeightedRoundRobinSelection)(nil)
	_ Selector = (*FirstSelection)(nil)
	_ Selector = (*IPHashSelection)(nil)

Review comment (suggestion):
As I'm unfamiliar with this hash, do we really want to truncate to 32bits? We could change the return signature to uint64 to accommodate the 64-bit hash output.

Code after:
func hostByHashing(pool []*Upstream, s string) *Upstream {
	// Highest Random Weight (HRW, or "Rendezvous") hashing,
	// guarantees stability when the list of upstreams changes;
	// see https://medium.com/i0exception/rendezvous-hashing-8c00e2fb58b0,
	// https://randorithms.com/2020/12/26/rendezvous-hashing.html,
	// and https://en.wikipedia.org/wiki/Rendezvous_hashing.
	var highestHash uint64
	var upstream *Upstream
	for _, up := range pool {
		if !up.Available() {
			continue
		}
		h := hash(up.String() + s) // important to hash key and server together
		if h > highestHash {
			highestHash = h
			upstream = up
		}
	}
	return upstream
}

// hash calculates a fast hash based on s.
func hash(s string) uint64 {
	h := xxhash.New()
	_, _ = h.Write([]byte(s))
	return h.Sum64()
}

func loadFallbackPolicy(d *caddyfile.Dispenser) (json.RawMessage, error) {
	name := d.Val()
	modID := "http.reverse_proxy.selection_policies." + name
	unm, err := caddyfile.UnmarshalModule(d, modID)
	if err != nil {
		return nil, err
	}
	sel, ok := unm.(Selector)
	if !ok {
		return nil, d.Errf("module %s (%T) is not a reverseproxy.Selector", modID, unm)
	}
	return caddyconfig.JSONModuleObject(sel, "policy", name, nil), nil
}

// Interface guards
var (
	_ Selector = (*RandomSelection)(nil)
	_ Selector = (*RandomChoiceSelection)(nil)
	_ Selector = (*LeastConnSelection)(nil)
	_ Selector = (*RoundRobinSelection)(nil)
	_ Selector = (*WeightedRoundRobinSelection)(nil)
	_ Selector = (*FirstSelection)(nil)
	_ Selector = (*IPHashSelection)(nil)