github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/flushable.go (about) 1 // Copyright 2020 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 pebble 6 7 import ( 8 "context" 9 "fmt" 10 "sync/atomic" 11 "time" 12 13 "github.com/cockroachdb/pebble/internal/keyspan" 14 "github.com/cockroachdb/pebble/internal/manifest" 15 ) 16 17 // flushable defines the interface for immutable memtables. 18 type flushable interface { 19 newIter(o *IterOptions) internalIterator 20 newFlushIter(o *IterOptions, bytesFlushed *uint64) internalIterator 21 newRangeDelIter(o *IterOptions) keyspan.FragmentIterator 22 newRangeKeyIter(o *IterOptions) keyspan.FragmentIterator 23 containsRangeKeys() bool 24 // inuseBytes returns the number of inuse bytes by the flushable. 25 inuseBytes() uint64 26 // totalBytes returns the total number of bytes allocated by the flushable. 27 totalBytes() uint64 28 // readyForFlush returns true when the flushable is ready for flushing. See 29 // memTable.readyForFlush for one implementation which needs to check whether 30 // there are any outstanding write references. 31 readyForFlush() bool 32 } 33 34 // flushableEntry wraps a flushable and adds additional metadata and 35 // functionality that is common to all flushables. 36 type flushableEntry struct { 37 flushable 38 // Channel which is closed when the flushable has been flushed. 39 flushed chan struct{} 40 // flushForced indicates whether a flush was forced on this memtable (either 41 // manual, or due to ingestion). Protected by DB.mu. 42 flushForced bool 43 // delayedFlushForcedAt indicates whether a timer has been set to force a 44 // flush on this memtable at some point in the future. Protected by DB.mu. 45 // Holds the timestamp of when the flush will be issued. 46 delayedFlushForcedAt time.Time 47 // logNum corresponds to the WAL that contains the records present in the 48 // receiver. 49 logNum FileNum 50 // logSize is the size in bytes of the associated WAL. Protected by DB.mu. 51 logSize uint64 52 // The current logSeqNum at the time the memtable was created. This is 53 // guaranteed to be less than or equal to any seqnum stored in the memtable. 54 logSeqNum uint64 55 // readerRefs tracks the read references on the flushable. The two sources of 56 // reader references are DB.mu.mem.queue and readState.memtables. The memory 57 // reserved by the flushable in the cache is released when the reader refs 58 // drop to zero. If the flushable is referencing sstables, then the file 59 // refount is also decreased once the reader refs drops to 0. If the 60 // flushable is a memTable, when the reader refs drops to zero, the writer 61 // refs will already be zero because the memtable will have been flushed and 62 // that only occurs once the writer refs drops to zero. 63 readerRefs atomic.Int32 64 // Closure to invoke to release memory accounting. 65 releaseMemAccounting func() 66 // unrefFiles, if not nil, should be invoked to decrease the ref count of 67 // files which are backing the flushable. 68 unrefFiles func() []*fileBacking 69 // deleteFnLocked should be called if the caller is holding DB.mu. 70 deleteFnLocked func(obsolete []*fileBacking) 71 // deleteFn should be called if the caller is not holding DB.mu. 72 deleteFn func(obsolete []*fileBacking) 73 } 74 75 func (e *flushableEntry) readerRef() { 76 switch v := e.readerRefs.Add(1); { 77 case v <= 1: 78 panic(fmt.Sprintf("pebble: inconsistent reference count: %d", v)) 79 } 80 } 81 82 // db.mu must not be held when this is called. 83 func (e *flushableEntry) readerUnref(deleteFiles bool) { 84 e.readerUnrefHelper(deleteFiles, e.deleteFn) 85 } 86 87 // db.mu must be held when this is called. 88 func (e *flushableEntry) readerUnrefLocked(deleteFiles bool) { 89 e.readerUnrefHelper(deleteFiles, e.deleteFnLocked) 90 } 91 92 func (e *flushableEntry) readerUnrefHelper( 93 deleteFiles bool, deleteFn func(obsolete []*fileBacking), 94 ) { 95 switch v := e.readerRefs.Add(-1); { 96 case v < 0: 97 panic(fmt.Sprintf("pebble: inconsistent reference count: %d", v)) 98 case v == 0: 99 if e.releaseMemAccounting == nil { 100 panic("pebble: memtable reservation already released") 101 } 102 e.releaseMemAccounting() 103 e.releaseMemAccounting = nil 104 if e.unrefFiles != nil { 105 obsolete := e.unrefFiles() 106 e.unrefFiles = nil 107 if deleteFiles { 108 deleteFn(obsolete) 109 } 110 } 111 } 112 } 113 114 type flushableList []*flushableEntry 115 116 // ingestedFlushable is the implementation of the flushable interface for the 117 // ingesting sstables which are added to the flushable list. 118 type ingestedFlushable struct { 119 files []physicalMeta 120 comparer *Comparer 121 newIters tableNewIters 122 newRangeKeyIters keyspan.TableNewSpanIter 123 124 // Since the level slice is immutable, we construct and set it once. It 125 // should be safe to read from slice in future reads. 126 slice manifest.LevelSlice 127 // hasRangeKeys is set on ingestedFlushable construction. 128 hasRangeKeys bool 129 } 130 131 func newIngestedFlushable( 132 files []*fileMetadata, 133 comparer *Comparer, 134 newIters tableNewIters, 135 newRangeKeyIters keyspan.TableNewSpanIter, 136 ) *ingestedFlushable { 137 var physicalFiles []physicalMeta 138 var hasRangeKeys bool 139 for _, f := range files { 140 if f.HasRangeKeys { 141 hasRangeKeys = true 142 } 143 physicalFiles = append(physicalFiles, f.PhysicalMeta()) 144 } 145 146 ret := &ingestedFlushable{ 147 files: physicalFiles, 148 comparer: comparer, 149 newIters: newIters, 150 newRangeKeyIters: newRangeKeyIters, 151 // slice is immutable and can be set once and used many times. 152 slice: manifest.NewLevelSliceKeySorted(comparer.Compare, files), 153 hasRangeKeys: hasRangeKeys, 154 } 155 156 return ret 157 } 158 159 // TODO(sumeer): ingestedFlushable iters also need to plumb context for 160 // tracing. 161 162 // newIter is part of the flushable interface. 163 func (s *ingestedFlushable) newIter(o *IterOptions) internalIterator { 164 var opts IterOptions 165 if o != nil { 166 opts = *o 167 } 168 // TODO(bananabrick): The manifest.Level in newLevelIter is only used for 169 // logging. Update the manifest.Level encoding to account for levels which 170 // aren't truly levels in the lsm. Right now, the encoding only supports 171 // L0 sublevels, and the rest of the levels in the lsm. 172 return newLevelIter( 173 opts, s.comparer, s.newIters, s.slice.Iter(), manifest.Level(0), internalIterOpts{}, 174 ) 175 } 176 177 // newFlushIter is part of the flushable interface. 178 func (s *ingestedFlushable) newFlushIter(o *IterOptions, bytesFlushed *uint64) internalIterator { 179 // newFlushIter is only used for writing memtables to disk as sstables. 180 // Since ingested sstables are already present on disk, they don't need to 181 // make use of a flush iter. 182 panic("pebble: not implemented") 183 } 184 185 func (s *ingestedFlushable) constructRangeDelIter( 186 file *manifest.FileMetadata, _ keyspan.SpanIterOptions, 187 ) (keyspan.FragmentIterator, error) { 188 // Note that the keyspan level iter expects a non-nil iterator to be 189 // returned even if there is an error. So, we return the emptyKeyspanIter. 190 iter, rangeDelIter, err := s.newIters(context.Background(), file, nil, internalIterOpts{}) 191 if err != nil { 192 return emptyKeyspanIter, err 193 } 194 iter.Close() 195 if rangeDelIter == nil { 196 return emptyKeyspanIter, nil 197 } 198 return rangeDelIter, nil 199 } 200 201 // newRangeDelIter is part of the flushable interface. 202 // TODO(bananabrick): Using a level iter instead of a keyspan level iter to 203 // surface range deletes is more efficient. 204 func (s *ingestedFlushable) newRangeDelIter(_ *IterOptions) keyspan.FragmentIterator { 205 return keyspan.NewLevelIter( 206 keyspan.SpanIterOptions{}, s.comparer.Compare, 207 s.constructRangeDelIter, s.slice.Iter(), manifest.Level(0), 208 manifest.KeyTypePoint, 209 ) 210 } 211 212 // newRangeKeyIter is part of the flushable interface. 213 func (s *ingestedFlushable) newRangeKeyIter(o *IterOptions) keyspan.FragmentIterator { 214 if !s.containsRangeKeys() { 215 return nil 216 } 217 218 return keyspan.NewLevelIter( 219 keyspan.SpanIterOptions{}, s.comparer.Compare, s.newRangeKeyIters, 220 s.slice.Iter(), manifest.Level(0), manifest.KeyTypeRange, 221 ) 222 } 223 224 // containsRangeKeys is part of the flushable interface. 225 func (s *ingestedFlushable) containsRangeKeys() bool { 226 return s.hasRangeKeys 227 } 228 229 // inuseBytes is part of the flushable interface. 230 func (s *ingestedFlushable) inuseBytes() uint64 { 231 // inuseBytes is only used when memtables are flushed to disk as sstables. 232 panic("pebble: not implemented") 233 } 234 235 // totalBytes is part of the flushable interface. 236 func (s *ingestedFlushable) totalBytes() uint64 { 237 // We don't allocate additional bytes for the ingestedFlushable. 238 return 0 239 } 240 241 // readyForFlush is part of the flushable interface. 242 func (s *ingestedFlushable) readyForFlush() bool { 243 // ingestedFlushable should always be ready to flush. However, note that 244 // memtables before the ingested sstables in the memtable queue must be 245 // flushed before an ingestedFlushable can be flushed. This is because the 246 // ingested sstables need an updated view of the Version to 247 // determine where to place the files in the lsm. 248 return true 249 }