Files
orly/cmd/benchmark/main.go

158 lines
3.7 KiB
Go

package main
import (
"flag"
"fmt"
"sync"
"sync/atomic"
"time"
"orly.dev/pkg/encoders/filter"
"orly.dev/pkg/encoders/kind"
"orly.dev/pkg/encoders/kinds"
"orly.dev/pkg/encoders/timestamp"
"orly.dev/pkg/protocol/ws"
"orly.dev/pkg/utils/context"
"orly.dev/pkg/utils/log"
"orly.dev/pkg/utils/lol"
)
func main() {
var (
relayURL = flag.String("relay", "ws://localhost:3334", "Relay URL")
eventCount = flag.Int("events", 10000, "Number of events")
queryCount = flag.Int("queries", 100, "Number of queries")
concurrency = flag.Int("concurrency", 10, "Concurrent publishers")
verbose = flag.Bool("v", false, "Verbose output")
)
flag.Parse()
if *verbose {
lol.SetLogLevel("trace")
}
c := context.Bg()
if *eventCount > 0 {
fmt.Printf("Publishing %d events...\n", *eventCount)
publishEvents(c, *relayURL, *eventCount, *concurrency)
}
if *queryCount > 0 {
fmt.Printf("Executing %d queries...\n", *queryCount)
runQueries(c, *relayURL, *queryCount)
}
}
func publishEvents(c context.T, relayURL string, eventCount, concurrency int) {
signers := make([]*testSigner, concurrency)
for i := range signers {
signers[i] = newTestSigner()
}
var published atomic.Int64
var errors atomic.Int64
var wg sync.WaitGroup
eventsPerPublisher := eventCount / concurrency
extraEvents := eventCount % concurrency
startTime := time.Now()
for i := 0; i < concurrency; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
relay, err := ws.RelayConnect(c, relayURL)
if err != nil {
log.E.F("Failed to connect: %v", err)
errors.Add(1)
return
}
defer relay.Close()
count := eventsPerPublisher
if id < extraEvents {
count++
}
for j := 0; j < count; j++ {
ev := generateSimpleEvent(signers[id], 1024)
if err := relay.Publish(c, ev); err != nil {
errors.Add(1)
continue
}
published.Add(1)
}
}(i)
}
wg.Wait()
duration := time.Since(startTime)
rate := float64(published.Load()) / duration.Seconds()
fmt.Printf(" Published: %d\n", published.Load())
fmt.Printf(" Duration: %.2fs\n", duration.Seconds())
fmt.Printf(" Rate: %.2f events/s\n", rate)
if errors.Load() > 0 {
fmt.Printf(" Errors: %d\n", errors.Load())
}
}
func runQueries(c context.T, relayURL string, queryCount int) {
relay, err := ws.RelayConnect(c, relayURL)
if err != nil {
log.E.F("Failed to connect: %v", err)
return
}
defer relay.Close()
var totalEvents atomic.Int64
startTime := time.Now()
for i := 0; i < queryCount; i++ {
f := generateQueryFilter(i)
events, err := relay.QuerySync(c, f)
if err != nil {
continue
}
totalEvents.Add(int64(len(events)))
}
duration := time.Since(startTime)
rate := float64(queryCount) / duration.Seconds()
fmt.Printf(" Executed: %d\n", queryCount)
fmt.Printf(" Duration: %.2fs\n", duration.Seconds())
fmt.Printf(" Rate: %.2f queries/s\n", rate)
fmt.Printf(" Events returned: %d\n", totalEvents.Load())
}
func generateQueryFilter(index int) *filter.F {
limit := uint(100)
switch index % 5 {
case 0:
// Query all events by kind
return &filter.F{Kinds: kinds.New(kind.TextNote), Limit: &limit}
case 1:
// Query recent events
now := timestamp.Now()
since := timestamp.New(now.I64() - 3600)
return &filter.F{Since: since, Until: now, Limit: &limit}
case 2:
// Query all events (no filter)
return &filter.F{Limit: &limit}
case 3:
// Query by multiple kinds
return &filter.F{Kinds: kinds.New(kind.TextNote, kind.Repost, kind.Reaction), Limit: &limit}
default:
// Query older events
now := timestamp.Now()
until := timestamp.New(now.I64() - 1800)
since := timestamp.New(now.I64() - 7200)
return &filter.F{
Since: since,
Until: until,
Limit: &limit,
}
}
}