github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/member_list_test.go (about)

     1  package cluster
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  //func TestPublishRaceCondition(t *testing.T) {
    14  //	actorSystem := actor.NewActorSystem()
    15  //	c := New(actorSystem, Configure("mycluster", nil, nil, remote.Configure("127.0.0.1", 0)))
    16  //	NewMemberList(c)
    17  //	rounds := 1000
    18  //
    19  //	var wg sync.WaitGroup
    20  //	wg.Add(2 * rounds)
    21  //
    22  //	go func() {
    23  //		for i := 0; i < rounds; i++ {
    24  //			actorSystem.EventStream.Publish(TopologyEvent(Members{{}, {}}))
    25  //			actorSystem.EventStream.Publish(TopologyEvent(Members{{}}))
    26  //			wg.Done()
    27  //		}
    28  //	}()
    29  //
    30  //	go func() {
    31  //		for i := 0; i < rounds; i++ {
    32  //			s := actorSystem.EventStream.Subscribe(func(evt interface{}) {})
    33  //			actorSystem.EventStream.Unsubscribe(s)
    34  //			wg.Done()
    35  //		}
    36  //	}()
    37  //
    38  //	if waitTimeout(&wg, 2*time.Second) {
    39  //		t.Error("Should not run into a timeout")
    40  //	}
    41  //}
    42  
    43  // https://stackoverflow.com/questions/32840687/timeout-for-waitgroup-wait
    44  func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
    45  	c := make(chan struct{})
    46  	go func() {
    47  		defer close(c)
    48  		wg.Wait()
    49  	}()
    50  	select {
    51  	case <-c:
    52  		return false // completed normally
    53  	case <-time.After(timeout):
    54  		return true // timed out
    55  	}
    56  }
    57  
    58  func TestMemberList_UpdateClusterTopology(t *testing.T) {
    59  	c := newClusterForTest("test-UpdateClusterTopology", nil)
    60  	obj := NewMemberList(c)
    61  	empty := make([]*Member, 0)
    62  
    63  	t.Run("init", func(t *testing.T) {
    64  		assert := assert.New(t)
    65  		members := newMembersForTest(2)
    66  		changes, unchanged, actives, _, _ := obj.getTopologyChanges(members)
    67  		assert.False(unchanged)
    68  		expected := &ClusterTopology{TopologyHash: TopologyHash(members), Members: members, Joined: members, Left: empty}
    69  		assert.Equal(expected.TopologyHash, changes.TopologyHash)
    70  
    71  		var m1, m2 *MemberSet
    72  		m1 = NewMemberSet(expected.Members)
    73  		m2 = NewMemberSet(changes.Members)
    74  		assert.Equal(m1, m2)
    75  
    76  		m1 = NewMemberSet(expected.Joined)
    77  		m2 = NewMemberSet(changes.Joined)
    78  		assert.Equal(m1, m2)
    79  
    80  		m1 = NewMemberSet(expected.Left)
    81  		m2 = NewMemberSet(changes.Left)
    82  		assert.Equal(m1, m2)
    83  
    84  		// current members
    85  		obj.members = actives
    86  	})
    87  
    88  	t.Run("join", func(t *testing.T) {
    89  		assert := assert.New(t)
    90  		assert.Equal(2, obj.members.Len())
    91  		members := newMembersForTest(4)
    92  		changes, unchanged, actives, _, _ := obj.getTopologyChanges(members)
    93  		assert.False(unchanged)
    94  		// _sorted(changes)
    95  		expected := &ClusterTopology{TopologyHash: TopologyHash(members), Members: members, Joined: members[2:4], Left: empty}
    96  		assert.Equal(expected.TopologyHash, changes.TopologyHash)
    97  
    98  		var m1, m2 *MemberSet
    99  		m1 = NewMemberSet(expected.Members)
   100  		m2 = NewMemberSet(changes.Members)
   101  		assert.Equal(m1, m2)
   102  
   103  		m1 = NewMemberSet(expected.Joined)
   104  		m2 = NewMemberSet(changes.Joined)
   105  		assert.Equal(m1, m2)
   106  
   107  		m1 = NewMemberSet(expected.Left)
   108  		m2 = NewMemberSet(changes.Left)
   109  		assert.Equal(m1, m2)
   110  
   111  		obj.members = actives
   112  	})
   113  
   114  	t.Run("left", func(t *testing.T) {
   115  		assert := assert.New(t)
   116  		assert.Equal(4, obj.members.Len())
   117  		members := newMembersForTest(4)
   118  		changes, _, _, _, _ := obj.getTopologyChanges(members[2:4])
   119  		expected := &ClusterTopology{TopologyHash: TopologyHash(members[2:4]), Members: members[2:4], Joined: empty, Left: members[0:2]}
   120  		assert.Equal(expected.TopologyHash, changes.TopologyHash)
   121  
   122  		var m1, m2 *MemberSet
   123  		m1 = NewMemberSet(expected.Members)
   124  		m2 = NewMemberSet(changes.Members)
   125  		assert.Equal(m1, m2)
   126  
   127  		m1 = NewMemberSet(expected.Joined)
   128  		m2 = NewMemberSet(changes.Joined)
   129  		assert.Equal(m1, m2)
   130  
   131  		m1 = NewMemberSet(expected.Left)
   132  		m2 = NewMemberSet(changes.Left)
   133  		assert.Equal(m1, m2)
   134  	})
   135  }
   136  
   137  func newMembersForTest(count int, kinds ...string) Members {
   138  	if len(kinds) == 0 {
   139  		kinds = append(kinds, "kind")
   140  	}
   141  	members := make(Members, count)
   142  	for i := 0; i < count; i++ {
   143  		members[i] = &Member{
   144  			Id:    fmt.Sprintf("memberId-%d", i),
   145  			Host:  "127.0.0.1",
   146  			Port:  int32(i),
   147  			Kinds: kinds,
   148  		}
   149  	}
   150  	return members
   151  }
   152  
   153  func TestMemberList_UpdateClusterTopology2(t *testing.T) {
   154  	c := newClusterForTest("test-UpdateClusterTopology", nil)
   155  
   156  	obj := NewMemberList(c)
   157  	dumpMembers := func(list Members) {
   158  		t.Logf("membersByMemberId=%d", len(list))
   159  
   160  		for _, m := range list {
   161  			t.Logf("\t%s", m.Address())
   162  		}
   163  	}
   164  
   165  	empty := make([]*Member, 0)
   166  
   167  	_ = dumpMembers
   168  	_sorted := func(tpl *ClusterTopology) {
   169  		_sortMembers := func(list Members) {
   170  			sort.Slice(list, func(i, j int) bool {
   171  				return (list)[i].Port < (list)[j].Port
   172  			})
   173  		}
   174  		_sortMembers(tpl.Members)
   175  		_sortMembers(tpl.Left)
   176  		_sortMembers(tpl.Joined)
   177  	}
   178  
   179  	a := assert.New(t)
   180  	members := newMembersForTest(2)
   181  	changes, _, _, _, _ := obj.getTopologyChanges(members) //nolint:dogsled
   182  	_sorted(changes)
   183  
   184  	expected := &ClusterTopology{TopologyHash: TopologyHash(members), Members: members, Joined: members, Left: empty}
   185  
   186  	a.Equal(expected.TopologyHash, changes.TopologyHash)
   187  
   188  	var m1, m2 *MemberSet
   189  	m1 = NewMemberSet(expected.Members)
   190  	m2 = NewMemberSet(changes.Members)
   191  	a.Equal(m1, m2)
   192  
   193  	m1 = NewMemberSet(expected.Joined)
   194  	m2 = NewMemberSet(changes.Joined)
   195  	a.Equal(m1, m2)
   196  
   197  	m1 = NewMemberSet(expected.Left)
   198  	m2 = NewMemberSet(changes.Left)
   199  	a.Equal(m1, m2)
   200  }
   201  
   202  func TestMemberList_getPartitionMember(t *testing.T) {
   203  	t.Parallel()
   204  
   205  	c := newClusterForTest("test-memberlist", nil)
   206  	obj := NewMemberList(c)
   207  
   208  	for _, v := range []int{1, 2, 10, 100, 1000} {
   209  		members := newMembersForTest(v)
   210  		obj.UpdateClusterTopology(members)
   211  
   212  		testName := fmt.Sprintf("member*%d", v)
   213  		t.Run(testName, func(t *testing.T) {
   214  			//assert := assert.New(t)
   215  			//
   216  			//identity := NewClusterIdentity("name", "kind")
   217  			////	address := obj.getPartitionMemberV2(identity)
   218  			////	assert.NotEmpty(address)
   219  			//
   220  			//identity = NewClusterIdentity("name", "nonkind")
   221  			////		address = obj.getPartitionMemberV2(identity)
   222  			////	assert.Empty(address)
   223  		})
   224  	}
   225  }
   226  
   227  //func BenchmarkMemberList_getPartitionMemberV2(b *testing.B) {
   228  //	SetLogLevel(log.ErrorLevel)
   229  //	actorSystem := actor.NewActorSystem()
   230  //	c := New(actorSystem, Configure("mycluster", nil, nil, remote.Configure("127.0.0.1", 0)))
   231  //	obj := NewMemberList(c)
   232  //	for i, v := range []int{1, 2, 3, 5, 10, 100, 1000, 2000} {
   233  //		members := _newTopologyEventForTest(v)
   234  //		obj.UpdateClusterTopology(members)
   235  //		testName := fmt.Sprintf("member*%d", v)
   236  //		runtime.GC()
   237  //
   238  //		identity := &ClusterIdentity{Identity: fmt.Sprintf("name-%d", rand.Int()), Kind: "kind"}
   239  //		b.Run(testName, func(b *testing.B) {
   240  //			for i := 0; i < b.N; i++ {
   241  //				address := obj.getPartitionMemberV2(identity)
   242  //				if address == "" {
   243  //					b.Fatalf("empty address membersByMemberId=%d", v)
   244  //				}
   245  //			}
   246  //		})
   247  //	}
   248  //}
   249  
   250  //func TestMemberList_getPartitionMemberV2(t *testing.T) {
   251  //	assert := assert.New(t)
   252  //
   253  //	tplg := _newTopologyEventForTest(10)
   254  //	c := _newClusterForTest("test-memberlist")
   255  //	obj := NewMemberList(c)
   256  //	obj.UpdateClusterTopology(tplg, 1)
   257  //
   258  //	assert.Contains(obj.memberStrategyByKind, "kind")
   259  //	addr := obj.getPartitionMemberV2(&ClusterIdentity{Kind: "kind", Identity: "name"})
   260  //	assert.NotEmpty(addr)
   261  //
   262  //	// consistent
   263  //	for i := 0; i < 10; i++ {
   264  //		addr2 := obj.getPartitionMemberV2(&ClusterIdentity{Kind: "kind", Identity: "name"})
   265  //		assert.NotEmpty(addr2)
   266  //		assert.Equal(addr, addr2)
   267  //	}
   268  //}
   269  
   270  func TestMemberList_newMemberStrategies(t *testing.T) {
   271  	t.Parallel()
   272  	a := assert.New(t)
   273  
   274  	c := newClusterForTest("test-memberlist", nil)
   275  	obj := NewMemberList(c)
   276  
   277  	for _, v := range []int{1, 10, 100, 1000} {
   278  		members := newMembersForTest(v, "kind1", "kind2")
   279  		obj.UpdateClusterTopology(members)
   280  		a.Equal(2, len(obj.memberStrategyByKind))
   281  		a.Contains(obj.memberStrategyByKind, "kind1")
   282  
   283  		a.Equal(v, len(obj.memberStrategyByKind["kind1"].GetAllMembers()))
   284  		a.Equal(v, len(obj.memberStrategyByKind["kind2"].GetAllMembers()))
   285  	}
   286  }