github.com/m3db/m3@v1.5.0/src/dbnode/storage/namespace_readers_test.go (about) 1 // Copyright (c) 2019 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 "errors" 25 "testing" 26 27 "github.com/m3db/m3/src/dbnode/namespace" 28 "github.com/m3db/m3/src/dbnode/persist/fs" 29 "github.com/m3db/m3/src/dbnode/storage/block" 30 "github.com/m3db/m3/src/x/checked" 31 "github.com/m3db/m3/src/x/ident" 32 "github.com/m3db/m3/src/x/pool" 33 xtest "github.com/m3db/m3/src/x/test" 34 xtime "github.com/m3db/m3/src/x/time" 35 36 "github.com/golang/mock/gomock" 37 "github.com/stretchr/testify/require" 38 "github.com/uber-go/tally" 39 ) 40 41 func TestNamespaceReadersGet(t *testing.T) { 42 ctrl := xtest.NewController(t) 43 defer ctrl.Finish() 44 45 metadata, err := namespace.NewMetadata(defaultTestNs1ID, 46 defaultTestNs1Opts.SetColdWritesEnabled(true)) 47 require.NoError(t, err) 48 shard := uint32(0) 49 blockSize := metadata.Options().RetentionOptions().BlockSize() 50 start := xtime.Now().Truncate(blockSize) 51 52 mockBlockLeaseMgr := block.NewMockLeaseManager(ctrl) 53 mockBlockLeaseMgr.EXPECT().RegisterLeaser(gomock.Any()).Return(nil) 54 // Case 1: open new reader. 55 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 56 Return(block.LeaseState{Volume: 0}, nil) 57 // Case 2: open new reader and fast forward to the middle of the volume as 58 // specified by the reader position. 59 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 60 Return(block.LeaseState{Volume: 0}, nil) 61 // Case 3: request a reader for the middle of volume 1, but the latest 62 // volume is 3, so open a new reader for volume 3 from the beginning. 63 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 64 Return(block.LeaseState{Volume: 3}, nil) 65 // Case 4: request for a reader that we have cached, so make sure we use 66 // the cached reader. 67 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 68 Return(block.LeaseState{Volume: 2}, nil) 69 // Case 5: request for a reader that we don't have cached. However, we do 70 // have a closed reader so we should reuse that one. 71 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 72 Return(block.LeaseState{Volume: 1}, nil) 73 74 nsReaderMgr := newNamespaceReaderManager(metadata, tally.NoopScope, 75 DefaultTestOptions().SetBlockLeaseManager(mockBlockLeaseMgr)) 76 nsReaderMgrImpl := nsReaderMgr.(*namespaceReaderManager) 77 // Grabbing specific ID here so that the test can match on gomock arguments. 78 nsID := nsReaderMgrImpl.namespace.ID() 79 mockFSReader := fs.NewMockDataFileSetReader(ctrl) 80 // Case 1. 81 mockFSReader.EXPECT().Open(fs.DataReaderOpenOptions{ 82 Identifier: fs.FileSetFileIdentifier{ 83 Namespace: nsID, Shard: shard, BlockStart: start, 84 VolumeIndex: 0, 85 }, 86 }).Return(nil) 87 mockFSReader.EXPECT().ValidateMetadata().Return(nil) 88 // Case 2. 89 mockFSReader.EXPECT().Open(fs.DataReaderOpenOptions{ 90 Identifier: fs.FileSetFileIdentifier{ 91 Namespace: nsID, Shard: shard, BlockStart: start, 92 VolumeIndex: 0, 93 }, 94 }).Return(nil) 95 mockFSReader.EXPECT().ValidateMetadata().Return(nil) 96 mockFSReader.EXPECT().Read().Return(ident.StringID("id"), 97 ident.NewTagsIterator(ident.Tags{}), checked.NewBytes([]byte{}, nil), 98 uint32(0), nil).Times(2) 99 mockFSReader.EXPECT().ReadMetadata().Return(ident.StringID("id"), 100 ident.NewTagsIterator(ident.Tags{}), 0, uint32(0), nil).Times(3) 101 // Case 3. 102 mockFSReader.EXPECT().Open(fs.DataReaderOpenOptions{ 103 Identifier: fs.FileSetFileIdentifier{ 104 Namespace: nsID, Shard: shard, BlockStart: start, 105 VolumeIndex: 3, 106 }, 107 }).Return(nil) 108 mockFSReader.EXPECT().ValidateMetadata().Return(nil) 109 110 nsReaderMgrImpl.newReaderFn = func( 111 bytesPool pool.CheckedBytesPool, 112 opts fs.Options, 113 ) (fs.DataFileSetReader, error) { 114 return mockFSReader, nil 115 } 116 117 // Case 1. 118 _, err = nsReaderMgr.get(shard, start, readerPosition{ 119 volume: 0, dataIdx: 0, metadataIdx: 0, 120 }) 121 require.NoError(t, err) 122 123 // Case 2. 124 _, err = nsReaderMgr.get(shard, start, readerPosition{ 125 volume: 0, dataIdx: 2, metadataIdx: 3, 126 }) 127 require.NoError(t, err) 128 129 // Case 3. 130 _, err = nsReaderMgr.get(shard, start, readerPosition{ 131 volume: 1, dataIdx: 1, metadataIdx: 1, 132 }) 133 require.NoError(t, err) 134 135 // Case 4. 136 case4CachedReader := fs.NewMockDataFileSetReader(ctrl) 137 pos := readerPosition{ 138 volume: 2, dataIdx: 4, metadataIdx: 5, 139 } 140 nsReaderMgrImpl.openReaders[cachedOpenReaderKey{ 141 shard: shard, 142 blockStart: start, 143 position: pos, 144 }] = cachedReader{reader: case4CachedReader} 145 case4Reader, err := nsReaderMgr.get(shard, start, pos) 146 require.NoError(t, err) 147 require.Equal(t, case4CachedReader, case4Reader) 148 149 // Case 5. 150 case5CachedReader := fs.NewMockDataFileSetReader(ctrl) 151 case5CachedReader.EXPECT().Open(fs.DataReaderOpenOptions{ 152 Identifier: fs.FileSetFileIdentifier{ 153 Namespace: nsID, Shard: shard, BlockStart: start, 154 VolumeIndex: 1, 155 }, 156 }).Return(nil) 157 case5CachedReader.EXPECT().ValidateMetadata().Return(nil) 158 nsReaderMgrImpl.closedReaders = []cachedReader{ 159 {reader: case5CachedReader}, 160 } 161 case5Reader, err := nsReaderMgr.get(shard, start, readerPosition{ 162 volume: 1, dataIdx: 0, metadataIdx: 0, 163 }) 164 require.NoError(t, err) 165 require.Equal(t, case5CachedReader, case5Reader) 166 require.Len(t, nsReaderMgrImpl.closedReaders, 0) 167 } 168 169 func TestNamespaceReadersPutTickClose(t *testing.T) { 170 ctrl := xtest.NewController(t) 171 defer ctrl.Finish() 172 173 metadata, err := namespace.NewMetadata(defaultTestNs1ID, 174 defaultTestNs1Opts.SetColdWritesEnabled(true)) 175 require.NoError(t, err) 176 shard := uint32(0) 177 blockSize := metadata.Options().RetentionOptions().BlockSize() 178 start := xtime.Now().Truncate(blockSize) 179 180 mockFSReader := fs.NewMockDataFileSetReader(ctrl) 181 mockBlockLeaseMgr := block.NewMockLeaseManager(ctrl) 182 mockBlockLeaseMgr.EXPECT().RegisterLeaser(gomock.Any()).Return(nil) 183 mockBlockLeaseMgr.EXPECT().UnregisterLeaser(gomock.Any()).Return(nil) 184 // Case 2. 185 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 186 Return(block.LeaseState{Volume: 1}, nil) 187 // Case 3. 188 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 189 Return(block.LeaseState{Volume: 2}, nil) 190 // Case 4. 191 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 192 Return(block.LeaseState{Volume: 3}, nil) 193 194 nsReaderMgr := newNamespaceReaderManager(metadata, tally.NoopScope, 195 DefaultTestOptions().SetBlockLeaseManager(mockBlockLeaseMgr)) 196 nsReaderMgrImpl := nsReaderMgr.(*namespaceReaderManager) 197 // Grabbing specific ID here so that the test can match on gomock arguments. 198 nsID := nsReaderMgrImpl.namespace.ID() 199 200 // Case 1: putting a closed reader should add it to the slice of closed 201 // readers. 202 mockFSReader.EXPECT().Status().Return(fs.DataFileSetReaderStatus{ 203 Namespace: nsID, BlockStart: start, Shard: shard, 204 Volume: 0, 205 Open: false, 206 }) 207 require.Len(t, nsReaderMgrImpl.closedReaders, 0) 208 require.NoError(t, nsReaderMgr.put(mockFSReader)) 209 require.Len(t, nsReaderMgrImpl.closedReaders, 1) 210 211 // Case 2: a reader with a volume lower than the latest volume should be 212 // closed and added in the slice of closed readers. 213 mockFSReader.EXPECT().Status().Return(fs.DataFileSetReaderStatus{ 214 Namespace: nsID, BlockStart: start, Shard: shard, 215 Volume: 0, 216 Open: true, 217 }) 218 mockFSReader.EXPECT().Close().Return(nil) 219 require.Len(t, nsReaderMgrImpl.closedReaders, 1) 220 require.NoError(t, nsReaderMgr.put(mockFSReader)) 221 require.Len(t, nsReaderMgrImpl.closedReaders, 2) 222 223 // Case 3: an open reader with the correct volume gets added to the open 224 // readers. 225 mockFSReader.EXPECT().Status().Return(fs.DataFileSetReaderStatus{ 226 Namespace: nsID, BlockStart: start, Shard: shard, 227 Volume: 2, 228 Open: true, 229 }) 230 mockFSReader.EXPECT().EntriesRead().Return(5) 231 mockFSReader.EXPECT().MetadataRead().Return(6) 232 require.Len(t, nsReaderMgrImpl.openReaders, 0) 233 require.NoError(t, nsReaderMgr.put(mockFSReader)) 234 require.Len(t, nsReaderMgrImpl.openReaders, 1) 235 236 // Case 4: if trying to put a reader that happens to already have an open 237 // cached version for its exact key, close the reader and add to the slice 238 // of closed readers. 239 mockFSReader.EXPECT().Status().Return(fs.DataFileSetReaderStatus{ 240 Namespace: nsID, BlockStart: start, Shard: shard, 241 Volume: 3, 242 Open: true, 243 }) 244 mockFSReader.EXPECT().EntriesRead().Return(7) 245 mockFSReader.EXPECT().MetadataRead().Return(8) 246 mockFSReader.EXPECT().Close() 247 mockExistingFSReader := fs.NewMockDataFileSetReader(ctrl) 248 nsReaderMgrImpl.openReaders[cachedOpenReaderKey{ 249 shard: shard, 250 blockStart: start, 251 position: readerPosition{ 252 volume: 3, 253 dataIdx: 7, 254 metadataIdx: 8, 255 }, 256 }] = cachedReader{reader: mockExistingFSReader} 257 require.Len(t, nsReaderMgrImpl.closedReaders, 2) 258 require.NoError(t, nsReaderMgr.put(mockFSReader)) 259 require.Len(t, nsReaderMgrImpl.closedReaders, 3) 260 261 // Closing the reader manager should close any open readers and remove all 262 // readers. 263 require.Len(t, nsReaderMgrImpl.openReaders, 2) 264 require.Len(t, nsReaderMgrImpl.closedReaders, 3) 265 mockFSReader.EXPECT().Close() 266 mockExistingFSReader.EXPECT().Close() 267 nsReaderMgr.close() 268 require.Len(t, nsReaderMgrImpl.openReaders, 0) 269 require.Len(t, nsReaderMgrImpl.closedReaders, 0) 270 } 271 272 func TestNamespaceReadersUpdateOpenLease(t *testing.T) { 273 ctrl := xtest.NewController(t) 274 defer ctrl.Finish() 275 276 metadata, err := namespace.NewMetadata(defaultTestNs1ID, 277 defaultTestNs1Opts.SetColdWritesEnabled(true)) 278 require.NoError(t, err) 279 shard := uint32(0) 280 blockSize := metadata.Options().RetentionOptions().BlockSize() 281 start := xtime.Now().Truncate(blockSize) 282 283 mockFSReader := fs.NewMockDataFileSetReader(ctrl) 284 mockFSReader.EXPECT().Close().Return(nil).Times(2) 285 mockBlockLeaseMgr := block.NewMockLeaseManager(ctrl) 286 mockBlockLeaseMgr.EXPECT().RegisterLeaser(gomock.Any()).Return(nil) 287 nsReaderMgr := newNamespaceReaderManager(metadata, tally.NoopScope, 288 DefaultTestOptions().SetBlockLeaseManager(mockBlockLeaseMgr)) 289 nsReaderMgrImpl := nsReaderMgr.(*namespaceReaderManager) 290 // Grabbing specific ID here so that the test can match on gomock arguments. 291 nsID := nsReaderMgrImpl.namespace.ID() 292 293 nsReaderMgrImpl.openReaders[cachedOpenReaderKey{ 294 shard: shard, 295 blockStart: start, 296 position: readerPosition{ 297 volume: 0, 298 dataIdx: 1, 299 metadataIdx: 2, 300 }, 301 }] = cachedReader{reader: mockFSReader} 302 nsReaderMgrImpl.openReaders[cachedOpenReaderKey{ 303 shard: shard, 304 blockStart: start, 305 position: readerPosition{ 306 volume: 1, 307 dataIdx: 2, 308 metadataIdx: 3, 309 }, 310 }] = cachedReader{reader: mockFSReader} 311 remainingKey := cachedOpenReaderKey{ 312 shard: shard, 313 blockStart: start, 314 position: readerPosition{ 315 volume: 2, 316 dataIdx: 4, 317 metadataIdx: 5, 318 }, 319 } 320 nsReaderMgrImpl.openReaders[remainingKey] = cachedReader{reader: mockFSReader} 321 322 require.Len(t, nsReaderMgrImpl.openReaders, 3) 323 require.Len(t, nsReaderMgrImpl.closedReaders, 0) 324 // Of the three existing open readers, only two of them have volumes lower 325 // than the update lease volume, so those are expected to be closed, and 326 // put in the slice of closed readers. 327 res, err := nsReaderMgrImpl.UpdateOpenLease(block.LeaseDescriptor{ 328 Namespace: nsID, Shard: shard, BlockStart: start, 329 }, block.LeaseState{Volume: 2}) 330 require.NoError(t, err) 331 require.Equal(t, block.UpdateOpenLease, res) 332 require.Len(t, nsReaderMgrImpl.openReaders, 1) 333 require.Len(t, nsReaderMgrImpl.closedReaders, 2) 334 _, exists := nsReaderMgrImpl.openReaders[remainingKey] 335 require.True(t, exists) 336 337 // Test attempting update lease on wrong namespace. 338 res, err = nsReaderMgrImpl.UpdateOpenLease(block.LeaseDescriptor{ 339 Namespace: ident.StringID("wrong-ns"), Shard: shard, BlockStart: start, 340 }, block.LeaseState{Volume: 2}) 341 require.NoError(t, err) 342 require.Equal(t, block.NoOpenLease, res) 343 } 344 345 func TestNamespaceReadersFilesetExistsAt(t *testing.T) { 346 ctrl := xtest.NewController(t) 347 defer ctrl.Finish() 348 349 metadata, err := namespace.NewMetadata(defaultTestNs1ID, 350 defaultTestNs1Opts.SetColdWritesEnabled(true)) 351 require.NoError(t, err) 352 353 mockBlockLeaseMgr := block.NewMockLeaseManager(ctrl) 354 mockBlockLeaseMgr.EXPECT().RegisterLeaser(gomock.Any()).Return(nil) 355 // First open lease is good. 356 open1 := mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 357 Return(block.LeaseState{}, nil) 358 // Second open lease returns error. 359 mockBlockLeaseMgr.EXPECT().OpenLatestLease(gomock.Any(), gomock.Any()). 360 Return(block.LeaseState{}, errors.New("bad open")).After(open1) 361 nsReaderMgr := newNamespaceReaderManager(metadata, tally.NoopScope, 362 DefaultTestOptions().SetBlockLeaseManager(mockBlockLeaseMgr)) 363 364 exists, err := nsReaderMgr.filesetExistsAt(0, 0) 365 require.NoError(t, err) 366 require.False(t, exists) 367 368 _, err = nsReaderMgr.filesetExistsAt(0, 0) 369 require.Error(t, err) 370 }