Skip to content

Commit c402451

Browse files
committed
db: treesteps support for levelIter
Add treesteps instrumentation to levelIter and a corresponding test.
1 parent 82b6421 commit c402451

File tree

8 files changed

+206
-21
lines changed

8 files changed

+206
-21
lines changed

data_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,13 +819,13 @@ func runCompactCmd(t *testing.T, td *datadriven.TestData, d *DB) error {
819819
// ParseInternalKey, followed a colon and the corresponding value.
820820
//
821821
// b#50,SET:foo
822-
// c#20,DEL
822+
// c#20,DEL:
823823
//
824-
// Range keys may be encoded by prefixing the line with `rangekey:`,
824+
// Range keys may be encoded by prefixing the line with `Span:`,
825825
// followed by the keyspan.Span string representation, as understood
826826
// by keyspan.ParseSpan.
827827
//
828-
// rangekey:b-d:{(#5,RANGEKEYSET,@2,foo)}
828+
// Span:b-d:{(#5,RANGEKEYSET,@2,foo)}
829829
//
830830
// # Mechanics
831831
//

internal/arenaskl/iterator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ func (it *Iterator) SetContext(_ context.Context) {}
251251

252252
// TreeStepsNode is part of the InternalIterator interface.
253253
func (it *Iterator) TreeStepsNode() treesteps.NodeInfo {
254-
return treesteps.NodeInfof(it, "%T(%p)", it, it)
254+
return treesteps.NodeInfof(it, "arenaskl.Iterator")
255255
}
256256

257257
func (it *Iterator) decodeKey() {

internal/invalidating/iter.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,8 @@ func (i *iter) SetContext(ctx context.Context) {
166166

167167
// TreeStepsNode is part of the InternalIterator interface.
168168
func (i *iter) TreeStepsNode() treesteps.NodeInfo {
169-
info := treesteps.NodeInfof(i, "%T(%p)", i, i)
170-
info.AddChildren(i.iter)
171-
return info
169+
// Pass through; this node will not be visible in the tree.
170+
return i.iter.TreeStepsNode()
172171
}
173172

174173
func (i *iter) String() string {

iterator.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3155,7 +3155,10 @@ var _ treesteps.Node = (*Iterator)(nil)
31553155

31563156
// TreeStepsNode implements the treesteps.Node interface.
31573157
func (i *Iterator) TreeStepsNode() treesteps.NodeInfo {
3158-
info := treesteps.NodeInfof(i, "%T(%p)", i, i)
3159-
info.AddChildren(i.iter, i.pointIter)
3158+
info := treesteps.NodeInfof(i, "pebble.Iterator")
3159+
info.AddChildren(i.iter)
3160+
if i.pointIter != i.iter {
3161+
info.AddChildren(i.pointIter)
3162+
}
31603163
return info
31613164
}

level_iter.go

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,9 @@ func (l *levelIter) loadFile(file *manifest.TableMetadata, dir int) loadFileRetu
608608
// Relinquish iters.rangeDeletion to the caller.
609609
l.rangeDelIterSetter.setRangeDelIter(iters.rangeDeletion)
610610
}
611+
if treesteps.Enabled && treesteps.IsRecording(l) {
612+
treesteps.NodeUpdated(l, fmt.Sprintf("file %s loaded", l.iterFile.TableNum))
613+
}
611614
return newFileLoaded
612615
}
613616
}
@@ -632,7 +635,13 @@ func (l *levelIter) verify(kv *base.InternalKV) *base.InternalKV {
632635
return kv
633636
}
634637

635-
func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) *base.InternalKV {
638+
func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) (kv *base.InternalKV) {
639+
if treesteps.Enabled && treesteps.IsRecording(l) {
640+
op := treesteps.StartOpf(l, "SeekGE(%q, %d)", key, flags)
641+
defer func() {
642+
op.Finishf("= %s", kv.String())
643+
}()
644+
}
636645
if invariants.Enabled && l.lower != nil && l.comparer.Compare(key, l.lower) < 0 {
637646
panic(errors.AssertionFailedf("levelIter SeekGE to key %q violates lower bound %q", key, l.lower))
638647
}
@@ -658,7 +667,13 @@ func (l *levelIter) SeekGE(key []byte, flags base.SeekGEFlags) *base.InternalKV
658667
return l.verify(l.skipEmptyFileForward())
659668
}
660669

661-
func (l *levelIter) SeekPrefixGE(prefix, key []byte, flags base.SeekGEFlags) *base.InternalKV {
670+
func (l *levelIter) SeekPrefixGE(prefix, key []byte, flags base.SeekGEFlags) (kv *base.InternalKV) {
671+
if treesteps.Enabled && treesteps.IsRecording(l) {
672+
op := treesteps.StartOpf(l, "SeekPrefixGE(%q, %q, %d)", prefix, key, flags)
673+
defer func() {
674+
op.Finishf("= %s", kv.String())
675+
}()
676+
}
662677
if invariants.Enabled && l.lower != nil && l.comparer.Compare(key, l.lower) < 0 {
663678
panic(errors.AssertionFailedf("levelIter SeekGE to key %q violates lower bound %q", key, l.lower))
664679
}
@@ -687,7 +702,13 @@ func (l *levelIter) SeekPrefixGE(prefix, key []byte, flags base.SeekGEFlags) *ba
687702
return l.verify(l.skipEmptyFileForward())
688703
}
689704

690-
func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) *base.InternalKV {
705+
func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) (kv *base.InternalKV) {
706+
if treesteps.Enabled && treesteps.IsRecording(l) {
707+
op := treesteps.StartOpf(l, "SeekLT(%q, %d)", key, flags)
708+
defer func() {
709+
op.Finishf("= %s", kv.String())
710+
}()
711+
}
691712
if invariants.Enabled && l.upper != nil && l.comparer.Compare(key, l.upper) > 0 {
692713
panic(errors.AssertionFailedf("levelIter SeekLT to key %q violates upper bound %q", key, l.upper))
693714
}
@@ -708,7 +729,13 @@ func (l *levelIter) SeekLT(key []byte, flags base.SeekLTFlags) *base.InternalKV
708729
return l.verify(l.skipEmptyFileBackward())
709730
}
710731

711-
func (l *levelIter) First() *base.InternalKV {
732+
func (l *levelIter) First() (kv *base.InternalKV) {
733+
if treesteps.Enabled && treesteps.IsRecording(l) {
734+
op := treesteps.StartOpf(l, "First()")
735+
defer func() {
736+
op.Finishf("= %s", kv.String())
737+
}()
738+
}
712739
if invariants.Enabled && l.lower != nil {
713740
panic(errors.AssertionFailedf("levelIter First called while lower bound %q is set", l.lower))
714741
}
@@ -729,7 +756,13 @@ func (l *levelIter) First() *base.InternalKV {
729756
return l.verify(l.skipEmptyFileForward())
730757
}
731758

732-
func (l *levelIter) Last() *base.InternalKV {
759+
func (l *levelIter) Last() (kv *base.InternalKV) {
760+
if treesteps.Enabled && treesteps.IsRecording(l) {
761+
op := treesteps.StartOpf(l, "Last()")
762+
defer func() {
763+
op.Finishf("= %s", kv.String())
764+
}()
765+
}
733766
if invariants.Enabled && l.upper != nil {
734767
panic(errors.AssertionFailedf("levelIter Last called while upper bound %q is set", l.upper))
735768
}
@@ -750,7 +783,13 @@ func (l *levelIter) Last() *base.InternalKV {
750783
return l.verify(l.skipEmptyFileBackward())
751784
}
752785

753-
func (l *levelIter) Next() *base.InternalKV {
786+
func (l *levelIter) Next() (kv *base.InternalKV) {
787+
if treesteps.Enabled && treesteps.IsRecording(l) {
788+
op := treesteps.StartOpf(l, "Next()")
789+
defer func() {
790+
op.Finishf("= %s", kv.String())
791+
}()
792+
}
754793
if l.exhaustedDir == -1 {
755794
if l.lower != nil {
756795
return l.SeekGE(l.lower, base.SeekGEFlagsNone)
@@ -766,7 +805,13 @@ func (l *levelIter) Next() *base.InternalKV {
766805
return l.verify(l.skipEmptyFileForward())
767806
}
768807

769-
func (l *levelIter) NextPrefix(succKey []byte) *base.InternalKV {
808+
func (l *levelIter) NextPrefix(succKey []byte) (kv *base.InternalKV) {
809+
if treesteps.Enabled && treesteps.IsRecording(l) {
810+
op := treesteps.StartOpf(l, "NextPrefix(%q)", succKey)
811+
defer func() {
812+
op.Finishf("= %s", kv.String())
813+
}()
814+
}
770815
if l.err != nil || l.iter == nil {
771816
return nil
772817
}
@@ -799,7 +844,13 @@ func (l *levelIter) NextPrefix(succKey []byte) *base.InternalKV {
799844
return nil
800845
}
801846

802-
func (l *levelIter) Prev() *base.InternalKV {
847+
func (l *levelIter) Prev() (kv *base.InternalKV) {
848+
if treesteps.Enabled && treesteps.IsRecording(l) {
849+
op := treesteps.StartOpf(l, "Prev()")
850+
defer func() {
851+
op.Finishf("= %s", kv.String())
852+
}()
853+
}
803854
if l.exhaustedDir == +1 {
804855
if l.upper != nil {
805856
return l.SeekLT(l.upper, base.SeekLTFlagsNone)
@@ -952,11 +1003,9 @@ func (l *levelIter) SetContext(ctx context.Context) {
9521003

9531004
// TreeStepsNode is part of the InternalIterator interface.
9541005
func (l *levelIter) TreeStepsNode() treesteps.NodeInfo {
955-
info := treesteps.NodeInfof(l, "%T(%p) %s", l, l, l.layer)
1006+
info := treesteps.NodeInfof(l, "levelIter %s", l.layer)
9561007
if l.iterFile != nil {
9571008
info.AddPropf("file", "%s", l.iterFile.TableNum)
958-
} else {
959-
info.AddPropf("file", "<none>")
9601009
}
9611010
info.AddChildren(l.iter)
9621011
return info

merging_iter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,7 @@ func (m *mergingIter) SetContext(ctx context.Context) {
13321332

13331333
// TreeStepsNode is part of the InternalIterator interface.
13341334
func (m *mergingIter) TreeStepsNode() treesteps.NodeInfo {
1335-
info := treesteps.NodeInfof(m, "%T(%p)", m, m)
1335+
info := treesteps.NodeInfof(m, "mergingIter")
13361336
for i := range m.levels {
13371337
info.AddChildren(m.levels[i].iter)
13381338
}

testdata/treesteps/level_iter

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Test treesteps recording for levelIter SeekGE operation.
2+
# This test creates a DB with multiple sstables in L6 and performs a SeekGE
3+
# operation while recording the operation steps with treesteps.
4+
5+
define
6+
L1
7+
a#10,SET:va
8+
b#10,SET:vb
9+
c#10,SET:vc
10+
L1
11+
d#5,SET:vd
12+
d@10#5,SET:vd10
13+
d@20#5,SET:vd20
14+
e#5,SET:ve
15+
f#5,SET:vf
16+
L1
17+
g#1,SET:vg
18+
h#1,SET:vh
19+
i#1,SET:vi
20+
----
21+
L1:
22+
000004:[a#10,SET-c#10,SET] seqnums:[10-10] points:[a#10,SET-c#10,SET] size:471
23+
000005:[d#5,SET-f#5,SET] seqnums:[5-5] points:[d#5,SET-f#5,SET] size:491
24+
000006:[g#1,SET-i#1,SET] seqnums:[1-1] points:[g#1,SET-i#1,SET] size:471
25+
26+
level-iter depth=1
27+
seek-ge b
28+
next
29+
next
30+
next
31+
next
32+
next
33+
next
34+
next
35+
----
36+
b#10,SET:vb
37+
c#10,SET:vc
38+
d#5,SET:vd
39+
d@20#5,SET:vd20
40+
d@10#5,SET:vd10
41+
e#5,SET:ve
42+
f#5,SET:vf
43+
g#1,SET:vg
44+
https://raduberinde.github.io/treesteps/decode.html#eNrslkGL1DAUx-9-ikfmsgvFbVdnDoUFL6sIy17Wm10k076uwZoOzVMEERYPXrx4mM_hh5pPIs3skGk3lTJNIA72NLw07__v_5dk8pVd84_IUkaoqODEz6hBVIQrdVbhZ6zeCcImPV-wiN0Qrtq3FUvfMiEFCV61ZcQPry6hlqAnvCZsQBFvCAsWMVkXaAZSKEWFELfPc6hqXuiXTAuliC8rfKqEvKvwajePU91AKaRQ73sTjObe8DV-oSFDu7GRSo9b-dEZzmlucnopGhWi-f86_-riWnhZXLfbw0IfFGb4KskkizoFgM2PX7DdzScZW2Ysgvh09GuwWX_XH5M-HCm6tN6s7zfr-799Rybh4ZE1wapWgkQtsTD1blfffuwN4QKWsySObi7f7Iy1ApxSa70nvK3__D3SfVdsarDufLbL7OT0uCztt4ELyAcg546cdiVc5ZiHhzYPDm32KY6f5VJU-gfuG7OfPO4sz70chnNfLrZt9J-QTq6YzW17wlLuqY3dEqbT1OAcezsqR_3QX5zHA2CtI4ey3W_mLExnDo_N1KP0k0HIiUvIiQ_ISYiQk-Ago50wOsOLztlicGAxNKqlnWrpjGrpnGoZHNUyNKpTb6ETLC-83EIXvlz0b6F3s8S2HyzlntrY_WA6TQ3uIG-33578CQAA__8blpXQ
45+
46+
level-iter depth=1
47+
seek-prefix-ge d@10
48+
next
49+
next-prefix
50+
next-prefix
51+
----
52+
d@10#5,SET:vd10
53+
e#5,SET:ve
54+
f#5,SET:vf
55+
.
56+
https://raduberinde.github.io/treesteps/decode.html#eNrcVc1K60AU3t-nOMzdtBBuU-5tF4HA3ZTLhVKEunOKjOZEB8dJyRylIEJx4caNizyHD5UnkSQt-TEN1aZYOqvJ-fm-78z5IA9sIm6ROYzQkCdI9ChENIRz01N4j-pcEobOnyGz2JRwnlQb5pwxqSVJoZIw4s1JiL5c_BtBoCFt-08YgiEREnrMYjrwME844EuFYCdnACoQXlpUBTKGxIXCX0bqK4XjdbegIARfammua9ty_kLRBBe0Sdw6tyXfR6hKMhPTxJZXfIKzDvYAmGeZMVJT5Olxn2tmlQIA8fMrFLfV4cz727c5syC_2d1dOiGOnlJ3OSt7paEojpZxtGyamWtYHR0QzAMjSQYavTxeRv0GiVtzgAvJx8-BNR2drgdIWAU5GzIVPVn85e3rc1Y17LqXNuUnnu50j01UEQZcwPr1Yzs6SwRtPWMr2jJfdjhDzhe2zVn3qBU2wYILfr0L_P3MUSJs683b1ervwRWHp7AJFlzgd7b9-1JLlV6wKLz-79fWSBu4C9BDrtns8cd7AAAA__-bep2b

treesteps_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2025 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2+
// of this source code is governed by a BSD-style license that can be found in
3+
// the LICENSE file.
4+
5+
//go:build invariants
6+
7+
package pebble
8+
9+
import (
10+
"testing"
11+
12+
"github.com/cockroachdb/datadriven"
13+
"github.com/cockroachdb/pebble/internal/itertest"
14+
"github.com/cockroachdb/pebble/internal/manifest"
15+
"github.com/cockroachdb/pebble/internal/testkeys"
16+
"github.com/cockroachdb/pebble/internal/treesteps"
17+
"github.com/cockroachdb/pebble/vfs"
18+
"github.com/stretchr/testify/require"
19+
)
20+
21+
// TestTreeSteps tests the treesteps recording for various iterator types,
22+
// generating visualization URLs showing iterator behavior.
23+
func TestTreeSteps(t *testing.T) {
24+
if !treesteps.Enabled {
25+
t.Skip("treesteps not available in this build")
26+
}
27+
datadriven.Walk(t, "testdata/treesteps", func(t *testing.T, path string) {
28+
var d *DB
29+
defer func() {
30+
if d != nil {
31+
require.NoError(t, d.Close())
32+
d = nil
33+
}
34+
}()
35+
datadriven.RunTest(t, path, func(t *testing.T, td *datadriven.TestData) string {
36+
switch td.Cmd {
37+
case "define":
38+
if d != nil {
39+
require.NoError(t, d.Close())
40+
d = nil
41+
}
42+
opts := &Options{
43+
Comparer: testkeys.Comparer,
44+
FS: vfs.NewMem(),
45+
}
46+
var err error
47+
d, err = runDBDefineCmd(td, opts)
48+
require.NoError(t, err)
49+
return d.DebugString()
50+
51+
case "level-iter":
52+
v := d.DebugCurrentVersion()
53+
var opts IterOptions
54+
iter := newLevelIter(t.Context(), opts, testkeys.Comparer, d.newIters, v.Levels[1].Iter(), manifest.Level(1), internalIterOpts{})
55+
defer iter.Close()
56+
rec := treeStepsStartRecording(t, td, iter)
57+
out := itertest.RunInternalIterCmd(t, td, iter, itertest.Verbose)
58+
url := rec.Finish().URL()
59+
return out + url.String()
60+
61+
default:
62+
return "unknown command"
63+
}
64+
})
65+
})
66+
}
67+
68+
func treeStepsStartRecording(
69+
t *testing.T, td *datadriven.TestData, node treesteps.Node,
70+
) *treesteps.Recording {
71+
var opts []treesteps.RecordingOption
72+
var depth int
73+
td.MaybeScanArgs(t, "depth", &depth)
74+
if depth != 0 {
75+
opts = append(opts, treesteps.MaxTreeDepth(depth))
76+
}
77+
return treesteps.StartRecording(node, td.Pos, opts...)
78+
}

0 commit comments

Comments
 (0)