github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/bootstrapper/peers/source_test.go (about) 1 // Copyright (c) 2018 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 peers 22 23 import ( 24 "errors" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/m3db/m3/src/cluster/shard" 30 "github.com/m3db/m3/src/dbnode/persist/fs" 31 m3dbruntime "github.com/m3db/m3/src/dbnode/runtime" 32 "github.com/m3db/m3/src/dbnode/storage/bootstrap" 33 "github.com/m3db/m3/src/dbnode/storage/bootstrap/result" 34 "github.com/m3db/m3/src/dbnode/topology" 35 tu "github.com/m3db/m3/src/dbnode/topology/testutil" 36 "github.com/m3db/m3/src/x/context" 37 "github.com/m3db/m3/src/x/instrument" 38 xtime "github.com/m3db/m3/src/x/time" 39 40 "github.com/golang/mock/gomock" 41 "github.com/stretchr/testify/require" 42 ) 43 44 const ( 45 notSelfID1 = "not-self1" 46 notSelfID2 = "not-self2" 47 ) 48 49 func TestPeersSourceAvailableDataAndIndex(t *testing.T) { 50 ctrl := gomock.NewController(t) 51 defer ctrl.Finish() 52 53 var ( 54 blockSize = 2 * time.Hour 55 nsMetadata = testNamespaceMetadata(t) 56 numShards = uint32(4) 57 blockStart = xtime.Now().Truncate(blockSize) 58 shardTimeRangesToBootstrap = result.NewShardTimeRanges() 59 bootstrapRanges = xtime.NewRanges(xtime.Range{ 60 Start: blockStart, 61 End: blockStart.Add(blockSize), 62 }) 63 cacheOptions = bootstrap.NewCacheOptions(). 64 SetFilesystemOptions(fs.NewOptions()). 65 SetInstrumentOptions(instrument.NewOptions()) 66 ) 67 68 for i := 0; i < int(numShards); i++ { 69 shardTimeRangesToBootstrap.Set(uint32(i), bootstrapRanges) 70 } 71 72 shardTimeRangesToBootstrapOneExtra := shardTimeRangesToBootstrap.Copy() 73 shardTimeRangesToBootstrapOneExtra.Set(100, bootstrapRanges) 74 75 testCases := []struct { 76 title string 77 topoState *topology.StateSnapshot 78 bootstrapReadConsistency topology.ReadConsistencyLevel 79 shardsTimeRangesToBootstrap result.ShardTimeRanges 80 expectedAvailableShardsTimeRanges result.ShardTimeRanges 81 expectedErr error 82 }{ 83 { 84 title: "Returns empty if only self is available", 85 topoState: tu.NewStateSnapshot(1, tu.HostShardStates{ 86 tu.SelfID: tu.ShardsRange(0, numShards, shard.Available), 87 }), 88 bootstrapReadConsistency: topology.ReadConsistencyLevelMajority, 89 shardsTimeRangesToBootstrap: shardTimeRangesToBootstrap, 90 expectedAvailableShardsTimeRanges: result.NewShardTimeRanges(), 91 }, 92 { 93 title: "Returns empty if all other peers initializing/unknown", 94 topoState: tu.NewStateSnapshot(2, tu.HostShardStates{ 95 tu.SelfID: tu.ShardsRange(0, numShards, shard.Available), 96 notSelfID1: tu.ShardsRange(0, numShards, shard.Initializing), 97 notSelfID2: tu.ShardsRange(0, numShards, shard.Unknown), 98 }), 99 bootstrapReadConsistency: topology.ReadConsistencyLevelMajority, 100 shardsTimeRangesToBootstrap: shardTimeRangesToBootstrap, 101 expectedAvailableShardsTimeRanges: result.NewShardTimeRanges(), 102 expectedErr: errors.New("unknown shard state: Unknown"), 103 }, 104 { 105 title: "Returns success if consistency can be met (available/leaving)", 106 topoState: tu.NewStateSnapshot(2, tu.HostShardStates{ 107 tu.SelfID: tu.ShardsRange(0, numShards, shard.Initializing), 108 notSelfID1: tu.ShardsRange(0, numShards, shard.Available), 109 notSelfID2: tu.ShardsRange(0, numShards, shard.Leaving), 110 }), 111 bootstrapReadConsistency: topology.ReadConsistencyLevelMajority, 112 shardsTimeRangesToBootstrap: shardTimeRangesToBootstrap, 113 expectedAvailableShardsTimeRanges: shardTimeRangesToBootstrap, 114 }, 115 { 116 title: "Skips shards that were not in the topology at start", 117 topoState: tu.NewStateSnapshot(2, tu.HostShardStates{ 118 tu.SelfID: tu.ShardsRange(0, numShards, shard.Initializing), 119 notSelfID1: tu.ShardsRange(0, numShards, shard.Available), 120 notSelfID2: tu.ShardsRange(0, numShards, shard.Available), 121 }), 122 bootstrapReadConsistency: topology.ReadConsistencyLevelMajority, 123 shardsTimeRangesToBootstrap: shardTimeRangesToBootstrapOneExtra, 124 expectedAvailableShardsTimeRanges: shardTimeRangesToBootstrap, 125 }, 126 { 127 title: "Returns empty if consistency can not be met", 128 topoState: tu.NewStateSnapshot(2, tu.HostShardStates{ 129 tu.SelfID: tu.ShardsRange(0, numShards, shard.Initializing), 130 notSelfID1: tu.ShardsRange(0, numShards, shard.Available), 131 notSelfID2: tu.ShardsRange(0, numShards, shard.Available), 132 }), 133 bootstrapReadConsistency: topology.ReadConsistencyLevelAll, 134 shardsTimeRangesToBootstrap: shardTimeRangesToBootstrap, 135 expectedAvailableShardsTimeRanges: result.NewShardTimeRanges(), 136 }, 137 } 138 139 for _, tc := range testCases { 140 t.Run(tc.title, func(t *testing.T) { 141 mockRuntimeOpts := m3dbruntime.NewMockOptions(ctrl) 142 mockRuntimeOpts. 143 EXPECT(). 144 ClientBootstrapConsistencyLevel(). 145 Return(tc.bootstrapReadConsistency). 146 AnyTimes() 147 148 mockRuntimeOptsMgr := m3dbruntime.NewMockOptionsManager(ctrl) 149 mockRuntimeOptsMgr. 150 EXPECT(). 151 Get(). 152 Return(mockRuntimeOpts). 153 AnyTimes() 154 155 opts := newTestDefaultOpts(t, ctrl). 156 SetRuntimeOptionsManager(mockRuntimeOptsMgr) 157 158 src, err := newPeersSource(opts) 159 require.NoError(t, err) 160 161 var shards []uint32 162 for shard := range tc.shardsTimeRangesToBootstrap.Iter() { 163 shards = append(shards, shard) 164 } 165 cache, sErr := bootstrap.NewCache(cacheOptions. 166 SetNamespaceDetails([]bootstrap.NamespaceDetails{ 167 { 168 Namespace: nsMetadata, 169 Shards: shards, 170 }, 171 })) 172 require.NoError(t, sErr) 173 174 runOpts := testDefaultRunOpts.SetInitialTopologyState(tc.topoState) 175 dataRes, err := src.AvailableData(nsMetadata, tc.shardsTimeRangesToBootstrap, cache, runOpts) 176 if tc.expectedErr != nil { 177 require.Equal(t, tc.expectedErr, err) 178 } else { 179 require.NoError(t, err) 180 require.Equal(t, tc.expectedAvailableShardsTimeRanges, dataRes) 181 } 182 183 indexRes, err := src.AvailableIndex(nsMetadata, tc.shardsTimeRangesToBootstrap, cache, runOpts) 184 if tc.expectedErr != nil { 185 require.Equal(t, tc.expectedErr, err) 186 } else { 187 require.NoError(t, err) 188 require.Equal(t, tc.expectedAvailableShardsTimeRanges, indexRes) 189 } 190 }) 191 } 192 } 193 194 func TestPeersSourceReturnsErrorIfUnknownPersistenceFileSetType(t *testing.T) { 195 ctrl := gomock.NewController(t) 196 defer ctrl.Finish() 197 198 var ( 199 testNsMd = testNamespaceMetadata(t) 200 opts = newTestDefaultOpts(t, ctrl) 201 ropts = testNsMd.Options().RetentionOptions() 202 203 start = xtime.Now().Add(-ropts.RetentionPeriod()).Truncate(ropts.BlockSize()) 204 end = start.Add(2 * ropts.BlockSize()) 205 ) 206 207 src, err := newPeersSource(opts) 208 require.NoError(t, err) 209 210 target := result.NewShardTimeRanges().Set( 211 0, 212 xtime.NewRanges(xtime.Range{Start: start, End: end}), 213 ).Set( 214 1, 215 xtime.NewRanges(xtime.Range{Start: start, End: end}), 216 ) 217 218 runOpts := testRunOptsWithPersist.SetPersistConfig(bootstrap.PersistConfig{Enabled: true, FileSetType: 999}) 219 tester := bootstrap.BuildNamespacesTesterWithFilesystemOptions(t, runOpts, target, opts.FilesystemOptions(), testNsMd) 220 defer tester.Finish() 221 222 ctx := context.NewBackground() 223 defer ctx.Close() 224 225 _, err = src.Read(ctx, tester.Namespaces, tester.Cache) 226 require.Error(t, err) 227 require.True(t, strings.Contains(err.Error(), "unknown persist config fileset file type")) 228 tester.EnsureNoLoadedBlocks() 229 tester.EnsureNoWrites() 230 }