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 }