package badger
import (
"fmt"
"sort"
"sync"
"github.com/dgraph-io/badger/v4/table"
"github.com/dgraph-io/badger/v4/y"
)
type levelHandler struct {
sync .RWMutex
tables []*table .Table
totalSize int64
totalStaleSize int64
level int
strLevel string
db *DB
}
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
}
func (s *levelHandler ) getTotalSize () int64 {
s .RLock ()
defer s .RUnlock ()
return s .totalSize
}
func (s *levelHandler ) initTables (tables []*table .Table ) {
s .Lock ()
defer s .Unlock ()
s .tables = tables
s .totalSize = 0
s .totalStaleSize = 0
for _ , t := range tables {
s .addSize (t )
}
if s .level == 0 {
sort .Slice (s .tables , func (i , j int ) bool {
return s .tables [i ].ID () < s .tables [j ].ID ()
})
} else {
sort .Slice (s .tables , func (i , j int ) bool {
return y .CompareKeys (s .tables [i ].Smallest (), s .tables [j ].Smallest ()) < 0
})
}
}
func (s *levelHandler ) deleteTables (toDel []*table .Table ) error {
s .Lock ()
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 )
}
s .tables = newTables
s .Unlock ()
return decrRefs (toDel )
}
func (s *levelHandler ) replaceTables (toDel , toAdd []*table .Table ) error {
s .Lock ()
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 )
}
for _ , t := range toAdd {
s .addSize (t )
t .IncrRef ()
newTables = append (newTables , t )
}
s .tables = newTables
sort .Slice (s .tables , func (i , j int ) bool {
return y .CompareKeys (s .tables [i ].Smallest (), s .tables [j ].Smallest ()) < 0
})
s .Unlock ()
return decrRefs (toDel )
}
func (s *levelHandler ) addTable (t *table .Table ) {
s .Lock ()
defer s .Unlock ()
s .addSize (t )
t .IncrRef ()
s .tables = append (s .tables , t )
}
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
})
}
func decrRefs(tables []*table .Table ) error {
for _ , table := range tables {
if err := table .DecrRef (); err != nil {
return err
}
}
return nil
}
func newLevelHandler(db *DB , level int ) *levelHandler {
return &levelHandler {
level : level ,
strLevel : fmt .Sprintf ("l%d" , level ),
db : db ,
}
}
func (s *levelHandler ) tryAddLevel0Table (t *table .Table ) bool {
y .AssertTrue (s .level == 0 )
s .Lock ()
defer s .Unlock ()
if len (s .tables ) >= s .db .opt .NumLevelZeroTablesStall {
return false
}
s .tables = append (s .tables , t )
t .IncrRef ()
s .addSize (t )
return true
}
func (s *levelHandler ) addSize (t *table .Table ) {
s .totalSize += t .Size ()
s .totalStaleSize += int64 (t .StaleDataSize ())
}
func (s *levelHandler ) subtractSize (t *table .Table ) {
s .totalSize -= t .Size ()
s .totalStaleSize -= int64 (t .StaleDataSize ())
}
func (s *levelHandler ) numTables () int {
s .RLock ()
defer s .RUnlock ()
return len (s .tables )
}
func (s *levelHandler ) close () error {
s .RLock ()
defer s .RUnlock ()
var err error
for _ , t := range s .tables {
if closeErr := t .Close (-1 ); closeErr != nil && err == nil {
err = closeErr
}
}
return y .Wrap (err , "levelHandler.close" )
}
func (s *levelHandler ) getTableForKey (key []byte ) ([]*table .Table , func () error ) {
s .RLock ()
defer s .RUnlock ()
if s .level == 0 {
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 ()
}
return out , func () error {
for _ , t := range out {
if err := t .DecrRef (); err != nil {
return err
}
}
return nil
}
}
idx := sort .Search (len (s .tables ), func (i int ) bool {
return y .CompareKeys (s .tables [i ].Biggest (), key ) >= 0
})
if idx >= len (s .tables ) {
return nil , func () error { return nil }
}
tbl := s .tables [idx ]
tbl .IncrRef ()
return []*table .Table {tbl }, tbl .DecrRef
}
func (s *levelHandler ) get (key []byte ) (y .ValueStruct , error ) {
tables , decr := s .getTableForKey (key )
keyNoTs := y .ParseKey (key )
hash := y .Hash (keyNoTs )
var maxVs y .ValueStruct
for _ , th := range tables {
if th .DoesNotHave (hash ) {
y .NumLSMBloomHitsAdd (s .db .opt .MetricsEnabled , s .strLevel , 1 )
continue
}
it := th .NewIterator (0 )
defer it .Close ()
y .NumLSMGetsAdd (s .db .opt .MetricsEnabled , s .strLevel , 1 )
it .Seek (key )
if !it .Valid () {
continue
}
if y .SameKey (key , it .Key ()) {
if version := y .ParseTs (it .Key ()); maxVs .Version < version {
maxVs = it .ValueCopy ()
maxVs .Version = version
}
}
}
return maxVs , decr ()
}
func (s *levelHandler ) appendIterators (iters []y .Iterator , opt *IteratorOptions ) []y .Iterator {
s .RLock ()
defer s .RUnlock ()
var topt int
if opt .Reverse {
topt = table .REVERSED
}
if s .level == 0 {
var out []*table .Table
for _ , t := range s .tables {
if opt .pickTable (t ) {
out = append (out , t )
}
}
return appendIteratorsReversed (iters , out , topt )
}
tables := opt .pickTables (s .tables )
if len (tables ) == 0 {
return iters
}
return append (iters , table .NewConcatIterator (tables , topt ))
}
type levelHandlerRLocked struct {}
func (s *levelHandler ) overlappingTables (_ levelHandlerRLocked , kr keyRange ) (int , int ) {
if len (kr .left ) == 0 || len (kr .right ) == 0 {
return 0 , 0
}
left := sort .Search (len (s .tables ), func (i int ) bool {
return y .CompareKeys (kr .left , s .tables [i ].Biggest ()) <= 0
})
right := sort .Search (len (s .tables ), func (i int ) bool {
return y .CompareKeys (kr .right , s .tables [i ].Smallest ()) < 0
})
return left , right
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .