github.com/m3db/m3@v1.5.0/src/cluster/shard/shard_test.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package shard
    22  
    23  import (
    24  	"math"
    25  	"math/rand"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  func TestShard(t *testing.T) {
    33  	s := NewShard(1).
    34  		SetState(Initializing).
    35  		SetSourceID("id").
    36  		SetCutoffNanos(1000).
    37  		SetCutoverNanos(100).
    38  		SetRedirectToShardID(uint32Ptr(5))
    39  
    40  	assert.Equal(t, uint32(1), s.ID())
    41  	assert.Equal(t, Initializing, s.State())
    42  	assert.Equal(t, "id", s.SourceID())
    43  	assert.Equal(t, int64(1000), s.CutoffNanos())
    44  	assert.Equal(t, int64(100), s.CutoverNanos())
    45  	assert.Equal(t, uint32(5), *s.RedirectToShardID())
    46  }
    47  
    48  func TestShardEquals(t *testing.T) {
    49  	s := NewShard(1).SetState(Initializing).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100)
    50  	assert.True(t, s.Equals(s))
    51  	assert.False(t, s.Equals(NewShard(1).SetState(Initializing).SetSourceID("id").SetCutoffNanos(1000)))
    52  	assert.False(t, s.Equals(NewShard(1).SetState(Initializing).SetSourceID("id").SetCutoverNanos(100)))
    53  	assert.False(t, s.Equals(NewShard(1).SetState(Initializing).SetCutoffNanos(1000).SetCutoverNanos(100)))
    54  	assert.False(t, s.Equals(NewShard(1).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100)))
    55  	assert.False(t, s.Equals(NewShard(1).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100)))
    56  	assert.False(t, s.Equals(NewShard(2).SetState(Initializing).SetSourceID("id").SetCutoffNanos(1000).SetCutoverNanos(100)))
    57  }
    58  
    59  func TestShardEqualsWithRedirectShardID(t *testing.T) {
    60  	s := NewShard(1).SetRedirectToShardID(uint32Ptr(1))
    61  	assert.True(t, s.Equals(s))
    62  	assert.False(t, s.Equals(NewShard(1).SetRedirectToShardID(nil)))
    63  	assert.False(t, NewShard(1).SetRedirectToShardID(nil).Equals(s))
    64  	assert.False(t, s.Equals(NewShard(1).SetRedirectToShardID(uint32Ptr(0))))
    65  	assert.True(t, s.Equals(NewShard(1).SetRedirectToShardID(uint32Ptr(1))))
    66  }
    67  
    68  func TestShards(t *testing.T) {
    69  	redirectToShardID := new(uint32)
    70  	*redirectToShardID = 9
    71  
    72  	shards := NewShards(nil)
    73  	assert.Equal(t, 0, shards.NumShards())
    74  
    75  	s1 := NewShard(1).SetState(Initializing).SetSourceID("id").SetRedirectToShardID(redirectToShardID)
    76  	shards.Add(s1)
    77  	assert.Equal(t, 1, shards.NumShards())
    78  	assert.Equal(t, 1, shards.NumShardsForState(Initializing))
    79  	assert.Equal(t, 0, shards.NumShardsForState(Available))
    80  	assert.Equal(t, 0, shards.NumShardsForState(Leaving))
    81  	assert.True(t, shards.Contains(1))
    82  	s, ok := shards.Shard(1)
    83  	assert.True(t, ok)
    84  	assert.Equal(t, s1, s)
    85  	shardsInState := shards.ShardsForState(Initializing)
    86  	assert.NotNil(t, shardsInState)
    87  	assert.Equal(t, []Shard{s1}, shards.All())
    88  	assert.Equal(t, shardsInState, shards.All())
    89  
    90  	s2 := NewShard(2).SetState(Available)
    91  	shards.Add(s2)
    92  	assert.Equal(t, 2, shards.NumShards())
    93  	assert.Equal(t, 1, shards.NumShardsForState(Initializing))
    94  	assert.Equal(t, 1, shards.NumShardsForState(Available))
    95  	assert.Equal(t, 0, shards.NumShardsForState(Leaving))
    96  	s, ok = shards.Shard(2)
    97  	assert.True(t, ok)
    98  	assert.Equal(t, s2, s)
    99  
   100  	shards.Remove(1)
   101  	assert.False(t, shards.Contains(1))
   102  	_, ok = shards.Shard(1)
   103  	assert.False(t, ok)
   104  
   105  	shards = NewShards([]Shard{s1, s2})
   106  	assert.Equal(t, 2, shards.NumShards())
   107  
   108  	shards.Add(NewShard(3).SetState(Leaving))
   109  	assert.Equal(t, "[Initializing=[1 -> 9], Available=[2], Leaving=[3]]", shards.String())
   110  
   111  	shards.Add(NewShard(4))
   112  	shards.Add(NewShard(4))
   113  	shards.Add(NewShard(4))
   114  	assert.Equal(t, 4, shards.NumShards())
   115  }
   116  
   117  func TestShardsEquals(t *testing.T) {
   118  	ss1 := NewShards(nil)
   119  	ss2 := NewShards(nil)
   120  
   121  	s1 := NewShard(1).SetState(Initializing).SetSourceID("id")
   122  	s2 := NewShard(2).SetState(Available)
   123  	ss1.Add(s1)
   124  	ss2.Add(s2)
   125  	assert.False(t, ss1.Equals(ss2))
   126  	ss2.Add(s1)
   127  	assert.False(t, ss1.Equals(ss2))
   128  
   129  	ss2.Remove(s2.ID())
   130  	assert.True(t, ss1.Equals(ss2))
   131  }
   132  
   133  func TestSort(t *testing.T) {
   134  	var shards []Shard
   135  	shards = append(shards, NewShard(1))
   136  	shards = append(shards, NewShard(2))
   137  	shards = append(shards, NewShard(0))
   138  	shards = append(shards, NewShard(3))
   139  
   140  	shardsAreSorted(t, NewShards(shards))
   141  }
   142  
   143  func TestShardCutoverTimes(t *testing.T) {
   144  	s := NewShard(1).SetState(Initializing).SetSourceID("id")
   145  	inputs := []struct {
   146  		actualNanos   int64
   147  		expectedNanos int64
   148  	}{
   149  		{actualNanos: 0, expectedNanos: 0},
   150  		{actualNanos: 12345, expectedNanos: 12345},
   151  		{actualNanos: math.MaxInt64, expectedNanos: math.MaxInt64},
   152  	}
   153  
   154  	for _, input := range inputs {
   155  		s.SetCutoverNanos(input.actualNanos)
   156  		require.Equal(t, input.expectedNanos, s.CutoverNanos())
   157  	}
   158  }
   159  
   160  func TestShardCutoffTimes(t *testing.T) {
   161  	s := NewShard(1).SetState(Initializing).SetSourceID("id").(*shard)
   162  	inputs := []struct {
   163  		actualNanos   int64
   164  		expectedNanos int64
   165  		storedNanos   int64
   166  	}{
   167  		{actualNanos: 0, expectedNanos: math.MaxInt64, storedNanos: 0},
   168  		{actualNanos: 12345, expectedNanos: 12345, storedNanos: 12345},
   169  		{actualNanos: math.MaxInt64, expectedNanos: math.MaxInt64, storedNanos: 0},
   170  	}
   171  
   172  	for _, input := range inputs {
   173  		s.SetCutoffNanos(input.actualNanos)
   174  		require.Equal(t, input.expectedNanos, s.CutoffNanos())
   175  		require.Equal(t, input.storedNanos, s.cutoffNanos)
   176  	}
   177  }
   178  
   179  func TestShardStateToProtoError(t *testing.T) {
   180  	_, err := Unknown.Proto()
   181  	assert.Error(t, err)
   182  }
   183  
   184  func TestShardsToProto(t *testing.T) {
   185  	shardSet := []Shard{
   186  		NewShard(0).SetState(Initializing).SetCutoverNanos(1234).SetCutoffNanos(5678),
   187  		NewShard(1).SetState(Initializing).SetCutoverNanos(0).SetCutoffNanos(5678),
   188  		NewShard(2).SetState(Initializing).SetCutoverNanos(math.MaxInt64).SetCutoffNanos(5678),
   189  		NewShard(3).SetState(Initializing).SetCutoverNanos(1234).SetCutoffNanos(0),
   190  		NewShard(4).SetState(Initializing).SetCutoverNanos(1234).SetCutoffNanos(math.MaxInt64),
   191  	}
   192  	shards := NewShards(shardSet)
   193  	proto, err := shards.Proto()
   194  	require.NoError(t, err)
   195  
   196  	expected := []struct {
   197  		cutoverNanos int64
   198  		cutoffNanos  int64
   199  	}{
   200  		{cutoverNanos: 1234, cutoffNanos: 5678},
   201  		{cutoverNanos: 0, cutoffNanos: 5678},
   202  		{cutoverNanos: math.MaxInt64, cutoffNanos: 5678},
   203  		{cutoverNanos: 1234, cutoffNanos: 0},
   204  		{cutoverNanos: 1234, cutoffNanos: 0},
   205  	}
   206  	for i, shardProto := range proto {
   207  		require.Equal(t, expected[i].cutoverNanos, shardProto.CutoverNanos)
   208  		require.Equal(t, expected[i].cutoffNanos, shardProto.CutoffNanos)
   209  	}
   210  
   211  	reconstructed, err := NewShardsFromProto(proto)
   212  	require.NoError(t, err)
   213  	require.Equal(t, shards.NumShards(), reconstructed.NumShards())
   214  	for i := 0; i < shards.NumShards(); i++ {
   215  		shardID := uint32(i)
   216  		expected, found := shards.Shard(shardID)
   217  		require.True(t, found)
   218  		actual, found := shards.Shard(shardID)
   219  		require.True(t, found)
   220  		require.Equal(t, expected, actual)
   221  	}
   222  }
   223  
   224  func TestClone(t *testing.T) {
   225  	s1 := NewShard(1).SetState(Initializing).SetSourceID("s").SetCutoffNanos(123).SetCutoverNanos(100)
   226  	s2 := NewShard(2).SetState(Available)
   227  
   228  	ss1 := NewShards([]Shard{s1, s2})
   229  	ss2 := NewShards([]Shard{s1, s2})
   230  	require.True(t, ss1.Clone().Equals(ss2))
   231  
   232  	ss1.Add(NewShard(2).SetState(Leaving))
   233  	require.False(t, ss1.Equals(ss2))
   234  }
   235  
   236  func TestCloneCopiesToShardMap(t *testing.T) {
   237  	s := NewShard(1).SetState(Available)
   238  
   239  	ss := NewShards([]Shard{s})
   240  	clonedSS := ss.Clone()
   241  	require.True(t, ss.Equals(clonedSS))
   242  
   243  	s.SetState(Leaving)
   244  
   245  	clonedS, ok := clonedSS.Shard(1)
   246  	require.True(t, ok)
   247  	assert.Equal(t, Available, clonedS.State())
   248  }
   249  
   250  func TestShardAdd(t *testing.T) {
   251  	for i := 1; i < 500; i++ {
   252  		rndShards := makeTestShards(i)
   253  		shards := NewShards(nil)
   254  		for j := 0; j < len(rndShards); j++ {
   255  			id := rndShards[j].ID()
   256  			require.False(t, shards.Contains(id))
   257  
   258  			shards.Add(rndShards[j])
   259  			require.True(t, shards.Contains(id))
   260  
   261  			shrd, ok := shards.Shard(id)
   262  			require.True(t, ok)
   263  			require.Equal(t, id, shrd.ID())
   264  		}
   265  		shardsAreSorted(t, shards)
   266  	}
   267  }
   268  
   269  func TestShardRemove(t *testing.T) {
   270  	for i := 1; i < 500; i++ {
   271  		rndShards := makeTestShards(i)
   272  		shards := NewShards(rndShards)
   273  		for j := 0; j < len(rndShards); j++ {
   274  			id := rndShards[j].ID()
   275  			require.True(t, shards.Contains(id))
   276  			shards.Remove(id)
   277  			require.Equal(t, len(rndShards)-j-1, shards.NumShards())
   278  			require.False(t, shards.Contains(id))
   279  		}
   280  		shardsAreSorted(t, shards)
   281  	}
   282  }
   283  
   284  func randomIDs(seed int64, num int) []uint32 {
   285  	rnd := rand.New(rand.NewSource(seed)) // #nosec
   286  	ids := make([]uint32, num)
   287  
   288  	for i := uint32(0); i < uint32(num); i++ {
   289  		ids[i] = i
   290  	}
   291  
   292  	rnd.Shuffle(len(ids), func(i, j int) {
   293  		ids[i], ids[j] = ids[j], ids[i]
   294  	})
   295  	return ids
   296  }
   297  
   298  func makeTestShards(num int) []Shard {
   299  	shardIDs := randomIDs(0, num)
   300  	s := make([]Shard, num)
   301  	for i, shardID := range shardIDs {
   302  		s[i] = NewShard(shardID)
   303  	}
   304  	return s
   305  }
   306  
   307  func shardsAreSorted(t *testing.T, shards Shards) {
   308  	prev := -1
   309  	for _, shard := range shards.All() {
   310  		id := int(shard.ID())
   311  		if id <= prev {
   312  			t.Fatalf("expected id to be greater than %d, got %d", prev, id)
   313  		}
   314  		prev = id
   315  	}
   316  }
   317  
   318  func uint32Ptr(value uint32) *uint32 {
   319  	ptr := new(uint32)
   320  	*ptr = value
   321  	return ptr
   322  }