github.com/cockroachdb/pebble@v1.1.2/objstorage/objstorageprovider/vfs_readable.go (about) 1 // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package objstorageprovider 6 7 import ( 8 "context" 9 "fmt" 10 "os" 11 "sync" 12 13 "github.com/cockroachdb/pebble/internal/invariants" 14 "github.com/cockroachdb/pebble/objstorage" 15 "github.com/cockroachdb/pebble/vfs" 16 ) 17 18 const fileMaxReadaheadSize = 256 * 1024 /* 256KB */ 19 20 // fileReadable implements objstorage.Readable on top of a vfs.File. 21 // 22 // The implementation might use Prealloc and might reopen the file with 23 // SequentialReadsOption. 24 type fileReadable struct { 25 file vfs.File 26 size int64 27 28 // The following fields are used to possibly open the file again using the 29 // sequential reads option (see vfsReadHandle). 30 filename string 31 fs vfs.FS 32 readaheadConfig ReadaheadConfig 33 } 34 35 var _ objstorage.Readable = (*fileReadable)(nil) 36 37 func newFileReadable( 38 file vfs.File, fs vfs.FS, readaheadConfig ReadaheadConfig, filename string, 39 ) (*fileReadable, error) { 40 info, err := file.Stat() 41 if err != nil { 42 return nil, err 43 } 44 r := &fileReadable{ 45 file: file, 46 size: info.Size(), 47 filename: filename, 48 fs: fs, 49 readaheadConfig: readaheadConfig, 50 } 51 invariants.SetFinalizer(r, func(obj interface{}) { 52 if obj.(*fileReadable).file != nil { 53 fmt.Fprintf(os.Stderr, "Readable was not closed") 54 os.Exit(1) 55 } 56 }) 57 return r, nil 58 } 59 60 // ReadAt is part of the objstorage.Readable interface. 61 func (r *fileReadable) ReadAt(_ context.Context, p []byte, off int64) error { 62 n, err := r.file.ReadAt(p, off) 63 if invariants.Enabled && err == nil && n != len(p) { 64 panic("short read") 65 } 66 return err 67 } 68 69 // Close is part of the objstorage.Readable interface. 70 func (r *fileReadable) Close() error { 71 defer func() { r.file = nil }() 72 return r.file.Close() 73 } 74 75 // Size is part of the objstorage.Readable interface. 76 func (r *fileReadable) Size() int64 { 77 return r.size 78 } 79 80 // NewReadHandle is part of the objstorage.Readable interface. 81 func (r *fileReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle { 82 rh := readHandlePool.Get().(*vfsReadHandle) 83 rh.init(r) 84 return rh 85 } 86 87 type vfsReadHandle struct { 88 r *fileReadable 89 rs readaheadState 90 readaheadMode ReadaheadMode 91 92 // sequentialFile holds a file descriptor to the same underlying File, 93 // except with fadvise(FADV_SEQUENTIAL) called on it to take advantage of 94 // OS-level readahead. Once this is non-nil, the other variables in 95 // readaheadState don't matter much as we defer to OS-level readahead. 96 sequentialFile vfs.File 97 } 98 99 var _ objstorage.ReadHandle = (*vfsReadHandle)(nil) 100 101 var readHandlePool = sync.Pool{ 102 New: func() interface{} { 103 i := &vfsReadHandle{} 104 // Note: this is a no-op if invariants are disabled or race is enabled. 105 invariants.SetFinalizer(i, func(obj interface{}) { 106 if obj.(*vfsReadHandle).r != nil { 107 fmt.Fprintf(os.Stderr, "ReadHandle was not closed") 108 os.Exit(1) 109 } 110 }) 111 return i 112 }, 113 } 114 115 func (rh *vfsReadHandle) init(r *fileReadable) { 116 *rh = vfsReadHandle{ 117 r: r, 118 rs: makeReadaheadState(fileMaxReadaheadSize), 119 readaheadMode: r.readaheadConfig.Speculative, 120 } 121 } 122 123 // Close is part of the objstorage.ReadHandle interface. 124 func (rh *vfsReadHandle) Close() error { 125 var err error 126 if rh.sequentialFile != nil { 127 err = rh.sequentialFile.Close() 128 } 129 *rh = vfsReadHandle{} 130 readHandlePool.Put(rh) 131 return err 132 } 133 134 // ReadAt is part of the objstorage.ReadHandle interface. 135 func (rh *vfsReadHandle) ReadAt(_ context.Context, p []byte, offset int64) error { 136 if rh.sequentialFile != nil { 137 // Use OS-level read-ahead. 138 n, err := rh.sequentialFile.ReadAt(p, offset) 139 if invariants.Enabled && err == nil && n != len(p) { 140 panic("short read") 141 } 142 return err 143 } 144 if rh.readaheadMode != NoReadahead { 145 if readaheadSize := rh.rs.maybeReadahead(offset, int64(len(p))); readaheadSize > 0 { 146 if rh.readaheadMode == FadviseSequential && readaheadSize >= fileMaxReadaheadSize { 147 // We've reached the maximum readahead size. Beyond this point, rely on 148 // OS-level readahead. 149 rh.switchToOSReadahead() 150 } else { 151 _ = rh.r.file.Prefetch(offset, readaheadSize) 152 } 153 } 154 } 155 n, err := rh.r.file.ReadAt(p, offset) 156 if invariants.Enabled && err == nil && n != len(p) { 157 panic("short read") 158 } 159 return err 160 } 161 162 // SetupForCompaction is part of the objstorage.ReadHandle interface. 163 func (rh *vfsReadHandle) SetupForCompaction() { 164 rh.readaheadMode = rh.r.readaheadConfig.Informed 165 if rh.readaheadMode == FadviseSequential { 166 rh.switchToOSReadahead() 167 } 168 } 169 170 func (rh *vfsReadHandle) switchToOSReadahead() { 171 if invariants.Enabled && rh.readaheadMode != FadviseSequential { 172 panic("readheadMode not respected") 173 } 174 if rh.sequentialFile != nil { 175 return 176 } 177 178 // TODO(radu): we could share the reopened file descriptor across multiple 179 // handles. 180 f, err := rh.r.fs.Open(rh.r.filename, vfs.SequentialReadsOption) 181 if err == nil { 182 rh.sequentialFile = f 183 } 184 } 185 186 // RecordCacheHit is part of the objstorage.ReadHandle interface. 187 func (rh *vfsReadHandle) RecordCacheHit(_ context.Context, offset, size int64) { 188 if rh.sequentialFile != nil || rh.readaheadMode == NoReadahead { 189 // Using OS-level or no readahead, so do nothing. 190 return 191 } 192 rh.rs.recordCacheHit(offset, size) 193 } 194 195 // TestingCheckMaxReadahead returns true if the ReadHandle has switched to 196 // OS-level read-ahead. 197 func TestingCheckMaxReadahead(rh objstorage.ReadHandle) bool { 198 switch rh := rh.(type) { 199 case *vfsReadHandle: 200 return rh.sequentialFile != nil 201 case *PreallocatedReadHandle: 202 return rh.sequentialFile != nil 203 default: 204 panic("unknown ReadHandle type") 205 } 206 } 207 208 // PreallocatedReadHandle is used to avoid an allocation in NewReadHandle; see 209 // UsePreallocatedReadHandle. 210 type PreallocatedReadHandle struct { 211 vfsReadHandle 212 } 213 214 // Close is part of the objstorage.ReadHandle interface. 215 func (rh *PreallocatedReadHandle) Close() error { 216 var err error 217 if rh.sequentialFile != nil { 218 err = rh.sequentialFile.Close() 219 } 220 rh.vfsReadHandle = vfsReadHandle{} 221 return err 222 } 223 224 // UsePreallocatedReadHandle is equivalent to calling readable.NewReadHandle() 225 // but uses the existing storage of a PreallocatedReadHandle when possible 226 // (currently this happens if we are reading from a local file). 227 // The returned handle still needs to be closed. 228 func UsePreallocatedReadHandle( 229 ctx context.Context, readable objstorage.Readable, rh *PreallocatedReadHandle, 230 ) objstorage.ReadHandle { 231 if r, ok := readable.(*fileReadable); ok { 232 rh.init(r) 233 return rh 234 } 235 return readable.NewReadHandle(ctx) 236 }