go.uber.org/yarpc@v1.72.1/peer/hashring32/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 hashring32
    22  
    23  import (
    24  	"context"
    25  	"testing"
    26  	"time"
    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/peer/hashring32/internal/farmhashring"
    33  	"go.uber.org/yarpc/yarpctest"
    34  	"go.uber.org/zap/zaptest"
    35  )
    36  
    37  func TestAddRemoveAndChoose(t *testing.T) {
    38  	trans := yarpctest.NewFakeTransport(yarpctest.InitialConnectionStatus(peer.Available))
    39  	pl := New(
    40  		trans,
    41  		farmhashring.Fingerprint32,
    42  		OffsetHeader("test"),
    43  		PeerOverrideHeader("poTest"),
    44  		ReplicaDelimiter("#"),
    45  		Logger(zaptest.NewLogger(t)),
    46  		NumReplicas(5),
    47  		NumPeersEstimate(2),
    48  	)
    49  
    50  	pl.Start()
    51  
    52  	pl.Update(
    53  		peer.ListUpdates{
    54  			Additions: []peer.Identifier{
    55  				&FakeShardIdentifier{id: "id1", shard: "shard-1"},
    56  				&FakeShardIdentifier{id: "id2", shard: "shard-2"},
    57  			},
    58  		},
    59  	)
    60  
    61  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    62  	defer cancel()
    63  
    64  	r, _, err := pl.Choose(ctx, &transport.Request{ShardKey: "foo1"})
    65  	require.NoError(t, err)
    66  	assert.Equal(t, "id1", r.Identifier())
    67  
    68  	r, _, err = pl.Choose(ctx, &transport.Request{ShardKey: "foo2"})
    69  	require.NoError(t, err)
    70  	assert.Equal(t, "id2", r.Identifier())
    71  
    72  	pl.Update(
    73  		peer.ListUpdates{
    74  			Removals: []peer.Identifier{
    75  				&FakeShardIdentifier{id: "id2", shard: "shard2"},
    76  			},
    77  		},
    78  	)
    79  
    80  	r, _, _ = pl.Choose(ctx, &transport.Request{ShardKey: "foo1"})
    81  	assert.Equal(t, "id1", r.Identifier())
    82  
    83  	r, _, _ = pl.Choose(ctx, &transport.Request{ShardKey: "foo2"})
    84  	assert.Equal(t, "id1", r.Identifier())
    85  
    86  }
    87  
    88  func TestAddRemoveAndChooseWithAlternateShardKeyHeader(t *testing.T) {
    89  	trans := yarpctest.NewFakeTransport(yarpctest.InitialConnectionStatus(peer.Available))
    90  	pl := New(
    91  		trans,
    92  		farmhashring.Fingerprint32,
    93  		ReplicaDelimiter("#"),
    94  		AlternateShardKeyHeader("test-header-shard-key"),
    95  		Logger(zaptest.NewLogger(t)),
    96  		NumReplicas(5),
    97  		NumPeersEstimate(2),
    98  	)
    99  
   100  	pl.Start()
   101  
   102  	pl.Update(
   103  		peer.ListUpdates{
   104  			Additions: []peer.Identifier{
   105  				&FakeShardIdentifier{id: "id1", shard: "shard-1"},
   106  				&FakeShardIdentifier{id: "id2", shard: "shard-2"},
   107  			},
   108  		},
   109  	)
   110  
   111  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   112  	defer cancel()
   113  
   114  	r, _, err := pl.Choose(ctx, &transport.Request{Headers: transport.NewHeaders().With("test-header-shard-key", "foo1")})
   115  	require.NoError(t, err)
   116  	assert.Equal(t, "id1", r.Identifier())
   117  
   118  	r, _, err = pl.Choose(ctx, &transport.Request{Headers: transport.NewHeaders().With("test-header-shard-key", "foo2")})
   119  	require.NoError(t, err)
   120  	assert.Equal(t, "id2", r.Identifier())
   121  
   122  	pl.Update(
   123  		peer.ListUpdates{
   124  			Removals: []peer.Identifier{
   125  				&FakeShardIdentifier{id: "id2", shard: "shard2"},
   126  			},
   127  		},
   128  	)
   129  
   130  	r, _, _ = pl.Choose(ctx, &transport.Request{Headers: transport.NewHeaders().With("test-header-shard-key", "foo1")})
   131  	assert.Equal(t, "id1", r.Identifier())
   132  
   133  	r, _, _ = pl.Choose(ctx, &transport.Request{Headers: transport.NewHeaders().With("test-header-shard-key", "foo2")})
   134  	assert.Equal(t, "id1", r.Identifier())
   135  
   136  }
   137  
   138  func TestOverrideChooseAndRemoveOverrideChoose(t *testing.T) {
   139  	var headers transport.Headers
   140  	trans := yarpctest.NewFakeTransport(yarpctest.InitialConnectionStatus(peer.Available))
   141  	pl := New(
   142  		trans,
   143  		farmhashring.Fingerprint32,
   144  		OffsetHeader("test"),
   145  		PeerOverrideHeader("poTest"),
   146  		ReplicaDelimiter("#"),
   147  		NumReplicas(5),
   148  		NumPeersEstimate(2),
   149  	)
   150  
   151  	pl.Start()
   152  	t.Log("started")
   153  
   154  	pl.Update(
   155  		peer.ListUpdates{
   156  			Additions: []peer.Identifier{
   157  				&FakeShardIdentifier{id: "id1", shard: "shard-1"},
   158  				&FakeShardIdentifier{id: "id2", shard: "shard-2"},
   159  			},
   160  		},
   161  	)
   162  	t.Log("updated")
   163  
   164  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   165  	defer cancel()
   166  
   167  	// Test overriden by header.
   168  	headers = headers.With("poTest", "shard-2")
   169  	r, _, _ := pl.Choose(ctx, &transport.Request{ShardKey: "foo1", Headers: headers})
   170  	assert.Equal(t, "id2", r.Identifier())
   171  	t.Log("chose once")
   172  
   173  	// Test invalid override header.
   174  	headers = headers.With("poTest", "shard-3")
   175  	r, _, _ = pl.Choose(ctx, &transport.Request{ShardKey: "foo1", Headers: headers})
   176  	assert.Equal(t, "id1", r.Identifier())
   177  	t.Log("chose twice")
   178  
   179  	pl.Update(
   180  		peer.ListUpdates{
   181  			Removals: []peer.Identifier{
   182  				&FakeShardIdentifier{id: "id2", shard: "shard2"},
   183  			},
   184  		},
   185  	)
   186  	t.Log("updated again")
   187  
   188  	// Test removed key in override header.
   189  	headers = headers.With("poTest", "shard-2")
   190  	r, _, _ = pl.Choose(ctx, &transport.Request{ShardKey: "foo2", Headers: headers})
   191  	assert.Equal(t, "id1", r.Identifier())
   192  	t.Log("chose a third time")
   193  
   194  }
   195  
   196  func TestAddRemoveAndChooseWithOffsetGeneratorValue(t *testing.T) {
   197  	trans := yarpctest.NewFakeTransport(yarpctest.InitialConnectionStatus(peer.Available))
   198  	pl := New(
   199  		trans,
   200  		farmhashring.Fingerprint32,
   201  		OffsetGeneratorValue(3),
   202  		Logger(zaptest.NewLogger(t)),
   203  		NumReplicas(5),
   204  		NumPeersEstimate(2),
   205  	)
   206  
   207  	pl.Start()
   208  
   209  	pl.Update(
   210  		peer.ListUpdates{
   211  			Additions: []peer.Identifier{
   212  				&FakeShardIdentifier{id: "id1", shard: "shard-1"},
   213  				&FakeShardIdentifier{id: "id2", shard: "shard-2"},
   214  				&FakeShardIdentifier{id: "id3", shard: "shard-3"},
   215  				&FakeShardIdentifier{id: "id4", shard: "shard-4"},
   216  				&FakeShardIdentifier{id: "id5", shard: "shard-5"},
   217  			},
   218  		},
   219  	)
   220  
   221  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   222  	defer cancel()
   223  
   224  	for i := 0; i < 100; i++ {
   225  		r, _, err := pl.Choose(ctx, &transport.Request{ShardKey: "foo1"})
   226  		require.NoError(t, err)
   227  		assert.NotEqual(t, "id4", r.Identifier())
   228  		assert.NotEqual(t, "id5", r.Identifier())
   229  	}
   230  
   231  }