github.com/pingcap/badger@v1.5.1-0.20230103063557-828f39b09b6d/level_handler.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  	"fmt"
    21  	"sort"
    22  	"strings"
    23  	"sync"
    24  
    25  	"github.com/pingcap/badger/epoch"
    26  	"github.com/pingcap/badger/table"
    27  	"github.com/pingcap/badger/y"
    28  	"github.com/pingcap/errors"
    29  	"github.com/pingcap/log"
    30  	"go.uber.org/zap"
    31  )
    32  
    33  type levelHandler struct {
    34  	// Guards tables, totalSize.
    35  	sync.RWMutex
    36  
    37  	// For level >= 1, tables are sorted by key ranges, which do not overlap.
    38  	// For level 0, tables are sorted by time.
    39  	// For level 0, newest table are at the back. Compact the oldest one first, which is at the front.
    40  	tables    []table.Table
    41  	totalSize int64
    42  
    43  	// The following are initialized once and const.
    44  	level        int
    45  	strLevel     string
    46  	maxTotalSize int64
    47  	db           *DB
    48  	metrics      *y.LevelMetricsSet
    49  }
    50  
    51  func (s *levelHandler) getTotalSize() int64 {
    52  	s.RLock()
    53  	defer s.RUnlock()
    54  	return s.totalSize
    55  }
    56  
    57  // initTables replaces s.tables with given tables. This is done during loading.
    58  func (s *levelHandler) initTables(tables []table.Table) {
    59  	s.Lock()
    60  	defer s.Unlock()
    61  
    62  	s.tables = tables
    63  	s.totalSize = 0
    64  	for _, t := range tables {
    65  		s.totalSize += t.Size()
    66  	}
    67  
    68  	if s.level == 0 {
    69  		// Key range will overlap. Just sort by fileID in ascending order
    70  		// because newer tables are at the end of level 0.
    71  		sort.Slice(s.tables, func(i, j int) bool {
    72  			return s.tables[i].ID() < s.tables[j].ID()
    73  		})
    74  	} else {
    75  		// Sort tables by keys.
    76  		sortTables(s.tables)
    77  	}
    78  }
    79  
    80  // deleteTables remove tables idx0, ..., idx1-1.
    81  func (s *levelHandler) deleteTables(toDel []table.Table, guard *epoch.Guard, isMove bool) {
    82  	s.Lock() // s.Unlock() below
    83  
    84  	toDelMap := make(map[uint64]struct{})
    85  	for _, t := range toDel {
    86  		toDelMap[t.ID()] = struct{}{}
    87  	}
    88  
    89  	// Make a copy as iterators might be keeping a slice of tables.
    90  	var newTables []table.Table
    91  	for _, t := range s.tables {
    92  		_, found := toDelMap[t.ID()]
    93  		if !found {
    94  			newTables = append(newTables, t)
    95  			continue
    96  		}
    97  		s.totalSize -= t.Size()
    98  	}
    99  	s.tables = newTables
   100  
   101  	assertTablesOrder(s.level, newTables, nil)
   102  	s.Unlock()
   103  
   104  	if !isMove {
   105  		del := make([]epoch.Resource, len(toDel))
   106  		for i := range toDel {
   107  			del[i] = toDel[i]
   108  		}
   109  		guard.Delete(del)
   110  	}
   111  }
   112  
   113  func assertTablesOrder(level int, tables []table.Table, cd *CompactDef) {
   114  	if level == 0 {
   115  		return
   116  	}
   117  
   118  	for i := 0; i < len(tables)-1; i++ {
   119  		if tables[i].Smallest().Compare(tables[i].Biggest()) > 0 ||
   120  			tables[i].Smallest().Compare(tables[i+1].Smallest()) >= 0 ||
   121  			tables[i].Biggest().Compare(tables[i+1].Biggest()) >= 0 {
   122  
   123  			var sb strings.Builder
   124  			if cd != nil {
   125  				fmt.Fprintf(&sb, "%s\n", cd)
   126  			}
   127  			fmt.Fprintf(&sb, "the order of level %d tables is invalid:\n", level)
   128  			for idx, tbl := range tables {
   129  				tag := "  "
   130  				if idx == i {
   131  					tag = "->"
   132  				}
   133  				fmt.Fprintf(&sb, "%s %v %v\n", tag, tbl.Smallest(), tbl.Biggest())
   134  			}
   135  			panic(sb.String())
   136  		}
   137  	}
   138  }
   139  
   140  func sortTables(tables []table.Table) {
   141  	sort.Slice(tables, func(i, j int) bool {
   142  		return tables[i].Smallest().Compare(tables[j].Smallest()) < 0
   143  	})
   144  }
   145  
   146  // replaceTables will replace tables[left:right] with newTables. Note this EXCLUDES tables[right].
   147  // You must call decr() to delete the old tables _after_ writing the update to the manifest.
   148  func (s *levelHandler) replaceTables(newTables []table.Table, cd *CompactDef, guard *epoch.Guard) {
   149  	// Do not return even if len(newTables) is 0 because we need to delete bottom tables.
   150  	assertTablesOrder(s.level, newTables, cd)
   151  
   152  	s.Lock() // We s.Unlock() below.
   153  
   154  	// Increase totalSize first.
   155  	for _, tbl := range newTables {
   156  		s.totalSize += tbl.Size()
   157  	}
   158  	left, right := s.overlappingTables(levelHandlerRLocked{}, cd.nextRange)
   159  	toDelete := make([]epoch.Resource, 0, right-left)
   160  	// Update totalSize and reference counts.
   161  	for i := left; i < right; i++ {
   162  		tbl := s.tables[i]
   163  		if containsTable(cd.Bot, tbl) {
   164  			s.totalSize -= tbl.Size()
   165  			toDelete = append(toDelete, tbl)
   166  		}
   167  	}
   168  	tables := make([]table.Table, 0, left+len(newTables)+len(cd.SkippedTbls)+(len(s.tables)-right))
   169  	tables = append(tables, s.tables[:left]...)
   170  	tables = append(tables, newTables...)
   171  	tables = append(tables, cd.SkippedTbls...)
   172  	tables = append(tables, s.tables[right:]...)
   173  	sortTables(tables)
   174  	assertTablesOrder(s.level, tables, cd)
   175  	s.tables = tables
   176  	s.Unlock()
   177  	guard.Delete(toDelete)
   178  }
   179  
   180  func containsTable(tables []table.Table, tbl table.Table) bool {
   181  	for _, t := range tables {
   182  		if tbl == t {
   183  			return true
   184  		}
   185  	}
   186  	return false
   187  }
   188  
   189  func newLevelHandler(db *DB, level int) *levelHandler {
   190  	label := fmt.Sprintf("L%d", level)
   191  	return &levelHandler{
   192  		level:    level,
   193  		strLevel: label,
   194  		db:       db,
   195  		metrics:  db.metrics.NewLevelMetricsSet(label),
   196  	}
   197  }
   198  
   199  // tryAddLevel0Table returns true if ok and no stalling.
   200  func (s *levelHandler) tryAddLevel0Table(t table.Table) bool {
   201  	y.Assert(s.level == 0)
   202  	// Need lock as we may be deleting the first table during a level 0 compaction.
   203  	s.Lock()
   204  	defer s.Unlock()
   205  	// Return false only if number of tables is more than number of
   206  	// ZeroTableStall. For on disk L0, we should just add the tables to the level.
   207  	if len(s.tables) >= s.db.opt.NumLevelZeroTablesStall {
   208  		return false
   209  	}
   210  
   211  	s.tables = append(s.tables, t)
   212  	s.totalSize += t.Size()
   213  
   214  	return true
   215  }
   216  
   217  func (s *levelHandler) addTable(t table.Table) {
   218  	s.Lock()
   219  	defer s.Unlock()
   220  
   221  	s.totalSize += t.Size()
   222  	if s.level == 0 {
   223  		s.tables = append(s.tables, t)
   224  		return
   225  	}
   226  
   227  	i := sort.Search(len(s.tables), func(i int) bool {
   228  		return s.tables[i].Smallest().Compare(t.Biggest()) >= 0
   229  	})
   230  	if i == len(s.tables) {
   231  		s.tables = append(s.tables, t)
   232  	} else {
   233  		s.tables = append(s.tables[:i+1], s.tables[i:]...)
   234  		s.tables[i] = t
   235  	}
   236  }
   237  
   238  func (s *levelHandler) numTables() int {
   239  	s.RLock()
   240  	defer s.RUnlock()
   241  	return len(s.tables)
   242  }
   243  
   244  func (s *levelHandler) close() error {
   245  	s.RLock()
   246  	defer s.RUnlock()
   247  	var err error
   248  	for _, t := range s.tables {
   249  		if closeErr := t.Close(); closeErr != nil && err == nil {
   250  			err = closeErr
   251  		}
   252  	}
   253  	return errors.Wrap(err, "levelHandler.close")
   254  }
   255  
   256  // getTablesForKey acquires a read-lock to access s.tables. It returns a list of tables.
   257  func (s *levelHandler) getTablesForKey(key y.Key) []table.Table {
   258  	s.RLock()
   259  	defer s.RUnlock()
   260  
   261  	if s.level == 0 {
   262  		return s.getLevel0Tables()
   263  	}
   264  	tbl := s.getLevelNTable(key)
   265  	if tbl == nil {
   266  		return nil
   267  	}
   268  	return []table.Table{tbl}
   269  }
   270  
   271  // getTablesForKeys returns tables for pairs.
   272  // level0 returns all tables.
   273  // level1+ returns tables for every key.
   274  func (s *levelHandler) getTablesForKeys(pairs []keyValuePair) []table.Table {
   275  	s.RLock()
   276  	defer s.RUnlock()
   277  	if s.level == 0 {
   278  		return s.getLevel0Tables()
   279  	}
   280  	out := make([]table.Table, len(pairs))
   281  	for i, pair := range pairs {
   282  		out[i] = s.getLevelNTable(pair.key)
   283  	}
   284  	return out
   285  }
   286  
   287  func (s *levelHandler) getLevel0Tables() []table.Table {
   288  	// For level 0, we need to check every table. Remember to make a copy as s.tables may change
   289  	// once we exit this function, and we don't want to lock s.tables while seeking in tables.
   290  	// CAUTION: Reverse the tables.
   291  	out := make([]table.Table, 0, len(s.tables))
   292  	for i := len(s.tables) - 1; i >= 0; i-- {
   293  		out = append(out, s.tables[i])
   294  	}
   295  	return out
   296  }
   297  
   298  func (s *levelHandler) getLevelNTable(key y.Key) table.Table {
   299  	// For level >= 1, we can do a binary search as key range does not overlap.
   300  	idx := sort.Search(len(s.tables), func(i int) bool {
   301  		return s.tables[i].Biggest().Compare(key) >= 0
   302  	})
   303  	if idx >= len(s.tables) {
   304  		// Given key is strictly > than every element we have.
   305  		return nil
   306  	}
   307  	tbl := s.tables[idx]
   308  	return tbl
   309  }
   310  
   311  // get returns value for a given key or the key after that. If not found, return nil.
   312  func (s *levelHandler) get(key y.Key, keyHash uint64) y.ValueStruct {
   313  	tables := s.getTablesForKey(key)
   314  	return s.getInTables(key, keyHash, tables)
   315  }
   316  
   317  func (s *levelHandler) getInTables(key y.Key, keyHash uint64, tables []table.Table) y.ValueStruct {
   318  	for _, table := range tables {
   319  		result := s.getInTable(key, keyHash, table)
   320  		if result.Valid() {
   321  			return result
   322  		}
   323  	}
   324  	return y.ValueStruct{}
   325  }
   326  
   327  func (s *levelHandler) getInTable(key y.Key, keyHash uint64, table table.Table) y.ValueStruct {
   328  	s.metrics.NumLSMGets.Inc()
   329  	// TODO: error handling here
   330  	result, err := table.Get(key, keyHash)
   331  	if err != nil {
   332  		log.Error("get data in table failed", zap.Error(err))
   333  	}
   334  	if !result.Valid() {
   335  		s.metrics.NumLSMBloomFalsePositive.Inc()
   336  	}
   337  	return result
   338  }
   339  
   340  func (s *levelHandler) multiGet(pairs []keyValuePair) {
   341  	tables := s.getTablesForKeys(pairs)
   342  	if s.level == 0 {
   343  		s.multiGetLevel0(pairs, tables)
   344  	} else {
   345  		s.multiGetLevelN(pairs, tables)
   346  	}
   347  }
   348  
   349  func (s *levelHandler) multiGetLevel0(pairs []keyValuePair, tables []table.Table) {
   350  	for _, table := range tables {
   351  		for i := range pairs {
   352  			pair := &pairs[i]
   353  			if pair.found {
   354  				continue
   355  			}
   356  			if pair.key.Compare(table.Smallest()) < 0 || pair.key.Compare(table.Biggest()) > 0 {
   357  				continue
   358  			}
   359  			for {
   360  				val := s.getInTable(pair.key, pair.hash, table)
   361  				if val.Valid() {
   362  					pair.val = val
   363  					pair.found = true
   364  				}
   365  				break
   366  			}
   367  		}
   368  	}
   369  }
   370  
   371  func (s *levelHandler) multiGetLevelN(pairs []keyValuePair, tables []table.Table) {
   372  	for i := range pairs {
   373  		pair := &pairs[i]
   374  		if pair.found {
   375  			continue
   376  		}
   377  		table := tables[i]
   378  		if table == nil {
   379  			continue
   380  		}
   381  		for {
   382  			val := s.getInTable(pair.key, pair.hash, table)
   383  			if val.Valid() {
   384  				pair.val = val
   385  				pair.found = true
   386  			}
   387  			break
   388  		}
   389  	}
   390  }
   391  
   392  // appendIterators appends iterators to an array of iterators, for merging.
   393  // Note: This obtains references for the table handlers. Remember to close these iterators.
   394  func (s *levelHandler) appendIterators(iters []y.Iterator, opts *IteratorOptions) []y.Iterator {
   395  	s.RLock()
   396  	defer s.RUnlock()
   397  
   398  	if s.level == 0 {
   399  		// Remember to add in reverse order!
   400  		// The newer table at the end of s.tables should be added first as it takes precedence.
   401  		overlapTables := make([]table.Table, 0, len(s.tables))
   402  		for _, t := range s.tables {
   403  			if opts.OverlapTable(t) {
   404  				overlapTables = append(overlapTables, t)
   405  			}
   406  		}
   407  		return appendIteratorsReversed(iters, overlapTables, opts.Reverse)
   408  	}
   409  	overlapTables := opts.OverlapTables(s.tables)
   410  	if len(overlapTables) == 0 {
   411  		return iters
   412  	}
   413  	return append(iters, table.NewConcatIterator(overlapTables, opts.Reverse))
   414  }
   415  
   416  type levelHandlerRLocked struct{}
   417  
   418  // overlappingTables returns the tables that intersect with key range. Returns a half-interval.
   419  // This function should already have acquired a read lock, and this is so important the caller must
   420  // pass an empty parameter declaring such.
   421  func (s *levelHandler) overlappingTables(_ levelHandlerRLocked, kr keyRange) (int, int) {
   422  	return getTablesInRange(s.tables, kr.left, kr.right)
   423  }
   424  
   425  func getTablesInRange(tbls []table.Table, start, end y.Key) (int, int) {
   426  	left := sort.Search(len(tbls), func(i int) bool {
   427  		return start.Compare(tbls[i].Biggest()) <= 0
   428  	})
   429  	right := sort.Search(len(tbls), func(i int) bool {
   430  		return end.Compare(tbls[i].Smallest()) < 0
   431  	})
   432  	return left, right
   433  }