github.com/m3db/m3@v1.5.0/src/cluster/placement/service/service_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 service
    22  
    23  import (
    24  	"errors"
    25  	"testing"
    26  
    27  	"github.com/m3db/m3/src/cluster/kv/mem"
    28  	"github.com/m3db/m3/src/cluster/placement"
    29  	"github.com/m3db/m3/src/cluster/placement/algo"
    30  	"github.com/m3db/m3/src/cluster/placement/storage"
    31  	"github.com/m3db/m3/src/cluster/shard"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func TestGoodWorkflow(t *testing.T) {
    37  	p := NewPlacementService(newMockStorage(),
    38  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
    39  	testGoodWorkflow(t, p)
    40  }
    41  
    42  func testGoodWorkflow(t *testing.T, ps placement.Service) {
    43  	i1 := placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 2)
    44  	i2 := placement.NewEmptyInstance("i2", "r2", "z1", "endpoint", 2)
    45  	i3 := placement.NewEmptyInstance("i3", "r3", "z1", "endpoint", 2)
    46  	_, err := ps.BuildInitialPlacement([]placement.Instance{i1, i2}, 10, 1)
    47  	assert.NoError(t, err)
    48  
    49  	_, err = ps.AddReplica()
    50  	assert.NoError(t, err)
    51  
    52  	for _, instance := range []placement.Instance{i1, i2} {
    53  		_, err = ps.MarkInstanceAvailable(instance.ID())
    54  		assert.NoError(t, err)
    55  	}
    56  	_, ai, err := ps.AddInstances([]placement.Instance{i3})
    57  	assert.NoError(t, err)
    58  	assertPlacementInstanceEqualExceptShards(t, i3, ai[0])
    59  
    60  	_, err = ps.MarkInstanceAvailable(i3.ID())
    61  	assert.NoError(t, err)
    62  
    63  	_, err = ps.RemoveInstances([]string{i1.ID()})
    64  	assert.NoError(t, err)
    65  
    66  	markAllInstancesAvailable(t, ps)
    67  
    68  	var (
    69  		i21 = placement.NewEmptyInstance("i21", "r2", "z1", "endpoint", 1)
    70  		i4  = placement.NewEmptyInstance("i4", "r4", "z1", "endpoint", 1)
    71  	)
    72  	_, usedInstances, err := ps.ReplaceInstances(
    73  		[]string{i2.ID()},
    74  		[]placement.Instance{i21, i4,
    75  			i3, // already in placement
    76  			placement.NewEmptyInstance("i31", "r3", "z1", "endpoint", 1), // conflict
    77  		},
    78  	)
    79  	assert.NoError(t, err)
    80  	assert.Equal(t, 2, len(usedInstances))
    81  	assertPlacementInstanceEqualExceptShards(t, i21, usedInstances[0])
    82  	assertPlacementInstanceEqualExceptShards(t, i4, usedInstances[1])
    83  
    84  	for _, id := range []string{"i21", "i4"} {
    85  		_, err = ps.MarkInstanceAvailable(id)
    86  		assert.NoError(t, err)
    87  	}
    88  
    89  	s, err := ps.Placement()
    90  	assert.NoError(t, err)
    91  	assert.Equal(t, 3, s.NumInstances())
    92  	_, exist := s.Instance("i21")
    93  	assert.True(t, exist)
    94  	_, exist = s.Instance("i4")
    95  	assert.True(t, exist)
    96  
    97  	_, ai, err = ps.AddInstances([]placement.Instance{i1})
    98  	assert.NoError(t, err)
    99  	assertPlacementInstanceEqualExceptShards(t, i1, ai[0])
   100  
   101  	i24 := placement.NewEmptyInstance("i24", "r2", "z1", "endpoint", 1)
   102  	_, ai, err = ps.AddInstances([]placement.Instance{i24})
   103  	assert.NoError(t, err)
   104  	assertPlacementInstanceEqualExceptShards(t, i24, ai[0])
   105  
   106  	i34 := placement.NewEmptyInstance("i34", "r3", "z1", "endpoint", 1)
   107  	_, ai, err = ps.AddInstances([]placement.Instance{i34})
   108  	assert.NoError(t, err)
   109  	assertPlacementInstanceEqualExceptShards(t, i34, ai[0])
   110  
   111  	i35 := placement.NewEmptyInstance("i35", "r3", "z1", "endpoint", 1)
   112  	_, ai, err = ps.AddInstances([]placement.Instance{i35})
   113  	assert.NoError(t, err)
   114  	assertPlacementInstanceEqualExceptShards(t, i35, ai[0])
   115  
   116  	i41 := placement.NewEmptyInstance("i41", "r4", "z1", "endpoint", 1)
   117  	instances := []placement.Instance{
   118  		placement.NewEmptyInstance("i15", "r1", "z1", "endpoint", 1),
   119  		placement.NewEmptyInstance("i34", "r3", "z1", "endpoint", 1),
   120  		placement.NewEmptyInstance("i35", "r3", "z1", "endpoint", 1),
   121  		placement.NewEmptyInstance("i36", "r3", "z1", "endpoint", 1),
   122  		placement.NewEmptyInstance("i23", "r2", "z1", "endpoint", 1),
   123  		i41,
   124  	}
   125  	_, ai, err = ps.AddInstances(instances)
   126  	assert.NoError(t, err)
   127  	assertPlacementInstanceEqualExceptShards(t, i41, ai[0])
   128  	s, err = ps.Placement()
   129  	assert.NoError(t, err)
   130  	// Instance added from least weighted isolation group.
   131  	_, exist = s.Instance("i41")
   132  	assert.True(t, exist)
   133  }
   134  
   135  func assertPlacementInstanceEqualExceptShards(
   136  	t *testing.T,
   137  	expected placement.Instance,
   138  	observed placement.Instance,
   139  ) {
   140  	assert.Equal(t, expected.ID(), observed.ID())
   141  	assert.Equal(t, expected.IsolationGroup(), observed.IsolationGroup())
   142  	assert.Equal(t, expected.Zone(), observed.Zone())
   143  	assert.Equal(t, expected.Weight(), observed.Weight())
   144  	assert.Equal(t, expected.Endpoint(), observed.Endpoint())
   145  	assert.True(t, len(observed.Shards().All()) > 0)
   146  }
   147  
   148  func TestNonShardedWorkflow(t *testing.T) {
   149  	ps := NewPlacementService(newMockStorage(),
   150  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1").SetIsSharded(false)))
   151  
   152  	_, err := ps.BuildInitialPlacement([]placement.Instance{
   153  		placement.NewEmptyInstance("i1", "r1", "z1", "e1", 1),
   154  		placement.NewEmptyInstance("i2", "r1", "z1", "e2", 1),
   155  	}, 10, 1)
   156  	assert.Error(t, err)
   157  
   158  	p, err := ps.BuildInitialPlacement([]placement.Instance{
   159  		placement.NewEmptyInstance("i1", "r1", "z1", "e1", 1),
   160  		placement.NewEmptyInstance("i2", "r1", "z1", "e2", 1),
   161  	}, 0, 1)
   162  	assert.NoError(t, err)
   163  	assert.Equal(t, 2, p.NumInstances())
   164  	assert.Equal(t, 0, p.NumShards())
   165  	assert.Equal(t, 1, p.ReplicaFactor())
   166  	assert.False(t, p.IsSharded())
   167  
   168  	p, err = ps.AddReplica()
   169  	assert.NoError(t, err)
   170  	assert.Equal(t, 2, p.NumInstances())
   171  	assert.Equal(t, 0, p.NumShards())
   172  	assert.Equal(t, 2, p.ReplicaFactor())
   173  	assert.False(t, p.IsSharded())
   174  
   175  	i3 := placement.NewEmptyInstance("i3", "r1", "z1", "e3", 1)
   176  	i4 := placement.NewEmptyInstance("i4", "r1", "z1", "e4", 1)
   177  	p, ai, err := ps.AddInstances([]placement.Instance{i3, i4})
   178  	assert.NoError(t, err)
   179  	assert.Equal(t, i3, ai[0])
   180  	assert.Equal(t, 3, p.NumInstances())
   181  	assert.Equal(t, 0, p.NumShards())
   182  	assert.Equal(t, 2, p.ReplicaFactor())
   183  	assert.False(t, p.IsSharded())
   184  
   185  	p, err = ps.RemoveInstances([]string{"i1"})
   186  	assert.NoError(t, err)
   187  	assert.Equal(t, 2, p.NumInstances())
   188  	assert.Equal(t, 0, p.NumShards())
   189  	assert.Equal(t, 2, p.ReplicaFactor())
   190  	assert.False(t, p.IsSharded())
   191  
   192  	p, usedInstances, err := ps.ReplaceInstances([]string{"i2"}, []placement.Instance{i3, i4})
   193  	assert.NoError(t, err)
   194  	assert.Equal(t, 1, len(usedInstances))
   195  	assert.Equal(t, i4, usedInstances[0])
   196  	assert.Equal(t, 2, p.NumInstances())
   197  	assert.Equal(t, 0, p.NumShards())
   198  	assert.Equal(t, 2, p.ReplicaFactor())
   199  	assert.False(t, p.IsSharded())
   200  
   201  	// nothing happens because i3 has no shards
   202  	_, err = ps.MarkInstanceAvailable("i3")
   203  	assert.NoError(t, err)
   204  	assert.Equal(t, 2, p.NumInstances())
   205  	assert.Equal(t, 0, p.NumShards())
   206  	assert.Equal(t, 2, p.ReplicaFactor())
   207  	assert.False(t, p.IsSharded())
   208  
   209  	// nothing happens because there are no shards
   210  	_, err = ps.BalanceShards()
   211  	assert.NoError(t, err)
   212  	assert.Equal(t, 2, p.NumInstances())
   213  	assert.Equal(t, 0, p.NumShards())
   214  	assert.Equal(t, 2, p.ReplicaFactor())
   215  	assert.False(t, p.IsSharded())
   216  }
   217  
   218  func TestBadInitialPlacement(t *testing.T) {
   219  	p := NewPlacementService(newMockStorage(),
   220  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1").SetIsSharded(false)))
   221  
   222  	// invalid numShards
   223  	_, err := p.BuildInitialPlacement([]placement.Instance{
   224  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   225  		placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1),
   226  	}, -1, 1)
   227  	assert.Error(t, err)
   228  
   229  	// invalid rf
   230  	_, err = p.BuildInitialPlacement([]placement.Instance{
   231  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   232  		placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1),
   233  	}, 10, 0)
   234  	assert.Error(t, err)
   235  
   236  	// numshards > 0 && sharded == false
   237  	_, err = p.BuildInitialPlacement([]placement.Instance{
   238  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   239  		placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1),
   240  	}, 10, 1)
   241  	assert.Error(t, err)
   242  
   243  	p = NewPlacementService(newMockStorage(),
   244  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   245  
   246  	// Not enough instances.
   247  	_, err = p.BuildInitialPlacement([]placement.Instance{}, 10, 1)
   248  	assert.Error(t, err)
   249  
   250  	// Error: rf == 0 && sharded == true.
   251  	_, err = p.BuildInitialPlacement([]placement.Instance{
   252  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   253  		placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1),
   254  	}, 10, 0)
   255  	assert.Error(t, err)
   256  
   257  	// Not enough isolation groups.
   258  	_, err = p.BuildInitialPlacement([]placement.Instance{
   259  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   260  		placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1),
   261  	}, 100, 2)
   262  	assert.Error(t, err)
   263  
   264  	_, err = p.BuildInitialPlacement([]placement.Instance{
   265  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   266  		placement.NewEmptyInstance("i2", "r2", "z1", "endpoint", 1),
   267  	}, 100, 2)
   268  	assert.NoError(t, err)
   269  
   270  	// Placement already exist.
   271  	_, err = p.BuildInitialPlacement([]placement.Instance{
   272  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   273  		placement.NewEmptyInstance("i2", "r2", "z1", "endpoint", 1),
   274  	}, 100, 2)
   275  	assert.Error(t, err)
   276  }
   277  
   278  func TestBadAddReplica(t *testing.T) {
   279  	p := NewPlacementService(newMockStorage(),
   280  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   281  
   282  	_, err := p.BuildInitialPlacement(
   283  		[]placement.Instance{placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)},
   284  		10, 1)
   285  	assert.NoError(t, err)
   286  
   287  	// Not enough isolation groups/instances.
   288  	_, err = p.AddReplica()
   289  	assert.Error(t, err)
   290  
   291  	// Could not find placement for service.
   292  	p = NewPlacementService(newMockStorage(),
   293  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   294  	_, err = p.AddReplica()
   295  	assert.Error(t, err)
   296  }
   297  
   298  func TestBadAddInstance(t *testing.T) {
   299  	ms := newMockStorage()
   300  	p := NewPlacementService(ms,
   301  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   302  
   303  	_, err := p.BuildInitialPlacement(
   304  		[]placement.Instance{placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)},
   305  		10, 1)
   306  	assert.NoError(t, err)
   307  
   308  	// adding instance already exist
   309  	_, _, err = p.AddInstances([]placement.Instance{placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)})
   310  	assert.Error(t, err)
   311  
   312  	// too many zones
   313  	_, _, err = p.AddInstances([]placement.Instance{placement.NewEmptyInstance("i2", "r2", "z2", "endpoint", 1)})
   314  	assert.Error(t, err)
   315  
   316  	p = NewPlacementService(ms,
   317  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   318  	_, _, err = p.AddInstances([]placement.Instance{placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)})
   319  	assert.Error(t, err)
   320  
   321  	// could not find placement for service
   322  	p = NewPlacementService(newMockStorage(),
   323  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   324  	_, _, err = p.AddInstances([]placement.Instance{placement.NewEmptyInstance("i2", "r2", "z1", "endpoint", 1)})
   325  	assert.Error(t, err)
   326  }
   327  
   328  func TestBadRemoveInstance(t *testing.T) {
   329  	p := NewPlacementService(newMockStorage(),
   330  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   331  
   332  	_, err := p.BuildInitialPlacement(
   333  		[]placement.Instance{placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)},
   334  		10, 1)
   335  	assert.NoError(t, err)
   336  
   337  	// Leaving instance not exist.
   338  	_, err = p.RemoveInstances([]string{"not_exist"})
   339  	assert.Error(t, err)
   340  
   341  	// Not enough isolation groups/instances after removal.
   342  	_, err = p.RemoveInstances([]string{"i1"})
   343  	assert.Error(t, err)
   344  
   345  	// Could not find placement for service.
   346  	p = NewPlacementService(newMockStorage(),
   347  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   348  	_, err = p.RemoveInstances([]string{"i1"})
   349  	assert.Error(t, err)
   350  }
   351  
   352  func TestBadReplaceInstance(t *testing.T) {
   353  	p := NewPlacementService(newMockStorage(),
   354  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   355  
   356  	_, err := p.BuildInitialPlacement([]placement.Instance{
   357  		placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1),
   358  		placement.NewEmptyInstance("i4", "r4", "z1", "endpoint", 1),
   359  	}, 10, 1)
   360  	assert.NoError(t, err)
   361  
   362  	// Leaving instance not exist.
   363  	_, _, err = p.ReplaceInstances(
   364  		[]string{"not_exist"},
   365  		[]placement.Instance{placement.NewEmptyInstance("i2", "r2", "z1", "endpoint", 1)},
   366  	)
   367  	assert.Error(t, err)
   368  
   369  	// Adding instance already exist.
   370  	_, _, err = p.ReplaceInstances(
   371  		[]string{"i1"},
   372  		[]placement.Instance{placement.NewEmptyInstance("i4", "r4", "z1", "endpoint", 1)},
   373  	)
   374  	assert.Error(t, err)
   375  
   376  	// Not enough isolation groups after replace.
   377  	_, err = p.AddReplica()
   378  	assert.NoError(t, err)
   379  	_, _, err = p.ReplaceInstances(
   380  		[]string{"i4"},
   381  		[]placement.Instance{placement.NewEmptyInstance("i12", "r1", "z1", "endpoint", 1)},
   382  	)
   383  	assert.Error(t, err)
   384  
   385  	// Could not find placement for service.
   386  	p = NewPlacementService(newMockStorage(),
   387  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   388  	_, _, err = p.ReplaceInstances(
   389  		[]string{"i1"},
   390  		[]placement.Instance{placement.NewEmptyInstance("i2", "r2", "z1", "endpoint", 1)},
   391  	)
   392  	assert.Error(t, err)
   393  }
   394  
   395  func TestMarkShard(t *testing.T) {
   396  	ms := newMockStorage()
   397  
   398  	i1 := placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)
   399  	i1.Shards().Add(shard.NewShard(1).SetState(shard.Leaving))
   400  	i1.Shards().Add(shard.NewShard(2).SetState(shard.Available))
   401  	i1.Shards().Add(shard.NewShard(3).SetState(shard.Available))
   402  
   403  	i2 := placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1)
   404  	i2.Shards().Add(shard.NewShard(4).SetState(shard.Available))
   405  	i2.Shards().Add(shard.NewShard(5).SetState(shard.Available))
   406  	i2.Shards().Add(shard.NewShard(0).SetState(shard.Available))
   407  
   408  	i3 := placement.NewEmptyInstance("i3", "r2", "z1", "endpoint", 1)
   409  	i3.Shards().Add(shard.NewShard(1).SetState(shard.Available))
   410  	i3.Shards().Add(shard.NewShard(3).SetState(shard.Available))
   411  	i3.Shards().Add(shard.NewShard(5).SetState(shard.Available))
   412  
   413  	i4 := placement.NewEmptyInstance("i4", "r2", "z1", "endpoint", 1)
   414  	i4.Shards().Add(shard.NewShard(2).SetState(shard.Available))
   415  	i4.Shards().Add(shard.NewShard(4).SetState(shard.Available))
   416  	i4.Shards().Add(shard.NewShard(0).SetState(shard.Available))
   417  
   418  	i5 := placement.NewEmptyInstance("i5", "r2", "z1", "endpoint", 1)
   419  	i5.Shards().Add(shard.NewShard(1).SetState(shard.Initializing).SetSourceID("i1"))
   420  
   421  	instances := []placement.Instance{i1, i2, i3, i4, i5}
   422  	p := placement.NewPlacement().
   423  		SetInstances(instances).
   424  		SetShards([]uint32{0, 1, 2, 3, 4, 5}).
   425  		SetReplicaFactor(2).
   426  		SetIsSharded(true)
   427  	_, err := ms.SetIfNotExist(p)
   428  	assert.NoError(t, err)
   429  
   430  	ps := NewPlacementService(ms,
   431  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   432  	_, err = ps.MarkShardsAvailable("i5", 1)
   433  	assert.NoError(t, err)
   434  	p, err = ms.Placement()
   435  	assert.NoError(t, err)
   436  	assert.NoError(t, placement.Validate(p))
   437  	for _, instance := range p.Instances() {
   438  		for _, s := range instance.Shards().All() {
   439  			assert.Equal(t, shard.Available, s.State())
   440  		}
   441  	}
   442  
   443  	_, err = ps.MarkShardsAvailable("i1", 1)
   444  	assert.Error(t, err)
   445  
   446  	_, err = ps.MarkShardsAvailable("i5", 5)
   447  	assert.Error(t, err)
   448  }
   449  
   450  func TestMarkInstance(t *testing.T) {
   451  	ms := newMockStorage()
   452  
   453  	i1 := placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)
   454  	i1.Shards().Add(shard.NewShard(1).SetState(shard.Leaving))
   455  	i1.Shards().Add(shard.NewShard(2).SetState(shard.Leaving))
   456  	i1.Shards().Add(shard.NewShard(3).SetState(shard.Available))
   457  
   458  	i2 := placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1)
   459  	i2.Shards().Add(shard.NewShard(4).SetState(shard.Available))
   460  	i2.Shards().Add(shard.NewShard(5).SetState(shard.Available))
   461  	i2.Shards().Add(shard.NewShard(0).SetState(shard.Available))
   462  
   463  	i3 := placement.NewEmptyInstance("i3", "r2", "z1", "endpoint", 1)
   464  	i3.Shards().Add(shard.NewShard(1).SetState(shard.Available))
   465  	i3.Shards().Add(shard.NewShard(3).SetState(shard.Available))
   466  	i3.Shards().Add(shard.NewShard(5).SetState(shard.Available))
   467  
   468  	i4 := placement.NewEmptyInstance("i4", "r2", "z1", "endpoint", 1)
   469  	i4.Shards().Add(shard.NewShard(2).SetState(shard.Available))
   470  	i4.Shards().Add(shard.NewShard(4).SetState(shard.Available))
   471  	i4.Shards().Add(shard.NewShard(0).SetState(shard.Available))
   472  
   473  	i5 := placement.NewEmptyInstance("i5", "r2", "z1", "endpoint", 1)
   474  	i5.Shards().Add(shard.NewShard(1).SetState(shard.Initializing).SetSourceID("i1"))
   475  	i5.Shards().Add(shard.NewShard(2).SetState(shard.Initializing).SetSourceID("i1"))
   476  
   477  	instances := []placement.Instance{i1, i2, i3, i4, i5}
   478  	p := placement.NewPlacement().
   479  		SetInstances(instances).
   480  		SetShards([]uint32{0, 1, 2, 3, 4, 5}).
   481  		SetReplicaFactor(2).
   482  		SetIsSharded(true)
   483  	_, err := ms.SetIfNotExist(p)
   484  	assert.NoError(t, err)
   485  
   486  	ps := NewPlacementService(ms, WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   487  
   488  	// instance not exist
   489  	_, err = ps.MarkInstanceAvailable("i6")
   490  	assert.Error(t, err)
   491  
   492  	_, err = ps.MarkInstanceAvailable("i5")
   493  	assert.NoError(t, err)
   494  	p, err = ps.Placement()
   495  	assert.NoError(t, err)
   496  	for _, instance := range p.Instances() {
   497  		assert.True(t, instance.IsAvailable())
   498  	}
   499  }
   500  
   501  func TestFindReplaceInstance(t *testing.T) {
   502  	i1 := placement.NewEmptyInstance("i1", "r11", "z1", "endpoint", 1)
   503  	i1.Shards().Add(shard.NewShard(1).SetState(shard.Available))
   504  	i1.Shards().Add(shard.NewShard(2).SetState(shard.Available))
   505  	i1.Shards().Add(shard.NewShard(3).SetState(shard.Available))
   506  
   507  	i10 := placement.NewEmptyInstance("i10", "r11", "z1", "endpoint", 1)
   508  	i10.Shards().Add(shard.NewShard(4).SetState(shard.Available))
   509  	i10.Shards().Add(shard.NewShard(5).SetState(shard.Available))
   510  
   511  	i2 := placement.NewEmptyInstance("i2", "r12", "z1", "endpoint", 1)
   512  	i2.Shards().Add(shard.NewShard(6).SetState(shard.Available))
   513  	i2.Shards().Add(shard.NewShard(7).SetState(shard.Available))
   514  	i2.Shards().Add(shard.NewShard(8).SetState(shard.Available))
   515  	i2.Shards().Add(shard.NewShard(9).SetState(shard.Available))
   516  
   517  	i3 := placement.NewEmptyInstance("i3", "r13", "z1", "endpoint", 3)
   518  	i3.Shards().Add(shard.NewShard(1).SetState(shard.Available))
   519  	i3.Shards().Add(shard.NewShard(3).SetState(shard.Available))
   520  	i3.Shards().Add(shard.NewShard(4).SetState(shard.Available))
   521  	i3.Shards().Add(shard.NewShard(5).SetState(shard.Available))
   522  	i3.Shards().Add(shard.NewShard(6).SetState(shard.Available))
   523  
   524  	i4 := placement.NewEmptyInstance("i4", "r14", "z1", "endpoint", 1)
   525  	i4.Shards().Add(shard.NewShard(2).SetState(shard.Available))
   526  	i4.Shards().Add(shard.NewShard(7).SetState(shard.Available))
   527  	i4.Shards().Add(shard.NewShard(8).SetState(shard.Available))
   528  	i4.Shards().Add(shard.NewShard(9).SetState(shard.Available))
   529  
   530  	instances := []placement.Instance{i1, i2, i3, i4, i10}
   531  
   532  	ids := []uint32{1, 2, 3, 4, 5, 6, 7, 8}
   533  	s := placement.NewPlacement().SetInstances(instances).SetShards(ids).SetReplicaFactor(2)
   534  
   535  	candidates := []placement.Instance{
   536  		placement.NewEmptyInstance("i11", "r11", "z1", "endpoint", 1),
   537  		placement.NewEmptyInstance("i22", "r22", "z2", "endpoint", 1),
   538  	}
   539  	noConflictCandidates := []placement.Instance{
   540  		placement.NewEmptyInstance("i11", "r0", "z1", "endpoint", 1),
   541  		placement.NewEmptyInstance("i22", "r0", "z2", "endpoint", 1),
   542  	}
   543  
   544  	testCases := []struct {
   545  		opts       placement.Options
   546  		input      []placement.Instance
   547  		replaceIDs []string
   548  		expectRes  []placement.Instance
   549  		expectErr  bool
   550  	}{
   551  		{
   552  			opts:       placement.NewOptions().SetValidZone("z1"),
   553  			input:      candidates,
   554  			replaceIDs: []string{i4.ID()},
   555  			expectRes:  []placement.Instance{candidates[0]},
   556  		},
   557  		{
   558  			opts:       placement.NewOptions().SetValidZone("z1").SetAllowPartialReplace(false),
   559  			input:      candidates,
   560  			replaceIDs: []string{i4.ID()},
   561  			expectErr:  true,
   562  		},
   563  		{
   564  			opts:       placement.NewOptions().SetValidZone("z1"),
   565  			input:      noConflictCandidates,
   566  			replaceIDs: []string{i3.ID()},
   567  			expectRes:  []placement.Instance{noConflictCandidates[0]},
   568  		},
   569  		{
   570  			// Disable AllowPartialReplace, so the weight from the candidate instances must be more than the replacing instance.
   571  			opts:       placement.NewOptions().SetValidZone("z1").SetAllowPartialReplace(false),
   572  			input:      noConflictCandidates,
   573  			replaceIDs: []string{i3.ID()},
   574  			expectErr:  true,
   575  		},
   576  	}
   577  	for _, test := range testCases {
   578  		p := NewPlacementService(nil, WithPlacementOptions(test.opts)).(*placementService)
   579  		res, err := p.selector.SelectReplaceInstances(test.input, test.replaceIDs, s)
   580  		if test.expectErr {
   581  			assert.Error(t, err)
   582  			continue
   583  		}
   584  		assert.Equal(t, test.expectRes, res)
   585  	}
   586  }
   587  
   588  func TestMirrorWorkflow(t *testing.T) {
   589  	h1p1 := placement.NewInstance().
   590  		SetID("h1p1").
   591  		SetHostname("h1").
   592  		SetPort(1).
   593  		SetIsolationGroup("r1").
   594  		SetZone("z1").
   595  		SetEndpoint("h1p1e").
   596  		SetWeight(1)
   597  	h1p2 := placement.NewInstance().
   598  		SetID("h1p2").
   599  		SetHostname("h1").
   600  		SetPort(2).
   601  		SetIsolationGroup("r1").
   602  		SetZone("z1").
   603  		SetEndpoint("h1p2e").
   604  		SetWeight(1)
   605  	h1p3 := placement.NewInstance().
   606  		SetID("h1p3").
   607  		SetHostname("h1").
   608  		SetPort(3).
   609  		SetIsolationGroup("r1").
   610  		SetZone("z1").
   611  		SetEndpoint("h1p3e").
   612  		SetWeight(1)
   613  	h2p1 := placement.NewInstance().
   614  		SetID("h2p1").
   615  		SetHostname("h2").
   616  		SetPort(1).
   617  		SetIsolationGroup("r2").
   618  		SetZone("z1").
   619  		SetEndpoint("h2p1e").
   620  		SetWeight(1)
   621  	h2p2 := placement.NewInstance().
   622  		SetID("h2p2").
   623  		SetHostname("h2").
   624  		SetPort(2).
   625  		SetIsolationGroup("r2").
   626  		SetZone("z1").
   627  		SetEndpoint("h2p2e").
   628  		SetWeight(1)
   629  	h2p3 := placement.NewInstance().
   630  		SetID("h2p3").
   631  		SetHostname("h2").
   632  		SetPort(3).
   633  		SetIsolationGroup("r2").
   634  		SetZone("z1").
   635  		SetEndpoint("h2p3e").
   636  		SetWeight(1)
   637  	h3p1 := placement.NewInstance().
   638  		SetID("h3p1").
   639  		SetHostname("h3").
   640  		SetPort(1).
   641  		SetIsolationGroup("r1").
   642  		SetZone("z1").
   643  		SetEndpoint("h3p1e").
   644  		SetWeight(2)
   645  	h3p2 := placement.NewInstance().
   646  		SetID("h3p2").
   647  		SetHostname("h3").
   648  		SetPort(2).
   649  		SetIsolationGroup("r1").
   650  		SetZone("z1").
   651  		SetEndpoint("h3p2e").
   652  		SetWeight(2)
   653  	h3p3 := placement.NewInstance().
   654  		SetID("h3p3").
   655  		SetHostname("h3").
   656  		SetPort(3).
   657  		SetIsolationGroup("r1").
   658  		SetZone("z1").
   659  		SetEndpoint("h3p3e").
   660  		SetWeight(2)
   661  	h4p1 := placement.NewInstance().
   662  		SetID("h4p1").
   663  		SetHostname("h4").
   664  		SetPort(1).
   665  		SetIsolationGroup("r2").
   666  		SetZone("z1").
   667  		SetEndpoint("h4p1e").
   668  		SetWeight(2)
   669  	h4p2 := placement.NewInstance().
   670  		SetID("h4p2").
   671  		SetHostname("h4").
   672  		SetPort(2).
   673  		SetIsolationGroup("r2").
   674  		SetZone("z1").
   675  		SetEndpoint("h4p2e").
   676  		SetWeight(2)
   677  	h4p3 := placement.NewInstance().
   678  		SetID("h4p3").
   679  		SetHostname("h4").
   680  		SetPort(3).
   681  		SetIsolationGroup("r2").
   682  		SetZone("z1").
   683  		SetEndpoint("h4p3e").
   684  		SetWeight(2)
   685  
   686  	ps := NewPlacementService(
   687  		newMockStorage(),
   688  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1").SetIsMirrored(true)),
   689  	)
   690  
   691  	p, err := ps.BuildInitialPlacement(
   692  		[]placement.Instance{h1p1, h1p2, h1p3, h2p1, h2p2, h2p3, h3p1, h3p2, h3p3, h4p1, h4p2, h4p3},
   693  		20,
   694  		2,
   695  	)
   696  	assert.NoError(t, err)
   697  	assert.Equal(t, h1p1.ShardSetID(), h2p1.ShardSetID())
   698  	assert.Equal(t, h1p2.ShardSetID(), h2p2.ShardSetID())
   699  	assert.Equal(t, h1p3.ShardSetID(), h2p3.ShardSetID())
   700  	assert.Equal(t, h3p1.ShardSetID(), h4p1.ShardSetID())
   701  	assert.Equal(t, h3p2.ShardSetID(), h4p2.ShardSetID())
   702  	assert.Equal(t, h3p3.ShardSetID(), h4p3.ShardSetID())
   703  	assert.Equal(t, 12, p.NumInstances())
   704  
   705  	h5p1 := placement.NewInstance().
   706  		SetID("h5p1").
   707  		SetHostname("h5").
   708  		SetPort(1).
   709  		SetIsolationGroup("r1").
   710  		SetZone("z1").
   711  		SetEndpoint("h5p1e").
   712  		SetWeight(2)
   713  	h6p1 := placement.NewInstance().
   714  		SetID("h6p1").
   715  		SetHostname("h6").
   716  		SetPort(1).
   717  		SetIsolationGroup("r2").
   718  		SetZone("z1").
   719  		SetEndpoint("h6p1e").
   720  		SetWeight(2)
   721  
   722  	_, addedInstances, err := ps.AddInstances([]placement.Instance{h5p1, h6p1})
   723  	assert.NoError(t, err)
   724  	assert.Equal(t, 2, len(addedInstances))
   725  	assert.Equal(t, addedInstances[0].ShardSetID(), addedInstances[1].ShardSetID())
   726  	assert.Equal(t, uint32(7), addedInstances[0].ShardSetID())
   727  
   728  	_, err = ps.RemoveInstances([]string{h5p1.ID(), h6p1.ID()})
   729  	assert.NoError(t, err)
   730  
   731  	// Make sure reverting the removed instances reuses the old shard set id.
   732  	_, addedInstances, err = ps.AddInstances([]placement.Instance{h5p1.SetShardSetID(0), h6p1.SetShardSetID(0)})
   733  	for _, instance := range addedInstances {
   734  		assert.Equal(t, uint32(7), instance.ShardSetID())
   735  	}
   736  	assert.NoError(t, err)
   737  
   738  	h7p1 := placement.NewInstance().
   739  		SetID("h7p1").
   740  		SetHostname("h7").
   741  		SetPort(1).
   742  		SetIsolationGroup("r2").
   743  		SetZone("z1").
   744  		SetEndpoint("h7p1e").
   745  		SetWeight(2)
   746  	h7p2 := placement.NewInstance().
   747  		SetID("h7p2").
   748  		SetHostname("h7").
   749  		SetPort(2).
   750  		SetIsolationGroup("r2").
   751  		SetZone("z1").
   752  		SetEndpoint("h7p2e").
   753  		SetWeight(2)
   754  	h7p3 := placement.NewInstance().
   755  		SetID("h7p3").
   756  		SetHostname("h7").
   757  		SetPort(3).
   758  		SetIsolationGroup("r2").
   759  		SetZone("z1").
   760  		SetEndpoint("h7p3e").
   761  		SetWeight(2)
   762  
   763  	p, addedInstances, err = ps.ReplaceInstances(
   764  		[]string{h4p1.ID(), h4p2.ID(), h4p3.ID()},
   765  		[]placement.Instance{h3p1, h3p2, h3p3, h7p1, h7p2, h7p3},
   766  	)
   767  	require.NoError(t, err)
   768  	h4p1, ok := p.Instance(h4p1.ID())
   769  	assert.True(t, ok)
   770  	h4p2, ok = p.Instance(h4p2.ID())
   771  	assert.True(t, ok)
   772  	h4p3, ok = p.Instance(h4p3.ID())
   773  	assert.True(t, ok)
   774  	assert.Equal(t, h4p1.ShardSetID(), addedInstances[0].ShardSetID())
   775  	assert.Equal(t, h4p1.Shards().AllIDs(), addedInstances[0].Shards().AllIDs())
   776  	assert.Equal(t, h4p2.ShardSetID(), addedInstances[1].ShardSetID())
   777  	assert.Equal(t, h4p2.Shards().AllIDs(), addedInstances[1].Shards().AllIDs())
   778  	assert.Equal(t, h4p3.ShardSetID(), addedInstances[2].ShardSetID())
   779  	assert.Equal(t, h4p3.Shards().AllIDs(), addedInstances[2].Shards().AllIDs())
   780  }
   781  
   782  func TestManyShards(t *testing.T) {
   783  	p := NewPlacementService(newMockStorage(),
   784  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1")))
   785  	i1 := placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 2)
   786  	i2 := placement.NewEmptyInstance("i2", "r2", "z1", "endpoint", 2)
   787  	i3 := placement.NewEmptyInstance("i3", "r3", "z1", "endpoint", 2)
   788  	i4 := placement.NewEmptyInstance("i4", "r1", "z1", "endpoint", 2)
   789  	i5 := placement.NewEmptyInstance("i5", "r2", "z1", "endpoint", 2)
   790  	i6 := placement.NewEmptyInstance("i6", "r3", "z1", "endpoint", 2)
   791  	_, err := p.BuildInitialPlacement([]placement.Instance{i1, i2, i3, i4, i5, i6}, 8192, 1)
   792  	assert.NoError(t, err)
   793  }
   794  
   795  func TestAddMultipleInstances(t *testing.T) {
   796  	i1 := placement.NewInstance().
   797  		SetID("i1").
   798  		SetIsolationGroup("r1").
   799  		SetEndpoint("i1").
   800  		SetWeight(3)
   801  	i2 := placement.NewInstance().
   802  		SetID("i2").
   803  		SetIsolationGroup("r2").
   804  		SetEndpoint("i2").
   805  		SetWeight(1)
   806  	i3 := placement.NewInstance().
   807  		SetID("i3").
   808  		SetIsolationGroup("r2").
   809  		SetEndpoint("i3").
   810  		SetWeight(1)
   811  	i4 := placement.NewInstance().
   812  		SetID("i4").
   813  		SetIsolationGroup("r4").
   814  		SetEndpoint("i4").
   815  		SetWeight(2)
   816  
   817  	tests := []struct {
   818  		name               string
   819  		opts               placement.Options
   820  		initialInstances   []placement.Instance
   821  		candidateInstances []placement.Instance
   822  		expectAdded        []placement.Instance
   823  	}{
   824  		{
   825  			name:               "Add Single Candidate",
   826  			opts:               placement.NewOptions().SetAddAllCandidates(false),
   827  			initialInstances:   []placement.Instance{i1.Clone(), i2.Clone()},
   828  			candidateInstances: []placement.Instance{i3.Clone(), i4.Clone()},
   829  			expectAdded:        []placement.Instance{i4}, // Prefer instance from different isolation group.
   830  		},
   831  		{
   832  			name:               "Add All Candidates",
   833  			opts:               placement.NewOptions().SetAddAllCandidates(true),
   834  			initialInstances:   []placement.Instance{i1.Clone(), i2.Clone()},
   835  			candidateInstances: []placement.Instance{i3.Clone(), i4.Clone()},
   836  			expectAdded:        []placement.Instance{i3, i4},
   837  		},
   838  	}
   839  
   840  	for _, test := range tests {
   841  		t.Run(test.name, func(t *testing.T) {
   842  			ps := NewPlacementService(newMockStorage(), WithPlacementOptions(test.opts))
   843  			_, err := ps.BuildInitialPlacement(test.initialInstances, 4, 2)
   844  			require.NoError(t, err)
   845  
   846  			_, added, err := ps.AddInstances(test.candidateInstances)
   847  			require.NoError(t, err)
   848  			require.True(t, compareInstances(test.expectAdded, added))
   849  		})
   850  	}
   851  }
   852  
   853  func TestReplaceInstances(t *testing.T) {
   854  	i1 := placement.NewInstance().
   855  		SetID("i1").
   856  		SetIsolationGroup("r1").
   857  		SetEndpoint("i1").
   858  		SetWeight(4)
   859  	i2 := placement.NewInstance().
   860  		SetID("i2").
   861  		SetIsolationGroup("r2").
   862  		SetEndpoint("i2").
   863  		SetWeight(2)
   864  	i3 := placement.NewInstance().
   865  		SetID("i3").
   866  		SetIsolationGroup("r2").
   867  		SetEndpoint("i3").
   868  		SetWeight(2)
   869  	i4 := placement.NewInstance().
   870  		SetID("i4").
   871  		SetIsolationGroup("r4").
   872  		SetEndpoint("i4").
   873  		SetWeight(1)
   874  
   875  	tests := []struct {
   876  		name               string
   877  		opts               placement.Options
   878  		initialInstances   []placement.Instance
   879  		candidateInstances []placement.Instance
   880  		leavingIDs         []string
   881  		expectErr          bool
   882  		expectAdded        []placement.Instance
   883  	}{
   884  		{
   885  			name:               "Replace With Instance of Same Weight",
   886  			opts:               placement.NewOptions().SetAddAllCandidates(false).SetAllowPartialReplace(false),
   887  			initialInstances:   []placement.Instance{i1.Clone(), i2.Clone()},
   888  			candidateInstances: []placement.Instance{i3.Clone(), i4.Clone()},
   889  			leavingIDs:         []string{"i2"},
   890  			expectErr:          false,
   891  			expectAdded:        []placement.Instance{i3},
   892  		},
   893  		{
   894  			name:               "Add All Candidates",
   895  			opts:               placement.NewOptions().SetAddAllCandidates(true).SetAllowPartialReplace(false),
   896  			initialInstances:   []placement.Instance{i1.Clone(), i2.Clone()},
   897  			candidateInstances: []placement.Instance{i3.Clone(), i4.Clone()},
   898  			leavingIDs:         []string{"i2"},
   899  			expectErr:          false,
   900  			expectAdded:        []placement.Instance{i3, i4},
   901  		},
   902  		{
   903  			name:               "Not Enough Weight With Partial Replace",
   904  			opts:               placement.NewOptions().SetAddAllCandidates(false).SetAllowPartialReplace(true),
   905  			initialInstances:   []placement.Instance{i1.Clone(), i2.Clone()},
   906  			candidateInstances: []placement.Instance{i3.Clone(), i4.Clone()},
   907  			leavingIDs:         []string{"i1"},
   908  			expectErr:          false,
   909  			expectAdded:        []placement.Instance{i3, i4},
   910  		},
   911  		{
   912  			name:               "Not Enough Weight Without Partial Replace",
   913  			opts:               placement.NewOptions().SetAddAllCandidates(false).SetAllowPartialReplace(false),
   914  			initialInstances:   []placement.Instance{i1.Clone(), i2.Clone()},
   915  			candidateInstances: []placement.Instance{i3.Clone(), i4.Clone()},
   916  			leavingIDs:         []string{"i1"},
   917  			expectErr:          true,
   918  		},
   919  	}
   920  
   921  	for _, test := range tests {
   922  		t.Run(test.name, func(t *testing.T) {
   923  			ps := NewPlacementService(newMockStorage(), WithPlacementOptions(test.opts))
   924  			_, err := ps.BuildInitialPlacement(test.initialInstances, 4, 2)
   925  			require.NoError(t, err)
   926  
   927  			_, added, err := ps.ReplaceInstances(test.leavingIDs, test.candidateInstances)
   928  			if test.expectErr {
   929  				require.Error(t, err)
   930  				return
   931  			}
   932  			require.NoError(t, err)
   933  			require.True(t, compareInstances(test.expectAdded, added))
   934  		})
   935  	}
   936  }
   937  
   938  func TestValidateFnBeforeUpdate(t *testing.T) {
   939  	p := NewPlacementService(newMockStorage(),
   940  		WithPlacementOptions(placement.NewOptions().SetValidZone("z1"))).(*placementService)
   941  
   942  	_, err := p.BuildInitialPlacement(
   943  		[]placement.Instance{placement.NewEmptyInstance("i1", "r1", "z1", "endpoint1", 1)},
   944  		10, 1)
   945  	assert.NoError(t, err)
   946  
   947  	expectErr := errors.New("err")
   948  	p.opts = p.opts.SetValidateFnBeforeUpdate(func(placement.Placement) error { return expectErr })
   949  	_, _, err = p.AddInstances([]placement.Instance{placement.NewEmptyInstance("i2", "r2", "z1", "endpoint2", 1)})
   950  	assert.Error(t, err)
   951  	assert.Equal(t, expectErr, err)
   952  }
   953  
   954  func TestPlacementServiceImplOptions(t *testing.T) {
   955  	placementOptions := placement.NewOptions().SetValidZone("foozone").SetIsSharded(true)
   956  	al := algo.NewAlgorithm(placementOptions.SetIsSharded(false))
   957  
   958  	defaultImpl := newPlacementServiceImpl(nil)
   959  	require.NotNil(t, defaultImpl)
   960  	assert.NotNil(t, defaultImpl.opts)
   961  	assert.NotNil(t, defaultImpl.algo)
   962  	assert.NotEqual(t, placementOptions.ValidZone(), defaultImpl.opts.ValidZone())
   963  
   964  	customImpl := newPlacementServiceImpl(nil,
   965  		WithPlacementOptions(placementOptions),
   966  		WithAlgorithm(al))
   967  	assert.Equal(t, placementOptions.ValidZone(), customImpl.opts.ValidZone())
   968  	assert.Equal(t, al, customImpl.algo)
   969  }
   970  
   971  func TestBalanceShards(t *testing.T) {
   972  	ms := newMockStorage()
   973  
   974  	i1 := placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)
   975  	i1.Shards().Add(shard.NewShard(0).SetState(shard.Available))
   976  	i1.Shards().Add(shard.NewShard(1).SetState(shard.Available))
   977  	i1.Shards().Add(shard.NewShard(2).SetState(shard.Available))
   978  
   979  	i2 := placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1)
   980  	i2.Shards().Add(shard.NewShard(3).SetState(shard.Available))
   981  
   982  	instances := []placement.Instance{i1, i2}
   983  	p := placement.NewPlacement().
   984  		SetInstances(instances).
   985  		SetShards([]uint32{0, 1, 2, 3}).
   986  		SetReplicaFactor(1).
   987  		SetIsSharded(true)
   988  
   989  	_, err := ms.SetIfNotExist(p)
   990  	assert.NoError(t, err)
   991  
   992  	ps := NewPlacementService(ms, WithPlacementOptions(placement.NewOptions()))
   993  
   994  	p, err = ps.BalanceShards()
   995  	assert.NoError(t, err)
   996  
   997  	bi1 := placement.NewEmptyInstance("i1", "r1", "z1", "endpoint", 1)
   998  	bi1.Shards().Add(shard.NewShard(0).SetState(shard.Leaving))
   999  	bi1.Shards().Add(shard.NewShard(1).SetState(shard.Available))
  1000  	bi1.Shards().Add(shard.NewShard(2).SetState(shard.Available))
  1001  
  1002  	bi2 := placement.NewEmptyInstance("i2", "r1", "z1", "endpoint", 1)
  1003  	bi2.Shards().Add(shard.NewShard(0).SetState(shard.Initializing).SetSourceID("i1"))
  1004  	bi2.Shards().Add(shard.NewShard(3).SetState(shard.Available))
  1005  
  1006  	expectedInstances := []placement.Instance{bi1, bi2}
  1007  	assert.Equal(t, expectedInstances, p.Instances())
  1008  }
  1009  
  1010  func newMockStorage() placement.Storage {
  1011  	return storage.NewPlacementStorage(mem.NewStore(), "", nil)
  1012  }
  1013  
  1014  func markAllInstancesAvailable(
  1015  	t *testing.T,
  1016  	ps placement.Service,
  1017  ) {
  1018  	p, err := ps.Placement()
  1019  	require.NoError(t, err)
  1020  	for _, i := range p.Instances() {
  1021  		if len(i.Shards().ShardsForState(shard.Initializing)) == 0 {
  1022  			continue
  1023  		}
  1024  		_, err := ps.MarkInstanceAvailable(i.ID())
  1025  		require.NoError(t, err)
  1026  	}
  1027  }
  1028  
  1029  func compareInstances(left, right []placement.Instance) bool {
  1030  	if len(left) != len(right) {
  1031  		return false
  1032  	}
  1033  
  1034  	ids := make(map[string]struct{}, len(left))
  1035  	for _, intce := range left {
  1036  		ids[intce.ID()] = struct{}{}
  1037  	}
  1038  
  1039  	for _, intce := range right {
  1040  		if _, ok := ids[intce.ID()]; !ok {
  1041  			return false
  1042  		}
  1043  	}
  1044  	return true
  1045  }