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 }