Skip to content

Commit 94d6168

Browse files
authored
chore(memtable): refactor code for memtable flush (#1866)
removes the `flushTask` struct that was used in two different contexts.
1 parent 38c1c0e commit 94d6168

File tree

2 files changed

+58
-71
lines changed

2 files changed

+58
-71
lines changed

Diff for: db.go

+48-60
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ type DB struct {
109109
lc *levelsController
110110
vlog valueLog
111111
writeCh chan *request
112-
flushChan chan flushTask // For flushing memtables.
112+
flushChan chan *memTable // For flushing memtables.
113113
closeOnce sync.Once // For closing DB only once.
114114

115115
blockWrites atomic.Int32
@@ -240,7 +240,7 @@ func Open(opt Options) (*DB, error) {
240240

241241
db := &DB{
242242
imm: make([]*memTable, 0, opt.NumMemtables),
243-
flushChan: make(chan flushTask, opt.NumMemtables),
243+
flushChan: make(chan *memTable, opt.NumMemtables),
244244
writeCh: make(chan *request, kvWriteChCapacity),
245245
opt: opt,
246246
manifest: manifestFile,
@@ -351,11 +351,11 @@ func Open(opt Options) (*DB, error) {
351351

352352
db.closers.memtable = z.NewCloser(1)
353353
go func() {
354-
_ = db.flushMemtable(db.closers.memtable) // Need levels controller to be up.
354+
db.flushMemtable(db.closers.memtable) // Need levels controller to be up.
355355
}()
356356
// Flush them to disk asap.
357357
for _, mt := range db.imm {
358-
db.flushChan <- flushTask{mt: mt}
358+
db.flushChan <- mt
359359
}
360360
}
361361
// We do increment nextTxnTs below. So, no need to do it here.
@@ -568,12 +568,12 @@ func (db *DB) close() (err error) {
568568
} else {
569569
db.opt.Debugf("Flushing memtable")
570570
for {
571-
pushedFlushTask := func() bool {
571+
pushedMemTable := func() bool {
572572
db.lock.Lock()
573573
defer db.lock.Unlock()
574574
y.AssertTrue(db.mt != nil)
575575
select {
576-
case db.flushChan <- flushTask{mt: db.mt}:
576+
case db.flushChan <- db.mt:
577577
db.imm = append(db.imm, db.mt) // Flusher will attempt to remove this from s.imm.
578578
db.mt = nil // Will segfault if we try writing!
579579
db.opt.Debugf("pushed to flush chan\n")
@@ -586,7 +586,7 @@ func (db *DB) close() (err error) {
586586
}
587587
return false
588588
}()
589-
if pushedFlushTask {
589+
if pushedMemTable {
590590
break
591591
}
592592
time.Sleep(10 * time.Millisecond)
@@ -826,6 +826,7 @@ func (db *DB) writeRequests(reqs []*request) error {
826826
}
827827
count += len(b.Entries)
828828
var i uint64
829+
var err error
829830
for err = db.ensureRoomForWrite(); err == errNoRoom; err = db.ensureRoomForWrite() {
830831
i++
831832
if i%100 == 0 {
@@ -987,7 +988,7 @@ func (db *DB) ensureRoomForWrite() error {
987988
}
988989

989990
select {
990-
case db.flushChan <- flushTask{mt: db.mt}:
991+
case db.flushChan <- db.mt:
991992
db.opt.Debugf("Flushing memtable, mt.size=%d size of flushChan: %d\n",
992993
db.mt.sl.MemSize(), len(db.flushChan))
993994
// We manage to push this task. Let's modify imm.
@@ -1009,12 +1010,12 @@ func arenaSize(opt Options) int64 {
10091010
}
10101011

10111012
// buildL0Table builds a new table from the memtable.
1012-
func buildL0Table(ft flushTask, bopts table.Options) *table.Builder {
1013-
iter := ft.mt.sl.NewIterator()
1013+
func buildL0Table(iter y.Iterator, dropPrefixes [][]byte, bopts table.Options) *table.Builder {
10141014
defer iter.Close()
1015+
10151016
b := table.NewTableBuilder(bopts)
1016-
for iter.SeekToFirst(); iter.Valid(); iter.Next() {
1017-
if len(ft.dropPrefixes) > 0 && hasAnyPrefixes(iter.Key(), ft.dropPrefixes) {
1017+
for iter.Rewind(); iter.Valid(); iter.Next() {
1018+
if len(dropPrefixes) > 0 && hasAnyPrefixes(iter.Key(), dropPrefixes) {
10181019
continue
10191020
}
10201021
vs := iter.Value()
@@ -1024,23 +1025,15 @@ func buildL0Table(ft flushTask, bopts table.Options) *table.Builder {
10241025
}
10251026
b.Add(iter.Key(), iter.Value(), vp.Len)
10261027
}
1027-
return b
1028-
}
10291028

1030-
type flushTask struct {
1031-
mt *memTable
1032-
dropPrefixes [][]byte
1029+
return b
10331030
}
10341031

1035-
// handleFlushTask must be run serially.
1036-
func (db *DB) handleFlushTask(ft flushTask) error {
1037-
// There can be a scenario, when empty memtable is flushed.
1038-
if ft.mt.sl.Empty() {
1039-
return nil
1040-
}
1041-
1032+
// handleMemTableFlush must be run serially.
1033+
func (db *DB) handleMemTableFlush(mt *memTable, dropPrefixes [][]byte) error {
10421034
bopts := buildTableOptions(db)
1043-
builder := buildL0Table(ft, bopts)
1035+
itr := mt.sl.NewUniIterator(false)
1036+
builder := buildL0Table(itr, nil, bopts)
10441037
defer builder.Close()
10451038

10461039
// buildL0Table can return nil if the none of the items in the skiplist are
@@ -1069,39 +1062,39 @@ func (db *DB) handleFlushTask(ft flushTask) error {
10691062
return err
10701063
}
10711064

1072-
// flushMemtable must keep running until we send it an empty flushTask. If there
1073-
// are errors during handling the flush task, we'll retry indefinitely.
1074-
func (db *DB) flushMemtable(lc *z.Closer) error {
1065+
// flushMemtable must keep running until we send it an empty memtable. If there
1066+
// are errors during handling the memtable flush, we'll retry indefinitely.
1067+
func (db *DB) flushMemtable(lc *z.Closer) {
10751068
defer lc.Done()
10761069

1077-
for ft := range db.flushChan {
1078-
if ft.mt == nil {
1079-
// We close db.flushChan now, instead of sending a nil ft.mt.
1070+
for mt := range db.flushChan {
1071+
if mt == nil {
10801072
continue
10811073
}
1082-
for {
1083-
err := db.handleFlushTask(ft)
1084-
if err == nil {
1085-
// Update s.imm. Need a lock.
1086-
db.lock.Lock()
1087-
// This is a single-threaded operation. ft.mt corresponds to the head of
1088-
// db.imm list. Once we flush it, we advance db.imm. The next ft.mt
1089-
// which would arrive here would match db.imm[0], because we acquire a
1090-
// lock over DB when pushing to flushChan.
1091-
// TODO: This logic is dirty AF. Any change and this could easily break.
1092-
y.AssertTrue(ft.mt == db.imm[0])
1093-
db.imm = db.imm[1:]
1094-
ft.mt.DecrRef() // Return memory.
1095-
db.lock.Unlock()
10961074

1097-
break
1075+
for {
1076+
if err := db.handleMemTableFlush(mt, nil); err != nil {
1077+
// Encountered error. Retry indefinitely.
1078+
db.opt.Errorf("error flushing memtable to disk: %v, retrying", err)
1079+
time.Sleep(time.Second)
1080+
continue
10981081
}
1099-
// Encountered error. Retry indefinitely.
1100-
db.opt.Errorf("Failure while flushing memtable to disk: %v. Retrying...\n", err)
1101-
time.Sleep(time.Second)
1082+
1083+
// Update s.imm. Need a lock.
1084+
db.lock.Lock()
1085+
// This is a single-threaded operation. mt corresponds to the head of
1086+
// db.imm list. Once we flush it, we advance db.imm. The next mt
1087+
// which would arrive here would match db.imm[0], because we acquire a
1088+
// lock over DB when pushing to flushChan.
1089+
// TODO: This logic is dirty AF. Any change and this could easily break.
1090+
y.AssertTrue(mt == db.imm[0])
1091+
db.imm = db.imm[1:]
1092+
mt.DecrRef() // Return memory.
1093+
// unlock
1094+
db.lock.Unlock()
1095+
break
11021096
}
11031097
}
1104-
return nil
11051098
}
11061099

11071100
func exists(path string) (bool, error) {
@@ -1521,10 +1514,10 @@ func (db *DB) startCompactions() {
15211514
func (db *DB) startMemoryFlush() {
15221515
// Start memory fluhser.
15231516
if db.closers.memtable != nil {
1524-
db.flushChan = make(chan flushTask, db.opt.NumMemtables)
1517+
db.flushChan = make(chan *memTable, db.opt.NumMemtables)
15251518
db.closers.memtable = z.NewCloser(1)
15261519
go func() {
1527-
_ = db.flushMemtable(db.closers.memtable)
1520+
db.flushMemtable(db.closers.memtable)
15281521
}()
15291522
}
15301523
}
@@ -1627,7 +1620,7 @@ func (db *DB) prepareToDrop() (func(), error) {
16271620
panic("Attempting to drop data in read-only mode.")
16281621
}
16291622
// In order prepare for drop, we need to block the incoming writes and
1630-
// write it to db. Then, flush all the pending flushtask. So that, we
1623+
// write it to db. Then, flush all the pending memtable. So that, we
16311624
// don't miss any entries.
16321625
if err := db.blockWrite(); err != nil {
16331626
return nil, err
@@ -1676,7 +1669,7 @@ func (db *DB) dropAll() (func(), error) {
16761669
if err != nil {
16771670
return f, err
16781671
}
1679-
// prepareToDrop will stop all the incomming write and flushes any pending flush tasks.
1672+
// prepareToDrop will stop all the incomming write and flushes any pending memtables.
16801673
// Before we drop, we'll stop the compaction because anyways all the datas are going to
16811674
// be deleted.
16821675
db.stopCompactions()
@@ -1758,13 +1751,8 @@ func (db *DB) DropPrefix(prefixes ...[]byte) error {
17581751
memtable.DecrRef()
17591752
continue
17601753
}
1761-
task := flushTask{
1762-
mt: memtable,
1763-
// Ensure that the head of value log gets persisted to disk.
1764-
dropPrefixes: filtered,
1765-
}
17661754
db.opt.Debugf("Flushing memtable")
1767-
if err := db.handleFlushTask(task); err != nil {
1755+
if err := db.handleMemTableFlush(memtable, filtered); err != nil {
17681756
db.opt.Errorf("While trying to flush memtable: %v", err)
17691757
return err
17701758
}

Diff for: db_test.go

+10-11
Original file line numberDiff line numberDiff line change
@@ -1464,7 +1464,7 @@ func TestGetSetDeadlock(t *testing.T) {
14641464

14651465
db, err := Open(DefaultOptions(dir).WithValueLogFileSize(1 << 20))
14661466
require.NoError(t, err)
1467-
defer db.Close()
1467+
defer func() { require.NoError(t, db.Close()) }()
14681468

14691469
val := make([]byte, 1<<19)
14701470
key := []byte("key1")
@@ -1506,7 +1506,7 @@ func TestWriteDeadlock(t *testing.T) {
15061506

15071507
db, err := Open(DefaultOptions(dir).WithValueLogFileSize(10 << 20))
15081508
require.NoError(t, err)
1509-
defer db.Close()
1509+
defer func() { require.NoError(t, db.Close()) }()
15101510
print := func(count *int) {
15111511
*count++
15121512
if *count%100 == 0 {
@@ -1886,7 +1886,7 @@ func ExampleOpen() {
18861886
if err != nil {
18871887
panic(err)
18881888
}
1889-
defer db.Close()
1889+
defer func() { y.Check(db.Close()) }()
18901890

18911891
err = db.View(func(txn *Txn) error {
18921892
_, err := txn.Get([]byte("key"))
@@ -1942,7 +1942,7 @@ func ExampleTxn_NewIterator() {
19421942
if err != nil {
19431943
panic(err)
19441944
}
1945-
defer db.Close()
1945+
defer func() { y.Check(db.Close()) }()
19461946

19471947
bkey := func(i int) []byte {
19481948
return []byte(fmt.Sprintf("%09d", i))
@@ -1962,8 +1962,7 @@ func ExampleTxn_NewIterator() {
19621962
}
19631963
}
19641964

1965-
err = txn.Commit()
1966-
if err != nil {
1965+
if err := txn.Commit(); err != nil {
19671966
panic(err)
19681967
}
19691968

@@ -1995,7 +1994,7 @@ func TestSyncForRace(t *testing.T) {
19951994

19961995
db, err := Open(DefaultOptions(dir).WithSyncWrites(false))
19971996
require.NoError(t, err)
1998-
defer db.Close()
1997+
defer func() { require.NoError(t, db.Close()) }()
19991998

20001999
closeChan := make(chan struct{})
20012000
doneChan := make(chan struct{})
@@ -2038,14 +2037,14 @@ func TestSyncForRace(t *testing.T) {
20382037

20392038
func TestForceFlushMemtable(t *testing.T) {
20402039
dir, err := os.MkdirTemp("", "badger-test")
2041-
require.NoError(t, err, "temp dir for badger count not be created")
2040+
require.NoError(t, err, "temp dir for badger could not be created")
20422041

20432042
ops := getTestOptions(dir)
20442043
ops.ValueLogMaxEntries = 1
20452044

20462045
db, err := Open(ops)
20472046
require.NoError(t, err, "error while openning db")
2048-
defer db.Close()
2047+
defer func() { require.NoError(t, db.Close()) }()
20492048

20502049
for i := 0; i < 3; i++ {
20512050
err = db.Update(func(txn *Txn) error {
@@ -2179,7 +2178,7 @@ func TestMinCacheSize(t *testing.T) {
21792178

21802179
func TestUpdateMaxCost(t *testing.T) {
21812180
dir, err := os.MkdirTemp("", "badger-test")
2182-
require.NoError(t, err, "temp dir for badger count not be created")
2181+
require.NoError(t, err, "temp dir for badger could not be created")
21832182
defer os.RemoveAll(dir)
21842183

21852184
ops := getTestOptions(dir).
@@ -2286,7 +2285,7 @@ func TestOpenDBReadOnly(t *testing.T) {
22862285

22872286
func TestBannedPrefixes(t *testing.T) {
22882287
dir, err := os.MkdirTemp("", "badger-test")
2289-
require.NoError(t, err, "temp dir for badger count not be created")
2288+
require.NoError(t, err, "temp dir for badger could not be created")
22902289
defer os.RemoveAll(dir)
22912290

22922291
opt := getTestOptions(dir).WithNamespaceOffset(3)

0 commit comments

Comments
 (0)