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  }