github.com/m3db/m3@v1.5.0/src/cluster/placement/placement_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 placement 22 23 import ( 24 "fmt" 25 "sort" 26 "testing" 27 28 "github.com/m3db/m3/src/cluster/generated/proto/placementpb" 29 "github.com/m3db/m3/src/cluster/shard" 30 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestPlacement(t *testing.T) { 36 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 37 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 38 i1.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 39 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 40 41 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 42 i2.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 43 i2.Shards().Add(shard.NewShard(5).SetState(shard.Available)) 44 i2.Shards().Add(shard.NewShard(6).SetState(shard.Available)) 45 46 i3 := NewEmptyInstance("i3", "r3", "z1", "endpoint", 1) 47 i3.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 48 i3.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 49 i3.Shards().Add(shard.NewShard(5).SetState(shard.Available)) 50 51 i4 := NewEmptyInstance("i4", "r4", "z1", "endpoint", 1) 52 i4.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 53 i4.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 54 i4.Shards().Add(shard.NewShard(6).SetState(shard.Available)) 55 56 i5 := NewEmptyInstance("i5", "r5", "z1", "endpoint", 1) 57 i5.Shards().Add(shard.NewShard(5).SetState(shard.Available)) 58 i5.Shards().Add(shard.NewShard(6).SetState(shard.Available)) 59 i5.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 60 61 i6 := NewEmptyInstance("i6", "r6", "z1", "endpoint", 1) 62 i6.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 63 i6.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 64 i6.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 65 66 instances := []Instance{i1, i2, i3, i4, i5, i6} 67 68 ids := []uint32{1, 2, 3, 4, 5, 6} 69 p := NewPlacement().SetInstances(instances).SetShards(ids).SetReplicaFactor(3) 70 assert.False(t, p.IsSharded()) 71 p = p.SetIsSharded(true) 72 assert.True(t, p.IsSharded()) 73 p = p.SetCutoverNanos(1234) 74 assert.Equal(t, int64(1234), p.CutoverNanos()) 75 assert.NoError(t, Validate(p)) 76 77 i, exist := p.Instance("i6") 78 assert.True(t, exist) 79 assert.Equal(t, i6, i) 80 _, exist = p.Instance("not_exist") 81 assert.False(t, exist) 82 83 assert.Equal(t, 6, p.NumInstances()) 84 assert.Equal(t, 3, p.ReplicaFactor()) 85 assert.Equal(t, ids, p.Shards()) 86 assert.Equal(t, 6, p.NumShards()) 87 is := p.Instances() 88 sort.Sort(ByIDAscending(is)) 89 assert.Equal(t, instances, is) 90 91 expectedInstancesByShard := map[uint32][]Instance{ 92 1: {i1, i3, i5}, 93 2: {i1, i4, i6}, 94 3: {i1, i3, i6}, 95 4: {i2, i4, i6}, 96 5: {i2, i3, i5}, 97 6: {i2, i4, i5}, 98 } 99 for _, shard := range ids { 100 assert.Equal(t, expectedInstancesByShard[shard], p.InstancesForShard(shard)) 101 } 102 } 103 104 func TestValidateGood(t *testing.T) { 105 ids := []uint32{1, 2, 3, 4, 5, 6} 106 107 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 108 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 109 i1.Shards().Add(shard.NewShard(2).SetState(shard.Initializing)) 110 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 111 112 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 113 i2.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 114 i2.Shards().Add(shard.NewShard(5).SetState(shard.Available)) 115 i2.Shards().Add(shard.NewShard(6).SetState(shard.Initializing)) 116 117 instances := []Instance{i1, i2} 118 p := NewPlacement().SetInstances(instances).SetShards(ids).SetReplicaFactor(1).SetIsSharded(true) 119 assert.NoError(t, Validate(p)) 120 } 121 122 func TestUnknownShardState(t *testing.T) { 123 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 124 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 125 126 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 127 i2.Shards().Add(shard.NewShard(0)) 128 129 // unknown shard state 130 p := NewPlacement().SetInstances([]Instance{i1, i2}).SetShards([]uint32{0, 1}).SetReplicaFactor(1).SetIsSharded(true) 131 assert.Error(t, Validate(p)) 132 } 133 134 func TestMismatchShards(t *testing.T) { 135 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 136 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 137 138 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 139 i2.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 140 141 // mismatch shards 142 p := NewPlacement().SetInstances([]Instance{i1, i2}).SetShards([]uint32{1, 2, 3}).SetReplicaFactor(1) 143 require.Error(t, Validate(p)) 144 } 145 146 func TestNonSharded(t *testing.T) { 147 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 148 p := NewPlacement().SetInstances([]Instance{i1}).SetShards([]uint32{1}).SetReplicaFactor(1) 149 assert.NoError(t, Validate(p)) 150 151 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 152 p = NewPlacement().SetInstances([]Instance{i1}).SetShards([]uint32{1}).SetReplicaFactor(1) 153 assert.Error(t, Validate(p)) 154 } 155 156 func TestValidateMirrorButNotSharded(t *testing.T) { 157 p := NewPlacement().SetIsMirrored(true) 158 err := Validate(p) 159 require.Error(t, err) 160 assert.Equal(t, errMirrorNotSharded.Error(), err.Error()) 161 } 162 163 func TestValidateMissingShard(t *testing.T) { 164 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 165 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 166 i1.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 167 168 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 169 i2.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 170 171 ids := []uint32{1, 2} 172 p := NewPlacement().SetInstances([]Instance{i1, i2}).SetShards(ids).SetReplicaFactor(2).SetIsSharded(true) 173 err := Validate(p) 174 require.Error(t, err) 175 assert.Equal(t, "invalid placement, the total available shards in the placement is 3, expecting 4", err.Error()) 176 } 177 178 func TestValidateUnexpectedShard(t *testing.T) { 179 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 180 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 181 i1.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 182 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 183 184 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 185 i2.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 186 187 p := NewPlacement(). 188 SetInstances([]Instance{i1, i2}). 189 SetShards([]uint32{1, 2}). 190 SetReplicaFactor(2). 191 SetIsSharded(true) 192 193 err := Validate(p) 194 require.Error(t, err) 195 assert.Equal(t, errUnexpectedShards.Error(), err.Error()) 196 } 197 198 func TestValidateDuplicatedShards(t *testing.T) { 199 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 200 i1.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 201 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 202 i1.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 203 204 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 205 i2.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 206 i2.Shards().Add(shard.NewShard(5).SetState(shard.Available)) 207 i2.Shards().Add(shard.NewShard(6).SetState(shard.Available)) 208 209 p := NewPlacement(). 210 SetInstances([]Instance{i1, i2}). 211 SetShards([]uint32{2, 3, 4, 4, 5, 6}). 212 SetReplicaFactor(1) 213 err := Validate(p) 214 require.Error(t, err) 215 assert.Equal(t, errDuplicatedShards.Error(), err.Error()) 216 } 217 218 func TestValidateWrongReplicaForSomeShards(t *testing.T) { 219 // three shard 2 and only one shard 4 220 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 221 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 222 i1.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 223 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 224 225 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 226 i2.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 227 i2.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 228 i2.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 229 230 i3 := NewEmptyInstance("i3", "r3", "z1", "endpoint", 1) 231 i3.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 232 i3.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 233 234 p := NewPlacement(). 235 SetInstances([]Instance{i1, i2, i3}). 236 SetShards([]uint32{1, 2, 3, 4}). 237 SetReplicaFactor(2) 238 assert.Error(t, Validate(p)) 239 } 240 241 func TestValidateLeavingMoreThanInitializing(t *testing.T) { 242 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 243 i1.Shards().Add(shard.NewShard(1).SetState(shard.Initializing)) 244 i1.Shards().Add(shard.NewShard(2).SetState(shard.Initializing)) 245 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 246 247 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 248 i2.Shards().Add(shard.NewShard(1).SetState(shard.Leaving)) 249 i2.Shards().Add(shard.NewShard(2).SetState(shard.Leaving)) 250 i2.Shards().Add(shard.NewShard(3).SetState(shard.Leaving)) 251 252 p := NewPlacement(). 253 SetInstances([]Instance{i1, i2}). 254 SetShards([]uint32{1, 2, 3}). 255 SetReplicaFactor(1). 256 SetIsSharded(true) 257 err := Validate(p) 258 assert.Error(t, err) 259 assert.Equal(t, err.Error(), "invalid placement, 3 shards in Leaving state, more than 2 in Initializing state") 260 } 261 262 func TestValidateLeavingNotMatchInitializingWithSourceID(t *testing.T) { 263 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 264 i1.Shards().Add(shard.NewShard(1).SetState(shard.Leaving)) 265 i1.Shards().Add(shard.NewShard(2).SetState(shard.Leaving)) 266 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 267 268 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 269 i2.Shards().Add(shard.NewShard(1).SetState(shard.Initializing)) 270 i2.Shards().Add(shard.NewShard(2).SetState(shard.Initializing).SetSourceID("i1")) 271 272 p := NewPlacement(). 273 SetInstances([]Instance{i1, i2}). 274 SetShards([]uint32{1, 2, 3}). 275 SetReplicaFactor(1). 276 SetIsSharded(true) 277 err := Validate(p) 278 assert.Error(t, err) 279 assert.Equal(t, err.Error(), "invalid placement, 2 shards in Leaving state, not equal 1 in Initializing state with source id") 280 } 281 282 func TestValidateLeavingAndInitializingWithSourceIDMissing(t *testing.T) { 283 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 284 i1.Shards().Add(shard.NewShard(1).SetState(shard.Leaving)) 285 i1.Shards().Add(shard.NewShard(2).SetState(shard.Leaving)) 286 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 287 288 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 289 i2.Shards().Add(shard.NewShard(1).SetState(shard.Initializing).SetSourceID("unknown")) 290 i2.Shards().Add(shard.NewShard(2).SetState(shard.Initializing).SetSourceID("i1")) 291 292 p := NewPlacement(). 293 SetInstances([]Instance{i1, i2}). 294 SetShards([]uint32{1, 2, 3}). 295 SetReplicaFactor(1). 296 SetIsSharded(true) 297 err := Validate(p) 298 require.Error(t, err) 299 assert.Equal(t, err.Error(), "instance i2 has initializing shard 1 with source ID unknown but no such instance in placement") 300 } 301 302 func TestValidateLeavingAndInitializingWithSourceIDNoSuchShard(t *testing.T) { 303 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 304 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 305 i1.Shards().Add(shard.NewShard(2).SetState(shard.Leaving)) 306 307 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 308 i2.Shards().Add(shard.NewShard(2).SetState(shard.Initializing).SetSourceID("i1")) 309 i2.Shards().Add(shard.NewShard(3).SetState(shard.Initializing).SetSourceID("i1")) 310 311 p := NewPlacement(). 312 SetInstances([]Instance{i1, i2}). 313 SetShards([]uint32{1, 2, 3}). 314 SetReplicaFactor(1). 315 SetIsSharded(true) 316 err := Validate(p) 317 require.Error(t, err) 318 assert.Equal(t, err.Error(), "instance i2 has initializing shard 3 with source ID i1 but leaving instance has no such shard") 319 } 320 321 func TestValidateLeavingAndInitializingWithSourceIDShardNotLeaving(t *testing.T) { 322 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 323 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 324 i1.Shards().Add(shard.NewShard(2).SetState(shard.Leaving)) 325 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 326 327 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 328 i2.Shards().Add(shard.NewShard(1).SetState(shard.Initializing).SetSourceID("i1")) 329 i2.Shards().Add(shard.NewShard(2).SetState(shard.Initializing).SetSourceID("i1")) 330 331 p := NewPlacement(). 332 SetInstances([]Instance{i1, i2}). 333 SetShards([]uint32{1, 2, 3}). 334 SetReplicaFactor(1). 335 SetIsSharded(true) 336 err := Validate(p) 337 require.Error(t, err) 338 assert.Equal(t, err.Error(), "instance i2 has initializing shard 1 with source ID i1 but leaving instance has shard with state Available") 339 } 340 341 func TestValidateLeavingAndInitializingWithSourceIDDoubleMatched(t *testing.T) { 342 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 343 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 344 i1.Shards().Add(shard.NewShard(2).SetState(shard.Leaving)) 345 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 346 347 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 348 i2.Shards().Add(shard.NewShard(2).SetState(shard.Initializing).SetSourceID("i1")) 349 350 i3 := NewEmptyInstance("i3", "r2", "z1", "endpoint", 1) 351 i3.Shards().Add(shard.NewShard(2).SetState(shard.Initializing).SetSourceID("i1")) 352 353 p := NewPlacement(). 354 SetInstances([]Instance{i1, i2, i3}). 355 SetShards([]uint32{1, 2, 3}). 356 SetReplicaFactor(1). 357 SetIsSharded(true) 358 err := Validate(p) 359 require.Error(t, err) 360 assert.Equal(t, err.Error(), "instance i3 has initializing shard 2 with source ID i1 but leaving instance has shard already matched by i2") 361 } 362 363 func TestValidateNoEndpoint(t *testing.T) { 364 i1 := NewEmptyInstance("i1", "r1", "z1", "", 1) 365 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 366 367 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 368 i2.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 369 370 p := NewPlacement(). 371 SetInstances([]Instance{i1, i2}). 372 SetShards([]uint32{1, 2}). 373 SetReplicaFactor(1). 374 SetIsSharded(true) 375 err := Validate(p) 376 assert.Error(t, err) 377 assert.Contains(t, err.Error(), "does not contain valid endpoint") 378 } 379 380 func TestValidateInstanceWithNoShard(t *testing.T) { 381 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 382 383 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 384 i2.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 385 386 p := NewPlacement(). 387 SetInstances([]Instance{i1, i2}). 388 SetShards([]uint32{1}). 389 SetReplicaFactor(1). 390 SetIsSharded(true) 391 err := Validate(p) 392 assert.Error(t, err) 393 assert.Contains(t, err.Error(), "contains no shard") 394 } 395 396 func TestValidateInstanceWithSameShardSetIDSameShards(t *testing.T) { 397 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint1", 1).SetShardSetID(1) 398 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 399 400 i2 := NewEmptyInstance("i2", "r2", "z2", "endpoint2", 1).SetShardSetID(1) 401 i2.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 402 403 p := NewPlacement(). 404 SetInstances([]Instance{i1, i2}). 405 SetShards([]uint32{1}). 406 SetReplicaFactor(2). 407 SetIsSharded(true). 408 SetMaxShardSetID(1) 409 assert.NoError(t, Validate(p)) 410 } 411 412 func TestValidateInstanceWithSameShardSetIDDifferentShards(t *testing.T) { 413 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint1", 1).SetShardSetID(1) 414 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 415 416 i2 := NewEmptyInstance("i2", "r2", "z2", "endpoint2", 1).SetShardSetID(1) 417 i2.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 418 419 p := NewPlacement(). 420 SetInstances([]Instance{i1, i2}). 421 SetShards([]uint32{1, 2}). 422 SetReplicaFactor(1). 423 SetIsSharded(true). 424 SetMaxShardSetID(1) 425 err := Validate(p) 426 assert.Error(t, err) 427 assert.Contains(t, err.Error(), "have the same shard set id") 428 } 429 430 func TestValidateInstanceWithValidMaxShardSetID(t *testing.T) { 431 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1).SetShardSetID(1) 432 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 433 434 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1).SetShardSetID(3) 435 i2.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 436 437 p := NewPlacement(). 438 SetInstances([]Instance{i1, i2}). 439 SetShards([]uint32{1}). 440 SetReplicaFactor(2). 441 SetIsSharded(true). 442 SetMaxShardSetID(3) 443 assert.NoError(t, Validate(p)) 444 } 445 446 func TestValidateInstanceWithShardSetIDLargerThanMax(t *testing.T) { 447 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1).SetShardSetID(1) 448 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 449 450 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1).SetShardSetID(3) 451 i2.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 452 453 p := NewPlacement(). 454 SetInstances([]Instance{i1, i2}). 455 SetShards([]uint32{1}). 456 SetReplicaFactor(1). 457 SetIsSharded(true). 458 SetMaxShardSetID(2) 459 err := Validate(p) 460 assert.Error(t, err) 461 assert.Contains(t, err.Error(), "larger than max shard set id") 462 } 463 464 func TestInstance(t *testing.T) { 465 i1 := NewInstance(). 466 SetID("id"). 467 SetEndpoint("endpoint"). 468 SetIsolationGroup("isolationGroup"). 469 SetWeight(1). 470 SetShardSetID(0). 471 SetZone("zone"). 472 SetHostname("host1"). 473 SetPort(123). 474 SetMetadata(InstanceMetadata{DebugPort: 456}) 475 assert.NotNil(t, i1.Shards()) 476 s := shard.NewShards([]shard.Shard{ 477 shard.NewShard(1).SetState(shard.Available), 478 shard.NewShard(2).SetState(shard.Available), 479 shard.NewShard(3).SetState(shard.Available), 480 }) 481 i1.SetShards(s) 482 description := fmt.Sprintf( 483 "Instance[ID=id, IsolationGroup=isolationGroup, Zone=zone, Weight=1, Endpoint=endpoint, Hostname=host1, Port=123, ShardSetID=0, Shards=%s, Metadata={DebugPort:456}]", 484 s.String()) 485 assert.Equal(t, description, i1.String()) 486 487 assert.True(t, i1.Shards().Contains(1)) 488 assert.False(t, i1.Shards().Contains(100)) 489 assert.Equal(t, 3, i1.Shards().NumShards()) 490 assert.Equal(t, "id", i1.ID()) 491 assert.Equal(t, "endpoint", i1.Endpoint()) 492 assert.Equal(t, uint32(1), i1.Weight()) 493 assert.Equal(t, "zone", i1.Zone()) 494 assert.Equal(t, "isolationGroup", i1.IsolationGroup()) 495 496 i1.Shards().Remove(1) 497 assert.False(t, i1.Shards().Contains(1)) 498 assert.False(t, i1.Shards().Contains(100)) 499 assert.Equal(t, 2, i1.Shards().NumShards()) 500 assert.Equal(t, "id", i1.ID()) 501 assert.Equal(t, "isolationGroup", i1.IsolationGroup()) 502 } 503 504 func TestInstanceIsLeaving(t *testing.T) { 505 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 506 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 507 assert.False(t, i1.IsLeaving()) 508 509 i1.Shards().Add(shard.NewShard(1).SetState(shard.Initializing)) 510 assert.False(t, i1.IsLeaving()) 511 512 i1.Shards().Add(shard.NewShard(1).SetState(shard.Leaving)) 513 assert.True(t, i1.IsLeaving()) 514 515 i1.SetShards(shard.NewShards(nil)) 516 assert.False(t, i1.IsLeaving()) 517 } 518 519 func TestInstanceIsInitializing(t *testing.T) { 520 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 521 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 522 assert.False(t, i1.IsInitializing()) 523 524 i1.Shards().Add(shard.NewShard(1).SetState(shard.Initializing)) 525 assert.True(t, i1.IsInitializing()) 526 527 i1.Shards().Add(shard.NewShard(1).SetState(shard.Leaving)) 528 assert.False(t, i1.IsInitializing()) 529 530 i1.SetShards(shard.NewShards(nil)) 531 assert.False(t, i1.IsInitializing()) 532 } 533 534 func TestInstanceIsAvailable(t *testing.T) { 535 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 536 i1.Shards().Add(shard.NewShard(1).SetState(shard.Leaving)) 537 assert.False(t, i1.IsAvailable()) 538 539 i1.Shards().Add(shard.NewShard(1).SetState(shard.Initializing)) 540 assert.False(t, i1.IsAvailable()) 541 542 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 543 assert.True(t, i1.IsAvailable()) 544 545 i1.SetShards(shard.NewShards(nil)) 546 assert.False(t, i1.IsAvailable()) 547 } 548 549 func TestSortInstanceByID(t *testing.T) { 550 i1 := NewEmptyInstance("i1", "", "", "endpoint", 1) 551 i2 := NewEmptyInstance("i2", "", "", "endpoint", 1) 552 i3 := NewEmptyInstance("i3", "", "", "endpoint", 1) 553 i4 := NewEmptyInstance("i4", "", "", "endpoint", 1) 554 i5 := NewEmptyInstance("i5", "", "", "endpoint", 1) 555 i6 := NewEmptyInstance("i6", "", "", "endpoint", 1) 556 557 i := []Instance{i1, i6, i4, i2, i3, i5} 558 sort.Sort(ByIDAscending(i)) 559 560 assert.Equal(t, []Instance{i1, i2, i3, i4, i5, i6}, i) 561 } 562 563 func TestClonePlacement(t *testing.T) { 564 i1 := NewEmptyInstance("i1", "r1", "z1", "endpoint", 1) 565 i1.Shards().Add(shard.NewShard(1).SetState(shard.Available)) 566 i1.Shards().Add(shard.NewShard(2).SetState(shard.Available)) 567 i1.Shards().Add(shard.NewShard(3).SetState(shard.Available)) 568 569 i2 := NewEmptyInstance("i2", "r2", "z1", "endpoint", 1) 570 i2.Shards().Add(shard.NewShard(4).SetState(shard.Available)) 571 i2.Shards().Add(shard.NewShard(5).SetState(shard.Available)) 572 i2.Shards().Add(shard.NewShard(6).SetState(shard.Available)) 573 574 instances := []Instance{i1, i2} 575 576 ids := []uint32{1, 2, 3, 4, 5, 6} 577 p := NewPlacement(). 578 SetInstances(instances). 579 SetShards(ids). 580 SetReplicaFactor(1). 581 SetIsMirrored(false). 582 SetIsSharded(true). 583 SetCutoverNanos(1234). 584 SetMaxShardSetID(2) 585 copy := p.Clone() 586 assert.Equal(t, p.NumInstances(), copy.NumInstances()) 587 assert.Equal(t, p.Shards(), copy.Shards()) 588 assert.Equal(t, p.ReplicaFactor(), copy.ReplicaFactor()) 589 assert.Equal(t, p.MaxShardSetID(), copy.MaxShardSetID()) 590 for _, instance := range p.Instances() { 591 copiedInstance, exist := copy.Instance(instance.ID()) 592 assert.True(t, exist) 593 for _, s := range copiedInstance.Shards().All() { 594 otherS, _ := instance.Shards().Shard(s.ID()) 595 assert.Equal(t, s, otherS) 596 } 597 assert.Equal(t, copiedInstance, instance) 598 // make sure they are different objects, updating one won't update the other 599 instance.Shards().Add(shard.NewShard(100).SetState(shard.Available)) 600 assert.NotEqual(t, copiedInstance, instance) 601 } 602 } 603 604 func TestVersion(t *testing.T) { 605 p1 := NewPlacement() 606 assert.Equal(t, 0, p1.Version()) 607 608 p1 = p1.SetVersion(100) 609 assert.Equal(t, 100, p1.Version()) 610 } 611 612 func TestConvertBetweenProtoAndPlacement(t *testing.T) { 613 protoShards := getProtoShards([]uint32{0, 1, 2}) 614 placementProto := &placementpb.Placement{ 615 Instances: map[string]*placementpb.Instance{ 616 "i1": &placementpb.Instance{ 617 Id: "i1", 618 IsolationGroup: "r1", 619 Zone: "z1", 620 Endpoint: "e1", 621 Weight: 1, 622 Shards: protoShards, 623 ShardSetId: 0, 624 Metadata: &placementpb.InstanceMetadata{DebugPort: 123}, 625 }, 626 "i2": &placementpb.Instance{ 627 Id: "i2", 628 IsolationGroup: "r2", 629 Zone: "z1", 630 Endpoint: "e2", 631 Weight: 1, 632 Shards: protoShards, 633 ShardSetId: 1, 634 Metadata: &placementpb.InstanceMetadata{DebugPort: 456}, 635 }, 636 }, 637 ReplicaFactor: 2, 638 NumShards: 3, 639 IsSharded: true, 640 CutoverTime: 1234, 641 MaxShardSetId: 1, 642 } 643 644 p, err := NewPlacementFromProto(placementProto) 645 assert.NoError(t, err) 646 assert.Equal(t, 2, p.NumInstances()) 647 assert.Equal(t, 2, p.ReplicaFactor()) 648 assert.True(t, p.IsSharded()) 649 assert.Equal(t, []uint32{0, 1, 2}, p.Shards()) 650 assert.Equal(t, int64(1234), p.CutoverNanos()) 651 assert.Equal(t, uint32(1), p.MaxShardSetID()) 652 instances := p.Instances() 653 assert.Equal(t, uint32(0), instances[0].ShardSetID()) 654 assert.Equal(t, uint32(123), instances[0].Metadata().DebugPort) 655 assert.Equal(t, uint32(1), instances[1].ShardSetID()) 656 assert.Equal(t, uint32(456), instances[1].Metadata().DebugPort) 657 658 placementProtoNew, err := p.Proto() 659 assert.NoError(t, err) 660 assert.Equal(t, placementProto.ReplicaFactor, placementProtoNew.ReplicaFactor) 661 assert.Equal(t, placementProto.NumShards, placementProtoNew.NumShards) 662 assert.Equal(t, placementProto.CutoverTime, placementProtoNew.CutoverTime) 663 assert.Equal(t, placementProto.MaxShardSetId, placementProtoNew.MaxShardSetId) 664 for id, h := range placementProto.Instances { 665 instance := placementProtoNew.Instances[id] 666 assert.Equal(t, h.Id, instance.Id) 667 assert.Equal(t, h.IsolationGroup, instance.IsolationGroup) 668 assert.Equal(t, h.Zone, instance.Zone) 669 assert.Equal(t, h.Weight, instance.Weight) 670 assert.Equal(t, h.Shards, instance.Shards) 671 assert.Equal(t, h.ShardSetId, instance.ShardSetId) 672 } 673 } 674 675 func TestPlacementInstanceFromProto(t *testing.T) { 676 protoShardsUnsorted := getProtoShards([]uint32{2, 1, 0}) 677 678 instanceProto := &placementpb.Instance{ 679 Id: "i1", 680 IsolationGroup: "r1", 681 Zone: "z1", 682 Endpoint: "e1", 683 Weight: 1, 684 Shards: protoShardsUnsorted, 685 } 686 687 instance, err := NewInstanceFromProto(instanceProto) 688 assert.NoError(t, err) 689 690 instanceShards := instance.Shards() 691 692 // assert.Equal can't compare shards due to pointer types, we check them 693 // manually 694 instance.SetShards(shard.NewShards(nil)) 695 696 expShards := shard.NewShards([]shard.Shard{ 697 shard.NewShard(0).SetSourceID("i1").SetState(shard.Available), 698 shard.NewShard(1).SetSourceID("i1").SetState(shard.Available), 699 shard.NewShard(2).SetSourceID("i1").SetState(shard.Available), 700 }) 701 702 expInstance := NewInstance(). 703 SetID("i1"). 704 SetIsolationGroup("r1"). 705 SetZone("z1"). 706 SetEndpoint("e1"). 707 SetWeight(1) 708 709 assert.Equal(t, expInstance, instance) 710 assert.Equal(t, expShards.AllIDs(), instanceShards.AllIDs()) 711 712 instanceProto.Shards[0].State = placementpb.ShardState(1000) 713 instance, err = NewInstanceFromProto(instanceProto) 714 assert.Error(t, err) 715 assert.Nil(t, instance) 716 } 717 718 func TestPlacementInstanceToProto(t *testing.T) { 719 shards := shard.NewShards([]shard.Shard{ 720 shard.NewShard(2).SetSourceID("i1").SetState(shard.Available), 721 shard.NewShard(1).SetSourceID("i1").SetState(shard.Available), 722 shard.NewShard(0).SetSourceID("i1").SetState(shard.Available), 723 }) 724 725 instance := NewInstance(). 726 SetID("i1"). 727 SetIsolationGroup("r1"). 728 SetZone("z1"). 729 SetEndpoint("e1"). 730 SetWeight(1). 731 SetShards(shards). 732 SetMetadata(InstanceMetadata{ 733 DebugPort: 123, 734 }) 735 736 instanceProto, err := instance.Proto() 737 assert.NoError(t, err) 738 739 protoShards := getProtoShards([]uint32{0, 1, 2}) 740 for _, s := range protoShards { 741 s.SourceId = "i1" 742 } 743 744 expInstance := &placementpb.Instance{ 745 Id: "i1", 746 IsolationGroup: "r1", 747 Zone: "z1", 748 Endpoint: "e1", 749 Weight: 1, 750 Shards: protoShards, 751 Metadata: &placementpb.InstanceMetadata{ 752 DebugPort: 123, 753 }, 754 } 755 756 assert.Equal(t, expInstance, instanceProto) 757 } 758 759 func getProtoShards(ids []uint32) []*placementpb.Shard { 760 r := make([]*placementpb.Shard, len(ids)) 761 for i, id := range ids { 762 r[i] = &placementpb.Shard{ 763 Id: id, 764 State: placementpb.ShardState_AVAILABLE, 765 } 766 } 767 return r 768 }