From be3fd71ebe5f28eeaaa1843c9be5568d3bd40da4 Mon Sep 17 00:00:00 2001 From: Alain Gilbert Date: Thu, 19 Dec 2019 18:45:10 -1000 Subject: [PATCH] Fix loadIndex to be deterministic (#115) --- AUTHORS | 1 + bitcask.go | 17 ++++++++++++++++- bitcask_test.go | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 2a894e2..5cd98f7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,6 +2,7 @@ # Name or Organization # The email address is not required for organizations. +Alain Gilbert Awn Umar Christian Muehlhaeuser Ignacio Hagopian diff --git a/bitcask.go b/bitcask.go index bb7f4b5..604ba0d 100644 --- a/bitcask.go +++ b/bitcask.go @@ -8,6 +8,7 @@ import ( "os" "path" "path/filepath" + "sort" "sync" "github.com/gofrs/flock" @@ -468,13 +469,27 @@ func loadDatafiles(path string, maxKeySize uint32, maxValueSize uint64) (datafil return } +func getSortedDatafiles(datafiles map[int]data.Datafile) []data.Datafile { + out := make([]data.Datafile, len(datafiles)) + idx := 0 + for _, df := range datafiles { + out[idx] = df + idx++ + } + sort.Slice(out, func(i, j int) bool { + return out[i].FileID() < out[j].FileID() + }) + return out +} + func loadIndex(path string, indexer index.Indexer, maxKeySize uint32, datafiles map[int]data.Datafile) (art.Tree, error) { t, found, err := indexer.Load(filepath.Join(path, "index"), maxKeySize) if err != nil { return nil, err } if !found { - for _, df := range datafiles { + sortedDatafiles := getSortedDatafiles(datafiles) + for _, df := range sortedDatafiles { var offset int64 for { e, n, err := df.Read() diff --git a/bitcask_test.go b/bitcask_test.go index b6a76ba..7416ffe 100644 --- a/bitcask_test.go +++ b/bitcask_test.go @@ -133,6 +133,23 @@ func TestAll(t *testing.T) { }) } +func TestReopen1(t *testing.T) { + assert := assert.New(t) + for i := 0; i < 10; i ++ { + testdir, _ := ioutil.TempDir("", "bitcask") + db, _ := Open(testdir, WithMaxDatafileSize(1)) + _ = db.Put([]byte("foo"), []byte("bar")) + _ = db.Put([]byte("foo"), []byte("bar1")) + _ = db.Put([]byte("foo"), []byte("bar2")) + _ = db.Put([]byte("foo"), []byte("bar3")) + _ = db.Put([]byte("foo"), []byte("bar4")) + _ = db.Put([]byte("foo"), []byte("bar5")) + _ = db.Reopen() + val, _ := db.Get([]byte("foo")) + assert.Equal("bar5", string(val)) + } +} + func TestReopen(t *testing.T) { assert := assert.New(t)