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  }