github.com/m3db/m3@v1.5.0/src/cluster/shard/shard_test.go (about) 1 // Copyright (c) 2016 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 shard 22 23 import ( 24 "math" 25 "math/rand" 26 "testing" 27 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestShard(t *testing.T) { 33 s := NewShard(1). 34 SetState(Initializing). 35 SetSourceID("id"). 36 SetCutoffNanos(1000). 37 SetCutoverNanos(100). 38 SetRedirectToShardID(uint32Ptr(5)) 39 40 assert.Equal(t, uint32(1), s.ID()) 41 assert.Equal(t, Initializing, s.State()) 42 assert.Equal(t, "id", s.SourceID()) 43 assert.Equal(t, int64(1000), s.CutoffNanos()) 44 assert.Equal(t, int64(100), s.CutoverNanos()) 45 assert.Equal(t, uint32(5), *s.RedirectToShardID()) 46 } 47 48 func TestShardEquals(t *testing.T) { 49 s := NewShard(1).SetState(Initializing).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100) 50 assert.True(t, s.Equals(s)) 51 assert.False(t, s.Equals(NewShard(1).SetState(Initializing).SetSourceID("id").SetCutoffNanos(1000))) 52 assert.False(t, s.Equals(NewShard(1).SetState(Initializing).SetSourceID("id").SetCutoverNanos(100))) 53 assert.False(t, s.Equals(NewShard(1).SetState(Initializing).SetCutoffNanos(1000).SetCutoverNanos(100))) 54 assert.False(t, s.Equals(NewShard(1).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100))) 55 assert.False(t, s.Equals(NewShard(1).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100))) 56 assert.False(t, s.Equals(NewShard(2).SetState(Initializing).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100))) 57 } 58 59 func TestShardEqualsWithRedirectShardID(t *testing.T) { 60 s := NewShard(1).SetRedirectToShardID(uint32Ptr(1)) 61 assert.True(t, s.Equals(s)) 62 assert.False(t, s.Equals(NewShard(1).SetRedirectToShardID(nil))) 63 assert.False(t, NewShard(1).SetRedirectToShardID(nil).Equals(s)) 64 assert.False(t, s.Equals(NewShard(1).SetRedirectToShardID(uint32Ptr(0)))) 65 assert.True(t, s.Equals(NewShard(1).SetRedirectToShardID(uint32Ptr(1)))) 66 } 67 68 func TestShards(t *testing.T) { 69 redirectToShardID := new(uint32) 70 *redirectToShardID = 9 71 72 shards := NewShards(nil) 73 assert.Equal(t, 0, shards.NumShards()) 74 75 s1 := NewShard(1).SetState(Initializing).SetSourceID("id").SetRedirectToShardID(redirectToShardID) 76 shards.Add(s1) 77 assert.Equal(t, 1, shards.NumShards()) 78 assert.Equal(t, 1, shards.NumShardsForState(Initializing)) 79 assert.Equal(t, 0, shards.NumShardsForState(Available)) 80 assert.Equal(t, 0, shards.NumShardsForState(Leaving)) 81 assert.True(t, shards.Contains(1)) 82 s, ok := shards.Shard(1) 83 assert.True(t, ok) 84 assert.Equal(t, s1, s) 85 shardsInState := shards.ShardsForState(Initializing) 86 assert.NotNil(t, shardsInState) 87 assert.Equal(t, []Shard{s1}, shards.All()) 88 assert.Equal(t, shardsInState, shards.All()) 89 90 s2 := NewShard(2).SetState(Available) 91 shards.Add(s2) 92 assert.Equal(t, 2, shards.NumShards()) 93 assert.Equal(t, 1, shards.NumShardsForState(Initializing)) 94 assert.Equal(t, 1, shards.NumShardsForState(Available)) 95 assert.Equal(t, 0, shards.NumShardsForState(Leaving)) 96 s, ok = shards.Shard(2) 97 assert.True(t, ok) 98 assert.Equal(t, s2, s) 99 100 shards.Remove(1) 101 assert.False(t, shards.Contains(1)) 102 _, ok = shards.Shard(1) 103 assert.False(t, ok) 104 105 shards = NewShards([]Shard{s1, s2}) 106 assert.Equal(t, 2, shards.NumShards()) 107 108 shards.Add(NewShard(3).SetState(Leaving)) 109 assert.Equal(t, "[Initializing=[1 -> 9], Available=[2], Leaving=[3]]", shards.String()) 110 111 shards.Add(NewShard(4)) 112 shards.Add(NewShard(4)) 113 shards.Add(NewShard(4)) 114 assert.Equal(t, 4, shards.NumShards()) 115 } 116 117 func TestShardsEquals(t *testing.T) { 118 ss1 := NewShards(nil) 119 ss2 := NewShards(nil) 120 121 s1 := NewShard(1).SetState(Initializing).SetSourceID("id") 122 s2 := NewShard(2).SetState(Available) 123 ss1.Add(s1) 124 ss2.Add(s2) 125 assert.False(t, ss1.Equals(ss2)) 126 ss2.Add(s1) 127 assert.False(t, ss1.Equals(ss2)) 128 129 ss2.Remove(s2.ID()) 130 assert.True(t, ss1.Equals(ss2)) 131 } 132 133 func TestSort(t *testing.T) { 134 var shards []Shard 135 shards = append(shards, NewShard(1)) 136 shards = append(shards, NewShard(2)) 137 shards = append(shards, NewShard(0)) 138 shards = append(shards, NewShard(3)) 139 140 shardsAreSorted(t, NewShards(shards)) 141 } 142 143 func TestShardCutoverTimes(t *testing.T) { 144 s := NewShard(1).SetState(Initializing).SetSourceID("id") 145 inputs := []struct { 146 actualNanos int64 147 expectedNanos int64 148 }{ 149 {actualNanos: 0, expectedNanos: 0}, 150 {actualNanos: 12345, expectedNanos: 12345}, 151 {actualNanos: math.MaxInt64, expectedNanos: math.MaxInt64}, 152 } 153 154 for _, input := range inputs { 155 s.SetCutoverNanos(input.actualNanos) 156 require.Equal(t, input.expectedNanos, s.CutoverNanos()) 157 } 158 } 159 160 func TestShardCutoffTimes(t *testing.T) { 161 s := NewShard(1).SetState(Initializing).SetSourceID("id").(*shard) 162 inputs := []struct { 163 actualNanos int64 164 expectedNanos int64 165 storedNanos int64 166 }{ 167 {actualNanos: 0, expectedNanos: math.MaxInt64, storedNanos: 0}, 168 {actualNanos: 12345, expectedNanos: 12345, storedNanos: 12345}, 169 {actualNanos: math.MaxInt64, expectedNanos: math.MaxInt64, storedNanos: 0}, 170 } 171 172 for _, input := range inputs { 173 s.SetCutoffNanos(input.actualNanos) 174 require.Equal(t, input.expectedNanos, s.CutoffNanos()) 175 require.Equal(t, input.storedNanos, s.cutoffNanos) 176 } 177 } 178 179 func TestShardStateToProtoError(t *testing.T) { 180 _, err := Unknown.Proto() 181 assert.Error(t, err) 182 } 183 184 func TestShardsToProto(t *testing.T) { 185 shardSet := []Shard{ 186 NewShard(0).SetState(Initializing).SetCutoverNanos(1234).SetCutoffNanos(5678), 187 NewShard(1).SetState(Initializing).SetCutoverNanos(0).SetCutoffNanos(5678), 188 NewShard(2).SetState(Initializing).SetCutoverNanos(math.MaxInt64).SetCutoffNanos(5678), 189 NewShard(3).SetState(Initializing).SetCutoverNanos(1234).SetCutoffNanos(0), 190 NewShard(4).SetState(Initializing).SetCutoverNanos(1234).SetCutoffNanos(math.MaxInt64), 191 } 192 shards := NewShards(shardSet) 193 proto, err := shards.Proto() 194 require.NoError(t, err) 195 196 expected := []struct { 197 cutoverNanos int64 198 cutoffNanos int64 199 }{ 200 {cutoverNanos: 1234, cutoffNanos: 5678}, 201 {cutoverNanos: 0, cutoffNanos: 5678}, 202 {cutoverNanos: math.MaxInt64, cutoffNanos: 5678}, 203 {cutoverNanos: 1234, cutoffNanos: 0}, 204 {cutoverNanos: 1234, cutoffNanos: 0}, 205 } 206 for i, shardProto := range proto { 207 require.Equal(t, expected[i].cutoverNanos, shardProto.CutoverNanos) 208 require.Equal(t, expected[i].cutoffNanos, shardProto.CutoffNanos) 209 } 210 211 reconstructed, err := NewShardsFromProto(proto) 212 require.NoError(t, err) 213 require.Equal(t, shards.NumShards(), reconstructed.NumShards()) 214 for i := 0; i < shards.NumShards(); i++ { 215 shardID := uint32(i) 216 expected, found := shards.Shard(shardID) 217 require.True(t, found) 218 actual, found := shards.Shard(shardID) 219 require.True(t, found) 220 require.Equal(t, expected, actual) 221 } 222 } 223 224 func TestClone(t *testing.T) { 225 s1 := NewShard(1).SetState(Initializing).SetSourceID("s").SetCutoffNanos(123).SetCutoverNanos(100) 226 s2 := NewShard(2).SetState(Available) 227 228 ss1 := NewShards([]Shard{s1, s2}) 229 ss2 := NewShards([]Shard{s1, s2}) 230 require.True(t, ss1.Clone().Equals(ss2)) 231 232 ss1.Add(NewShard(2).SetState(Leaving)) 233 require.False(t, ss1.Equals(ss2)) 234 } 235 236 func TestCloneCopiesToShardMap(t *testing.T) { 237 s := NewShard(1).SetState(Available) 238 239 ss := NewShards([]Shard{s}) 240 clonedSS := ss.Clone() 241 require.True(t, ss.Equals(clonedSS)) 242 243 s.SetState(Leaving) 244 245 clonedS, ok := clonedSS.Shard(1) 246 require.True(t, ok) 247 assert.Equal(t, Available, clonedS.State()) 248 } 249 250 func TestShardAdd(t *testing.T) { 251 for i := 1; i < 500; i++ { 252 rndShards := makeTestShards(i) 253 shards := NewShards(nil) 254 for j := 0; j < len(rndShards); j++ { 255 id := rndShards[j].ID() 256 require.False(t, shards.Contains(id)) 257 258 shards.Add(rndShards[j]) 259 require.True(t, shards.Contains(id)) 260 261 shrd, ok := shards.Shard(id) 262 require.True(t, ok) 263 require.Equal(t, id, shrd.ID()) 264 } 265 shardsAreSorted(t, shards) 266 } 267 } 268 269 func TestShardRemove(t *testing.T) { 270 for i := 1; i < 500; i++ { 271 rndShards := makeTestShards(i) 272 shards := NewShards(rndShards) 273 for j := 0; j < len(rndShards); j++ { 274 id := rndShards[j].ID() 275 require.True(t, shards.Contains(id)) 276 shards.Remove(id) 277 require.Equal(t, len(rndShards)-j-1, shards.NumShards()) 278 require.False(t, shards.Contains(id)) 279 } 280 shardsAreSorted(t, shards) 281 } 282 } 283 284 func randomIDs(seed int64, num int) []uint32 { 285 rnd := rand.New(rand.NewSource(seed)) // #nosec 286 ids := make([]uint32, num) 287 288 for i := uint32(0); i < uint32(num); i++ { 289 ids[i] = i 290 } 291 292 rnd.Shuffle(len(ids), func(i, j int) { 293 ids[i], ids[j] = ids[j], ids[i] 294 }) 295 return ids 296 } 297 298 func makeTestShards(num int) []Shard { 299 shardIDs := randomIDs(0, num) 300 s := make([]Shard, num) 301 for i, shardID := range shardIDs { 302 s[i] = NewShard(shardID) 303 } 304 return s 305 } 306 307 func shardsAreSorted(t *testing.T, shards Shards) { 308 prev := -1 309 for _, shard := range shards.All() { 310 id := int(shard.ID()) 311 if id <= prev { 312 t.Fatalf("expected id to be greater than %d, got %d", prev, id) 313 } 314 prev = id 315 } 316 } 317 318 func uint32Ptr(value uint32) *uint32 { 319 ptr := new(uint32) 320 *ptr = value 321 return ptr 322 }