package utils import ( "errors" "github.com/spaolacci/murmur3" ) type Hasher = func(member string, key string) uint64 type Member = string type Members = []Member type Key = string // assign assigns a key to a member using the rendezvous hashing algorithm. func Assign(key Key, members Members, hasher Hasher) (Member, error) { if len(members) == 0 { return "", errors.New("cannot assign key to empty member list") } if len(members) == 1 { return members[0], nil } if key == "" { return "", errors.New("cannot assign empty key") } maxScore := uint64(0) var maxMember Member for _, member := range members { score := hasher(string(member), string(key)) if score > maxScore { maxScore = score maxMember = member } } return maxMember, nil } func mergeHashes(a uint64, b uint64) uint64 { acc := a ^ b acc ^= acc >> 33 acc *= 0xff51afd7ed558ccd acc ^= acc >> 33 acc *= 0xc4ceb9fe1a85ec53 acc ^= acc >> 33 return acc } // NOTE: The python implementation of murmur3 may differ from the golang implementation. // For now, this is fine since go and python don't need to agree on any hashing schemes // but if we ever need to agree on a hashing scheme, we should verify that the implementations // are the same. func Murmur3Hasher(member string, key string) uint64 { hasher := murmur3.New64() hasher.Write([]byte(member)) memberHash := hasher.Sum64() hasher.Reset() hasher.Write([]byte(key)) keyHash := hasher.Sum64() return mergeHashes(memberHash, keyHash) }