github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/objstorage/objstorageprovider/remote_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 "io" 10 11 "github.com/cockroachdb/pebble/internal/base" 12 "github.com/cockroachdb/pebble/objstorage" 13 "github.com/cockroachdb/pebble/objstorage/objstorageprovider/sharedcache" 14 "github.com/cockroachdb/pebble/objstorage/remote" 15 ) 16 17 const remoteMaxReadaheadSize = 1024 * 1024 /* 1MB */ 18 19 // remoteReadable is a very simple implementation of Readable on top of the 20 // ReadCloser returned by remote.Storage.CreateObject. 21 type remoteReadable struct { 22 objReader remote.ObjectReader 23 size int64 24 fileNum base.DiskFileNum 25 provider *provider 26 } 27 28 var _ objstorage.Readable = (*remoteReadable)(nil) 29 30 func (p *provider) newRemoteReadable( 31 objReader remote.ObjectReader, size int64, fileNum base.DiskFileNum, 32 ) *remoteReadable { 33 return &remoteReadable{ 34 objReader: objReader, 35 size: size, 36 fileNum: fileNum, 37 provider: p, 38 } 39 } 40 41 // ReadAt is part of the objstorage.Readable interface. 42 func (r *remoteReadable) ReadAt(ctx context.Context, p []byte, offset int64) error { 43 return r.readInternal(ctx, p, offset, false /* forCompaction */) 44 } 45 46 // readInternal performs a read for the object, using the cache when 47 // appropriate. 48 func (r *remoteReadable) readInternal( 49 ctx context.Context, p []byte, offset int64, forCompaction bool, 50 ) error { 51 if cache := r.provider.remote.cache; cache != nil { 52 flags := sharedcache.ReadFlags{ 53 // Don't add data to the cache if this read is for a compaction. 54 ReadOnly: forCompaction, 55 } 56 return r.provider.remote.cache.ReadAt(ctx, r.fileNum, p, offset, r.objReader, r.size, flags) 57 } 58 return r.objReader.ReadAt(ctx, p, offset) 59 } 60 61 func (r *remoteReadable) Close() error { 62 defer func() { r.objReader = nil }() 63 return r.objReader.Close() 64 } 65 66 func (r *remoteReadable) Size() int64 { 67 return r.size 68 } 69 70 func (r *remoteReadable) NewReadHandle(_ context.Context) objstorage.ReadHandle { 71 // TODO(radu): use a pool. 72 rh := &remoteReadHandle{readable: r} 73 rh.readahead.state = makeReadaheadState(remoteMaxReadaheadSize) 74 return rh 75 } 76 77 type remoteReadHandle struct { 78 readable *remoteReadable 79 readahead struct { 80 state readaheadState 81 data []byte 82 offset int64 83 } 84 forCompaction bool 85 } 86 87 var _ objstorage.ReadHandle = (*remoteReadHandle)(nil) 88 89 // ReadAt is part of the objstorage.ReadHandle interface. 90 func (r *remoteReadHandle) ReadAt(ctx context.Context, p []byte, offset int64) error { 91 readaheadSize := r.maybeReadahead(offset, len(p)) 92 93 // Check if we already have the data from a previous read-ahead. 94 if rhSize := int64(len(r.readahead.data)); rhSize > 0 { 95 if r.readahead.offset <= offset && r.readahead.offset+rhSize > offset { 96 n := copy(p, r.readahead.data[offset-r.readahead.offset:]) 97 if n == len(p) { 98 // All data was available. 99 return nil 100 } 101 // Use the data that we had and do a shorter read. 102 offset += int64(n) 103 p = p[n:] 104 readaheadSize -= n 105 } 106 } 107 108 if readaheadSize > len(p) { 109 // Don't try to read past EOF. 110 if offset+int64(readaheadSize) > r.readable.size { 111 readaheadSize = int(r.readable.size - offset) 112 if readaheadSize <= 0 { 113 // This shouldn't happen in practice (Pebble should never try to read 114 // past EOF). 115 return io.EOF 116 } 117 } 118 r.readahead.offset = offset 119 // TODO(radu): we need to somehow account for this memory. 120 if cap(r.readahead.data) >= readaheadSize { 121 r.readahead.data = r.readahead.data[:readaheadSize] 122 } else { 123 r.readahead.data = make([]byte, readaheadSize) 124 } 125 126 if err := r.readable.readInternal(ctx, r.readahead.data, offset, r.forCompaction); err != nil { 127 // Make sure we don't treat the data as valid next time. 128 r.readahead.data = r.readahead.data[:0] 129 return err 130 } 131 copy(p, r.readahead.data) 132 return nil 133 } 134 135 return r.readable.readInternal(ctx, p, offset, r.forCompaction) 136 } 137 138 func (r *remoteReadHandle) maybeReadahead(offset int64, len int) int { 139 if r.forCompaction { 140 return remoteMaxReadaheadSize 141 } 142 return int(r.readahead.state.maybeReadahead(offset, int64(len))) 143 } 144 145 // Close is part of the objstorage.ReadHandle interface. 146 func (r *remoteReadHandle) Close() error { 147 r.readable = nil 148 r.readahead.data = nil 149 return nil 150 } 151 152 // SetupForCompaction is part of the objstorage.ReadHandle interface. 153 func (r *remoteReadHandle) SetupForCompaction() { 154 r.forCompaction = true 155 } 156 157 // RecordCacheHit is part of the objstorage.ReadHandle interface. 158 func (r *remoteReadHandle) RecordCacheHit(_ context.Context, offset, size int64) { 159 if !r.forCompaction { 160 r.readahead.state.recordCacheHit(offset, size) 161 } 162 }