Skip to content

Commit

Permalink
Use cache for storing block offsets (#1336)
Browse files Browse the repository at this point in the history
Fixes #1335

Signed-off-by: Tiger <rbalajis25@gmail.com>
  • Loading branch information
poonai committed May 28, 2020
1 parent 00b86b2 commit e7b6e76
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 38 deletions.
33 changes: 32 additions & 1 deletion iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func TestPickSortTables(t *testing.T) {
}

func TestIteratePrefix(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
testIteratorPrefix := func(t *testing.T, db *DB) {
bkey := func(i int) []byte {
return []byte(fmt.Sprintf("%04d", i))
}
Expand Down Expand Up @@ -198,7 +198,38 @@ func TestIteratePrefix(t *testing.T) {
for i := 0; i < n; i++ {
require.Equal(t, 1, countOneKey(bkey(i)))
}
}

t.Run("With Default options", func(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
testIteratorPrefix(t, db)
})
})

t.Run("With Block Offsets in Cache", func(t *testing.T) {
opts := getTestOptions("")
opts = opts.WithKeepBlockIndicesInCache(true)
runBadgerTest(t, &opts, func(t *testing.T, db *DB) {
testIteratorPrefix(t, db)
})
})

t.Run("With Block Offsets and Blocks in Cache", func(t *testing.T) {
opts := getTestOptions("")
opts = opts.WithKeepBlockIndicesInCache(true).WithKeepBlocksInCache(true)
runBadgerTest(t, &opts, func(t *testing.T, db *DB) {
testIteratorPrefix(t, db)
})
})

t.Run("With Blocks in Cache", func(t *testing.T) {
opts := getTestOptions("")
opts = opts.WithKeepBlocksInCache(true)
runBadgerTest(t, &opts, func(t *testing.T, db *DB) {
testIteratorPrefix(t, db)
})
})

}

// go test -v -run=XXX -bench=BenchmarkIterate -benchtime=3s
Expand Down
61 changes: 53 additions & 8 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ type Options struct {
// ChecksumVerificationMode decides when db should verify checksums for SSTable blocks.
ChecksumVerificationMode options.ChecksumVerificationMode

// KeepBlockIndicesInCache decides whether to keep the block offsets in the cache or not.
KeepBlockIndicesInCache bool

// KeepBlocksInCache decides whether to keep the sst blocks in the cache or not.
KeepBlocksInCache bool

// Transaction start and commit timestamps are managed by end-user.
// This is only useful for databases built on top of Badger (like Dgraph).
// Not recommended for most users.
Expand Down Expand Up @@ -157,19 +163,23 @@ func DefaultOptions(path string) Options {
LogRotatesToFlush: 2,
EncryptionKey: []byte{},
EncryptionKeyRotationDuration: 10 * 24 * time.Hour, // Default 10 days.
KeepBlocksInCache: false,
KeepBlockIndicesInCache: false,
}
}

func buildTableOptions(opt Options) table.Options {
return table.Options{
TableSize: uint64(opt.MaxTableSize),
BlockSize: opt.BlockSize,
BloomFalsePositive: opt.BloomFalsePositive,
LoadBloomsOnOpen: opt.LoadBloomsOnOpen,
LoadingMode: opt.TableLoadingMode,
ChkMode: opt.ChecksumVerificationMode,
Compression: opt.Compression,
ZSTDCompressionLevel: opt.ZSTDCompressionLevel,
TableSize: uint64(opt.MaxTableSize),
BlockSize: opt.BlockSize,
BloomFalsePositive: opt.BloomFalsePositive,
LoadBloomsOnOpen: opt.LoadBloomsOnOpen,
LoadingMode: opt.TableLoadingMode,
ChkMode: opt.ChecksumVerificationMode,
Compression: opt.Compression,
ZSTDCompressionLevel: opt.ZSTDCompressionLevel,
KeepBlockIndicesInCache: opt.KeepBlockIndicesInCache,
KeepBlocksInCache: opt.KeepBlocksInCache,
}
}

Expand Down Expand Up @@ -631,3 +641,38 @@ func (opt Options) WithLoadBloomsOnOpen(b bool) Options {
opt.LoadBloomsOnOpen = b
return opt
}

// WithKeepBlockIndicesInCache returns a new Option value with KeepBlockOffsetInCache set to the
// given value.
//
// When this option is set badger will store the block offsets in a cache along with the blocks.
// The size of the cache is determined by the MaxCacheSize option.If the MaxCacheSize is set to
// zero, then MaxCacheSize is set to 100 mb. When indices are stored in the cache, the read
// performance might be affected but the cache limits the amount of memory used by the indices.
//
// The default value of KeepBlockOffsetInCache is false.
func (opt Options) WithKeepBlockIndicesInCache(val bool) Options {
opt.KeepBlockIndicesInCache = val

if val && opt.MaxCacheSize == 0 {
opt.MaxCacheSize = 100 << 20
}
return opt
}

// WithKeepBlocksInCache returns a new Option value with KeepBlocksInCache set to the
// given value.
//
// When this option is set badger will store the block in the cache. The size of the cache is
// determined by the MaxCacheSize option.If the MaxCacheSize is set to zero,
// then MaxCacheSize is set to 100 mb.
//
// The default value of KeepBlocksInCache is false.
func (opt Options) WithKeepBlocksInCache(val bool) Options {
opt.KeepBlocksInCache = val

if val && opt.MaxCacheSize == 0 {
opt.MaxCacheSize = 100 << 20
}
return opt
}
4 changes: 2 additions & 2 deletions table/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ func TestTableIndex(t *testing.T) {
}

// Ensure index is built correctly
require.Equal(t, blockCount, len(tbl.blockIndex))
for i, ko := range tbl.blockIndex {
require.Equal(t, blockCount, tbl.noOfBlocks)
for i, ko := range tbl.readTableIndex().Offsets {
require.Equal(t, ko.Key, blockFirstKeys[i])
}
f.Close()
Expand Down
12 changes: 6 additions & 6 deletions table/iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (itr *Iterator) Valid() bool {
}

func (itr *Iterator) seekToFirst() {
numBlocks := len(itr.t.blockIndex)
numBlocks := itr.t.noOfBlocks
if numBlocks == 0 {
itr.err = io.EOF
return
Expand All @@ -212,7 +212,7 @@ func (itr *Iterator) seekToFirst() {
}

func (itr *Iterator) seekToLast() {
numBlocks := len(itr.t.blockIndex)
numBlocks := itr.t.noOfBlocks
if numBlocks == 0 {
itr.err = io.EOF
return
Expand Down Expand Up @@ -249,8 +249,8 @@ func (itr *Iterator) seekFrom(key []byte, whence int) {
case current:
}

idx := sort.Search(len(itr.t.blockIndex), func(idx int) bool {
ko := itr.t.blockIndex[idx]
idx := sort.Search(itr.t.noOfBlocks, func(idx int) bool {
ko := itr.t.blockOffsets()[idx]
return y.CompareKeys(ko.Key, key) > 0
})
if idx == 0 {
Expand All @@ -269,7 +269,7 @@ func (itr *Iterator) seekFrom(key []byte, whence int) {
itr.seekHelper(idx-1, key)
if itr.err == io.EOF {
// Case 1. Need to visit block[idx].
if idx == len(itr.t.blockIndex) {
if idx == itr.t.noOfBlocks {
// If idx == len(itr.t.blockIndex), then input key is greater than ANY element of table.
// There's nothing we can do. Valid() should return false as we seek to end of table.
return
Expand Down Expand Up @@ -297,7 +297,7 @@ func (itr *Iterator) seekForPrev(key []byte) {
func (itr *Iterator) next() {
itr.err = nil

if itr.bpos >= len(itr.t.blockIndex) {
if itr.bpos >= itr.t.noOfBlocks {
itr.err = io.EOF
return
}
Expand Down
Loading

0 comments on commit e7b6e76

Please sign in to comment.