From 617ed7c7db9d618b6511adfff5d22dcde2233049 Mon Sep 17 00:00:00 2001 From: Ibrahim Jarif Date: Thu, 27 Feb 2020 22:17:25 +0530 Subject: [PATCH] Initialize vlog before starting compactions in db.Open (#1226) If compaction is started before opening vlog, badger might crash with nil pointer error when trying to push discard stats at the end of compactions because discard stats are initialized in vlog open. This commit fixes it. --- db.go | 3 +++ value.go | 15 +++++++++++++-- value_test.go | 8 ++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/db.go b/db.go index a40aa8d92..d97c0e24a 100644 --- a/db.go +++ b/db.go @@ -331,6 +331,9 @@ func Open(opt Options) (db *DB, err error) { return nil, err } + // Initialize vlog struct. + db.vlog.init(db) + if !opt.ReadOnly { db.closers.compactors = y.NewCloser(1) db.lc.startCompact(db.closers.compactors) diff --git a/value.go b/value.go index b0d94ad86..c4fb41a71 100644 --- a/value.go +++ b/value.go @@ -1061,13 +1061,15 @@ func (vlog *valueLog) replayLog(lf *logFile, offset uint32, replayFn logEntry) e return nil } -func (vlog *valueLog) open(db *DB, ptr valuePointer, replayFn logEntry) error { +// init initializes the value log struct. This initialization needs to happen +// before compactions start. +func (vlog *valueLog) init(db *DB) { vlog.opt = db.opt vlog.db = db // We don't need to open any vlog files or collect stats for GC if DB is opened // in InMemory mode. InMemory mode doesn't create any files/directories on disk. if vlog.opt.InMemory { - return nil + return } vlog.dirPath = vlog.opt.ValueDir vlog.elog = y.NoEventLog @@ -1080,6 +1082,15 @@ func (vlog *valueLog) open(db *DB, ptr valuePointer, replayFn logEntry) error { closer: y.NewCloser(1), flushChan: make(chan map[uint32]int64, 16), } +} + +func (vlog *valueLog) open(db *DB, ptr valuePointer, replayFn logEntry) error { + // We don't need to open any vlog files or collect stats for GC if DB is opened + // in InMemory mode. InMemory mode doesn't create any files/directories on disk. + if db.opt.InMemory { + return nil + } + go vlog.flushDiscardStats() if err := vlog.populateFilesMap(); err != nil { return err diff --git a/value_test.go b/value_test.go index 8aa7c0909..8c3c67a8a 100644 --- a/value_test.go +++ b/value_test.go @@ -412,6 +412,7 @@ func TestValueGC4(t *testing.T) { err = kv.vlog.Close() require.NoError(t, err) + kv.vlog.init(kv) err = kv.vlog.open(kv, valuePointer{Fid: 2}, kv.replayFunction()) require.NoError(t, err) @@ -642,8 +643,11 @@ func TestPartialAppendToValueLog(t *testing.T) { checkKeys(t, kv, [][]byte{k3}) // Replay value log from beginning, badger head is past k2. require.NoError(t, kv.vlog.Close()) - require.NoError(t, - kv.vlog.open(kv, valuePointer{Fid: 0}, kv.replayFunction())) + + kv.vlog.init(kv) + require.NoError( + t, kv.vlog.open(kv, valuePointer{Fid: 0}, kv.replayFunction()), + ) require.NoError(t, kv.Close()) }