benchmark_test.go raw
1 package database
2
3 import (
4 "bufio"
5 "bytes"
6 "context"
7 "os"
8 "sort"
9 "testing"
10
11 "next.orly.dev/pkg/lol/chk"
12 "next.orly.dev/pkg/nostr/interfaces/signer/p8k"
13 "next.orly.dev/pkg/database/indexes/types"
14 "next.orly.dev/pkg/nostr/encoders/event"
15 "next.orly.dev/pkg/nostr/encoders/event/examples"
16 "next.orly.dev/pkg/nostr/encoders/filter"
17 "next.orly.dev/pkg/nostr/encoders/kind"
18 "next.orly.dev/pkg/nostr/encoders/tag"
19 )
20
21 var benchDB *D
22 var benchCtx context.Context
23 var benchCancel context.CancelFunc
24 var benchEvents []*event.E
25 var benchTempDir string
26
27 func setupBenchDB(b *testing.B) {
28 b.Helper()
29 if benchDB != nil {
30 return // Already set up
31 }
32 var err error
33 benchTempDir, err = os.MkdirTemp("", "bench-db-*")
34 if err != nil {
35 b.Fatalf("Failed to create temp dir: %v", err)
36 }
37 benchCtx, benchCancel = context.WithCancel(context.Background())
38 benchDB, err = New(benchCtx, benchCancel, benchTempDir, "error")
39 if err != nil {
40 b.Fatalf("Failed to create DB: %v", err)
41 }
42
43 // Load events from examples
44 scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
45 scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
46 benchEvents = make([]*event.E, 0, 1000)
47
48 for scanner.Scan() {
49 chk.E(scanner.Err())
50 b := scanner.Bytes()
51 ev := event.New()
52 if _, err = ev.Unmarshal(b); chk.E(err) {
53 ev.Free()
54 continue
55 }
56 benchEvents = append(benchEvents, ev)
57 }
58
59 // Sort events by CreatedAt
60 sort.Slice(benchEvents, func(i, j int) bool {
61 return benchEvents[i].CreatedAt < benchEvents[j].CreatedAt
62 })
63
64 // Save events to database for benchmarks
65 for _, ev := range benchEvents {
66 _, _ = benchDB.SaveEvent(benchCtx, ev)
67 }
68 }
69
70 func BenchmarkSaveEvent(b *testing.B) {
71 setupBenchDB(b)
72 b.ResetTimer()
73 b.ReportAllocs()
74 for i := 0; i < b.N; i++ {
75 // Create a simple test event
76 signer := p8k.MustNew()
77 if err := signer.Generate(); err != nil {
78 b.Fatal(err)
79 }
80 ev := event.New()
81 ev.Pubkey = signer.Pub()
82 ev.Kind = kind.TextNote.K
83 ev.Content = []byte("benchmark test event")
84 if err := ev.Sign(signer); err != nil {
85 b.Fatal(err)
86 }
87 _, _ = benchDB.SaveEvent(benchCtx, ev)
88 }
89 }
90
91 func BenchmarkQueryEvents(b *testing.B) {
92 setupBenchDB(b)
93 b.ResetTimer()
94 b.ReportAllocs()
95 f := &filter.F{
96 Kinds: kind.NewS(kind.New(1)),
97 Limit: pointerOf(uint(100)),
98 }
99 for i := 0; i < b.N; i++ {
100 _, _ = benchDB.QueryEvents(benchCtx, f)
101 }
102 }
103
104 func BenchmarkQueryForIds(b *testing.B) {
105 setupBenchDB(b)
106 b.ResetTimer()
107 b.ReportAllocs()
108 f := &filter.F{
109 Authors: tag.NewFromBytesSlice(benchEvents[0].Pubkey),
110 Kinds: kind.NewS(kind.New(1)),
111 Limit: pointerOf(uint(100)),
112 }
113 for i := 0; i < b.N; i++ {
114 _, _ = benchDB.QueryForIds(benchCtx, f)
115 }
116 }
117
118 func BenchmarkFetchEventsBySerials(b *testing.B) {
119 setupBenchDB(b)
120 // Get some serials first
121 var idxs []Range
122 idxs, _ = GetIndexesFromFilter(&filter.F{
123 Kinds: kind.NewS(kind.New(1)),
124 })
125 var serials []*types.Uint40
126 if len(idxs) > 0 {
127 serials, _ = benchDB.GetSerialsByRange(idxs[0])
128 if len(serials) > 100 {
129 serials = serials[:100]
130 }
131 }
132 b.ResetTimer()
133 b.ReportAllocs()
134 for i := 0; i < b.N; i++ {
135 _, _ = benchDB.FetchEventsBySerials(serials)
136 }
137 }
138
139 func BenchmarkGetSerialsByRange(b *testing.B) {
140 setupBenchDB(b)
141 var idxs []Range
142 idxs, _ = GetIndexesFromFilter(&filter.F{
143 Kinds: kind.NewS(kind.New(1)),
144 })
145 if len(idxs) == 0 {
146 b.Skip("No indexes to test")
147 }
148 b.ResetTimer()
149 b.ReportAllocs()
150 for i := 0; i < b.N; i++ {
151 _, _ = benchDB.GetSerialsByRange(idxs[0])
152 }
153 }
154
155 func BenchmarkGetFullIdPubkeyBySerials(b *testing.B) {
156 setupBenchDB(b)
157 var idxs []Range
158 idxs, _ = GetIndexesFromFilter(&filter.F{
159 Kinds: kind.NewS(kind.New(1)),
160 })
161 var serials []*types.Uint40
162 if len(idxs) > 0 {
163 serials, _ = benchDB.GetSerialsByRange(idxs[0])
164 if len(serials) > 100 {
165 serials = serials[:100]
166 }
167 }
168 b.ResetTimer()
169 b.ReportAllocs()
170 for i := 0; i < b.N; i++ {
171 _, _ = benchDB.GetFullIdPubkeyBySerials(serials)
172 }
173 }
174
175 func BenchmarkGetSerialById(b *testing.B) {
176 setupBenchDB(b)
177 if len(benchEvents) == 0 {
178 b.Skip("No events to test")
179 }
180 b.ResetTimer()
181 b.ReportAllocs()
182 for i := 0; i < b.N; i++ {
183 idx := i % len(benchEvents)
184 _, _ = benchDB.GetSerialById(benchEvents[idx].ID)
185 }
186 }
187
188 func BenchmarkGetSerialsByIds(b *testing.B) {
189 setupBenchDB(b)
190 if len(benchEvents) < 10 {
191 b.Skip("Not enough events to test")
192 }
193 ids := tag.New()
194 for i := 0; i < 10 && i < len(benchEvents); i++ {
195 ids.T = append(ids.T, benchEvents[i].ID)
196 }
197 b.ResetTimer()
198 b.ReportAllocs()
199 for i := 0; i < b.N; i++ {
200 _, _ = benchDB.GetSerialsByIds(ids)
201 }
202 }
203
204 func pointerOf[T any](v T) *T {
205 return &v
206 }
207
208