github.com/pingcap/badger@v1.5.1-0.20230103063557-828f39b09b6d/writer.go (about) 1 /* 2 * Copyright 2017 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package badger 18 19 import ( 20 "os" 21 "runtime" 22 "time" 23 24 "github.com/pingcap/badger/epoch" 25 "github.com/pingcap/badger/fileutil" 26 "github.com/pingcap/badger/table/memtable" 27 "github.com/pingcap/badger/y" 28 "github.com/pingcap/log" 29 "go.uber.org/zap" 30 ) 31 32 type writeWorker struct { 33 *DB 34 writeLSMCh chan postLogTask 35 mergeLSMCh chan mergeLSMTask 36 flushCh chan postLogTask 37 } 38 39 type mergeLSMTask struct { 40 mt *memtable.Table 41 guard *epoch.Guard 42 } 43 44 type postLogTask struct { 45 logFile *os.File 46 reqs []*request 47 } 48 49 func startWriteWorker(db *DB) *y.Closer { 50 numWorkers := 3 51 if db.opt.SyncWrites { 52 numWorkers += 1 53 } 54 closer := y.NewCloser(numWorkers) 55 w := &writeWorker{ 56 DB: db, 57 writeLSMCh: make(chan postLogTask, 1), 58 mergeLSMCh: make(chan mergeLSMTask, 1), 59 flushCh: make(chan postLogTask), 60 } 61 if db.opt.SyncWrites { 62 go w.runFlusher(closer) 63 } 64 go w.runWriteVLog(closer) 65 go w.runWriteLSM(closer) 66 go w.runMergeLSM(closer) 67 return closer 68 } 69 70 func (w *writeWorker) runFlusher(lc *y.Closer) { 71 defer lc.Done() 72 for { 73 select { 74 case t := <-w.flushCh: 75 start := time.Now() 76 err := fileutil.Fdatasync(t.logFile) 77 w.metrics.VlogSyncDuration.Observe(time.Since(start).Seconds()) 78 if err != nil { 79 w.done(t.reqs, err) 80 continue 81 } 82 w.writeLSMCh <- t 83 case <-lc.HasBeenClosed(): 84 close(w.writeLSMCh) 85 return 86 } 87 } 88 } 89 90 func (w *writeWorker) runWriteVLog(lc *y.Closer) { 91 defer lc.Done() 92 for { 93 var r *request 94 select { 95 case task := <-w.ingestCh: 96 w.ingestTables(task) 97 case r = <-w.writeCh: 98 reqs := make([]*request, len(w.writeCh)+1) 99 reqs[0] = r 100 w.pollWriteCh(reqs[1:]) 101 if err := w.writeVLog(reqs); err != nil { 102 return 103 } 104 case <-lc.HasBeenClosed(): 105 w.closeWriteVLog() 106 return 107 } 108 } 109 } 110 111 func (w *writeWorker) pollWriteCh(buf []*request) []*request { 112 for i := 0; i < len(buf); i++ { 113 buf[i] = <-w.writeCh 114 } 115 return buf 116 } 117 118 func (w *writeWorker) writeVLog(reqs []*request) error { 119 if !w.volatileMode { 120 if err := w.vlog.write(reqs); err != nil { 121 w.done(reqs, err) 122 return err 123 } 124 } 125 t := postLogTask{ 126 logFile: w.vlog.currentLogFile().fd, 127 reqs: reqs, 128 } 129 if w.opt.SyncWrites && !w.volatileMode { 130 w.flushCh <- t 131 } else { 132 w.writeLSMCh <- t 133 } 134 return nil 135 } 136 137 func (w *writeWorker) runWriteLSM(lc *y.Closer) { 138 defer lc.Done() 139 runtime.LockOSThread() 140 for { 141 t, ok := <-w.writeLSMCh 142 if !ok { 143 close(w.mergeLSMCh) 144 return 145 } 146 start := time.Now() 147 w.writeLSM(t.reqs) 148 w.metrics.WriteLSMDuration.Observe(time.Since(start).Seconds()) 149 } 150 } 151 152 func (w *writeWorker) runMergeLSM(lc *y.Closer) { 153 defer lc.Done() 154 for task := range w.mergeLSMCh { 155 task.mt.MergeListToSkl() 156 task.guard.Done() 157 } 158 } 159 160 func (w *writeWorker) closeWriteVLog() { 161 close(w.writeCh) 162 var reqs []*request 163 for r := range w.writeCh { // Flush the channel. 164 reqs = append(reqs, r) 165 } 166 var err error 167 if !w.volatileMode { 168 err = w.vlog.write(reqs) 169 } 170 if err != nil { 171 w.done(reqs, err) 172 } else { 173 err = w.vlog.curWriter.Sync() 174 // The store is closed, we don't need to write LSM. 175 w.done(reqs, err) 176 } 177 if !w.opt.SyncWrites { 178 close(w.writeLSMCh) 179 } else { 180 // The channel would be closed by the flusher. 181 } 182 } 183 184 // writeLSM is called serially by only one goroutine. 185 func (w *writeWorker) writeLSM(reqs []*request) { 186 if len(reqs) == 0 { 187 return 188 } 189 var count int 190 for _, b := range reqs { 191 if len(b.Entries) == 0 { 192 continue 193 } 194 count += len(b.Entries) 195 if err := w.writeToLSM(b.Entries); err != nil { 196 w.done(reqs, err) 197 return 198 } 199 } 200 201 w.done(reqs, nil) 202 log.Debug("entries written", zap.Int("count", count)) 203 return 204 } 205 206 func (w *writeWorker) done(reqs []*request, err error) { 207 for _, r := range reqs { 208 r.Err = err 209 r.Wg.Done() 210 } 211 if err != nil { 212 log.Warn("handle requests failed", zap.Int("count", len(reqs)), zap.Error(err)) 213 } 214 } 215 216 func newEntry(entry *Entry) memtable.Entry { 217 return memtable.Entry{ 218 Key: entry.Key.UserKey, 219 Value: y.ValueStruct{ 220 Value: entry.Value, 221 Meta: entry.meta, 222 UserMeta: entry.UserMeta, 223 Version: entry.Key.Version, 224 }, 225 } 226 } 227 228 func (w *writeWorker) writeToLSM(entries []*Entry) error { 229 mTbls := w.mtbls.Load().(*memTables) 230 for len(entries) != 0 { 231 e := newEntry(entries[0]) 232 free := w.ensureRoomForWrite(mTbls.getMutable(), e.EstimateSize()) 233 if free == w.opt.MaxMemTableSize { 234 mTbls = w.mtbls.Load().(*memTables) 235 } 236 237 es := make([]memtable.Entry, 0, len(entries)) 238 var i int 239 for i = 0; i < len(entries); i++ { 240 entry := entries[i] 241 if entry.meta&bitFinTxn != 0 { 242 continue 243 } 244 245 e := newEntry(entry) 246 if free < e.EstimateSize() { 247 break 248 } 249 free -= e.EstimateSize() 250 es = append(es, e) 251 } 252 w.updateOffset(entries[i-1].logOffset) 253 entries = entries[i:] 254 255 mTbls.getMutable().PutToPendingList(es) 256 w.mergeLSMCh <- mergeLSMTask{ 257 mt: mTbls.getMutable(), 258 guard: w.resourceMgr.Acquire(), 259 } 260 } 261 262 return nil 263 }