Skip to content

Commit d373249

Browse files
committed
treesteps: disable in non-invariants mode
Make the treesteps code only active in invariants mode, and add minimal stubs for non-invariants builds. This will allow using treesteps for iterators which are performance-sensitive.
1 parent 5a4e0fa commit d373249

File tree

4 files changed

+109
-69
lines changed

4 files changed

+109
-69
lines changed

internal/treesteps/node_state.go

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 treesteps
8+
9+
const Enabled = false
10+
11+
type RecordingOption struct{}
12+
13+
func MaxTreeDepth(maxTreeDepth int) RecordingOption { return RecordingOption{} }
14+
func MaxOpDepth(maxOpDepth int) RecordingOption { return RecordingOption{} }
15+
16+
func StartRecording(root Node, name string, opts ...RecordingOption) *Recording { return nil }
17+
18+
func NodeUpdated(n Node, reason string) {}
19+
20+
// Node must be implemented by every node in the hierarchy.
21+
type Node interface {
22+
TreeStepsNode() NodeInfo
23+
}
24+
type NodeInfo struct{}
25+
26+
func NodeInfof(format string, args ...any) NodeInfo { return NodeInfo{} }
27+
func (ni *NodeInfo) AddPropf(key string, format string, args ...any) {}
28+
func (ni *NodeInfo) AddChildren(nodes ...Node) {}
29+
30+
type Recording struct{}
31+
32+
func (r *Recording) Finish() Steps { return Steps{} }
33+
34+
func IsRecording(n Node) bool { return false }
35+
36+
type Op struct{}
37+
38+
func StartOpf(node Node, format string, args ...any) *Op { return nil }
39+
func (op *Op) Updatef(format string, args ...any) {}
40+
func (op *Op) Finishf(format string, args ...any) {}
41+
42+
func TreeToString(n Node) string { return "treesteps not supported in this build" }

internal/treesteps/tree_steps.go renamed to internal/treesteps/tree_steps_on.go

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22
// of this source code is governed by a BSD-style license that can be found in
33
// the LICENSE file.
44

5+
//go:build invariants
6+
57
package treesteps
68

79
import (
810
"fmt"
911
"reflect"
1012
"slices"
1113
"strings"
14+
"sync"
15+
"sync/atomic"
1216
"unicode"
1317

1418
"github.com/cockroachdb/pebble/internal/treeprinter"
1519
)
1620

21+
const Enabled = true
22+
1723
// StartRecording starts a new recording that captures step-by-step propagation
1824
// of operations and changes in a hierarchical structure.
1925
//
@@ -100,7 +106,7 @@ func NodeUpdated(n Node, reason string) {
100106
}
101107
}
102108

103-
// Node must be implemnented by every node in the hierarchy.
109+
// Node must be implemented by every node in the hierarchy.
104110
type Node interface {
105111
TreeStepsNode() NodeInfo
106112
}
@@ -273,3 +279,60 @@ func treePrint(n Node, tp treeprinter.Node) {
273279
treePrint(child, tpNode)
274280
}
275281
}
282+
283+
type nodeState struct {
284+
recording *Recording
285+
depth int
286+
node Node
287+
name string
288+
289+
// ops currently running for this node.
290+
ops []*Op
291+
}
292+
293+
var mu struct {
294+
sync.Mutex
295+
296+
recordingInProgress atomic.Bool
297+
nodeMap map[Node]*nodeState
298+
}
299+
300+
func buildTree(n *nodeState) TreeNode {
301+
var t TreeNode
302+
info := n.node.TreeStepsNode()
303+
n.name = info.name
304+
t.Name = info.name
305+
t.Properties = info.properties
306+
if len(n.ops) > 0 {
307+
t.Ops = make([]string, len(n.ops))
308+
for i := range t.Ops {
309+
t.Ops[i] = n.ops[i].details
310+
if n.ops[i].state != "" {
311+
t.Ops[i] += " " + n.ops[i].state
312+
}
313+
}
314+
}
315+
if n.depth < n.recording.maxTreeDepth {
316+
for i := range info.children {
317+
c := nodeStateLocked(n.recording, info.children[i])
318+
c.depth = n.depth + 1
319+
t.Children = append(t.Children, buildTree(c))
320+
}
321+
} else {
322+
for range info.children {
323+
t.Children = append(t.Children, TreeNode{Name: "..."})
324+
}
325+
}
326+
return t
327+
}
328+
329+
func nodeStateLocked(w *Recording, n Node) *nodeState {
330+
ns, ok := mu.nodeMap[n]
331+
if !ok {
332+
ns = &nodeState{recording: w, node: n}
333+
mu.nodeMap[n] = ns
334+
} else if w != ns.recording {
335+
panic(fmt.Sprintf("node %v part of multiple recordings", n))
336+
}
337+
return ns
338+
}

internal/treesteps/tree_steps_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ func (t *SegmentTree) sum(nodeIdx int, x1, x2 int) (result int) {
130130
}
131131

132132
func TestSegmentTree(t *testing.T) {
133+
if !Enabled {
134+
t.Skip("treesteps not available in this build")
135+
}
133136
var tree *SegmentTree
134137
datadriven.RunTest(t, "testdata/segment_tree", func(t *testing.T, td *datadriven.TestData) string {
135138
var r *Recording

0 commit comments

Comments
 (0)