github.com/m3db/m3@v1.5.0/src/cluster/placement/storage/storage_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 storage
    22  
    23  import (
    24  	"testing"
    25  
    26  	"github.com/m3db/m3/src/cluster/generated/proto/placementpb"
    27  	"github.com/m3db/m3/src/cluster/kv"
    28  	"github.com/m3db/m3/src/cluster/kv/mem"
    29  	"github.com/m3db/m3/src/cluster/placement"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  func TestStorageWithSinglePlacement(t *testing.T) {
    36  	ps := newTestPlacementStorage(mem.NewStore(), placement.NewOptions())
    37  	err := ps.Delete()
    38  	require.Error(t, err)
    39  	require.Equal(t, kv.ErrNotFound, err)
    40  
    41  	p := placement.NewPlacement().
    42  		SetInstances([]placement.Instance{}).
    43  		SetShards([]uint32{}).
    44  		SetReplicaFactor(0)
    45  
    46  	pGet, err := ps.SetIfNotExist(p)
    47  	assert.Equal(t, 1, pGet.Version())
    48  	require.NoError(t, err)
    49  
    50  	_, err = ps.SetIfNotExist(p)
    51  	require.Error(t, err)
    52  	require.Equal(t, kv.ErrAlreadyExists, err)
    53  
    54  	pGet, err = ps.Placement()
    55  	require.NoError(t, err)
    56  	require.Equal(t, p.SetVersion(1), pGet)
    57  
    58  	_, err = ps.PlacementForVersion(0)
    59  	require.Error(t, err)
    60  
    61  	_, err = ps.PlacementForVersion(2)
    62  	require.Error(t, err)
    63  
    64  	h, err := ps.PlacementForVersion(1)
    65  	require.NoError(t, err)
    66  	require.Equal(t, pGet, h)
    67  
    68  	pGet, err = ps.CheckAndSet(p, pGet.Version())
    69  	require.NoError(t, err)
    70  	assert.Equal(t, 2, pGet.Version())
    71  
    72  	_, err = ps.CheckAndSet(p, pGet.Version()-1)
    73  	require.Error(t, err)
    74  	require.Equal(t, kv.ErrVersionMismatch, err)
    75  
    76  	pGet, err = ps.Placement()
    77  	require.NoError(t, err)
    78  	require.Equal(t, 2, pGet.Version())
    79  	require.Equal(t, p.SetVersion(2), pGet)
    80  
    81  	err = ps.Delete()
    82  	require.NoError(t, err)
    83  
    84  	_, err = ps.Placement()
    85  	require.Error(t, err)
    86  	require.Equal(t, kv.ErrNotFound, err)
    87  
    88  	pGet, err = ps.SetIfNotExist(p)
    89  	require.NoError(t, err)
    90  	assert.Equal(t, 1, pGet.Version())
    91  
    92  	pGet, err = ps.Placement()
    93  	require.NoError(t, err)
    94  	require.Equal(t, 1, pGet.Version())
    95  	require.Equal(t, p.SetVersion(1), pGet)
    96  
    97  	proto, v, err := ps.Proto()
    98  	require.NoError(t, err)
    99  	require.Equal(t, 1, v)
   100  
   101  	actualP, err := placement.NewPlacementFromProto(proto.(*placementpb.Placement))
   102  	require.NoError(t, err)
   103  	require.Equal(t, p.SetVersion(0), actualP)
   104  }
   105  
   106  func TestStorageWithPlacementSnapshots(t *testing.T) {
   107  	ps := newTestPlacementStorage(mem.NewStore(), placement.NewOptions().SetIsStaged(true))
   108  
   109  	p := placement.NewPlacement().
   110  		SetInstances([]placement.Instance{}).
   111  		SetShards([]uint32{}).
   112  		SetReplicaFactor(0).
   113  		SetCutoverNanos(100)
   114  
   115  	p1, err := ps.SetIfNotExist(p)
   116  	require.NoError(t, err)
   117  	assert.Equal(t, 1, p1.Version())
   118  
   119  	_, err = ps.SetIfNotExist(p)
   120  	require.Error(t, err)
   121  
   122  	p1, err = ps.Placement()
   123  	require.NoError(t, err)
   124  	require.Equal(t, 1, p1.Version())
   125  	require.Equal(t, p.SetVersion(1), p1)
   126  
   127  	_, err = ps.PlacementForVersion(0)
   128  	require.Error(t, err)
   129  
   130  	_, err = ps.PlacementForVersion(2)
   131  	require.Error(t, err)
   132  
   133  	h, err := ps.PlacementForVersion(1)
   134  	require.NoError(t, err)
   135  	require.Equal(t, p1, h)
   136  
   137  	p2 := p1.Clone().
   138  		SetCutoverNanos(p1.CutoverNanos() + 100).
   139  		SetReplicaFactor(p1.ReplicaFactor() + 2)
   140  	pGet2, err := ps.CheckAndSet(p2, p1.Version())
   141  	require.NoError(t, err)
   142  	require.Equal(t, 2, pGet2.Version())
   143  	require.Equal(t, int64(0), pGet2.CutoverNanos())
   144  	require.Equal(t, p2.SetVersion(2), pGet2)
   145  
   146  	newProto, v, err := ps.Proto()
   147  	require.NoError(t, err)
   148  	require.Equal(t, 2, v)
   149  
   150  	// Only latest snapshot is retained.
   151  	newPs, err := placement.NewPlacementsFromProto(newProto.(*placementpb.PlacementSnapshots))
   152  	require.NoError(t, err)
   153  	require.Equal(t, pGet2.SetVersion(0), newPs.Latest())
   154  
   155  	err = ps.Delete()
   156  	require.NoError(t, err)
   157  
   158  	_, err = ps.Placement()
   159  	require.Error(t, err)
   160  	require.Equal(t, kv.ErrNotFound, err)
   161  
   162  	pGet2, err = ps.SetIfNotExist(p)
   163  	require.NoError(t, err)
   164  	assert.Equal(t, 1, pGet2.Version())
   165  
   166  	pGet3, err := ps.Placement()
   167  	require.NoError(t, err)
   168  	require.Equal(t, 1, pGet3.Version())
   169  	require.Equal(t, p.SetVersion(1), pGet3)
   170  }
   171  
   172  func TestStorageCompressesStagedPlacement(t *testing.T) {
   173  	expected := placement.NewPlacement().
   174  		SetInstances([]placement.Instance{
   175  			testInstance("i1"),
   176  			testInstance("i2"),
   177  		}).
   178  		SetShards([]uint32{}).
   179  		SetReplicaFactor(2).
   180  		SetCutoverNanos(100)
   181  
   182  	testCases := []struct {
   183  		name          string
   184  		storeActionFn func(s placement.Storage, p placement.Placement) error
   185  	}{
   186  		{
   187  			name: "set_if_not_exiss",
   188  			storeActionFn: func(s placement.Storage, p placement.Placement) error {
   189  				_, err := s.SetIfNotExist(p)
   190  				return err
   191  			},
   192  		},
   193  		{
   194  			name: "check_and_set",
   195  			storeActionFn: func(s placement.Storage, p placement.Placement) error {
   196  				_, err := s.CheckAndSet(p, 0)
   197  				return err
   198  			},
   199  		},
   200  		{
   201  			name: "set",
   202  			storeActionFn: func(s placement.Storage, p placement.Placement) error {
   203  				_, err := s.Set(p)
   204  				return err
   205  			},
   206  		},
   207  	}
   208  
   209  	for _, tc := range testCases {
   210  		t.Run(tc.name, func(t *testing.T) {
   211  			opts := placement.NewOptions().SetIsStaged(true).SetCompress(true)
   212  			storage := newTestPlacementStorage(mem.NewStore(), opts)
   213  
   214  			err := tc.storeActionFn(storage, expected.Clone()) // nolint: scopelint
   215  			require.NoError(t, err)
   216  
   217  			m, _, err := storage.Proto()
   218  			require.NoError(t, err)
   219  			proto := m.(*placementpb.PlacementSnapshots)
   220  			require.NotNil(t, proto)
   221  			require.Equal(t, placementpb.CompressMode_ZSTD, proto.CompressMode)
   222  			require.True(t, len(proto.CompressedPlacement) > 0)
   223  			require.Equal(t, 0, len(proto.Snapshots))
   224  
   225  			ps, err := placement.NewPlacementsFromProto(proto)
   226  			require.NoError(t, err)
   227  			actual := ps.Latest()
   228  			require.Equal(t, expected.String(), actual.String())
   229  		})
   230  	}
   231  }
   232  
   233  func TestCheckAndSetProto(t *testing.T) {
   234  	m := mem.NewStore()
   235  	ps := newTestPlacementStorage(m, placement.NewOptions())
   236  
   237  	p := placement.NewPlacement().
   238  		SetInstances([]placement.Instance{}).
   239  		SetShards([]uint32{}).
   240  		SetReplicaFactor(0)
   241  
   242  	pGet, err := ps.SetIfNotExist(p)
   243  	require.NoError(t, err)
   244  	assert.Equal(t, 1, pGet.Version())
   245  
   246  	newProto, v, err := ps.Proto()
   247  	require.NoError(t, err)
   248  	require.Equal(t, 1, v)
   249  
   250  	_, err = ps.CheckAndSetProto(newProto, 2)
   251  	require.Error(t, err)
   252  
   253  	version, err := ps.CheckAndSetProto(newProto, 1)
   254  	require.NoError(t, err)
   255  	assert.Equal(t, 2, version)
   256  
   257  	require.NoError(t, ps.Delete())
   258  
   259  	version, err = ps.CheckAndSetProto(newProto, 0)
   260  	require.NoError(t, err)
   261  	assert.Equal(t, 1, version)
   262  }
   263  
   264  func TestDryrun(t *testing.T) {
   265  	m := mem.NewStore()
   266  	dryrunPS := newTestPlacementStorage(m, placement.NewOptions().SetDryrun(true))
   267  	ps := newTestPlacementStorage(m, placement.NewOptions())
   268  
   269  	p := placement.NewPlacement().
   270  		SetInstances([]placement.Instance{}).
   271  		SetShards([]uint32{}).
   272  		SetReplicaFactor(0)
   273  
   274  	dryPGet, err := dryrunPS.SetIfNotExist(p)
   275  	require.NoError(t, err)
   276  	assert.Equal(t, 0, dryPGet.Version())
   277  
   278  	_, err = ps.Placement()
   279  	require.Error(t, err)
   280  
   281  	pGet, err := ps.SetIfNotExist(p)
   282  	require.NoError(t, err)
   283  	assert.Equal(t, 1, pGet.Version())
   284  
   285  	pGet, err = ps.Placement()
   286  	require.NoError(t, err)
   287  	require.Equal(t, 1, pGet.Version())
   288  
   289  	_, err = dryrunPS.CheckAndSet(p, 1)
   290  	require.NoError(t, err)
   291  
   292  	pGet, _ = ps.Placement()
   293  	require.Equal(t, 1, pGet.Version())
   294  
   295  	err = dryrunPS.Delete()
   296  	require.NoError(t, err)
   297  
   298  	pGet, err = ps.Placement()
   299  	require.NoError(t, err)
   300  	require.Equal(t, 1, pGet.Version())
   301  
   302  	dryPGet, err = dryrunPS.Placement()
   303  	require.NoError(t, err)
   304  	require.Equal(t, 1, dryPGet.Version())
   305  
   306  	err = ps.Delete()
   307  	require.NoError(t, err)
   308  
   309  	_, err = dryrunPS.Placement()
   310  	require.Error(t, err)
   311  }
   312  
   313  func newTestPlacementStorage(store kv.Store, pOpts placement.Options) placement.Storage {
   314  	return NewPlacementStorage(store, "key", pOpts)
   315  }
   316  
   317  func testInstance(id string) placement.Instance {
   318  	return placement.NewInstance().
   319  		SetID(id).
   320  		SetIsolationGroup("rack-" + id).
   321  		SetEndpoint("endpoint-" + id).
   322  		SetMetadata(placement.InstanceMetadata{DebugPort: 80}).
   323  		SetWeight(1)
   324  }