package badger
import (
"bytes"
"fmt"
"log"
"math"
"sync"
"github.com/dgraph-io/badger/v4/table"
"github.com/dgraph-io/badger/v4/y"
)
type keyRange struct {
left []byte
right []byte
inf bool
size int64
}
func (r keyRange ) isEmpty () bool {
return len (r .left ) == 0 && len (r .right ) == 0 && !r .inf
}
var infRange = keyRange {inf : true }
func (r keyRange ) String () string {
return fmt .Sprintf ("[left=%x, right=%x, inf=%v]" , r .left , r .right , r .inf )
}
func (r keyRange ) equals (dst keyRange ) bool {
return bytes .Equal (r .left , dst .left ) &&
bytes .Equal (r .right , dst .right ) &&
r .inf == dst .inf
}
func (r *keyRange ) extend (kr keyRange ) {
if kr .isEmpty () {
return
}
if r .isEmpty () {
*r = kr
}
if len (r .left ) == 0 || y .CompareKeys (kr .left , r .left ) < 0 {
r .left = kr .left
}
if len (r .right ) == 0 || y .CompareKeys (kr .right , r .right ) > 0 {
r .right = kr .right
}
if kr .inf {
r .inf = true
}
}
func (r keyRange ) overlapsWith (dst keyRange ) bool {
if r .isEmpty () {
return true
}
if dst .isEmpty () {
return false
}
if r .inf || dst .inf {
return true
}
if y .CompareKeys (r .left , dst .right ) > 0 {
return false
}
if y .CompareKeys (r .right , dst .left ) < 0 {
return false
}
return true
}
func getKeyRange(tables ...*table .Table ) keyRange {
if len (tables ) == 0 {
return keyRange {}
}
smallest := tables [0 ].Smallest ()
biggest := tables [0 ].Biggest ()
for i := 1 ; i < len (tables ); i ++ {
if y .CompareKeys (tables [i ].Smallest (), smallest ) < 0 {
smallest = tables [i ].Smallest ()
}
if y .CompareKeys (tables [i ].Biggest (), biggest ) > 0 {
biggest = tables [i ].Biggest ()
}
}
return keyRange {
left : y .KeyWithTs (y .ParseKey (smallest ), math .MaxUint64 ),
right : y .KeyWithTs (y .ParseKey (biggest ), 0 ),
}
}
type levelCompactStatus struct {
ranges []keyRange
delSize int64
}
func (lcs *levelCompactStatus ) debug () string {
var b bytes .Buffer
for _ , r := range lcs .ranges {
b .WriteString (r .String ())
}
return b .String ()
}
func (lcs *levelCompactStatus ) overlapsWith (dst keyRange ) bool {
for _ , r := range lcs .ranges {
if r .overlapsWith (dst ) {
return true
}
}
return false
}
func (lcs *levelCompactStatus ) remove (dst keyRange ) bool {
final := lcs .ranges [:0 ]
var found bool
for _ , r := range lcs .ranges {
if !r .equals (dst ) {
final = append (final , r )
} else {
found = true
}
}
lcs .ranges = final
return found
}
type compactStatus struct {
sync .RWMutex
levels []*levelCompactStatus
tables map [uint64 ]struct {}
}
func (cs *compactStatus ) overlapsWith (level int , this keyRange ) bool {
cs .RLock ()
defer cs .RUnlock ()
thisLevel := cs .levels [level ]
return thisLevel .overlapsWith (this )
}
func (cs *compactStatus ) delSize (l int ) int64 {
cs .RLock ()
defer cs .RUnlock ()
return cs .levels [l ].delSize
}
type thisAndNextLevelRLocked struct {}
func (cs *compactStatus ) compareAndAdd (_ thisAndNextLevelRLocked , cd compactDef ) bool {
cs .Lock ()
defer cs .Unlock ()
tl := cd .thisLevel .level
y .AssertTruef (tl < len (cs .levels ), "Got level %d. Max levels: %d" , tl , len (cs .levels ))
thisLevel := cs .levels [cd .thisLevel .level ]
nextLevel := cs .levels [cd .nextLevel .level ]
if thisLevel .overlapsWith (cd .thisRange ) {
return false
}
if nextLevel .overlapsWith (cd .nextRange ) {
return false
}
thisLevel .ranges = append (thisLevel .ranges , cd .thisRange )
nextLevel .ranges = append (nextLevel .ranges , cd .nextRange )
thisLevel .delSize += cd .thisSize
for _ , t := range append (cd .top , cd .bot ...) {
cs .tables [t .ID ()] = struct {}{}
}
return true
}
func (cs *compactStatus ) delete (cd compactDef ) {
cs .Lock ()
defer cs .Unlock ()
tl := cd .thisLevel .level
y .AssertTruef (tl < len (cs .levels ), "Got level %d. Max levels: %d" , tl , len (cs .levels ))
thisLevel := cs .levels [cd .thisLevel .level ]
nextLevel := cs .levels [cd .nextLevel .level ]
thisLevel .delSize -= cd .thisSize
found := thisLevel .remove (cd .thisRange )
if cd .thisLevel != cd .nextLevel && !cd .nextRange .isEmpty () {
found = nextLevel .remove (cd .nextRange ) && found
}
if !found {
this := cd .thisRange
next := cd .nextRange
fmt .Printf ("Looking for: %s in this level %d.\n" , this , tl )
fmt .Printf ("This Level:\n%s\n" , thisLevel .debug ())
fmt .Println ()
fmt .Printf ("Looking for: %s in next level %d.\n" , next , cd .nextLevel .level )
fmt .Printf ("Next Level:\n%s\n" , nextLevel .debug ())
log .Fatal ("keyRange not found" )
}
for _ , t := range append (cd .top , cd .bot ...) {
_ , ok := cs .tables [t .ID ()]
y .AssertTrue (ok )
delete (cs .tables , t .ID ())
}
}
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 .