~bigbes/sr-ht-dolt

ref: 12c7ff77f8281cd0ca61177bcf07b9c031a04c97 sr-ht-dolt/beads/bench_test.go -rw-r--r-- 7.8 KiB
12c7ff77 — Eugene Blikh ci: publish this build's own coverage and benchmarks 2 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package beads

import (
	"context"
	"fmt"
	"net/url"
	"testing"

	"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
)

// --- what these measure -------------------------------------------------------
//
// The board and the detail pane are what a hosted Dolt database costs to look
// at. There is no SQL engine behind either: a bare NBS store has no working
// set, so every table is read whole and projected in process (see the package
// doc), and that projection is the per-request work — the row reads themselves
// are the store's, and are the same reads whatever the page does with them.
//
// So the benchmarks below drive Build through a session that hands the rows
// over already read. That is deliberately the seam the projections were given:
// what is timed is the bucketing, the dependency walk, the label join, the
// filter options and the sorts, and nothing about how fast a disk is that day.
//
// The corpus is benchIssues issues with a dependency edge for most of them,
// which is the size a real bd tracker on this instance reaches; Max is 2000, so
// the fixture sits inside the cap and no clip path is being measured instead of
// the projection.

// benchIssues is how many issues the synthetic tracker holds. Chosen to be a
// realistic large tracker while staying under Max (2000), so what is measured
// is the projection and not the truncation branch.
const benchIssues = 1200

// benchStatuses cycles the three status categories over the corpus so all four
// lanes are populated and statusCategory is exercised on both its custom-status
// hit and its heuristic fallback.
var benchStatuses = []string{"open", "in_progress", "closed", "blocked"}

// benchSession builds a fakeSession over a synthetic tracker: issues, the
// dependency edges between them, labels, custom statuses, comments and events.
// Every table the board and the detail pane read is present, so nothing is
// measured through the optional-table degradation path.
func benchSession() *fakeSession {
	issues := &browse.RowPage{
		Columns: []string{
			"id", "title", "status", "priority", "issue_type", "assignee",
			"created_at", "started_at", "updated_at", "closed_at", "close_reason",
			"description", "design", "acceptance_criteria", "notes",
			"created_by", "owner", "estimated_minutes", "external_ref", "spec_id",
			"is_blocked",
		},
	}
	for i := 0; i < benchIssues; i++ {
		id := fmt.Sprintf("bench-%04d", i)
		status := benchStatuses[i%len(benchStatuses)]
		closedAt, closeReason := "", ""
		if status == "closed" {
			closedAt = fmt.Sprintf("2026-03-%02d", i%28+1)
			closeReason = "resolved in review"
		}
		issues.Rows = append(issues.Rows, []string{
			id,
			fmt.Sprintf("Issue %d: the projection this benchmark measures", i),
			status,
			fmt.Sprint(i % 5),
			[]string{"feature", "bug", "chore", "epic"}[i%4],
			fmt.Sprintf("dev%d", i%7),
			fmt.Sprintf("2026-01-%02d", i%28+1),
			fmt.Sprintf("2026-02-%02d", i%28+1),
			fmt.Sprintf("2026-02-%02d", i%28+1),
			closedAt,
			closeReason,
			"A description long enough that copying it is not free, written the " +
				"way an issue body is written and not as a placeholder token.",
			"The design note, likewise.",
			"Given a projection, when it runs, then it produces the same lanes.",
			"Notes.",
			fmt.Sprintf("dev%d", i%3),
			fmt.Sprintf("dev%d", i%3),
			"90",
			"",
			"",
			fmt.Sprint(i % 9 / 8), // roughly one issue in nine carries the flag
		})
	}
	issues.Total = len(issues.Rows)

	// A chain of edges: every issue past the first depends on an earlier one, so
	// the transitive walk in the detail pane has a real tree to descend and the
	// blocked/blocking counts are non-trivial for most cards.
	deps := &browse.RowPage{Columns: []string{"id", "issue_id", "depends_on_issue_id", "type"}}
	for i := 1; i < benchIssues; i++ {
		deps.Rows = append(deps.Rows, []string{
			fmt.Sprintf("d-%04d", i),
			fmt.Sprintf("bench-%04d", i),
			fmt.Sprintf("bench-%04d", i/2), // a binary tree, so depth is log2(n)
			[]string{"blocks", "related", "parent-child"}[i%3],
		})
	}
	deps.Total = len(deps.Rows)

	labels := &browse.RowPage{Columns: []string{"issue_id", "label"}}
	for i := 0; i < benchIssues; i++ {
		labels.Rows = append(labels.Rows,
			[]string{fmt.Sprintf("bench-%04d", i), fmt.Sprintf("area-%d", i%11)},
			[]string{fmt.Sprintf("bench-%04d", i), fmt.Sprintf("release-%d", i%3)},
		)
	}
	labels.Total = len(labels.Rows)

	statuses := &browse.RowPage{
		Columns: []string{"name", "category"},
		Rows: [][]string{
			{"open", "open"},
			{"in_progress", "in_progress"},
			{"closed", "closed"},
			// "blocked" is deliberately absent, so a quarter of the corpus falls
			// through to the name heuristics — the branch a tracker with a status
			// bd does not know about actually takes.
		},
	}
	statuses.Total = len(statuses.Rows)

	comments := &browse.RowPage{Columns: []string{"id", "issue_id", "author", "text", "created_at"}}
	events := &browse.RowPage{Columns: []string{
		"id", "issue_id", "actor", "event_type", "old_value", "new_value", "comment", "created_at",
	}}
	for i := 0; i < benchIssues; i++ {
		id := fmt.Sprintf("bench-%04d", i)
		comments.Rows = append(comments.Rows, []string{
			fmt.Sprintf("c-%04d", i), id, fmt.Sprintf("dev%d", i%7),
			"A comment on this issue, of the length a comment has.",
			fmt.Sprintf("2026-02-%02d", i%28+1),
		})
		events.Rows = append(events.Rows, []string{
			fmt.Sprintf("e-%04d", i), id, fmt.Sprintf("dev%d", i%7),
			"status_changed", "open", "in_progress", "",
			fmt.Sprintf("2026-02-%02d", i%28+1),
		})
	}
	comments.Total = len(comments.Rows)
	events.Total = len(events.Rows)

	return &fakeSession{rowsByTable: map[string]*browse.RowPage{
		"issues":          issues,
		"dependencies":    deps,
		"labels":          labels,
		"custom_statuses": statuses,
		"comments":        comments,
		"events":          events,
	}}
}

// BenchmarkBoardBuild is the whole board: four lanes bucketed out of the issue
// rows, the dependency edges aggregated into blocked/blocking counts, the label
// join, the filter dropdown options and the per-lane sort.
func BenchmarkBoardBuild(b *testing.B) {
	sess := benchSession()
	ctx := context.Background()

	b.ReportAllocs()
	for b.Loop() {
		data, err := Build(ctx, sess, "main", url.Values{})
		if err != nil {
			b.Fatalf("build: %v", err)
		}
		// Asserted rather than assumed: a projection that silently produced an
		// empty board would otherwise be the fastest one here.
		if data.Total != benchIssues {
			b.Fatalf("board carried %d cards, want %d", data.Total, benchIssues)
		}
	}
}

// BenchmarkBoardBuildFiltered is the same board under the sticky filters, which
// is the request a reader who narrowed the board sends: every row is still read
// and still matched, and only the survivors are bucketed.
func BenchmarkBoardBuildFiltered(b *testing.B) {
	sess := benchSession()
	ctx := context.Background()
	query := url.Values{
		"q":        {"projection"},
		"type":     {"bug"},
		"label":    {"area-3"},
		"assignee": {"dev2"},
	}

	b.ReportAllocs()
	for b.Loop() {
		data, err := Build(ctx, sess, "main", query)
		if err != nil {
			b.Fatalf("build: %v", err)
		}
		if data.Total == 0 {
			b.Fatal("the filtered board matched nothing; the filter is measuring an empty loop")
		}
	}
}

// BenchmarkDetailBuild is one issue's pane: the same four tables plus comments
// and events, the transitive dependency walk in both directions, the raw-row
// projection and the merged, time-ordered history.
func BenchmarkDetailBuild(b *testing.B) {
	sess := benchSession()
	ctx := context.Background()
	// An issue deep in the chain, so the transitive walk has something to walk.
	query := url.Values{"issue": {fmt.Sprintf("bench-%04d", benchIssues-1)}}

	b.ReportAllocs()
	for b.Loop() {
		data, err := Build(ctx, sess, "main", query)
		if err != nil {
			b.Fatalf("build: %v", err)
		}
		if data.Issue == nil {
			b.Fatal("the detail pane resolved no issue")
		}
	}
}