From 3ca6ed7afefec8996bfaf28eb360f1930d203bab Mon Sep 17 00:00:00 2001 From: Bilal Akhtar Date: Wed, 15 Apr 2020 17:38:19 -0400 Subject: [PATCH] *: Wire up L0SubLevels with iterator and version creation This change effectively lowers read amplification in L0 by taking advantage of knowledge about sublevels contained in Version.L0SubLevels and using it to create levelIters for each sublevel instead of directly adding one iterator per file to the mergingIter. The get_iter is also updated to take advantage of this information. The Version struct is also updated, as a result of this change, to hold a reference to an L0SubLevels instance. That reference is initialized during Version initialization in version_edit. Most of the test changes are a result of always printing the sublevel alongside the level for L0. --- compaction.go | 4 +- db.go | 56 +++---- db_test.go | 4 +- error_test.go | 4 +- get_iter.go | 15 +- get_iter_test.go | 5 +- ingest_test.go | 4 +- internal/manifest/l0_sublevels_test.go | 56 ++++++- internal/manifest/testdata/l0_sublevels | 42 ++++++ .../manifest/testdata/version_check_ordering | 51 ++++--- internal/manifest/testdata/version_edit_apply | 27 ++-- internal/manifest/version.go | 118 ++++++++++++--- internal/manifest/version_edit.go | 27 +++- internal/manifest/version_edit_test.go | 8 + internal/manifest/version_test.go | 3 + metrics.go | 14 +- metrics_test.go | 28 ++-- range_del_test.go | 2 +- testdata/event_listener | 20 +-- testdata/ingest | 142 +++++++++++------- testdata/ingest_target_level | 7 +- testdata/iterator_next_prev | 4 +- testdata/iterator_table_filter | 2 +- testdata/manual_compaction | 14 +- testdata/manual_flush | 18 ++- testdata/metrics | 126 ++++++++-------- testdata/range_del | 26 ++-- tool/testdata/db_lsm | 20 +-- version_set.go | 5 + 29 files changed, 552 insertions(+), 300 deletions(-) diff --git a/compaction.go b/compaction.go index 62c6a02f809..955cefb828b 100644 --- a/compaction.go +++ b/compaction.go @@ -650,10 +650,10 @@ func (c *compaction) newInputIter(newIters tableNewIters) (_ internalIterator, r // Check that the LSM ordering invariants are ok in order to prevent // generating corrupted sstables due to a violation of those invariants. - if err := manifest.CheckOrdering(c.cmp, c.format, c.startLevel, c.inputs[0]); err != nil { + if err := manifest.CheckOrdering(c.cmp, c.format, c.startLevel, manifest.InvalidSublevel, c.inputs[0]); err != nil { c.logger.Fatalf("%s", err) } - if err := manifest.CheckOrdering(c.cmp, c.format, c.outputLevel, c.inputs[1]); err != nil { + if err := manifest.CheckOrdering(c.cmp, c.format, c.outputLevel, manifest.InvalidSublevel, c.inputs[1]); err != nil { c.logger.Fatalf("%s", err) } diff --git a/db.go b/db.go index 873e70b86d6..558f1e3212b 100644 --- a/db.go +++ b/db.go @@ -17,6 +17,7 @@ import ( "github.com/cockroachdb/pebble/internal/arenaskl" "github.com/cockroachdb/pebble/internal/base" "github.com/cockroachdb/pebble/internal/invariants" + "github.com/cockroachdb/pebble/internal/manifest" "github.com/cockroachdb/pebble/internal/manual" "github.com/cockroachdb/pebble/internal/record" "github.com/cockroachdb/pebble/vfs" @@ -365,7 +366,7 @@ func (d *DB) getInternal(key []byte, b *Batch, s *Snapshot) ([]byte, io.Closer, get.key = key get.batch = b get.mem = readState.memtables - get.l0 = readState.current.Files[0] + get.l0 = readState.current.L0SubLevels.Files get.version = readState.current // Strip off memtables which cannot possibly contain the seqNum being read @@ -615,7 +616,7 @@ type iterAlloc struct { dbi Iterator merging mergingIter mlevels [3 + numLevels]mergingIterLevel - levels [numLevels]levelIter + levels [3 + numLevels]levelIter } var iterAllocPool = sync.Pool{ @@ -687,34 +688,16 @@ func (d *DB) newIterInternal( }) } - // The level 0 files need to be added from newest to oldest. - // - // Note that level 0 files do not contain untruncated tombstones, even in the presence of - // L0=>L0 compactions since such compactions output a single file. Therefore, we do not - // need to wrap level 0 files individually in level iterators. - current := readState.current - for i := len(current.Files[0]) - 1; i >= 0; i-- { - f := current.Files[0][i] - iter, rangeDelIter, err := d.newIters(f, &dbi.opts, nil) - if err != nil { - // Ensure the mergingIter is initialized so Iterator.Close will properly - // close any sstable iterators that have been opened. - buf.merging.init(&dbi.opts, d.cmp, mlevels...) - _ = dbi.Close() - // Return a new alloced Iterator structure, because dbi.Close will - // return dbi to a sync.Pool. - return &Iterator{err: err} - } - mlevels = append(mlevels, mergingIterLevel{ - iter: iter, - rangeDelIter: rangeDelIter, - }) - } - // Determine the final size for mlevels so that we can avoid any more // reallocations. This is important because each levelIter will hold a // reference to elements in mlevels. start := len(mlevels) + current := readState.current + for sl := 0; sl < len(current.L0SubLevels.Files); sl++ { + if len(current.L0SubLevels.Files[sl]) > 0 { + mlevels = append(mlevels, mergingIterLevel{}) + } + } for level := 1; level < len(current.Files); level++ { if len(current.Files[level]) == 0 { continue @@ -724,13 +707,11 @@ func (d *DB) newIterInternal( finalMLevels := mlevels mlevels = mlevels[start:] - // Add level iterators for the remaining files. levels := buf.levels[:] - for level := 1; level < len(current.Files); level++ { - if len(current.Files[level]) == 0 { - continue + addLevelIterForFiles := func(files []*manifest.FileMetadata, level int) { + if len(files) == 0 { + return } - var li *levelIter if len(levels) > 0 { li = &levels[0] @@ -739,7 +720,7 @@ func (d *DB) newIterInternal( li = &levelIter{} } - li.init(dbi.opts, d.cmp, d.newIters, current.Files[level], level, nil) + li.init(dbi.opts, d.cmp, d.newIters, files, level, nil) li.initRangeDel(&mlevels[0].rangeDelIter) li.initSmallestLargestUserKey(&mlevels[0].smallestUserKey, &mlevels[0].largestUserKey, &mlevels[0].isLargestUserKeyRangeDelSentinel) @@ -747,6 +728,17 @@ func (d *DB) newIterInternal( mlevels = mlevels[1:] } + // Add level iterators for the L0 sublevels, iterating from newest to + // oldest. + for i := len(current.L0SubLevels.Files) - 1; i >= 0; i-- { + addLevelIterForFiles(current.L0SubLevels.Files[i], 0) + } + + // Add level iterators for the non-empty non-L0 levels. + for level := 1; level < len(current.Files); level++ { + addLevelIterForFiles(current.Files[level], level) + } + buf.merging.init(&dbi.opts, d.cmp, finalMLevels...) buf.merging.snapshot = seqNum buf.merging.elideRangeTombstones = true diff --git a/db_test.go b/db_test.go index c8b160e6a04..745ba6adc7e 100644 --- a/db_test.go +++ b/db_test.go @@ -413,13 +413,13 @@ func TestLargeBatch(t *testing.T) { // Verify this results in one L0 table being created. require.NoError(t, try(100*time.Microsecond, 20*time.Second, - verifyLSM("0:\n 000005:[a-a]\n"))) + verifyLSM("0.0:\n 000005:[a-a]\n"))) require.NoError(t, d.Set([]byte("b"), bytes.Repeat([]byte("b"), 512), nil)) // Verify this results in a second L0 table being created. require.NoError(t, try(100*time.Microsecond, 20*time.Second, - verifyLSM("0:\n 000005:[a-a]\n 000007:[b-b]\n"))) + verifyLSM("0.0:\n 000005:[a-a]\n 000007:[b-b]\n"))) // Allocate a bunch of batches to exhaust the batchPool. None of these // batches should have a non-zero count. diff --git a/error_test.go b/error_test.go index 8ab9e3b2338..cf613d9bb4d 100644 --- a/error_test.go +++ b/error_test.go @@ -189,7 +189,7 @@ func TestRequireReadError(t *testing.T) { require.NoError(t, d.Set(key1, value, nil)) require.NoError(t, d.Flush()) expectLSM(` -0: +0.0: 000007:[a1#4,SET-a2#72057594037927935,RANGEDEL] 6: 000005:[a1#1,SET-a2#2,SET] @@ -274,7 +274,7 @@ func TestCorruptReadError(t *testing.T) { require.NoError(t, d.Set(key1, value, nil)) require.NoError(t, d.Flush()) expectLSM(` -0: +0.0: 000007:[a1#4,SET-a2#72057594037927935,RANGEDEL] 6: 000005:[a1#1,SET-a2#2,SET] diff --git a/get_iter.go b/get_iter.go index 610b233f3ec..ada36cabe3b 100644 --- a/get_iter.go +++ b/get_iter.go @@ -29,7 +29,7 @@ type getIter struct { level int batch *Batch mem flushableList - l0 []*fileMetadata + l0 [][]*fileMetadata version *version iterKey *InternalKey iterValue []byte @@ -139,15 +139,12 @@ func (g *getIter) Next() (*InternalKey, []byte) { if g.level == 0 { // Create iterators from L0 from newest to oldest. if n := len(g.l0); n > 0 { - l := g.l0[n-1] - g.iter, g.rangeDelIter, g.err = g.newIters( - l, - nil, /* iter options */ - nil /* bytes iterated */) - if g.err != nil { - return nil, nil - } + files := g.l0[n-1] g.l0 = g.l0[:n-1] + iterOpts := IterOptions{logger: g.logger} + g.levelIter.init(iterOpts, g.cmp, g.newIters, files, 0, nil) + g.levelIter.initRangeDel(&g.rangeDelIter) + g.iter = &g.levelIter g.iterKey, g.iterValue = g.iter.SeekGE(g.key) continue } diff --git a/get_iter_test.go b/get_iter_test.go index 4ecb842db2d..a474fc15406 100644 --- a/get_iter_test.go +++ b/get_iter_test.go @@ -519,6 +519,9 @@ func TestGetIter(t *testing.T) { v.Files[tt.level] = append(v.Files[tt.level], meta) } + if err := v.InitL0Sublevels(cmp, base.DefaultFormatter); err != nil { + t.Fatalf("desc=%q: internal error: %s", desc, err.Error()) + } err := v.CheckOrdering(cmp, base.DefaultFormatter) if tc.badOrdering && err == nil { t.Errorf("desc=%q: want bad ordering, got nil error", desc) @@ -539,7 +542,7 @@ func TestGetIter(t *testing.T) { get.equal = equal get.newIters = newIter get.key = ikey.UserKey - get.l0 = v.Files[0] + get.l0 = v.L0SubLevels.Files get.version = v get.snapshot = ikey.SeqNum() + 1 diff --git a/ingest_test.go b/ingest_test.go index 74bdd7277fd..32da882c006 100644 --- a/ingest_test.go +++ b/ingest_test.go @@ -763,7 +763,7 @@ func TestConcurrentIngestCompact(t *testing.T) { ingest("c") expectLSM(` -0: +0.0: 000005:[a#2,SET-a#2,SET] 000007:[c#4,SET-c#4,SET] 6: @@ -790,7 +790,7 @@ func TestConcurrentIngestCompact(t *testing.T) { compact("a", "z") expectLSM(` -0: +0.0: 000009:[b#5,SET-b#5,SET] 6: 000008:[a#0,SET-c#0,SET] diff --git a/internal/manifest/l0_sublevels_test.go b/internal/manifest/l0_sublevels_test.go index e25848f294c..fbd582690a8 100644 --- a/internal/manifest/l0_sublevels_test.go +++ b/internal/manifest/l0_sublevels_test.go @@ -96,6 +96,7 @@ func TestL0SubLevels(t *testing.T) { var level int var err error var fileMetas [NumLevels][]*FileMetadata + var explicitSublevels [][]*FileMetadata var sublevels *L0SubLevels baseLevel := NumLevels - 1 @@ -103,15 +104,27 @@ func TestL0SubLevels(t *testing.T) { switch td.Cmd { case "define": fileMetas = [NumLevels][]*FileMetadata{} + explicitSublevels = [][]*FileMetadata{} baseLevel = NumLevels - 1 + sublevel := -1 + sublevels = nil for _, data := range strings.Split(td.Input, "\n") { data = strings.TrimSpace(data) - switch data { + switch data[:2] { case "L0", "L1", "L2", "L3", "L4", "L5", "L6": - level, err = strconv.Atoi(data[1:]) + level, err = strconv.Atoi(data[1:2]) if err != nil { return err.Error() } + if level == 0 && len(data) > 3 { + // Sublevel was specified. + sublevel, err = strconv.Atoi(data[3:]) + if err != nil { + return err.Error() + } + } else { + sublevel = -1 + } default: meta, err := parseMeta(data) if err != nil { @@ -121,10 +134,17 @@ func TestL0SubLevels(t *testing.T) { baseLevel = level } fileMetas[level] = append(fileMetas[level], meta) + if sublevel != -1 { + for len(explicitSublevels) <= sublevel { + explicitSublevels = append(explicitSublevels, []*FileMetadata{}) + } + explicitSublevels[sublevel] = append(explicitSublevels[sublevel], meta) + } } } flushSplitMaxBytes := 64 + initialize := true for _, arg := range td.CmdArgs { switch arg.Key { case "flush_split_max_bytes": @@ -132,17 +152,32 @@ func TestL0SubLevels(t *testing.T) { if err != nil { t.Fatal(err) } + case "no_initialize": + // This case is for use with explicitly-specified sublevels + // only. + initialize = false } } for i := 0; i < NumLevels; i++ { SortBySeqNum(fileMetas[i]) } - sublevels, err = NewL0SubLevels( - fileMetas[0], - base.DefaultComparer.Compare, - base.DefaultFormatter, - uint64(flushSplitMaxBytes)) + if initialize { + sublevels, err = NewL0SubLevels( + fileMetas[0], + base.DefaultComparer.Compare, + base.DefaultFormatter, + uint64(flushSplitMaxBytes)) + } else { + // This case is for use with explicitly-specified sublevels + // only. + sublevels = &L0SubLevels{ + Files: explicitSublevels, + cmp: base.DefaultComparer.Compare, + format: base.DefaultFormatter, + filesByAge: fileMetas[0], + } + } if err != nil { t.Fatal(err) @@ -166,6 +201,13 @@ func TestL0SubLevels(t *testing.T) { return builder.String() case "max-depth-after-ongoing-compactions": return strconv.Itoa(sublevels.MaxDepthAfterOngoingCompactions()) + case "l0-check-ordering": + for sublevel, files := range sublevels.Files { + if err := CheckOrdering(base.DefaultComparer.Compare, base.DefaultFormatter, 0, sublevel, files); err != nil { + return err.Error() + } + } + return "OK" } return fmt.Sprintf("unrecognized command: %s", td.Cmd) }) diff --git a/internal/manifest/testdata/l0_sublevels b/internal/manifest/testdata/l0_sublevels index 17eaeb5cbb5..eb4b93d5024 100644 --- a/internal/manifest/testdata/l0_sublevels +++ b/internal/manifest/testdata/l0_sublevels @@ -14,6 +14,48 @@ flush split keys(3): [b, e, j] 000003:e#5,1-j#7,1 compacting file count: 0, base compacting intervals: +define no_initialize +L0.2 + 0009:a.SET.10-b.SET.10 +L0.1 + 0003:e.SET.5-j.SET.7 +L0.0 + 0007:b.SET.6-j.SET.8 +---- +file count: 3, sublevels: 3, intervals: 0 +flush split keys(0): [] +0.2: file count: 1, bytes: 256, width (mean, max): 1.0, 1, interval range: [0, 0] + 000009:a#10,1-b#10,1 +0.1: file count: 1, bytes: 256, width (mean, max): 1.0, 1, interval range: [0, 0] + 000003:e#5,1-j#7,1 +0.0: file count: 1, bytes: 256, width (mean, max): 1.0, 1, interval range: [0, 0] + 000007:b#6,1-j#8,1 +compacting file count: 0, base compacting intervals: + +l0-check-ordering +---- +OK + +define no_initialize +L0.1 + 0009:a.SET.10-b.SET.10 +L0.0 + 0007:b.SET.6-j.SET.8 + 0003:e.SET.5-j.SET.7 +---- +file count: 3, sublevels: 2, intervals: 0 +flush split keys(0): [] +0.1: file count: 1, bytes: 256, width (mean, max): 1.0, 1, interval range: [0, 0] + 000009:a#10,1-b#10,1 +0.0: file count: 2, bytes: 512, width (mean, max): 1.0, 1, interval range: [0, 0] + 000007:b#6,1-j#8,1 + 000003:e#5,1-j#7,1 +compacting file count: 0, base compacting intervals: + +l0-check-ordering +---- +L0.0 files 000007 and 000003 have overlapping ranges: [b#6,SET-j#8,SET] vs [e#5,SET-j#7,SET] + define L0 0001:a.SET.2-b.SET.3 diff --git a/internal/manifest/testdata/version_check_ordering b/internal/manifest/testdata/version_check_ordering index 16860f9944f..5f393be856b 100644 --- a/internal/manifest/testdata/version_check_ordering +++ b/internal/manifest/testdata/version_check_ordering @@ -17,9 +17,9 @@ L0 a.SET.1-b.SET.2 ---- L0 files 000001 and 000002 are not properly ordered: <#3-#4> vs <#1-#2> -0: - 000001:[c#3,SET-d#4,SET] +0.0: 000002:[a#1,SET-b#2,SET] + 000001:[c#3,SET-d#4,SET] check-ordering L0 @@ -66,17 +66,19 @@ L0 m.SET.20-n.SET.20 ---- L0 flushed file 000007 has smallest sequence number coincident with an ingested file : <#15-#17> vs <#15-#15> -0: - 000001:[c#3,SET-d#4,SET] +0.2: + 000007:[a#15,SET-j#17,SET] + 000010:[m#20,SET-n#20,SET] +0.1: + 000006:[b#15,SET-d#15,SET] + 000009:[k#16,SET-n#19,SET] +0.0: 000002:[a#1,SET-b#5,SET] + 000001:[c#3,SET-d#4,SET] 000003:[e#2,SET-f#7,SET] 000004:[g#6,SET-h#12,SET] 000005:[i#8,SET-j#13,SET] - 000006:[b#15,SET-d#15,SET] - 000007:[a#15,SET-j#17,SET] 000008:[m#18,SET-n#18,SET] - 000009:[k#16,SET-n#19,SET] - 000010:[m#20,SET-n#20,SET] check-ordering L0 @@ -84,9 +86,10 @@ L0 a.SET.1-b.SET.2 ---- L0 files 000001 and 000002 are not properly ordered: <#3-#3> vs <#1-#2> -0: - 000001:[a#3,SET-d#3,SET] +0.1: 000002:[a#1,SET-b#2,SET] +0.0: + 000001:[a#3,SET-d#3,SET] check-ordering L0 @@ -94,9 +97,10 @@ L0 a.SET.3-b.SET.3 ---- L0 files 000001 and 000002 are not properly ordered: <#2-#4> vs <#3-#3> -0: - 000001:[a#2,SET-d#4,SET] +0.1: 000002:[a#3,SET-b#3,SET] +0.0: + 000001:[a#2,SET-d#4,SET] check-ordering L0 @@ -104,9 +108,10 @@ L0 a.SET.3-b.SET.3 ---- L0 file 000002 does not have strictly increasing largest seqnum: <#3-#3> vs -0: - 000001:[a#3,SET-d#3,SET] +0.1: 000002:[a#3,SET-b#3,SET] +0.0: + 000001:[a#3,SET-d#3,SET] check-ordering L0 @@ -114,9 +119,10 @@ L0 a.SET.3-d.SET.5 ---- L0 flushed file 000002 has smallest sequence number coincident with an ingested file : <#3-#5> vs <#3-#3> -0: - 000001:[a#3,SET-d#3,SET] +0.1: 000002:[a#3,SET-d#5,SET] +0.0: + 000001:[a#3,SET-d#3,SET] check-ordering L0 @@ -131,9 +137,10 @@ L0 a.SET.5-d.SET.5 ---- L0 file 000002 does not have strictly increasing largest seqnum: <#5-#5> vs -0: - 000001:[a#3,SET-d#5,SET] +0.1: 000002:[a#5,SET-d#5,SET] +0.0: + 000001:[a#3,SET-d#5,SET] check-ordering L0 @@ -142,10 +149,12 @@ L0 a.SET.4-d.SET.6 ---- L0 flushed file 000003 has smallest sequence number coincident with an ingested file : <#4-#6> vs <#4-#4> -0: - 000001:[a#4,SET-d#4,SET] - 000002:[a#5,SET-d#5,SET] +0.2: 000003:[a#4,SET-d#6,SET] +0.1: + 000002:[a#5,SET-d#5,SET] +0.0: + 000001:[a#4,SET-d#4,SET] check-ordering L0 diff --git a/internal/manifest/testdata/version_edit_apply b/internal/manifest/testdata/version_edit_apply index 6a70337d62b..fc445fd4e0b 100644 --- a/internal/manifest/testdata/version_edit_apply +++ b/internal/manifest/testdata/version_edit_apply @@ -11,7 +11,7 @@ edit 1:a.SET.1-b.SET.2 4:c.SET.3-d.SET.4 ---- -0: +0.0: 000002:[c#3,SET-d#4,SET] 2: 000001:[a#1,SET-b#2,SET] @@ -53,10 +53,12 @@ edit L0 4:b.SET.3-d.SET.5 ---- -0: - 000001:[a#1,SET-c#2,SET] - 000002:[c#3,SET-d#4,SET] +0.2: 000004:[b#3,SET-d#5,SET] +0.1: + 000002:[c#3,SET-d#4,SET] +0.0: + 000001:[a#1,SET-c#2,SET] zombies [] apply @@ -68,10 +70,12 @@ edit L0 4:b.SET.0-d.SET.0 ---- -0: - 000004:[b#0,SET-d#0,SET] - 000001:[a#1,SET-c#2,SET] +0.2: 000002:[c#3,SET-d#4,SET] +0.1: + 000001:[a#1,SET-c#2,SET] +0.0: + 000004:[b#0,SET-d#0,SET] zombies [] @@ -82,16 +86,17 @@ edit 1:a.SET.1-c.SET.2 4:b.SET.3-d.SET.5 ---- -0: - 000001:[a#1,SET-c#2,SET] +0.1: 000004:[b#3,SET-d#5,SET] +0.0: + 000001:[a#1,SET-c#2,SET] zombies [] apply L0 1:a.SET.1-c.SET.2 ---- -0: +0.0: 000001:[a#1,SET-c#2,SET] zombies [] @@ -161,6 +166,6 @@ edit L0 2 ---- -0: +0.0: 000001:[a#1,SET-b#2,SET] zombies [] diff --git a/internal/manifest/version.go b/internal/manifest/version.go index 35b3f02db48..c6706ca8bf4 100644 --- a/internal/manifest/version.go +++ b/internal/manifest/version.go @@ -164,6 +164,18 @@ func SortBySmallest(files []*FileMetadata, cmp Compare) { sort.Sort(bySmallest{files, cmp}) } +func overlaps(files []*FileMetadata, cmp Compare, start, end []byte) (lower, upper int) { + // Binary search to find the range of files which overlaps with our target + // range. + lower = sort.Search(len(files), func(i int) bool { + return cmp(files[i].Largest.UserKey, start) >= 0 + }) + upper = sort.Search(len(files), func(i int) bool { + return cmp(files[i].Smallest.UserKey, end) > 0 + }) + return lower, upper +} + // NumLevels is the number of levels a Version contains. const NumLevels = 7 @@ -194,6 +206,18 @@ const NumLevels = 7 type Version struct { refs int32 + // The level 0 sstables are organized in a series of sublevels. Similar to + // the seqnum invariant in normal levels, there is no internal key in a + // higher level table that has both the same user key and a higher sequence + // number. Within a sublevel, tables are sorted by their internal key range + // and any two tables at the same sublevel do not overlap. Unlike the normal + // levels, sublevel n contains older tables (lower sequence numbers) than + // sublevel n+1. + // + // L0SubLevels.Files contains L0 files ordered by sublevels. All the files + // in Files[0] are in L0SubLevels.Files. + L0SubLevels *L0SubLevels + Files [NumLevels][]*FileMetadata // The callback to invoke when the last reference to a version is @@ -218,6 +242,18 @@ func (v *Version) Pretty(format base.Formatter) string { if len(v.Files[level]) == 0 { continue } + + if level == 0 { + for sublevel := len(v.L0SubLevels.Files) - 1; sublevel >= 0; sublevel-- { + fmt.Fprintf(&buf, "0.%d:\n", sublevel) + for _, f := range v.L0SubLevels.Files[sublevel] { + fmt.Fprintf(&buf, " %06d:[%s-%s]\n", f.FileNum, + format(f.Smallest.UserKey), format(f.Largest.UserKey)) + } + } + continue + } + fmt.Fprintf(&buf, "%d:\n", level) for j := range v.Files[level] { f := v.Files[level][j] @@ -236,6 +272,18 @@ func (v *Version) DebugString(format base.Formatter) string { if len(v.Files[level]) == 0 { continue } + + if level == 0 { + for sublevel := len(v.L0SubLevels.Files) - 1; sublevel >= 0; sublevel-- { + fmt.Fprintf(&buf, "0.%d:\n", sublevel) + for _, f := range v.L0SubLevels.Files[sublevel] { + fmt.Fprintf(&buf, " %06d:[%s-%s]\n", f.FileNum, + f.Smallest.Pretty(format), f.Largest.Pretty(format)) + } + } + continue + } + fmt.Fprintf(&buf, "%d:\n", level) for j := range v.Files[level] { f := v.Files[level][j] @@ -300,6 +348,15 @@ func (v *Version) Next() *Version { return v.next } +// InitL0Sublevels initializes the L0SubLevels +func (v *Version) InitL0Sublevels(cmp Compare, formatter base.Formatter) error { + // TODO(bilal): Experiment with flushSplitMaxBytes to pick a good value, and + // make it configurable. + var err error + v.L0SubLevels, err = NewL0SubLevels(v.Files[0], cmp, formatter, 10<<20) + return err +} + // Overlaps returns all elements of v.files[level] whose user key range // intersects the inclusive range [start, end]. If level is non-zero then the // user key ranges of v.files[level] are assumed to not overlap (although they @@ -362,15 +419,9 @@ func (v *Version) Overlaps(level int, cmp Compare, start, end []byte) (ret []*Fi } return } - // Binary search to find the range of files which overlaps with our target - // range. + files := v.Files[level] - lower := sort.Search(len(files), func(i int) bool { - return cmp(files[i].Largest.UserKey, start) >= 0 - }) - upper := sort.Search(len(files), func(i int) bool { - return cmp(files[i].Smallest.UserKey, end) > 0 - }) + lower, upper := overlaps(files, cmp, start, end) if lower >= upper { return nil } @@ -381,8 +432,14 @@ func (v *Version) Overlaps(level int, cmp Compare, start, end []byte) (ret []*Fi // increasing file numbers (for level 0 files) and increasing and non- // overlapping internal key ranges (for level non-0 files). func (v *Version) CheckOrdering(cmp Compare, format base.Formatter) error { - for level, files := range v.Files { - if err := CheckOrdering(cmp, format, level, files); err != nil { + for sublevel := len(v.L0SubLevels.Files) - 1; sublevel >= 0; sublevel-- { + if err := CheckOrdering(cmp, format, 0, sublevel, v.L0SubLevels.Files[sublevel]); err != nil { + return errors.Errorf("%s\n%s", err, v.DebugString(format)) + } + } + + for level := 0; level < len(v.Files); level++ { + if err := CheckOrdering(cmp, format, level, InvalidSublevel, v.Files[level]); err != nil { return errors.Errorf("%s\n%s", err, v.DebugString(format)) } } @@ -448,11 +505,32 @@ func (l *VersionList) Remove(v *Version) { v.list = nil // avoid memory leaks } +// InvalidSublevel denotes an invalid or non-applicable sublevel. For use with +// CheckOrdering. +const InvalidSublevel = -1 + +// levelInfo stores the level (and for L0, sublevel) for a set of files. Used +// in CheckOrdering. +type levelInfo struct{ + level, sublevel int +} + +func (l levelInfo) String() string { + if l.level == 0 && l.sublevel != InvalidSublevel { + return fmt.Sprintf("L0.%d", l.sublevel) + } + return fmt.Sprintf("L%d", l.level) +} + // CheckOrdering checks that the files are consistent with respect to // seqnums (for level 0 files -- see detailed comment below) and increasing and non- // overlapping internal key ranges (for non-level 0 files). -func CheckOrdering(cmp Compare, format base.Formatter, level int, files []*FileMetadata) error { - if level == 0 { +func CheckOrdering(cmp Compare, format base.Formatter, level int, sublevel int, files []*FileMetadata) error { + // The invariants to check for L0 sublevels are the same as the ones to + // check for all other levels. However, if L0 is not organized into + // sublevels, or if all L0 files are being passed in, we do the legacy L0 + // checks, defined in the detailed comment below. + if level == 0 && sublevel == InvalidSublevel { // We have 2 kinds of files: // - Files with exactly one sequence number: these could be either ingested files // or flushed files. We cannot tell the difference between them based on FileMetadata, @@ -548,24 +626,28 @@ func CheckOrdering(cmp Compare, format base.Formatter, level int, files []*FileM } } } else { + levelInfo := levelInfo{ + level: level, + sublevel: sublevel, + } for i := range files { f := files[i] if base.InternalCompare(cmp, f.Smallest, f.Largest) > 0 { - return errors.Errorf("L%d file %s has inconsistent bounds: %s vs %s", - errors.Safe(level), errors.Safe(f.FileNum), + return errors.Errorf("%s file %s has inconsistent bounds: %s vs %s", + errors.Safe(levelInfo), errors.Safe(f.FileNum), f.Smallest.Pretty(format), f.Largest.Pretty(format)) } if i > 0 { prev := files[i-1] if !prev.lessSmallestKey(f, cmp) { - return errors.Errorf("L%d files %s and %s are not properly ordered: [%s-%s] vs [%s-%s]", - errors.Safe(level), errors.Safe(prev.FileNum), errors.Safe(f.FileNum), + return errors.Errorf("%s files %s and %s are not properly ordered: [%s-%s] vs [%s-%s]", + errors.Safe(levelInfo), errors.Safe(prev.FileNum), errors.Safe(f.FileNum), prev.Smallest.Pretty(format), prev.Largest.Pretty(format), f.Smallest.Pretty(format), f.Largest.Pretty(format)) } if base.InternalCompare(cmp, prev.Largest, f.Smallest) >= 0 { - return errors.Errorf("L%d files %s and %s have overlapping ranges: [%s-%s] vs [%s-%s]", - errors.Safe(level), errors.Safe(prev.FileNum), errors.Safe(f.FileNum), + return errors.Errorf("%s files %s and %s have overlapping ranges: [%s-%s] vs [%s-%s]", + errors.Safe(levelInfo), errors.Safe(prev.FileNum), errors.Safe(f.FileNum), prev.Smallest.Pretty(format), prev.Largest.Pretty(format), f.Smallest.Pretty(format), f.Largest.Pretty(format)) } diff --git a/internal/manifest/version_edit.go b/internal/manifest/version_edit.go index 68a6074c9b8..82f9690c6a7 100644 --- a/internal/manifest/version_edit.go +++ b/internal/manifest/version_edit.go @@ -458,15 +458,15 @@ func (b *BulkVersionEdit) Accumulate(ve *VersionEdit) { } } -// Apply applies the delta b to the current version to produce a new version. The -// new version is consistent with respect to the internal key comparer icmp. +// Apply applies the delta b to the current version to produce a new +// version. The new version is consistent with respect to the comparer cmp. // // curr may be nil, which is equivalent to a pointer to a zero version. // -// If zombies is non-nil it is populated with the file numbers (and sizes) of -// deleted files. These files are considered zombies because they are no longer -// referenced by the returned Version, but cannot be deleted from disk as they -// are still in use by the incoming Version. +// On success, a map of zombie files containing the file numbers and sizes of +// deleted files is returned. These files are considered zombies because they +// are no longer referenced by the returned Version, but cannot be deleted from +// disk as they are still in use by the incoming Version. func (b *BulkVersionEdit) Apply( curr *Version, cmp Compare, format base.Formatter, ) (_ *Version, zombies map[base.FileNum]uint64, _ error) { @@ -488,6 +488,16 @@ func (b *BulkVersionEdit) Apply( for level := range v.Files { if len(b.Added[level]) == 0 && len(b.Deleted[level]) == 0 { // There are no edits on this level. + if level == 0 { + // Initialize L0SubLevels. + if curr == nil || curr.L0SubLevels == nil { + if err := v.InitL0Sublevels(cmp, format); err != nil { + return nil, nil, errors.Wrap(err, "pebble: internal error") + } + } else { + v.L0SubLevels = curr.L0SubLevels + } + } if curr == nil { continue } @@ -547,7 +557,10 @@ func (b *BulkVersionEdit) Apply( } } SortBySeqNum(v.Files[level]) - if err := CheckOrdering(cmp, format, 0, v.Files[level]); err != nil { + if err := v.InitL0Sublevels(cmp, format); err != nil { + return nil, nil, errors.Wrap(err, "pebble: internal error") + } + if err := CheckOrdering(cmp, format, 0, InvalidSublevel, v.Files[level]); err != nil { return nil, nil, errors.Wrap(err, "pebble: internal error") } continue diff --git a/internal/manifest/version_edit_test.go b/internal/manifest/version_edit_test.go index bcf90224613..9c769e03218 100644 --- a/internal/manifest/version_edit_test.go +++ b/internal/manifest/version_edit_test.go @@ -332,6 +332,14 @@ func TestVersionEditApply(t *testing.T) { } } } + + if v != nil { + SortBySeqNum(v.Files[0]) + if err := v.InitL0Sublevels(base.DefaultComparer.Compare, base.DefaultFormatter); err != nil { + return err.Error() + } + } + bve := BulkVersionEdit{} bve.Accumulate(ve) newv, zombies, err := bve.Apply(v, base.DefaultComparer.Compare, base.DefaultFormatter) diff --git a/internal/manifest/version_test.go b/internal/manifest/version_test.go index 885dbd2914c..34ad41d582e 100644 --- a/internal/manifest/version_test.go +++ b/internal/manifest/version_test.go @@ -315,6 +315,9 @@ func TestCheckOrdering(t *testing.T) { cmp := base.DefaultComparer.Compare result := "OK" + if err := v.InitL0Sublevels(cmp, base.DefaultFormatter); err != nil { + return fmt.Sprint(err) + } err := v.CheckOrdering(cmp, base.DefaultFormatter) if err != nil { result = fmt.Sprint(err) diff --git a/metrics.go b/metrics.go index 7ab0ad6a39e..af7aa88825f 100644 --- a/metrics.go +++ b/metrics.go @@ -30,6 +30,11 @@ func formatCacheMetrics(buf *bytes.Buffer, m *CacheMetrics, name string) { // LevelMetrics holds per-level metrics such as the number of files and total // size of the files, and compaction related metrics. type LevelMetrics struct { + // The number of sublevels within the level. The sublevel count corresponds + // to the read amplification for the level. An empty level will have a + // sublevel count of 0, implying no read amplification. Only L0 will have + // a sublevel count other than 0 or 1. + Sublevels int32 // The total number of files in the level. NumFiles int64 // The total size in bytes of the files in the level. @@ -93,7 +98,7 @@ func (m *LevelMetrics) WriteAmp() float64 { // format generates a string of the receiver's metrics, formatting it into the // supplied buffer. func (m *LevelMetrics) format(buf *bytes.Buffer, score string) { - fmt.Fprintf(buf, "%9d %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7.1f\n", + fmt.Fprintf(buf, "%9d %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7d %7.1f\n", m.NumFiles, humanize.IEC.Uint64(m.Size), score, @@ -105,6 +110,7 @@ func (m *LevelMetrics) format(buf *bytes.Buffer, score string) { humanize.IEC.Uint64(m.BytesFlushed+m.BytesCompacted), humanize.SI.Uint64(m.TablesFlushed+m.TablesCompacted), humanize.IEC.Uint64(m.BytesRead), + m.Sublevels, m.WriteAmp()) } @@ -182,7 +188,7 @@ func (m *Metrics) formatWAL(buf *bytes.Buffer) { if m.WAL.BytesIn > 0 { writeAmp = float64(m.WAL.BytesWritten) / float64(m.WAL.BytesIn) } - fmt.Fprintf(buf, " WAL %9d %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7.1f\n", + fmt.Fprintf(buf, " WAL %9d %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7s %7.1f\n", m.WAL.Files, humanize.Uint64(m.WAL.Size), notApplicable, @@ -194,6 +200,7 @@ func (m *Metrics) formatWAL(buf *bytes.Buffer) { humanize.Uint64(m.WAL.BytesWritten), notApplicable, notApplicable, + notApplicable, writeAmp) } @@ -229,13 +236,14 @@ func (m *Metrics) String() string { var buf bytes.Buffer var total LevelMetrics fmt.Fprintf(&buf, "__level_____count____size___score______in__ingest(sz_cnt)"+ - "____move(sz_cnt)___write(sz_cnt)____read___w-amp\n") + "____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp\n") m.formatWAL(&buf) for level := 0; level < numLevels; level++ { l := &m.Levels[level] fmt.Fprintf(&buf, "%7d ", level) l.format(&buf, fmt.Sprintf("%0.2f", l.Score)) total.Add(l) + total.Sublevels += l.Sublevels total.NumFiles += l.NumFiles total.Size += l.Size } diff --git a/metrics_test.go b/metrics_test.go index e2cad755344..431caab5c97 100644 --- a/metrics_test.go +++ b/metrics_test.go @@ -45,6 +45,7 @@ func TestMetricsFormat(t *testing.T) { for i := range m.Levels { l := &m.Levels[i] base := uint64((i + 1) * 100) + l.Sublevels = int32(i + 1) l.NumFiles = int64(base) + 1 l.Size = base + 2 l.Score = float64(base) + 3 @@ -61,16 +62,16 @@ func TestMetricsFormat(t *testing.T) { } const expected = ` -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 21 23 B - 24 B - - - - 25 B - - 1.0 - 0 101 102 B 103.00 104 B 104 B 112 106 B 113 217 B 221 107 B 2.1 - 1 201 202 B 203.00 204 B 204 B 212 206 B 213 417 B 421 207 B 2.0 - 2 301 302 B 303.00 304 B 304 B 312 306 B 313 617 B 621 307 B 2.0 - 3 401 402 B 403.00 404 B 404 B 412 406 B 413 817 B 821 407 B 2.0 - 4 501 502 B 503.00 504 B 504 B 512 506 B 513 1017 B 1.0 K 507 B 2.0 - 5 601 602 B 603.00 604 B 604 B 612 606 B 613 1.2 K 1.2 K 607 B 2.0 - 6 701 702 B 703.00 704 B 704 B 712 706 B 713 1.4 K 1.4 K 707 B 2.0 - total 2807 2.7 K - 2.8 K 2.8 K 2.9 K 2.8 K 2.9 K 8.4 K 5.7 K 2.8 K 3.0 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 21 23 B - 24 B - - - - 25 B - - - 1.0 + 0 101 102 B 103.00 104 B 104 B 112 106 B 113 217 B 221 107 B 1 2.1 + 1 201 202 B 203.00 204 B 204 B 212 206 B 213 417 B 421 207 B 2 2.0 + 2 301 302 B 303.00 304 B 304 B 312 306 B 313 617 B 621 307 B 3 2.0 + 3 401 402 B 403.00 404 B 404 B 412 406 B 413 817 B 821 407 B 4 2.0 + 4 501 502 B 503.00 504 B 504 B 512 506 B 513 1017 B 1.0 K 507 B 5 2.0 + 5 601 602 B 603.00 604 B 604 B 612 606 B 613 1.2 K 1.2 K 607 B 6 2.0 + 6 701 702 B 703.00 704 B 704 B 712 706 B 713 1.4 K 1.4 K 707 B 7 2.0 + total 2807 2.7 K - 2.8 K 2.8 K 2.9 K 2.8 K 2.9 K 8.4 K 5.7 K 2.8 K 28 3.0 flush 7 compact 5 6 B (size == estimated-debt) memtbl 11 10 B @@ -166,7 +167,12 @@ func TestMetrics(t *testing.T) { return err.Error() } } - iters[name] = d.NewIter(nil) + iter := d.NewIter(nil) + // Some iterators (eg. levelIter) do not instantiate the underlying + // iterator until the first positioning call. Position the iterator + // so that levelIters will have loaded an sstable. + iter.First() + iters[name] = iter return "" case "metrics": diff --git a/range_del_test.go b/range_del_test.go index 282ca89d6b5..03d6c7c185b 100644 --- a/range_del_test.go +++ b/range_del_test.go @@ -165,7 +165,7 @@ func TestRangeDelCompactionTruncation(t *testing.T) { require.NoError(t, d.Set([]byte("c"), []byte("e"), nil)) require.NoError(t, d.Flush()) expectLSM(` -0: +0.0: 000011:[b#4,SET-c#5,SET] 1: 000008:[a#3,RANGEDEL-b#72057594037927935,RANGEDEL] diff --git a/testdata/event_listener b/testdata/event_listener index 20af4090d2b..e1b4160c97b 100644 --- a/testdata/event_listener +++ b/testdata/event_listener @@ -153,16 +153,16 @@ sync: db metrics ---- -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 1 27 B - 48 B - - - - 108 B - - 2.2 - 0 2 1.6 K 0.50 81 B 825 B 1 0 B 0 2.3 K 3 0 B 28.5 - 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 6 1 770 B 0.00 1.5 K 0 B 0 0 B 0 770 B 1 1.5 K 0.5 - total 3 2.3 K - 933 B 825 B 1 0 B 0 3.9 K 4 1.5 K 4.3 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 1 27 B - 48 B - - - - 108 B - - - 2.2 + 0 2 1.6 K 0.50 81 B 825 B 1 0 B 0 2.3 K 3 0 B 2 28.5 + 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 6 1 770 B 0.00 1.5 K 0 B 0 0 B 0 770 B 1 1.5 K 1 0.5 + total 3 2.3 K - 933 B 825 B 1 0 B 0 3.9 K 4 1.5 K 3 4.3 flush 3 compact 1 1.6 K (size == estimated-debt) memtbl 1 256 K diff --git a/testdata/ingest b/testdata/ingest index 5ab2100a3e9..a4c24afae17 100644 --- a/testdata/ingest +++ b/testdata/ingest @@ -55,7 +55,7 @@ ingest ext1 lsm ---- -0: +0.0: 000007:[a#2,SET-b#2,DEL] 6: 000006:[a#1,SET-b#1,SET] @@ -85,9 +85,10 @@ ingest ext2 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] +0.1: 000008:[a#3,SET-c#3,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] 6: 000006:[a#1,SET-b#1,SET] @@ -119,10 +120,12 @@ ingest ext3 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.2: 000009:[b#4,MERGE-c#4,DEL] +0.1: + 000008:[a#3,SET-c#3,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] 6: 000006:[a#1,SET-b#1,SET] @@ -154,10 +157,12 @@ ingest ext4 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.2: 000009:[b#4,MERGE-c#4,DEL] +0.1: + 000008:[a#3,SET-c#3,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] 6: 000006:[a#1,SET-b#1,SET] 000010:[x#5,SET-y#5,SET] @@ -194,12 +199,14 @@ ingest ext5 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.2: 000009:[b#4,MERGE-c#4,DEL] - 000013:[j#6,SET-k#7,SET] +0.1: + 000008:[a#3,SET-c#3,SET] 000011:[k#8,SET-k#8,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] + 000013:[j#6,SET-k#7,SET] 6: 000006:[a#1,SET-b#1,SET] 000010:[x#5,SET-y#5,SET] @@ -233,12 +240,14 @@ ingest ext6 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.2: 000009:[b#4,MERGE-c#4,DEL] - 000013:[j#6,SET-k#7,SET] +0.1: + 000008:[a#3,SET-c#3,SET] 000011:[k#8,SET-k#8,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] + 000013:[j#6,SET-k#7,SET] 6: 000006:[a#1,SET-b#1,SET] 000014:[n#10,SET-n#10,SET] @@ -261,14 +270,17 @@ ingest ext7 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.3: + 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] +0.2: 000009:[b#4,MERGE-c#4,DEL] - 000013:[j#6,SET-k#7,SET] +0.1: + 000008:[a#3,SET-c#3,SET] 000011:[k#8,SET-k#8,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] + 000013:[j#6,SET-k#7,SET] 000017:[m#9,SET-m#9,SET] - 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] 6: 000006:[a#1,SET-b#1,SET] 000014:[n#10,SET-n#10,SET] @@ -327,16 +339,20 @@ ingest ext9 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.4: + 000019:[a#13,SET-g#13,SET] + 000018:[j#12,RANGEDEL-m#12,SET] +0.3: + 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] +0.2: 000009:[b#4,MERGE-c#4,DEL] - 000013:[j#6,SET-k#7,SET] +0.1: + 000008:[a#3,SET-c#3,SET] 000011:[k#8,SET-k#8,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] + 000013:[j#6,SET-k#7,SET] 000017:[m#9,SET-m#9,SET] - 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] - 000018:[j#12,RANGEDEL-m#12,SET] - 000019:[a#13,SET-g#13,SET] 6: 000006:[a#1,SET-b#1,SET] 000014:[n#10,SET-n#10,SET] @@ -376,16 +392,20 @@ d:40 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.4: + 000019:[a#13,SET-g#13,SET] + 000018:[j#12,RANGEDEL-m#12,SET] +0.3: + 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] +0.2: 000009:[b#4,MERGE-c#4,DEL] - 000013:[j#6,SET-k#7,SET] +0.1: + 000008:[a#3,SET-c#3,SET] 000011:[k#8,SET-k#8,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] + 000013:[j#6,SET-k#7,SET] 000017:[m#9,SET-m#9,SET] - 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] - 000018:[j#12,RANGEDEL-m#12,SET] - 000019:[a#13,SET-g#13,SET] 6: 000006:[a#1,SET-b#1,SET] 000021:[d#14,SET-d#14,SET] @@ -405,16 +425,20 @@ ingest ext13 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.4: + 000019:[a#13,SET-g#13,SET] + 000018:[j#12,RANGEDEL-m#12,SET] +0.3: + 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] +0.2: 000009:[b#4,MERGE-c#4,DEL] - 000013:[j#6,SET-k#7,SET] +0.1: + 000008:[a#3,SET-c#3,SET] 000011:[k#8,SET-k#8,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] + 000013:[j#6,SET-k#7,SET] 000017:[m#9,SET-m#9,SET] - 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] - 000018:[j#12,RANGEDEL-m#12,SET] - 000019:[a#13,SET-g#13,SET] 6: 000006:[a#1,SET-b#1,SET] 000021:[d#14,SET-d#14,SET] @@ -439,18 +463,24 @@ ingest ext14 lsm ---- -0: - 000007:[a#2,SET-b#2,DEL] - 000008:[a#3,SET-c#3,SET] +0.6: + 000024:[b#19,SET-b#19,SET] +0.5: + 000026:[a#18,RANGEDEL-d#72057594037927935,RANGEDEL] +0.4: + 000019:[a#13,SET-g#13,SET] + 000018:[j#12,RANGEDEL-m#12,SET] +0.3: + 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] +0.2: 000009:[b#4,MERGE-c#4,DEL] - 000013:[j#6,SET-k#7,SET] +0.1: + 000008:[a#3,SET-c#3,SET] 000011:[k#8,SET-k#8,SET] +0.0: + 000007:[a#2,SET-b#2,DEL] + 000013:[j#6,SET-k#7,SET] 000017:[m#9,SET-m#9,SET] - 000015:[a#11,RANGEDEL-z#72057594037927935,RANGEDEL] - 000018:[j#12,RANGEDEL-m#12,SET] - 000019:[a#13,SET-g#13,SET] - 000026:[a#18,RANGEDEL-d#72057594037927935,RANGEDEL] - 000024:[b#19,SET-b#19,SET] 6: 000006:[a#1,SET-b#1,SET] 000021:[d#14,SET-d#14,SET] @@ -526,7 +556,7 @@ ingest ext18 lsm ---- -0: +0.0: 000005:[a#2,SET-c#2,SET] 6: 000004:[a#1,RANGEDEL-b#72057594037927935,RANGEDEL] @@ -560,7 +590,7 @@ ingest ext21 lsm ---- -0: +0.0: 000006:[c#3,SET-c#3,SET] 6: 000005:[a#2,SET-b#2,SET] @@ -589,7 +619,7 @@ ingest ext23 lsm ---- -0: +0.0: 000005:[a#2,SET-b#2,SET] 6: 000004:[a#1,RANGEDEL-b#72057594037927935,RANGEDEL] @@ -616,7 +646,7 @@ ingest ext25 lsm ---- -0: +0.0: 000005:[a#2,RANGEDEL-b#72057594037927935,RANGEDEL] 6: 000004:[a#1,RANGEDEL-b#72057594037927935,RANGEDEL] diff --git a/testdata/ingest_target_level b/testdata/ingest_target_level index 804543a6f02..b9826be2713 100644 --- a/testdata/ingest_target_level +++ b/testdata/ingest_target_level @@ -44,9 +44,10 @@ L3 g.SET.1:1 h.SET.2:2 ---- -0: - 000004:[b#3,SET-e#4,SET] +0.1: 000005:[d#5,SET-f#6,SET] +0.0: + 000004:[b#3,SET-e#4,SET] 000006:[x#7,SET-y#8,SET] 3: 000007:[g#1,SET-h#2,SET] @@ -117,7 +118,7 @@ L0 L2 a.RANGEDEL.1:g ---- -0: +0.0: 000004:[c#4,SET-g#72057594037927935,RANGEDEL] 2: 000005:[a#1,RANGEDEL-g#72057594037927935,RANGEDEL] diff --git a/testdata/iterator_next_prev b/testdata/iterator_next_prev index cbef28cc159..d0dd0b87a2a 100644 --- a/testdata/iterator_next_prev +++ b/testdata/iterator_next_prev @@ -14,7 +14,7 @@ del-range b c ingest ext2 ---- -0: +0.0: 000005:[b#2,RANGEDEL-c#72057594037927935,RANGEDEL] 6: 000004:[a#1,MERGE-c#1,SET] @@ -65,7 +65,7 @@ del-range x y ingest ext2 ---- -0: +0.0: 000005:[x#2,RANGEDEL-y#72057594037927935,RANGEDEL] 6: 000004:[t#1,SET-z#1,MERGE] diff --git a/testdata/iterator_table_filter b/testdata/iterator_table_filter index 39e69802757..79ca9710ecb 100644 --- a/testdata/iterator_table_filter +++ b/testdata/iterator_table_filter @@ -8,7 +8,7 @@ L2 L3 a.SET.1:1 ---- -0: +0.0: 000004:[a#4,SET-a#4,SET] 1: 000005:[a#3,SET-a#3,SET] diff --git a/testdata/manual_compaction b/testdata/manual_compaction index e00437aedb1..61be2fc7a34 100644 --- a/testdata/manual_compaction +++ b/testdata/manual_compaction @@ -47,9 +47,9 @@ L0 L0 a.SET.2:v ---- -0: - 000004:[b-b] +0.0: 000005:[a-a] + 000004:[b-b] compact a-b ---- @@ -276,7 +276,7 @@ del-range c z ingest ext1 ext2 ---- -0: +0.0: 000006:[a#6,SET-a#6,SET] 000007:[b#7,SET-z#72057594037927935,RANGEDEL] 6: @@ -326,7 +326,7 @@ L2 L3 b.SET.1:1 ---- -0: +0.0: 000004:[c-c] 1: 000005:[a-a] @@ -337,7 +337,7 @@ L3 compact a-e L1 ---- -0: +0.0: 000004:[c#4,SET-c#4,SET] 2: 000008:[a#3,SET-b#72057594037927935,RANGEDEL] @@ -597,9 +597,9 @@ L0 L0 a.SET.2:v ---- -0: - 000004:[b-b] +0.0: 000005:[a-a] + 000004:[b-b] add-ongoing-compaction startLevel=0 outputLevel=1 ---- diff --git a/testdata/manual_flush b/testdata/manual_flush index fad6b5982e3..c5db65d1dbd 100644 --- a/testdata/manual_flush +++ b/testdata/manual_flush @@ -6,7 +6,7 @@ set b 2 # The first L0 table can have its seqnums zeroed. flush ---- -0: +0.0: 000005:[a#1,SET-b#2,SET] reset @@ -21,7 +21,7 @@ del b flush ---- -0: +0.0: 000005:[a#3,DEL-b#4,DEL] batch @@ -31,9 +31,10 @@ set a 3 # A second (overlapping) L0 table will have non-zero seqnums. flush ---- -0: - 000005:[a#3,DEL-b#4,DEL] +0.1: 000007:[a#5,SET-a#5,SET] +0.0: + 000005:[a#3,DEL-b#4,DEL] batch set c 4 @@ -42,9 +43,10 @@ set c 4 # A third (non-overlapping) L0 table will have non-zero seqnums. flush ---- -0: - 000005:[a#3,DEL-b#4,DEL] +0.1: 000007:[a#5,SET-a#5,SET] +0.0: + 000005:[a#3,DEL-b#4,DEL] 000009:[c#6,SET-c#6,SET] reset @@ -58,7 +60,7 @@ del-range a c flush ---- -0: +0.0: 000005:[a#3,RANGEDEL-c#72057594037927935,RANGEDEL] reset @@ -71,5 +73,5 @@ set b 2 async-flush ---- -0: +0.0: 000005:[a#1,SET-b#2,SET] diff --git a/testdata/metrics b/testdata/metrics index 51fb3dc4c29..96e1be1f2af 100644 --- a/testdata/metrics +++ b/testdata/metrics @@ -7,7 +7,7 @@ iter-new a flush ---- -0: +0.0: 000005:[a#1,SET-a#1,SET] # iter b references both a memtable and sstable 5. @@ -17,22 +17,22 @@ iter-new b metrics ---- -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 1 28 B - 17 B - - - - 56 B - - 3.3 - 0 1 771 B 0.25 28 B 0 B 0 0 B 0 771 B 1 0 B 27.5 - 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 6 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - total 1 771 B - 56 B 0 B 0 0 B 0 827 B 1 0 B 14.8 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 1 28 B - 17 B - - - - 56 B - - - 3.3 + 0 1 771 B 0.25 28 B 0 B 0 0 B 0 771 B 1 0 B 1 27.5 + 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 6 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + total 1 771 B - 56 B 0 B 0 0 B 0 827 B 1 0 B 1 14.8 flush 1 compact 0 771 B (size == estimated-debt) memtbl 1 256 K zmemtbl 1 256 K ztbl 0 0 B - bcache 3 677 B 0.0% (score == hit-rate) + bcache 4 698 B 0.0% (score == hit-rate) tcache 1 672 B 0.0% (score == hit-rate) titers 1 filter - - 0.0% (score == utility) @@ -43,7 +43,7 @@ set b 2 flush ---- -0: +0.0: 000005:[a#1,SET-a#1,SET] 000007:[b#2,SET-b#2,SET] @@ -59,24 +59,24 @@ compact a-z metrics ---- -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 1 28 B - 34 B - - - - 84 B - - 2.5 - 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 27.5 - 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 0.5 - total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 28.6 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 1 28 B - 34 B - - - - 84 B - - - 2.5 + 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 0 27.5 + 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 1 0.5 + total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 1 28.6 flush 2 compact 1 0 B (size == estimated-debt) memtbl 1 256 K zmemtbl 2 512 K ztbl 2 1.5 K - bcache 8 1.4 K 27.3% (score == hit-rate) - tcache 2 1.3 K 60.0% (score == hit-rate) - titers 3 + bcache 8 1.4 K 33.3% (score == hit-rate) + tcache 2 1.3 K 50.0% (score == hit-rate) + titers 2 filter - - 0.0% (score == utility) # Closing iter a will release one of the zombie memtables. @@ -86,24 +86,24 @@ iter-close a metrics ---- -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 1 28 B - 34 B - - - - 84 B - - 2.5 - 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 27.5 - 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 0.5 - total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 28.6 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 1 28 B - 34 B - - - - 84 B - - - 2.5 + 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 0 27.5 + 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 1 0.5 + total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 1 28.6 flush 2 compact 1 0 B (size == estimated-debt) memtbl 1 256 K zmemtbl 1 256 K ztbl 2 1.5 K - bcache 8 1.4 K 27.3% (score == hit-rate) - tcache 2 1.3 K 60.0% (score == hit-rate) - titers 3 + bcache 8 1.4 K 33.3% (score == hit-rate) + tcache 2 1.3 K 50.0% (score == hit-rate) + titers 2 filter - - 0.0% (score == utility) # Closing iter c will release one of the zombie sstables. The other @@ -114,23 +114,23 @@ iter-close c metrics ---- -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 1 28 B - 34 B - - - - 84 B - - 2.5 - 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 27.5 - 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 0.5 - total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 28.6 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 1 28 B - 34 B - - - - 84 B - - - 2.5 + 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 0 27.5 + 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 1 0.5 + total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 1 28.6 flush 2 compact 1 0 B (size == estimated-debt) memtbl 1 256 K zmemtbl 1 256 K ztbl 1 771 B - bcache 4 698 B 27.3% (score == hit-rate) - tcache 1 672 B 60.0% (score == hit-rate) + bcache 4 698 B 33.3% (score == hit-rate) + tcache 1 672 B 50.0% (score == hit-rate) titers 1 filter - - 0.0% (score == utility) @@ -141,22 +141,22 @@ iter-close b metrics ---- -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 1 28 B - 34 B - - - - 84 B - - 2.5 - 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 27.5 - 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 0.5 - total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 28.6 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 1 28 B - 34 B - - - - 84 B - - - 2.5 + 0 0 0 B 0.00 56 B 0 B 0 0 B 0 1.5 K 2 0 B 0 27.5 + 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 6 1 778 B 0.00 1.5 K 0 B 0 0 B 0 778 B 1 1.5 K 1 0.5 + total 1 778 B - 84 B 0 B 0 0 B 0 2.3 K 3 1.5 K 1 28.6 flush 2 compact 1 0 B (size == estimated-debt) memtbl 1 256 K zmemtbl 0 0 B ztbl 0 0 B - bcache 0 0 B 27.3% (score == hit-rate) - tcache 0 0 B 60.0% (score == hit-rate) + bcache 0 0 B 33.3% (score == hit-rate) + tcache 0 0 B 50.0% (score == hit-rate) titers 0 filter - - 0.0% (score == utility) diff --git a/testdata/range_del b/testdata/range_del index cc11f1fab9a..510ee32a2d9 100644 --- a/testdata/range_del +++ b/testdata/range_del @@ -915,7 +915,7 @@ L3 d.SET.1:1 ---- mem: 1 -0: +0.0: 000004:[a-d] 1: 000005:[a-d] @@ -1162,10 +1162,11 @@ L0 c.SET.3:v ---- mem: 1 -0: - 000004:[a-e] +0.1: 000005:[a-a] 000006:[c-c] +0.0: + 000004:[a-e] compact a-e ---- @@ -1199,10 +1200,11 @@ L0 c.SET.3:v ---- mem: 1 -0: - 000004:[a-e] +0.1: 000005:[a-a] 000006:[c-c] +0.0: + 000004:[a-e] compact a-e ---- @@ -1244,10 +1246,11 @@ L2 d.SET.0:v ---- mem: 1 -0: - 000004:[a-e] +0.1: 000005:[a-a] 000006:[c-c] +0.0: + 000004:[a-e] 2: 000007:[d-d] @@ -1302,17 +1305,18 @@ L2 d.SET.0:v ---- mem: 1 -0: - 000004:[a-e] +0.1: 000005:[a-a] 000006:[c-c] +0.0: + 000004:[a-e] 000007:[f-f] 2: 000008:[d-d] compact a-b ---- -0: +0.0: 000007:[f-f] 1: 000009:[a-c] @@ -1323,7 +1327,7 @@ compact a-b compact d-e ---- -0: +0.0: 000007:[f-f] 1: 000009:[a-c] diff --git a/tool/testdata/db_lsm b/tool/testdata/db_lsm index c36240541c8..0a7004aaabc 100644 --- a/tool/testdata/db_lsm +++ b/tool/testdata/db_lsm @@ -10,16 +10,16 @@ open non-existent: file does not exist db lsm ../testdata/db-stage-4 ---- -__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___w-amp - WAL 1 82 B - 0 B - - - - 82 B - - 0.0 - 0 1 986 B 0.25 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - 6 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0.0 - total 1 986 B - 82 B 0 B 0 0 B 0 82 B 0 0 B 1.0 +__level_____count____size___score______in__ingest(sz_cnt)____move(sz_cnt)___write(sz_cnt)____read___r-amp___w-amp + WAL 1 82 B - 0 B - - - - 82 B - - - 0.0 + 0 1 986 B 0.25 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 1 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 2 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 3 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 4 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 5 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + 6 0 0 B 0.00 0 B 0 B 0 0 B 0 0 B 0 0 B 0 0.0 + total 1 986 B - 82 B 0 B 0 0 B 0 82 B 0 0 B 0 1.0 flush 0 compact 0 986 B (size == estimated-debt) memtbl 2 768 K diff --git a/version_set.go b/version_set.go index 17449e5467a..deafe28a510 100644 --- a/version_set.go +++ b/version_set.go @@ -467,7 +467,12 @@ func (vs *versionSet) logAndApply( l := &vs.metrics.Levels[i] l.NumFiles = int64(len(newVersion.Files[i])) l.Size = uint64(totalSize(newVersion.Files[i])) + l.Sublevels = 0 + if l.NumFiles > 0 { + l.Sublevels = 1 + } } + vs.metrics.Levels[0].Sublevels = int32(len(newVersion.L0SubLevels.Files)) return nil }