~bigbes/sr-ht-dolt

ref: 12c7ff77f8281cd0ca61177bcf07b9c031a04c97 sr-ht-dolt/web/markdown_bench_test.go -rw-r--r-- 5.9 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
package web

import (
	"context"
	"fmt"
	"strings"
	"testing"
	"time"

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

// --- what this measures --------------------------------------------------------
//
// The memory pane renders every memory body it shows through memoryLinks.Body:
// goldmark parses the markdown, the wikilink parser resolves [[slug]] against
// the cross-database index, and the AST transformer scans every text node for
// id-shaped tokens and links the ones whose prefix the index knows. That is the
// per-memory cost of the page, and a memory list renders many of them per
// request.
//
// The index is built once, outside the timed loop, and built through the real
// beads.PrefixesAcross over a fake session rather than hand-assembled: what the
// render costs depends on how many prefixes and slugs the index holds, and the
// only honest source of an index of that shape is the function that builds one.

// benchMemoryDatabases is how many sibling trackers the caller may browse. Each
// contributes one issue prefix and a handful of memory slugs, so the scan has
// real prefixes to hit and real ones to miss.
const benchMemoryDatabases = 6

// benchMemorySlugs is how many memories each of those trackers stores.
const benchMemorySlugs = 12

// benchSession is a beads.ReadySession over one canned config table. Only
// Branches and Rows are reached by PrefixesAcross; Tables and Log are here to
// satisfy the interface and would be a bug to call.
type benchSession struct {
	config *browse.RowPage
}

func (s *benchSession) Rows(_ context.Context, _, table string, _, _ int) (*browse.RowPage, error) {
	if table != "config" {
		return nil, fmt.Errorf("%w: %s", browse.ErrTableNotFound, table)
	}
	return s.config, nil
}

func (s *benchSession) Branches(context.Context) ([]browse.Branch, error) {
	return []browse.Branch{{Name: "main", Head: "0123456789abcdef"}}, nil
}

func (s *benchSession) Tables(context.Context, string) ([]browse.TableInfo, error) {
	return nil, fmt.Errorf("web: Tables is not part of the prefix index read")
}

func (s *benchSession) Log(context.Context, string, string, int) ([]browse.CommitInfo, string, error) {
	return nil, "", fmt.Errorf("web: Log is not part of the prefix index read")
}

func (s *benchSession) Close() error { return nil }

// benchPrefixIndex builds the cross-database link index the render resolves
// against: benchMemoryDatabases trackers, each naming its own issue prefix and
// storing benchMemorySlugs memories.
func benchPrefixIndex() *beads.PrefixIndex {
	dbs := make([]beads.ReadyDatabase, 0, benchMemoryDatabases)
	configs := make(map[int]*browse.RowPage, benchMemoryDatabases)
	for i := 0; i < benchMemoryDatabases; i++ {
		dbs = append(dbs, beads.ReadyDatabase{
			ID:        i + 1,
			OwnerName: "bigbes",
			Name:      fmt.Sprintf("sr-ht-%d", i),
		})
		rows := [][]string{{"issue_prefix", fmt.Sprintf("sr-ht-%d", i)}}
		for j := 0; j < benchMemorySlugs; j++ {
			rows = append(rows, []string{
				fmt.Sprintf("kv.memory.memory-%d-%d", i, j),
				"stored elsewhere; the index only needs the key",
			})
		}
		page := &browse.RowPage{Columns: []string{"key", "value"}, Rows: rows}
		page.Total = len(rows)
		configs[i+1] = page
	}

	open := func(_ context.Context, d beads.ReadyDatabase) (beads.ReadySession, error) {
		page, ok := configs[d.ID]
		if !ok {
			return nil, fmt.Errorf("web: no config for database %s", d.Slug())
		}
		return &benchSession{config: page}, nil
	}
	return beads.PrefixesAcross(context.Background(), dbs, open, &beads.PrefixCache{}, time.Now())
}

// benchMemoryBody is one memory of the shape the corpus actually holds: a bold
// leader, code spans, a bullet list, a fenced recipe, a table, prose carrying
// both resolvable and unresolvable ids, wikilinks that hit and wikilinks that
// miss, and the angle-bracket placeholders the raw-HTML-as-text rendering
// exists for.
var benchMemoryBody = strings.Repeat(`**Why:** the pipeline builds tags too, and a tag build reports the tag.

- see [[memory-0-3]] for the deploy order, and [[memory-4-7]] for the token
- sr-ht-0-46c.2 blocks sr-ht-3-9a1, which is what sr-ht-5-nex was filed against
- an id nothing owns, other-repo-12b, stays text

`+"```"+`sh
ref="${GIT_REF#refs/heads/}"
curl -sS --fail-with-body -X POST "$ORIGIN/api/v1/repos/$REPO/reports"
`+"```"+`

| step | what it does |
| ---- | ------------ |
| push | `+"`labng push`"+` is the deploy |
| wait | the apk index catches up within 15 minutes |

The placeholder spellings are SRHT_<NAME>_VER and ~/data/home/<repo>, which
CommonMark reads as tags and this renderer shows as the text they are. See
[[memory-2-11]] and https://dolt.srht.bigb.es/~bigbes/sr-ht-2 for the rest.

`, 8)

// BenchmarkMemoryBodyRender is one memory body rendered the way the pane
// renders it: parsed, its wikilinks resolved against the index, its ids scanned
// and linked, and the whole document written out as HTML.
func BenchmarkMemoryBodyRender(b *testing.B) {
	links := &memoryLinks{index: benchPrefixIndex()}

	b.ReportAllocs()
	b.SetBytes(int64(len(benchMemoryBody)))
	for b.Loop() {
		html := string(links.Body(benchMemoryBody))
		// A render that resolved nothing would be the fastest one here, and the
		// fallback path (an escaped <p class="mem-raw">) is a whole document too.
		if !strings.Contains(html, `href="/~bigbes/sr-ht-0/view/memory?key=memory-0-3"`) {
			b.Fatal("the wikilink did not resolve; this is measuring the wrong render")
		}
	}
}

// BenchmarkMemoryBodyRenderNoIndex is the same body with no index at all —
// what a pane gets when the database listing failed. Every reference is left as
// text, so the difference between the two is what resolution costs.
func BenchmarkMemoryBodyRenderNoIndex(b *testing.B) {
	var links *memoryLinks

	b.ReportAllocs()
	b.SetBytes(int64(len(benchMemoryBody)))
	for b.Loop() {
		html := string(links.Body(benchMemoryBody))
		if strings.Contains(html, "/view/memory?key=") {
			b.Fatal("an index-less render resolved a memory reference")
		}
	}
}