github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/gossip_state_management.go (about) 1 package cluster 2 3 import ( 4 "time" 5 6 "google.golang.org/protobuf/proto" 7 "google.golang.org/protobuf/types/known/anypb" 8 ) 9 10 // convenience type alias 11 type GossipMemberState = GossipState_GossipMemberState 12 13 func ensureEntryExists(memberState *GossipMemberState, key string) *GossipKeyValue { 14 value, ok := memberState.Values[key] 15 if ok { 16 return value 17 } 18 19 value = &GossipKeyValue{} 20 memberState.Values[key] = value 21 22 return value 23 } 24 25 // returns back the GossipMemberState registered in the given GossipState 26 // under the given memberID key, if the key doesn't exists yet it is created 27 func ensureMemberStateExists(state *GossipState, memberID string) *GossipMemberState { 28 memberState, ok := state.Members[memberID] 29 if ok { 30 return memberState 31 } 32 33 memberState = &GossipMemberState{Values: make(map[string]*GossipKeyValue)} 34 state.Members[memberID] = memberState 35 36 return memberState 37 } 38 39 // sets the given key with the given value in the given gossip state and returns sequenceNo + 1 40 func setKey(state *GossipState, key string, value proto.Message, memberID string, sequenceNo int64) int64 { 41 // if entry does not exists, add it 42 memberState := ensureMemberStateExists(state, memberID) 43 entry := ensureEntryExists(memberState, key) 44 entry.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() 45 46 sequenceNo++ 47 entry.SequenceNumber = sequenceNo 48 49 a, _ := anypb.New(value) 50 entry.Value = a 51 52 return sequenceNo 53 } 54 55 // merges the local and the incoming remote states into a new states slice and return it 56 func mergeState(localState *GossipState, remoteState *GossipState) ([]*GossipUpdate, *GossipState, map[string]empty) { 57 // make a copy of the localState (we do not want to modify localState just yet) 58 mergedState := &GossipState{Members: make(map[string]*GossipState_GossipMemberState)} 59 for id, member := range localState.Members { 60 mergedState.Members[id] = member 61 } 62 63 var updates []*GossipUpdate 64 updatedKeys := make(map[string]empty) 65 66 for memberID, remoteMemberState := range remoteState.Members { 67 if _, ok := mergedState.Members[memberID]; !ok { 68 mergedState.Members[memberID] = remoteMemberState 69 for key, entry := range remoteMemberState.Values { 70 update := GossipUpdate{ 71 MemberID: memberID, 72 Key: key, 73 Value: entry.Value, 74 SeqNumber: entry.SequenceNumber, 75 } 76 updates = append(updates, &update) 77 entry.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() 78 updatedKeys[key] = empty{} 79 } 80 continue 81 } 82 83 // this entry exists in both mergedState and remoteState, we should merge them 84 newMemberState := mergedState.Members[memberID] 85 for key, remoteValue := range remoteMemberState.Values { 86 // this entry does not exist in newMemberState, just copy all of it 87 if _, ok := newMemberState.Values[key]; !ok { 88 newMemberState.Values[key] = remoteValue 89 update := GossipUpdate{ 90 MemberID: memberID, 91 Key: key, 92 Value: remoteValue.Value, 93 SeqNumber: remoteValue.SequenceNumber, 94 } 95 updates = append(updates, &update) 96 remoteValue.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() 97 updatedKeys[key] = empty{} 98 continue 99 } 100 101 newValue := newMemberState.Values[key] 102 103 // remote value is older, ignore 104 if remoteValue.SequenceNumber <= newValue.SequenceNumber { 105 continue 106 } 107 108 // just replace the existing value 109 newMemberState.Values[key] = remoteValue 110 update := GossipUpdate{ 111 MemberID: memberID, 112 Key: key, 113 Value: remoteValue.Value, 114 SeqNumber: remoteValue.SequenceNumber, 115 } 116 updates = append(updates, &update) 117 remoteValue.LocalTimestampUnixMilliseconds = time.Now().UnixMilli() 118 updatedKeys[key] = empty{} 119 } 120 } 121 return updates, mergedState, updatedKeys 122 }