117 lines
2.7 KiB
Go
117 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
func main() {
|
|
dbPath := "./data"
|
|
if _, err := os.Stat(filepath.Join(dbPath, "osv.db")); os.IsNotExist(err) {
|
|
log.Fatal("Real database not found at data/osv.db, cannot run load test")
|
|
}
|
|
|
|
// Build server
|
|
log.Println("Building osv-server...")
|
|
buildCmd := exec.Command("go", "build", "-o", "bin/osv-server", "./cmd/osv-server")
|
|
if out, err := buildCmd.CombinedOutput(); err != nil {
|
|
log.Fatalf("Build failed: %v\n%s", err, out)
|
|
}
|
|
|
|
// Start server
|
|
log.Println("Starting osv-server...")
|
|
port := "9091"
|
|
serverCmd := exec.Command("./bin/osv-server", "-port", port, "-data-dir", dbPath)
|
|
serverCmd.Env = append(os.Environ(), "OSV_SILENT=true")
|
|
if err := serverCmd.Start(); err != nil {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
defer serverCmd.Process.Kill()
|
|
|
|
// Wait for server to be ready
|
|
time.Sleep(2 * time.Second)
|
|
|
|
log.Println("Starting load test...")
|
|
const (
|
|
concurrency = 200
|
|
duration = 10 * time.Second
|
|
)
|
|
|
|
var (
|
|
requests int64
|
|
errors int64
|
|
latencySum int64
|
|
latencyCount int64
|
|
)
|
|
|
|
queryReq := map[string]interface{}{
|
|
"package": map[string]interface{}{
|
|
"name": "requests",
|
|
"ecosystem": "PyPI",
|
|
},
|
|
"version": "2.28.0",
|
|
}
|
|
body, _ := json.Marshal(queryReq)
|
|
url := fmt.Sprintf("http://localhost:%s/v1/query", port)
|
|
|
|
start := time.Now()
|
|
var wg sync.WaitGroup
|
|
wg.Add(concurrency)
|
|
|
|
for i := 0; i < concurrency; i++ {
|
|
go func() {
|
|
defer wg.Done()
|
|
client := &http.Client{
|
|
Timeout: 5 * time.Second,
|
|
}
|
|
for time.Since(start) < duration {
|
|
reqStart := time.Now()
|
|
resp, err := client.Post(url, "application/json", bytes.NewBuffer(body))
|
|
if err != nil {
|
|
atomic.AddInt64(&errors, 1)
|
|
continue
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
atomic.AddInt64(&errors, 1)
|
|
}
|
|
resp.Body.Close()
|
|
|
|
latency := time.Since(reqStart).Nanoseconds()
|
|
atomic.AddInt64(&latencySum, latency)
|
|
atomic.AddInt64(&latencyCount, 1)
|
|
atomic.AddInt64(&requests, 1)
|
|
}
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
totalDuration := time.Since(start).Seconds()
|
|
rps := float64(requests) / totalDuration
|
|
|
|
fmt.Printf("\n--- Load Test Results ---\n")
|
|
fmt.Printf("Total Requests: %d\n", requests)
|
|
fmt.Printf("Total Errors: %d\n", errors)
|
|
fmt.Printf("Duration: %.2fs\n", totalDuration)
|
|
fmt.Printf("Avg RPS: %.2f\n", rps)
|
|
if latencyCount > 0 {
|
|
avgLatency := time.Duration(latencySum / latencyCount)
|
|
fmt.Printf("Avg Latency: %v\n", avgLatency)
|
|
}
|
|
fmt.Printf("-------------------------\n")
|
|
|
|
if rps < 1000 {
|
|
log.Printf("Warning: RPS is below target of 1000: %.2f", rps)
|
|
} else {
|
|
log.Printf("Success: RPS target met: %.2f", rps)
|
|
}
|
|
}
|