go.uber.org/yarpc@v1.72.1/peer/peerlist/v2/list_test.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package peerlist
    22  
    23  import (
    24  	"context"
    25  	"math/rand"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  	"go.uber.org/yarpc/api/peer"
    31  	"go.uber.org/yarpc/api/transport"
    32  	"go.uber.org/yarpc/internal/testtime"
    33  	"go.uber.org/yarpc/peer/hostport"
    34  	"go.uber.org/yarpc/yarpctest"
    35  )
    36  
    37  const (
    38  	id1 = hostport.PeerIdentifier("1.2.3.4:1234")
    39  	id2 = hostport.PeerIdentifier("4.3.2.1:4321")
    40  	id3 = hostport.PeerIdentifier("1.1.1.1:1111")
    41  )
    42  
    43  func TestValues(t *testing.T) {
    44  	vs := values(map[string]peer.Identifier{})
    45  	assert.Equal(t, []peer.Identifier{}, vs)
    46  
    47  	vs = values(map[string]peer.Identifier{"_": id1, "__": id2})
    48  	assert.Equal(t, 2, len(vs))
    49  	assert.Contains(t, vs, id1)
    50  	assert.Contains(t, vs, id2)
    51  }
    52  
    53  func TestShuffle(t *testing.T) {
    54  	for _, test := range []struct {
    55  		msg  string
    56  		seed int64
    57  		in   []peer.Identifier
    58  		want []peer.Identifier
    59  	}{
    60  		{
    61  			"empty",
    62  			0,
    63  			[]peer.Identifier{},
    64  			[]peer.Identifier{},
    65  		},
    66  		{
    67  			"some",
    68  			0,
    69  			[]peer.Identifier{id1, id2, id3},
    70  			[]peer.Identifier{id2, id3, id1},
    71  		},
    72  		{
    73  			"different seed",
    74  			7,
    75  			[]peer.Identifier{id1, id2, id3},
    76  			[]peer.Identifier{id2, id1, id3},
    77  		},
    78  	} {
    79  		t.Run(test.msg, func(t *testing.T) {
    80  			randSrc := rand.NewSource(test.seed)
    81  			assert.Equal(t, test.want, shuffle(randSrc, test.in))
    82  		})
    83  	}
    84  }
    85  
    86  // most recently added peer list implementation for the test.
    87  type mraList struct {
    88  	mra peer.StatusPeer
    89  	mrr peer.StatusPeer
    90  }
    91  
    92  var _ Implementation = (*mraList)(nil)
    93  
    94  func (l *mraList) Add(peer peer.StatusPeer, pid peer.Identifier) peer.Subscriber {
    95  	l.mra = peer
    96  	return &mraSub{}
    97  }
    98  
    99  func (l *mraList) Remove(peer peer.StatusPeer, pid peer.Identifier, ps peer.Subscriber) {
   100  	l.mrr = peer
   101  }
   102  
   103  func (l *mraList) Choose(ctx context.Context, req *transport.Request) peer.StatusPeer {
   104  	return l.mra
   105  }
   106  
   107  func (l *mraList) Start() error {
   108  	return nil
   109  }
   110  
   111  func (l *mraList) Stop() error {
   112  	return nil
   113  }
   114  
   115  func (l *mraList) IsRunning() bool {
   116  	return true
   117  }
   118  
   119  type mraSub struct {
   120  }
   121  
   122  func (s *mraSub) NotifyStatusChanged(pid peer.Identifier) {
   123  }
   124  
   125  func TestPeerList(t *testing.T) {
   126  	fake := yarpctest.NewFakeTransport(yarpctest.InitialConnectionStatus(peer.Unavailable))
   127  	impl := &mraList{}
   128  	list := New("mra", fake, impl, Capacity(1), NoShuffle(), Seed(0))
   129  
   130  	peers := list.Peers()
   131  	assert.Len(t, peers, 0)
   132  
   133  	assert.NoError(t, list.Update(peer.ListUpdates{
   134  		Additions: []peer.Identifier{
   135  			hostport.Identify("1.1.1.1:4040"),
   136  			hostport.Identify("2.2.2.2:4040"),
   137  		},
   138  		Removals: []peer.Identifier{},
   139  	}))
   140  
   141  	// Invalid updates before start
   142  	assert.Error(t, list.Update(peer.ListUpdates{
   143  		Additions: []peer.Identifier{
   144  			hostport.Identify("1.1.1.1:4040"),
   145  		},
   146  		Removals: []peer.Identifier{
   147  			hostport.Identify("3.3.3.3:4040"),
   148  		},
   149  	}))
   150  
   151  	assert.Equal(t, 0, list.NumAvailable())
   152  	assert.Equal(t, 0, list.NumUnavailable())
   153  	assert.Equal(t, 2, list.NumUninitialized())
   154  	assert.False(t, list.Available(hostport.Identify("2.2.2.2:4040")))
   155  	assert.True(t, list.Uninitialized(hostport.Identify("2.2.2.2:4040")))
   156  
   157  	require.NoError(t, list.Start())
   158  
   159  	// Connect to the peer and simulate a request.
   160  	fake.SimulateConnect(hostport.Identify("2.2.2.2:4040"))
   161  	assert.Equal(t, 1, list.NumAvailable())
   162  	assert.Equal(t, 1, list.NumUnavailable())
   163  	assert.Equal(t, 0, list.NumUninitialized())
   164  	assert.True(t, list.Available(hostport.Identify("2.2.2.2:4040")))
   165  	assert.False(t, list.Uninitialized(hostport.Identify("2.2.2.2:4040")))
   166  	peers = list.Peers()
   167  	assert.Len(t, peers, 2)
   168  	p, onFinish, err := list.Choose(context.Background(), &transport.Request{})
   169  	assert.Equal(t, "2.2.2.2:4040", p.Identifier())
   170  	require.NoError(t, err)
   171  	onFinish(nil)
   172  
   173  	// Simulate a second connection and request.
   174  	fake.SimulateConnect(hostport.Identify("1.1.1.1:4040"))
   175  	assert.Equal(t, 2, list.NumAvailable())
   176  	assert.Equal(t, 0, list.NumUnavailable())
   177  	assert.Equal(t, 0, list.NumUninitialized())
   178  	peers = list.Peers()
   179  	assert.Len(t, peers, 2)
   180  	p, onFinish, err = list.Choose(context.Background(), &transport.Request{})
   181  	assert.Equal(t, "1.1.1.1:4040", p.Identifier())
   182  	require.NoError(t, err)
   183  	onFinish(nil)
   184  
   185  	fake.SimulateDisconnect(hostport.Identify("2.2.2.2:4040"))
   186  	assert.Equal(t, "2.2.2.2:4040", impl.mrr.Identifier())
   187  
   188  	assert.NoError(t, list.Update(peer.ListUpdates{
   189  		Additions: []peer.Identifier{
   190  			hostport.Identify("3.3.3.3:4040"),
   191  		},
   192  		Removals: []peer.Identifier{
   193  			hostport.Identify("2.2.2.2:4040"),
   194  		},
   195  	}))
   196  
   197  	// Invalid updates
   198  	assert.Error(t, list.Update(peer.ListUpdates{
   199  		Additions: []peer.Identifier{
   200  			hostport.Identify("3.3.3.3:4040"),
   201  		},
   202  		Removals: []peer.Identifier{
   203  			hostport.Identify("4.4.4.4:4040"),
   204  		},
   205  	}))
   206  
   207  	require.NoError(t, list.Stop())
   208  
   209  	// Invalid updates, after stop
   210  	assert.Error(t, list.Update(peer.ListUpdates{
   211  		Additions: []peer.Identifier{
   212  			hostport.Identify("3.3.3.3:4040"),
   213  		},
   214  		Removals: []peer.Identifier{
   215  			hostport.Identify("4.4.4.4:4040"),
   216  		},
   217  	}))
   218  
   219  	assert.NoError(t, list.Update(peer.ListUpdates{
   220  		Additions: []peer.Identifier{},
   221  		Removals: []peer.Identifier{
   222  			hostport.Identify("3.3.3.3:4040"),
   223  		},
   224  	}))
   225  }
   226  
   227  func TestFailFast(t *testing.T) {
   228  	fake := yarpctest.NewFakeTransport(yarpctest.InitialConnectionStatus(peer.Unavailable))
   229  	impl := &mraList{}
   230  	list := New("mra", fake, impl, FailFast())
   231  
   232  	ctx, cancel := context.WithTimeout(context.Background(), testtime.Second)
   233  	defer cancel()
   234  
   235  	require.NoError(t, list.Start())
   236  
   237  	_, _, err := list.Choose(ctx, &transport.Request{})
   238  	require.Error(t, err)
   239  	assert.Contains(t, err.Error(), "no peer available")
   240  }