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