Skip to content

Commit 9459a24

Browse files
authored
Ensure bitValuePointer flag is cleared for LSM entry values written to LSM (#1313)
When restoring a backup that was written with a lower `ValueThreshold` to a DB instance with a higher `ValueThreshold`, some entry values originally written to the value log in the backup DB will be written to the LSM along with the key when restored to the new DB. The `meta` field for those entries is not updated to reflect the correct value storage state. Those entries still have the `bitValuePointer` flag set. That causes `iterator.prefetch` to fail while looking up the value for those entries. This fix ensures the `meta` `bitValuePointer` is cleared when entry values are stored in the LSM. The original error can be reproduced by running the included `TestLSMVPClear` test after reverting the line 687 `db.go` change to clear the `bitValuePointer` flag.
1 parent 536fed1 commit 9459a24

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

backup_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,3 +485,58 @@ func TestBackupLoadIncremental(t *testing.T) {
485485
})
486486
require.NoError(t, err, "%v %v", updates, actual)
487487
}
488+
489+
func TestBackupBitClear(t *testing.T) {
490+
dir, err := ioutil.TempDir("", "badger-test")
491+
require.NoError(t, err)
492+
defer removeDir(dir)
493+
494+
opt := getTestOptions(dir)
495+
opt.ValueThreshold = 10 // This is important
496+
db, err := Open(opt)
497+
require.NoError(t, err)
498+
499+
key := []byte("foo")
500+
val := []byte(fmt.Sprintf("%0100d", 1))
501+
require.Greater(t, len(val), db.opt.ValueThreshold)
502+
503+
err = db.Update(func(txn *Txn) error {
504+
e := NewEntry(key, val)
505+
// Value > valueTheshold so bitValuePointer will be set.
506+
return txn.SetEntry(e)
507+
})
508+
require.NoError(t, err)
509+
510+
// Use different directory.
511+
dir, err = ioutil.TempDir("", "badger-test")
512+
require.NoError(t, err)
513+
defer removeDir(dir)
514+
515+
bak, err := ioutil.TempFile(dir, "badgerbak")
516+
require.NoError(t, err)
517+
_, err = db.Backup(bak, 0)
518+
require.NoError(t, err)
519+
require.NoError(t, bak.Close())
520+
require.NoError(t, db.Close())
521+
522+
opt = getTestOptions(dir)
523+
opt.ValueThreshold = 200 // This is important.
524+
db, err = Open(opt)
525+
require.NoError(t, err)
526+
defer db.Close()
527+
528+
bak, err = os.Open(bak.Name())
529+
require.NoError(t, err)
530+
defer bak.Close()
531+
532+
require.NoError(t, db.Load(bak, 16))
533+
534+
require.NoError(t, db.View(func(txn *Txn) error {
535+
e, err := txn.Get(key)
536+
require.NoError(t, err)
537+
v, err := e.ValueCopy(nil)
538+
require.NoError(t, err)
539+
require.Equal(t, val, v)
540+
return nil
541+
}))
542+
}

db.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,12 @@ func (db *DB) writeToLSM(b *request) error {
679679
if db.shouldWriteValueToLSM(*entry) { // Will include deletion / tombstone case.
680680
db.mt.Put(entry.Key,
681681
y.ValueStruct{
682-
Value: entry.Value,
683-
Meta: entry.meta,
682+
Value: entry.Value,
683+
// Ensure value pointer flag is removed. Otherwise, the value will fail
684+
// to be retrieved during iterator prefetch. `bitValuePointer` is only
685+
// known to be set in write to LSM when the entry is loaded from a backup
686+
// with lower ValueThreshold and its value was stored in the value log.
687+
Meta: entry.meta &^ bitValuePointer,
684688
UserMeta: entry.UserMeta,
685689
ExpiresAt: entry.ExpiresAt,
686690
})

0 commit comments

Comments
 (0)