github.com/m3db/m3@v1.5.0/src/dbnode/integration/peers_bootstrap_index_test.go (about) 1 // +build integration 2 3 // Copyright (c) 2018 Uber Technologies, Inc. 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 package integration 24 25 import ( 26 "testing" 27 "time" 28 29 indexpb "github.com/m3db/m3/src/dbnode/generated/proto/index" 30 "github.com/m3db/m3/src/dbnode/integration/generate" 31 "github.com/m3db/m3/src/dbnode/namespace" 32 "github.com/m3db/m3/src/dbnode/persist/fs" 33 "github.com/m3db/m3/src/dbnode/retention" 34 "github.com/m3db/m3/src/dbnode/storage/index" 35 "github.com/m3db/m3/src/m3ninx/generated/proto/fswriter" 36 "github.com/m3db/m3/src/m3ninx/idx" 37 idxpersist "github.com/m3db/m3/src/m3ninx/persist" 38 "github.com/m3db/m3/src/x/ident" 39 xtest "github.com/m3db/m3/src/x/test" 40 xtime "github.com/m3db/m3/src/x/time" 41 42 "github.com/stretchr/testify/require" 43 ) 44 45 func TestPeersBootstrapIndexWithIndexingEnabled(t *testing.T) { 46 if testing.Short() { 47 t.SkipNow() // Just skip if we're doing a short run 48 } 49 50 log := xtest.NewLogger(t) 51 52 blockSize := 2 * time.Hour 53 rOpts := retention.NewOptions(). 54 SetRetentionPeriod(20 * time.Hour). 55 SetBlockSize(blockSize). 56 SetBufferPast(10 * time.Minute). 57 SetBufferFuture(2 * time.Minute) 58 59 idxOpts := namespace.NewIndexOptions(). 60 SetEnabled(true). 61 SetBlockSize(blockSize) 62 nOpts := namespace.NewOptions(). 63 SetRetentionOptions(rOpts). 64 SetIndexOptions(idxOpts) 65 ns1, err := namespace.NewMetadata(testNamespaces[0], nOpts) 66 require.NoError(t, err) 67 opts := NewTestOptions(t). 68 SetNamespaces([]namespace.Metadata{ns1}). 69 // Use TChannel clients for writing / reading because we want to target individual nodes at a time 70 // and not write/read all nodes in the cluster. 71 SetUseTChannelClientForWriting(true). 72 SetUseTChannelClientForReading(true) 73 74 setupOpts := []BootstrappableTestSetupOptions{ 75 {DisablePeersBootstrapper: true}, 76 { 77 DisableCommitLogBootstrapper: true, 78 DisablePeersBootstrapper: false, 79 }, 80 } 81 setups, closeFn := NewDefaultBootstrappableTestSetups(t, opts, setupOpts) 82 defer closeFn() 83 84 // Write test data for first node 85 // Write test data 86 now := setups[0].NowFn()() 87 88 fooSeries := generate.Series{ 89 ID: ident.StringID("foo"), 90 Tags: ident.NewTags(ident.StringTag("city", "new_york"), ident.StringTag("foo", "foo")), 91 } 92 93 barSeries := generate.Series{ 94 ID: ident.StringID("bar"), 95 Tags: ident.NewTags(ident.StringTag("city", "new_jersey")), 96 } 97 98 bazSeries := generate.Series{ 99 ID: ident.StringID("baz"), 100 Tags: ident.NewTags(ident.StringTag("city", "seattle")), 101 } 102 103 quxSeries := generate.Series{ 104 ID: ident.StringID("qux"), 105 Tags: ident.NewTags(ident.StringTag("city", "new_orleans")), 106 } 107 108 seriesMaps := generate.BlocksByStart([]generate.BlockConfig{ 109 { 110 IDs: []string{quxSeries.ID.String()}, 111 Tags: quxSeries.Tags, 112 NumPoints: 100, 113 Start: now.Add(-2 * blockSize), 114 }, 115 { 116 IDs: []string{fooSeries.ID.String()}, 117 Tags: fooSeries.Tags, 118 NumPoints: 100, 119 Start: now.Add(-blockSize), 120 }, 121 { 122 IDs: []string{barSeries.ID.String()}, 123 Tags: barSeries.Tags, 124 NumPoints: 100, 125 Start: now.Add(-blockSize), 126 }, 127 { 128 IDs: []string{fooSeries.ID.String()}, 129 Tags: fooSeries.Tags, 130 NumPoints: 50, 131 Start: now, 132 }, 133 { 134 IDs: []string{bazSeries.ID.String()}, 135 Tags: bazSeries.Tags, 136 NumPoints: 50, 137 Start: now, 138 }, 139 }) 140 require.NoError(t, writeTestDataToDisk(ns1, setups[0], seriesMaps, 0)) 141 142 for blockStart, series := range seriesMaps { 143 docs := generate.ToDocMetadata(series) 144 require.NoError(t, writeTestIndexDataToDisk( 145 ns1, 146 setups[0].StorageOpts(), 147 idxpersist.DefaultIndexVolumeType, 148 blockStart, 149 setups[0].ShardSet().AllIDs(), 150 docs, 151 )) 152 } 153 154 // Start the first server with filesystem bootstrapper 155 require.NoError(t, setups[0].StartServer()) 156 157 // Start the last server with peers and filesystem bootstrappers 158 require.NoError(t, setups[1].StartServer()) 159 log.Debug("servers are now up") 160 161 // Stop the servers 162 defer func() { 163 setups.parallel(func(s TestSetup) { 164 require.NoError(t, s.StopServer()) 165 }) 166 log.Debug("servers are now down") 167 }() 168 169 // Verify in-memory data match what we expect 170 for _, setup := range setups { 171 verifySeriesMaps(t, setup, ns1.ID(), seriesMaps) 172 } 173 174 // Issue some index queries to the second node which bootstrapped the metadata 175 session, err := setups[1].M3DBClient().DefaultSession() 176 require.NoError(t, err) 177 178 start := now.Add(-rOpts.RetentionPeriod()) 179 end := now.Add(blockSize) 180 queryOpts := index.QueryOptions{StartInclusive: start, EndExclusive: end} 181 182 // Match all new_*r* 183 regexpQuery, err := idx.NewRegexpQuery([]byte("city"), []byte("new_.*r.*")) 184 require.NoError(t, err) 185 iter, fetchResponse, err := session.FetchTaggedIDs(ContextWithDefaultTimeout(), 186 ns1.ID(), index.Query{Query: regexpQuery}, queryOpts) 187 require.NoError(t, err) 188 defer iter.Finalize() 189 190 verifyQueryMetadataResults(t, iter, fetchResponse.Exhaustive, verifyQueryMetadataResultsOptions{ 191 namespace: ns1.ID(), 192 exhaustive: true, 193 expected: []generate.Series{fooSeries, barSeries, quxSeries}, 194 }) 195 196 // Match all *e*e* 197 regexpQuery, err = idx.NewRegexpQuery([]byte("city"), []byte(".*e.*e.*")) 198 require.NoError(t, err) 199 iter, fetchResponse, err = session.FetchTaggedIDs(ContextWithDefaultTimeout(), 200 ns1.ID(), index.Query{Query: regexpQuery}, queryOpts) 201 require.NoError(t, err) 202 defer iter.Finalize() 203 204 verifyQueryMetadataResults(t, iter, fetchResponse.Exhaustive, verifyQueryMetadataResultsOptions{ 205 namespace: ns1.ID(), 206 exhaustive: true, 207 expected: []generate.Series{barSeries, bazSeries, quxSeries}, 208 }) 209 210 // Ensure that the index data for qux has been written to disk. 211 numDocsPerBlockStart, err := getNumDocsPerBlockStart( 212 ns1.ID(), 213 setups[1].FilesystemOpts(), 214 ) 215 require.NoError(t, err) 216 numDocs, ok := numDocsPerBlockStart[now.Add(-2*blockSize).Truncate(blockSize)] 217 require.True(t, ok) 218 require.Equal(t, numDocs, 1) 219 } 220 221 type indexInfo struct { 222 Info indexpb.IndexVolumeInfo 223 VolumeIndex int 224 } 225 226 func getNumDocsPerBlockStart( 227 nsID ident.ID, 228 fsOpts fs.Options, 229 ) (map[xtime.UnixNano]int, error) { 230 numDocsPerBlockStart := make(map[xtime.UnixNano]int) 231 infoFiles := fs.ReadIndexInfoFiles(fs.ReadIndexInfoFilesOptions{ 232 FilePathPrefix: fsOpts.FilePathPrefix(), 233 Namespace: nsID, 234 ReaderBufferSize: fsOpts.InfoReaderBufferSize(), 235 }) 236 // Grab the latest index info file for each blockstart. 237 latestIndexInfoPerBlockStart := make(map[xtime.UnixNano]indexInfo) 238 for _, f := range infoFiles { 239 info, ok := latestIndexInfoPerBlockStart[xtime.UnixNano(f.Info.BlockStart)] 240 if !ok { 241 latestIndexInfoPerBlockStart[xtime.UnixNano(f.Info.BlockStart)] = indexInfo{ 242 Info: f.Info, 243 VolumeIndex: f.ID.VolumeIndex, 244 } 245 continue 246 } 247 248 if f.ID.VolumeIndex > info.VolumeIndex { 249 latestIndexInfoPerBlockStart[xtime.UnixNano(f.Info.BlockStart)] = indexInfo{ 250 Info: f.Info, 251 VolumeIndex: f.ID.VolumeIndex, 252 } 253 } 254 } 255 for blockStart, info := range latestIndexInfoPerBlockStart { 256 for _, segment := range info.Info.Segments { 257 metadata := fswriter.Metadata{} 258 if err := metadata.Unmarshal(segment.Metadata); err != nil { 259 return nil, err 260 } 261 numDocsPerBlockStart[blockStart] += int(metadata.NumDocs) 262 } 263 } 264 return numDocsPerBlockStart, nil 265 }