diff --git a/backup_test.go b/backup_test.go index 16ef8ed5c..747cda2d4 100644 --- a/backup_test.go +++ b/backup_test.go @@ -501,3 +501,58 @@ func TestBackupLoadIncremental(t *testing.T) { }) require.NoError(t, err, "%v %v", updates, actual) } + +func TestBackupBitClear(t *testing.T) { + dir, err := ioutil.TempDir("", "badger-test") + require.NoError(t, err) + defer removeDir(dir) + + opt := getTestOptions(dir) + opt.ValueThreshold = 10 // This is important + db, err := Open(opt) + require.NoError(t, err) + + key := []byte("foo") + val := []byte(fmt.Sprintf("%0100d", 1)) + require.Greater(t, len(val), db.opt.ValueThreshold) + + err = db.Update(func(txn *Txn) error { + e := NewEntry(key, val) + // Value > valueTheshold so bitValuePointer will be set. + return txn.SetEntry(e) + }) + require.NoError(t, err) + + // Use different directory. + dir, err = ioutil.TempDir("", "badger-test") + require.NoError(t, err) + defer removeDir(dir) + + bak, err := ioutil.TempFile(dir, "badgerbak") + require.NoError(t, err) + _, err = db.Backup(bak, 0) + require.NoError(t, err) + require.NoError(t, bak.Close()) + require.NoError(t, db.Close()) + + opt = getTestOptions(dir) + opt.ValueThreshold = 200 // This is important. + db, err = Open(opt) + require.NoError(t, err) + defer db.Close() + + bak, err = os.Open(bak.Name()) + require.NoError(t, err) + defer bak.Close() + + require.NoError(t, db.Load(bak, 16)) + + require.NoError(t, db.View(func(txn *Txn) error { + e, err := txn.Get(key) + require.NoError(t, err) + v, err := e.ValueCopy(nil) + require.NoError(t, err) + require.Equal(t, val, v) + return nil + })) +} diff --git a/db.go b/db.go index 4cffe9cea..2ac0658bb 100644 --- a/db.go +++ b/db.go @@ -647,8 +647,12 @@ func (db *DB) writeToLSM(b *request) error { if db.shouldWriteValueToLSM(*entry) { // Will include deletion / tombstone case. db.mt.Put(entry.Key, y.ValueStruct{ - Value: entry.Value, - Meta: entry.meta, + Value: entry.Value, + // Ensure value pointer flag is removed. Otherwise, the value will fail + // to be retrieved during iterator prefetch. `bitValuePointer` is only + // known to be set in write to LSM when the entry is loaded from a backup + // with lower ValueThreshold and its value was stored in the value log. + Meta: entry.meta &^ bitValuePointer, UserMeta: entry.UserMeta, ExpiresAt: entry.ExpiresAt, })