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