github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/shard_index_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 storage 22 23 import ( 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/m3db/m3/src/dbnode/namespace" 29 "github.com/m3db/m3/src/dbnode/runtime" 30 "github.com/m3db/m3/src/dbnode/storage/index" 31 "github.com/m3db/m3/src/dbnode/storage/index/convert" 32 "github.com/m3db/m3/src/dbnode/storage/series" 33 "github.com/m3db/m3/src/m3ninx/doc" 34 "github.com/m3db/m3/src/x/context" 35 "github.com/m3db/m3/src/x/ident" 36 xtest "github.com/m3db/m3/src/x/test" 37 xtime "github.com/m3db/m3/src/x/time" 38 39 "github.com/fortytw2/leaktest" 40 "github.com/golang/mock/gomock" 41 "github.com/stretchr/testify/assert" 42 "github.com/stretchr/testify/require" 43 ) 44 45 func TestShardInsertNamespaceIndex(t *testing.T) { 46 defer leaktest.CheckTimeout(t, 2*time.Second)() 47 opts := DefaultTestOptions() 48 49 lock := sync.Mutex{} 50 indexWrites := []doc.Metadata{} 51 52 now := xtime.Now() 53 blockSize := namespace.NewIndexOptions().BlockSize() 54 55 blockStart := now.Truncate(blockSize) 56 57 ctrl := xtest.NewController(t) 58 defer ctrl.Finish() 59 idx := NewMockNamespaceIndex(ctrl) 60 idx.EXPECT().BlockStartForWriteTime(gomock.Any()).Return(blockStart).AnyTimes() 61 idx.EXPECT().WriteBatch(gomock.Any()).Do( 62 func(batch *index.WriteBatch) { 63 64 lock.Lock() 65 indexWrites = append(indexWrites, batch.PendingDocs()...) 66 lock.Unlock() 67 for i, e := range batch.PendingEntries() { 68 e.OnIndexSeries.OnIndexSuccess(blockStart) 69 e.OnIndexSeries.OnIndexFinalize(blockStart) 70 batch.PendingEntries()[i].OnIndexSeries = nil 71 } 72 }).Return(nil).AnyTimes() 73 74 shard := testDatabaseShardWithIndexFn(t, opts, idx, false) 75 shard.SetRuntimeOptions(runtime.NewOptions().SetWriteNewSeriesAsync(false)) 76 defer shard.Close() 77 78 ctx := context.NewBackground() 79 defer ctx.Close() 80 81 seriesWrite, err := shard.WriteTagged(ctx, ident.StringID("foo"), 82 convert.NewTagsIterMetadataResolver(ident.NewTagsIterator(ident.NewTags(ident.StringTag("name", "value")))), 83 now, 1.0, xtime.Second, nil, series.WriteOptions{}) 84 require.NoError(t, err) 85 require.True(t, seriesWrite.WasWritten) 86 87 seriesWrite, err = shard.WriteTagged(ctx, ident.StringID("foo"), 88 convert.NewTagsIterMetadataResolver(ident.NewTagsIterator(ident.NewTags(ident.StringTag("name", "value")))), 89 now, 2.0, xtime.Second, nil, series.WriteOptions{}) 90 require.NoError(t, err) 91 require.True(t, seriesWrite.WasWritten) 92 93 seriesWrite, err = shard.Write( 94 ctx, ident.StringID("baz"), now, 1.0, xtime.Second, nil, series.WriteOptions{}) 95 require.NoError(t, err) 96 require.True(t, seriesWrite.WasWritten) 97 98 lock.Lock() 99 defer lock.Unlock() 100 101 require.Len(t, indexWrites, 1) 102 require.Equal(t, []byte("foo"), indexWrites[0].ID) 103 require.Equal(t, []byte("name"), indexWrites[0].Fields[0].Name) 104 require.Equal(t, []byte("value"), indexWrites[0].Fields[0].Value) 105 } 106 107 func TestShardAsyncInsertMarkIndexedForBlockStart(t *testing.T) { 108 ctrl := xtest.NewController(t) 109 defer ctrl.Finish() 110 defer leaktest.CheckTimeout(t, 2*time.Second)() 111 112 opts := DefaultTestOptions() 113 blockSize := time.Hour 114 now := xtime.Now() 115 nextWriteTime := now.Truncate(blockSize) 116 idx := NewMockNamespaceIndex(ctrl) 117 idx.EXPECT().BlockStartForWriteTime(gomock.Any()). 118 DoAndReturn(func(t xtime.UnixNano) xtime.UnixNano { 119 return t.Truncate(blockSize) 120 }). 121 AnyTimes() 122 shard := testDatabaseShardWithIndexFn(t, opts, idx, false) 123 shard.SetRuntimeOptions(runtime.NewOptions().SetWriteNewSeriesAsync(true)) 124 defer shard.Close() 125 126 ctx := context.NewBackground() 127 defer ctx.Close() 128 129 // write first time 130 seriesWrite, err := shard.WriteTagged(ctx, ident.StringID("foo"), 131 convert.NewTagsIterMetadataResolver(ident.NewTagsIterator(ident.NewTags(ident.StringTag("name", "value")))), 132 now, 1.0, xtime.Second, nil, series.WriteOptions{}) 133 assert.NoError(t, err) 134 assert.True(t, seriesWrite.WasWritten) 135 assert.True(t, seriesWrite.NeedsIndex) 136 137 // mark as indexed 138 seriesWrite.PendingIndexInsert.Entry.OnIndexSeries.OnIndexSuccess(nextWriteTime) 139 seriesWrite.PendingIndexInsert.Entry.OnIndexSeries.OnIndexFinalize(nextWriteTime) 140 141 start := time.Now() 142 for time.Since(start) < 10*time.Second { 143 entry, _, err := shard.TryRetrieveSeriesAndIncrementReaderWriterCount(ident.StringID("foo")) 144 require.NoError(t, err) 145 if entry == nil { 146 time.Sleep(10 * time.Millisecond) 147 continue 148 } 149 assert.True(t, entry.IndexedForBlockStart(nextWriteTime)) 150 break // done 151 } 152 } 153 154 func TestShardAsyncIndexIfExpired(t *testing.T) { 155 defer leaktest.CheckTimeout(t, 2*time.Second)() 156 157 // Make now not rounded exactly to the block size 158 blockSize := time.Minute 159 now := xtime.Now().Truncate(blockSize).Add(time.Second) 160 161 ctrl := xtest.NewController(t) 162 defer ctrl.Finish() 163 idx := NewMockNamespaceIndex(ctrl) 164 idx.EXPECT().BlockStartForWriteTime(gomock.Any()). 165 DoAndReturn(func(t xtime.UnixNano) xtime.UnixNano { 166 return t.Truncate(blockSize) 167 }). 168 AnyTimes() 169 170 opts := DefaultTestOptions() 171 shard := testDatabaseShardWithIndexFn(t, opts, idx, false) 172 shard.SetRuntimeOptions(runtime.NewOptions().SetWriteNewSeriesAsync(true)) 173 defer shard.Close() 174 175 ctx := context.NewBackground() 176 defer ctx.Close() 177 178 seriesWrite, err := shard.WriteTagged(ctx, ident.StringID("foo"), 179 convert.NewTagsIterMetadataResolver( 180 ident.NewTagsIterator(ident.NewTags(ident.StringTag("name", "value")))), 181 now, 1.0, xtime.Second, nil, series.WriteOptions{}) 182 assert.NoError(t, err) 183 assert.True(t, seriesWrite.WasWritten) 184 assert.True(t, seriesWrite.NeedsIndex) 185 186 // mark as indexed 187 seriesWrite.PendingIndexInsert.Entry.OnIndexSeries.OnIndexSuccess(now.Truncate(blockSize)) 188 seriesWrite.PendingIndexInsert.Entry.OnIndexSeries.OnIndexFinalize(now.Truncate(blockSize)) 189 190 // make sure next block not marked as indexed 191 start := time.Now() 192 for time.Since(start) < 10*time.Second { 193 entry, _, err := shard.TryRetrieveSeriesAndIncrementReaderWriterCount(ident.StringID("foo")) 194 require.NoError(t, err) 195 if entry == nil { 196 time.Sleep(10 * time.Millisecond) 197 continue 198 } 199 assert.True(t, entry.IndexedForBlockStart(now.Truncate(blockSize))) 200 break // done 201 } 202 203 // ensure we would need to index next block because it's expired 204 nextWriteTime := now.Add(blockSize) 205 seriesWrite, err = shard.WriteTagged(ctx, ident.StringID("foo"), 206 convert.NewTagsIterMetadataResolver( 207 ident.NewTagsIterator(ident.NewTags(ident.StringTag("name", "value")))), 208 nextWriteTime, 2.0, xtime.Second, nil, series.WriteOptions{}) 209 assert.NoError(t, err) 210 assert.True(t, seriesWrite.WasWritten) 211 assert.True(t, seriesWrite.NeedsIndex) 212 } 213 214 // TODO(prateek): wire tests above to use the field `ts` 215 // nolint 216 type testIndexWrite struct { 217 id ident.ID 218 tags ident.Tags 219 ts time.Time 220 }