github.com/m3db/m3@v1.5.0/src/dbnode/storage/block/wired_list_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 block 22 23 import ( 24 "bytes" 25 "fmt" 26 "testing" 27 28 "github.com/m3db/m3/src/dbnode/namespace" 29 "github.com/m3db/m3/src/dbnode/runtime" 30 "github.com/m3db/m3/src/dbnode/ts" 31 "github.com/m3db/m3/src/x/checked" 32 "github.com/m3db/m3/src/x/clock" 33 "github.com/m3db/m3/src/x/ident" 34 "github.com/m3db/m3/src/x/instrument" 35 36 "github.com/golang/mock/gomock" 37 "github.com/stretchr/testify/require" 38 "github.com/uber-go/tally" 39 ) 40 41 // The tests in this file use Start and Stop a lot to ensure 42 // access to the fields of the list is safe and not-concurrent. 43 44 // avoid creating a ton of new test options 45 var testOptions = NewOptions() 46 47 func newTestWiredList( 48 overrideRuntimeOpts runtime.Options, 49 overrideMetricsScope tally.Scope, 50 ) (*WiredList, runtime.OptionsManager) { 51 runtimeOptsMgr := runtime.NewOptionsManager() 52 iopts := instrument.NewOptions() 53 if overrideMetricsScope != nil { 54 iopts = iopts.SetMetricsScope(overrideMetricsScope) 55 } 56 copts := clock.NewOptions() 57 return NewWiredList(WiredListOptions{ 58 RuntimeOptionsManager: runtimeOptsMgr, 59 InstrumentOptions: iopts, 60 ClockOptions: copts, 61 // Use a small channel to stress-test the implementation 62 EventsChannelSize: 1, 63 }), runtimeOptsMgr 64 } 65 66 func newTestUnwireableBlock( 67 ctrl *gomock.Controller, 68 name string, 69 opts Options, 70 ) *dbBlock { 71 segment := ts.Segment{ 72 Head: checked.NewBytes([]byte(name), nil), 73 } 74 segment.Head.IncRef() 75 76 bl := NewDatabaseBlock(0, 0, segment, opts, namespace.Context{}).(*dbBlock) 77 bl.Lock() 78 bl.seriesID = ident.StringID(name) 79 bl.wasRetrievedFromDisk = true 80 bl.Unlock() 81 82 return bl 83 } 84 85 func TestWiredListInsertsAndUpdatesWiredBlocks(t *testing.T) { 86 ctrl := gomock.NewController(t) 87 defer ctrl.Finish() 88 89 l, _ := newTestWiredList(nil, nil) 90 91 opts := testOptions.SetWiredList(l) 92 93 l.Start() 94 95 var blocks []*dbBlock 96 for i := 0; i < 3; i++ { 97 bl := newTestUnwireableBlock(ctrl, fmt.Sprintf("foo.%d", i), opts) 98 blocks = append(blocks, bl) 99 } 100 101 l.BlockingUpdate(blocks[0]) 102 l.BlockingUpdate(blocks[1]) 103 l.BlockingUpdate(blocks[2]) 104 l.BlockingUpdate(blocks[1]) 105 106 l.Stop() 107 108 // Order due to LRU should be: 0, 2, 1 109 require.Equal(t, blocks[0], l.root.next()) 110 require.Equal(t, blocks[2], l.root.next().next()) 111 require.Equal(t, blocks[1], l.root.next().next().next()) 112 113 // Assert end 114 require.Equal(t, &l.root, l.root.next().next().next().next()) 115 116 // Assert tail 117 require.Equal(t, blocks[1], l.root.prev()) 118 } 119 120 func TestWiredListRemovesUnwiredBlocks(t *testing.T) { 121 ctrl := gomock.NewController(t) 122 defer ctrl.Finish() 123 124 l, _ := newTestWiredList(nil, nil) 125 126 opts := testOptions.SetWiredList(l) 127 128 l.Start() 129 130 var blocks []*dbBlock 131 for i := 0; i < 2; i++ { 132 bl := newTestUnwireableBlock(ctrl, fmt.Sprintf("foo.%d", i), opts) 133 blocks = append(blocks, bl) 134 } 135 136 l.BlockingUpdate(blocks[0]) 137 l.BlockingUpdate(blocks[1]) 138 l.BlockingUpdate(blocks[0]) 139 140 l.Stop() 141 142 // Order due to LRU should be: 1, 0 143 require.Equal(t, blocks[1], l.root.next()) 144 require.Equal(t, blocks[0], l.root.next().next()) 145 146 // Unwire block and assert removed 147 blocks[1].closed = true 148 149 l.Start() 150 l.BlockingUpdate(blocks[1]) 151 l.Stop() 152 153 require.Equal(t, 1, l.length) 154 require.Equal(t, blocks[0], l.root.next()) 155 require.Equal(t, &l.root, l.root.next().next()) 156 157 // Unwire final block and assert removed 158 blocks[0].closed = true 159 160 l.Start() 161 l.BlockingUpdate(blocks[0]) 162 l.Stop() 163 164 require.Equal(t, 0, l.length) 165 require.Equal(t, &l.root, l.root.next()) 166 require.Equal(t, &l.root, l.root.prev()) 167 } 168 169 // wiredListTestWiredBlocksString is used to debug the order of the wired list 170 func wiredListTestWiredBlocksString(l *WiredList) string { // nolint: unused 171 b := bytes.NewBuffer(nil) 172 for bl := l.root.next(); bl != &l.root; bl = bl.next() { 173 dbBlock := bl.(*dbBlock) 174 b.WriteString(fmt.Sprintf("%s\n", string(dbBlock.segment.Head.Bytes()))) 175 } 176 return b.String() 177 } 178 179 func TestWiredListUpdateNoopsAfterStop(t *testing.T) { 180 ctrl := gomock.NewController(t) 181 defer ctrl.Finish() 182 183 l, _ := newTestWiredList(nil, nil) 184 185 opts := testOptions.SetWiredList(l) 186 187 l.Start() 188 189 var blocks []*dbBlock 190 for i := 0; i < 2; i++ { 191 bl := newTestUnwireableBlock(ctrl, fmt.Sprintf("foo.%d", i), opts) 192 blocks = append(blocks, bl) 193 } 194 195 l.BlockingUpdate(blocks[0]) 196 l.BlockingUpdate(blocks[1]) 197 require.NoError(t, l.Stop()) 198 l.BlockingUpdate(blocks[0]) 199 l.NonBlockingUpdate(blocks[0]) 200 201 // Order due to LRU should be: 0, 1, since next updates are rejected 202 require.Equal(t, blocks[0], l.root.next()) 203 require.Equal(t, blocks[1], l.root.next().next()) 204 205 // Assert end 206 require.Equal(t, &l.root, l.root.next().next().next()) 207 208 // Assert tail 209 require.Equal(t, blocks[1], l.root.prev()) 210 }