Files

343 lines
9.2 KiB
Go
Raw Permalink Normal View History

2017-05-29 19:35:23 +10:00
/*
* SPDX-FileCopyrightText: © 2017-2025 Istari Digital, Inc.
2025-02-05 17:00:32 -05:00
* SPDX-License-Identifier: Apache-2.0
2017-05-29 19:35:23 +10:00
*/
package badger
import (
2017-07-25 17:37:27 +10:00
"fmt"
2017-05-29 19:35:23 +10:00
"sort"
2017-09-08 12:53:04 -07:00
"sync"
2017-05-29 19:35:23 +10:00
"github.com/luxfi/zapdb/table"
"github.com/luxfi/zapdb/y"
2017-05-29 19:35:23 +10:00
)
type levelHandler struct {
// Guards tables, totalSize.
2017-09-08 12:53:04 -07:00
sync.RWMutex
2017-05-29 19:35:23 +10:00
// For level >= 1, tables are sorted by key ranges, which do not overlap.
// For level 0, tables are sorted by time.
// For level 0, newest table are at the back. Compact the oldest one first, which is at the front.
tables []*table.Table
totalSize int64
totalStaleSize int64
2017-05-29 19:35:23 +10:00
// The following are initialized once and const.
level int
strLevel string
db *DB
2017-05-29 19:35:23 +10:00
}
func (s *levelHandler) isLastLevel() bool {
return s.level == s.db.opt.MaxLevels-1
}
func (s *levelHandler) getTotalStaleSize() int64 {
s.RLock()
defer s.RUnlock()
return s.totalStaleSize
}
2017-05-29 19:35:23 +10:00
func (s *levelHandler) getTotalSize() int64 {
s.RLock()
defer s.RUnlock()
return s.totalSize
}
// initTables replaces s.tables with given tables. This is done during loading.
func (s *levelHandler) initTables(tables []*table.Table) {
s.Lock()
defer s.Unlock()
s.tables = tables
s.totalSize = 0
s.totalStaleSize = 0
2017-05-29 19:35:23 +10:00
for _, t := range tables {
s.addSize(t)
2017-05-29 19:35:23 +10:00
}
if s.level == 0 {
// Key range will overlap. Just sort by fileID in ascending order
// because newer tables are at the end of level 0.
sort.Slice(s.tables, func(i, j int) bool {
return s.tables[i].ID() < s.tables[j].ID()
})
} else {
// Sort tables by keys.
sort.Slice(s.tables, func(i, j int) bool {
2017-10-04 10:53:02 +11:00
return y.CompareKeys(s.tables[i].Smallest(), s.tables[j].Smallest()) < 0
2017-05-29 19:35:23 +10:00
})
}
}
// deleteTables remove tables idx0, ..., idx1-1.
2017-07-30 21:13:47 -07:00
func (s *levelHandler) deleteTables(toDel []*table.Table) error {
s.Lock() // s.Unlock() below
2017-05-30 20:04:50 +10:00
2017-05-29 19:35:23 +10:00
toDelMap := make(map[uint64]struct{})
for _, t := range toDel {
toDelMap[t.ID()] = struct{}{}
}
2017-07-30 21:13:47 -07:00
2017-05-29 19:35:23 +10:00
// Make a copy as iterators might be keeping a slice of tables.
var newTables []*table.Table
for _, t := range s.tables {
_, found := toDelMap[t.ID()]
if !found {
newTables = append(newTables, t)
continue
}
s.subtractSize(t)
2017-05-29 19:35:23 +10:00
}
s.tables = newTables
2017-07-30 21:13:47 -07:00
s.Unlock() // Unlock s _before_ we DecrRef our tables, which can be slow.
return decrRefs(toDel)
2017-05-29 19:35:23 +10:00
}
// replaceTables will replace tables[left:right] with newTables. Note this EXCLUDES tables[right].
2017-07-30 21:13:47 -07:00
// You must call decr() to delete the old tables _after_ writing the update to the manifest.
2019-02-22 13:19:55 -08:00
func (s *levelHandler) replaceTables(toDel, toAdd []*table.Table) error {
2017-07-30 21:13:47 -07:00
// Need to re-search the range of tables in this level to be replaced as other goroutines might
// be changing it as well. (They can't touch our tables, but if they add/remove other tables,
// the indices get shifted around.)
s.Lock() // We s.Unlock() below.
2019-02-22 13:19:55 -08:00
toDelMap := make(map[uint64]struct{})
for _, t := range toDel {
toDelMap[t.ID()] = struct{}{}
}
var newTables []*table.Table
for _, t := range s.tables {
_, found := toDelMap[t.ID()]
if !found {
newTables = append(newTables, t)
continue
}
s.subtractSize(t)
2019-02-22 13:19:55 -08:00
}
2017-05-29 19:35:23 +10:00
// Increase totalSize first.
2019-02-22 13:19:55 -08:00
for _, t := range toAdd {
s.addSize(t)
2019-02-22 13:19:55 -08:00
t.IncrRef()
newTables = append(newTables, t)
2017-05-29 19:35:23 +10:00
}
2019-02-22 13:19:55 -08:00
// Assign tables.
s.tables = newTables
sort.Slice(s.tables, func(i, j int) bool {
return y.CompareKeys(s.tables[i].Smallest(), s.tables[j].Smallest()) < 0
})
2017-07-30 21:13:47 -07:00
s.Unlock() // s.Unlock before we DecrRef tables -- that can be slow.
2019-02-22 13:19:55 -08:00
return decrRefs(toDel)
2017-07-30 21:13:47 -07:00
}
// addTable adds toAdd table to levelHandler. Normally when we add tables to levelHandler, we sort
// tables based on table.Smallest. This is required for correctness of the system. But in case of
// stream writer this can be avoided. We can just add tables to levelHandler's table list
// and after all addTable calls, we can sort table list(check sortTable method).
// NOTE: levelHandler.sortTables() should be called after call addTable calls are done.
func (s *levelHandler) addTable(t *table.Table) {
s.Lock()
defer s.Unlock()
s.addSize(t) // Increase totalSize first.
t.IncrRef()
s.tables = append(s.tables, t)
}
// sortTables sorts tables of levelHandler based on table.Smallest.
// Normally it should be called after all addTable calls.
func (s *levelHandler) sortTables() {
s.Lock()
defer s.Unlock()
sort.Slice(s.tables, func(i, j int) bool {
return y.CompareKeys(s.tables[i].Smallest(), s.tables[j].Smallest()) < 0
})
}
2017-07-30 21:13:47 -07:00
func decrRefs(tables []*table.Table) error {
for _, table := range tables {
if err := table.DecrRef(); err != nil {
return err
}
}
return nil
2017-05-29 19:35:23 +10:00
}
2017-10-04 21:55:56 +11:00
func newLevelHandler(db *DB, level int) *levelHandler {
2017-05-29 19:35:23 +10:00
return &levelHandler{
2017-07-25 17:37:27 +10:00
level: level,
strLevel: fmt.Sprintf("l%d", level),
2017-10-04 21:55:56 +11:00
db: db,
2017-05-29 19:35:23 +10:00
}
}
// tryAddLevel0Table returns true if ok and no stalling.
func (s *levelHandler) tryAddLevel0Table(t *table.Table) bool {
y.AssertTrue(s.level == 0)
// Need lock as we may be deleting the first table during a level 0 compaction.
s.Lock()
defer s.Unlock()
// Stall (by returning false) if we are above the specified stall setting for L0.
if len(s.tables) >= s.db.opt.NumLevelZeroTablesStall {
2017-05-29 19:35:23 +10:00
return false
}
s.tables = append(s.tables, t)
t.IncrRef()
s.addSize(t)
2017-05-29 19:35:23 +10:00
return true
}
// This should be called while holding the lock on the level.
func (s *levelHandler) addSize(t *table.Table) {
s.totalSize += t.Size()
s.totalStaleSize += int64(t.StaleDataSize())
}
// This should be called while holding the lock on the level.
func (s *levelHandler) subtractSize(t *table.Table) {
s.totalSize -= t.Size()
s.totalStaleSize -= int64(t.StaleDataSize())
}
2017-05-29 19:35:23 +10:00
func (s *levelHandler) numTables() int {
s.RLock()
defer s.RUnlock()
return len(s.tables)
}
2017-05-30 20:04:50 +10:00
func (s *levelHandler) close() error {
2017-05-29 19:35:23 +10:00
s.RLock()
defer s.RUnlock()
var err error
2017-05-29 19:35:23 +10:00
for _, t := range s.tables {
if closeErr := t.Close(-1); closeErr != nil && err == nil {
err = closeErr
2017-05-30 20:04:50 +10:00
}
2017-05-29 19:35:23 +10:00
}
return y.Wrap(err, "levelHandler.close")
2017-05-29 19:35:23 +10:00
}
// getTableForKey acquires a read-lock to access s.tables. It returns a list of tableHandlers.
2017-05-30 20:04:50 +10:00
func (s *levelHandler) getTableForKey(key []byte) ([]*table.Table, func() error) {
2017-05-29 19:35:23 +10:00
s.RLock()
defer s.RUnlock()
2017-05-30 20:04:50 +10:00
2017-05-29 19:35:23 +10:00
if s.level == 0 {
// For level 0, we need to check every table. Remember to make a copy as s.tables may change
// once we exit this function, and we don't want to lock s.tables while seeking in tables.
// CAUTION: Reverse the tables.
out := make([]*table.Table, 0, len(s.tables))
for i := len(s.tables) - 1; i >= 0; i-- {
out = append(out, s.tables[i])
s.tables[i].IncrRef()
}
2017-05-30 20:04:50 +10:00
return out, func() error {
2017-05-29 19:35:23 +10:00
for _, t := range out {
2017-05-30 20:04:50 +10:00
if err := t.DecrRef(); err != nil {
return err
}
2017-05-29 19:35:23 +10:00
}
2017-05-30 20:04:50 +10:00
return nil
2017-05-29 19:35:23 +10:00
}
}
// For level >= 1, we can do a binary search as key range does not overlap.
idx := sort.Search(len(s.tables), func(i int) bool {
2017-10-04 10:53:02 +11:00
return y.CompareKeys(s.tables[i].Biggest(), key) >= 0
2017-05-29 19:35:23 +10:00
})
if idx >= len(s.tables) {
// Given key is strictly > than every element we have.
2017-05-30 20:04:50 +10:00
return nil, func() error { return nil }
2017-05-29 19:35:23 +10:00
}
tbl := s.tables[idx]
tbl.IncrRef()
2017-05-30 20:04:50 +10:00
return []*table.Table{tbl}, tbl.DecrRef
2017-05-29 19:35:23 +10:00
}
2017-09-28 10:33:28 +10:00
// get returns value for a given key or the key after that. If not found, return nil.
2017-05-30 20:04:50 +10:00
func (s *levelHandler) get(key []byte) (y.ValueStruct, error) {
2017-05-29 19:35:23 +10:00
tables, decr := s.getTableForKey(key)
2017-10-02 18:59:58 +11:00
keyNoTs := y.ParseKey(key)
2017-05-30 20:04:50 +10:00
hash := y.Hash(keyNoTs)
var maxVs y.ValueStruct
2017-05-29 19:35:23 +10:00
for _, th := range tables {
2019-09-06 10:42:54 +05:30
if th.DoesNotHave(hash) {
y.NumLSMBloomHitsAdd(s.db.opt.MetricsEnabled, s.strLevel, 1)
2017-10-02 18:59:58 +11:00
continue
}
2017-05-30 20:04:50 +10:00
it := th.NewIterator(0)
2017-05-29 19:35:23 +10:00
defer it.Close()
2017-05-30 20:04:50 +10:00
y.NumLSMGetsAdd(s.db.opt.MetricsEnabled, s.strLevel, 1)
2017-05-29 19:35:23 +10:00
it.Seek(key)
if !it.Valid() {
continue
}
2017-09-28 10:33:28 +10:00
if y.SameKey(key, it.Key()) {
if version := y.ParseTs(it.Key()); maxVs.Version < version {
maxVs = it.ValueCopy()
maxVs.Version = version
}
2017-05-29 19:35:23 +10:00
}
}
return maxVs, decr()
2017-05-29 19:35:23 +10:00
}
// appendIterators appends iterators to an array of iterators, for merging.
// Note: This obtains references for the table handlers. Remember to close these iterators.
func (s *levelHandler) appendIterators(iters []y.Iterator, opt *IteratorOptions) []y.Iterator {
2017-05-29 19:35:23 +10:00
s.RLock()
defer s.RUnlock()
2017-05-30 20:04:50 +10:00
var topt int
if opt.Reverse {
topt = table.REVERSED
}
2017-05-29 19:35:23 +10:00
if s.level == 0 {
// Remember to add in reverse order!
// The newer table at the end of s.tables should be added first as it takes precedence.
2019-08-23 04:12:40 -07:00
// Level 0 tables are not in key sorted order, so we need to consider them one by one.
var out []*table.Table
for _, t := range s.tables {
if opt.pickTable(t) {
out = append(out, t)
}
}
return appendIteratorsReversed(iters, out, topt)
2019-08-23 04:12:40 -07:00
}
tables := opt.pickTables(s.tables)
if len(tables) == 0 {
return iters
2017-05-29 19:35:23 +10:00
}
return append(iters, table.NewConcatIterator(tables, topt))
2017-05-29 19:35:23 +10:00
}
2017-09-08 12:53:04 -07:00
type levelHandlerRLocked struct{}
2017-05-31 17:57:38 +10:00
2017-09-08 12:53:04 -07:00
// overlappingTables returns the tables that intersect with key range. Returns a half-interval.
// This function should already have acquired a read lock, and this is so important the caller must
// pass an empty parameter declaring such.
func (s *levelHandler) overlappingTables(_ levelHandlerRLocked, kr keyRange) (int, int) {
2019-02-22 13:19:55 -08:00
if len(kr.left) == 0 || len(kr.right) == 0 {
return 0, 0
}
2017-05-29 19:35:23 +10:00
left := sort.Search(len(s.tables), func(i int) bool {
2017-10-04 10:53:02 +11:00
return y.CompareKeys(kr.left, s.tables[i].Biggest()) <= 0
2017-05-29 19:35:23 +10:00
})
right := sort.Search(len(s.tables), func(i int) bool {
2017-10-04 10:53:02 +11:00
return y.CompareKeys(kr.right, s.tables[i].Smallest()) < 0
2017-05-29 19:35:23 +10:00
})
return left, right
}