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  }