github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/rendezvous.go (about) 1 package cluster 2 3 // Rendezvous.go 4 // A revised FNV1A32 version of 5 // https://github.com/tysonmote/rendezvous/blob/master/rendezvous.go 6 7 import ( 8 "hash" 9 "hash/fnv" 10 "strings" 11 "sync" 12 ) 13 14 type memberData struct { 15 member *Member 16 hashBytes []byte 17 } 18 type Rendezvous struct { 19 mutex sync.RWMutex 20 hasher hash.Hash32 21 hasherLock sync.Mutex 22 members []*memberData 23 } 24 25 func NewRendezvous() *Rendezvous { 26 return &Rendezvous{ 27 hasher: fnv.New32a(), 28 members: make([]*memberData, 0), 29 } 30 } 31 32 func (r *Rendezvous) GetByClusterIdentity(ci *ClusterIdentity) string { 33 r.mutex.RLock() 34 defer r.mutex.RUnlock() 35 36 identity := ci.Identity 37 m := r.memberDataByKind(ci.Kind) 38 39 l := len(m) 40 41 if l == 0 { 42 return "" 43 } 44 45 if l == 1 { 46 return m[0].member.Address() 47 } 48 49 keyBytes := []byte(identity) 50 51 var maxScore uint32 52 var maxMember *memberData 53 var score uint32 54 55 for _, node := range m { 56 score = r.hash(node.hashBytes, keyBytes) 57 if score > maxScore { 58 maxScore = score 59 maxMember = node 60 } 61 } 62 63 if maxMember == nil { 64 return "" 65 } 66 return maxMember.member.Address() 67 } 68 69 func (r *Rendezvous) GetByIdentity(identity string) string { 70 parts := strings.SplitN(identity, "/", 2) 71 72 return r.GetByClusterIdentity(&ClusterIdentity{ 73 Kind: parts[0], 74 Identity: parts[1], 75 }) 76 } 77 78 func (r *Rendezvous) memberDataByKind(kind string) []*memberData { 79 m := make([]*memberData, 0) 80 for _, md := range r.members { 81 if md.member.HasKind(kind) { 82 m = append(m, md) 83 } 84 } 85 return m 86 } 87 88 func (r *Rendezvous) UpdateMembers(members Members) { 89 r.mutex.Lock() 90 defer r.mutex.Unlock() 91 92 tmp := members.ToSet() 93 r.members = make([]*memberData, 0) 94 95 for _, m := range tmp.Members() { 96 keyBytes := []byte(m.Address()) // TODO: should be utf8 to match .net 97 r.members = append(r.members, &memberData{ 98 member: m, 99 hashBytes: keyBytes, 100 }) 101 } 102 } 103 104 func (r *Rendezvous) hash(node, key []byte) uint32 { 105 r.hasherLock.Lock() 106 defer r.hasherLock.Unlock() 107 108 r.hasher.Reset() 109 r.hasher.Write(key) 110 r.hasher.Write(node) 111 return r.hasher.Sum32() 112 }