Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
groups:
all-go-deps:
patterns:
- "*"

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
groups:
all-actions:
patterns:
- "*"
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: "1.26"
- uses: golangci/golangci-lint-action@v9
with:
version: v2.9

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version: "1.26"
- name: Run tests
run: go test -race -count=1 -timeout 120s ./...
29 changes: 29 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Release

on:
release:
types: [published]

permissions:
contents: read

jobs:
publish:
name: Publish to Go Package Registry
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: "1.26"
- name: Verify module
run: |
go mod verify
go build ./...
go vet ./...
- name: Request pkg.go.dev indexing
run: |
TAG="${{ github.event.release.tag_name }}"
curl -s "https://proxy.golang.org/github.com/goceleris/loadgen/@v/${TAG}.info" || true
curl -s "https://sum.golang.org/lookup/github.com/goceleris/loadgen@${TAG}" || true
echo "Published github.com/goceleris/loadgen@${TAG}"
41 changes: 41 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
version: "2"

run:
timeout: 5m

formatters:
enable:
- gofmt
- goimports
settings:
goimports:
local-prefixes:
- github.com/goceleris/loadgen

linters:
default: none
enable:
- errcheck
- govet
- staticcheck
- revive
- ineffassign
- unused
- misspell
- unconvert
- unparam
- prealloc
- nolintlint
settings:
nolintlint:
require-explanation: true
require-specific: true
exclusions:
rules:
- path: _test\.go
linters:
- unparam
- path: _test\.go
linters:
- revive
text: "dot-imports"
2 changes: 1 addition & 1 deletion bench.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package bench provides a high-performance HTTP benchmarking tool.
// Package loadgen provides a high-performance HTTP benchmarking tool.
package loadgen

import (
Expand Down
42 changes: 21 additions & 21 deletions benchmarker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import (
)

func TestBenchmarkerSuccess(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, _ = w.Write([]byte("OK"))
}))
defer srv.Close()

Expand Down Expand Up @@ -55,9 +55,9 @@ func TestBenchmarkerSuccess(t *testing.T) {
}

func TestBenchmarkerWarmup(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, _ = w.Write([]byte("OK"))
}))
defer srv.Close()

Expand Down Expand Up @@ -96,9 +96,9 @@ func TestBenchmarkerWarmup(t *testing.T) {
}

func TestBenchmarkerContextCancellation(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, _ = w.Write([]byte("OK"))
}))
defer srv.Close()

Expand Down Expand Up @@ -141,9 +141,9 @@ func TestBenchmarkerContextCancellation(t *testing.T) {
}

func TestBenchmarkerErrors(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(500)
w.Write([]byte("Internal Server Error"))
_, _ = w.Write([]byte("Internal Server Error"))
}))
defer srv.Close()

Expand Down Expand Up @@ -172,9 +172,9 @@ func TestBenchmarkerErrors(t *testing.T) {
}

func TestBenchmarkerTimeseries(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, _ = w.Write([]byte("OK"))
}))
defer srv.Close()

Expand Down Expand Up @@ -207,9 +207,9 @@ func TestBenchmarkerTimeseries(t *testing.T) {
}

func TestBenchmarkerOnProgress(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, _ = w.Write([]byte("OK"))
}))
defer srv.Close()

Expand All @@ -222,7 +222,7 @@ func TestBenchmarkerOnProgress(t *testing.T) {
Connections: 4,
Workers: 4,
Warmup: 0,
OnProgress: func(elapsed time.Duration, snapshot Result) {
OnProgress: func(_ time.Duration, _ Result) {
progressCalls.Add(1)
},
}
Expand All @@ -244,9 +244,9 @@ func TestBenchmarkerOnProgress(t *testing.T) {
}

func TestBenchmarkerMaxRPS(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, _ = w.Write([]byte("OK"))
}))
defer srv.Close()

Expand Down Expand Up @@ -284,7 +284,7 @@ type mockClient struct {
calls atomic.Int64
}

func (m *mockClient) DoRequest(ctx context.Context, workerID int) (int, error) {
func (m *mockClient) DoRequest(_ context.Context, _ int) (int, error) {
m.calls.Add(1)
// Simulate a tiny bit of work
time.Sleep(100 * time.Microsecond)
Expand Down Expand Up @@ -346,10 +346,10 @@ func TestBenchmarkerValidation(t *testing.T) {
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Run(tt.name, func(st *testing.T) {
_, err := New(tt.cfg)
if err == nil {
t.Errorf("expected error for %s config", tt.name)
st.Errorf("expected error for %s config", tt.name)
}
})
}
Expand All @@ -358,9 +358,9 @@ func TestBenchmarkerValidation(t *testing.T) {
func TestBenchmarkerH2(t *testing.T) {
// Start an h2c server
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
w.Write([]byte("OK"))
_, _ = w.Write([]byte("OK"))
})

h2s := &http2.Server{}
Expand Down Expand Up @@ -429,7 +429,7 @@ func TestBenchmarkerH2Validation(t *testing.T) {
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Run(tt.name, func(_ *testing.T) {
// New() applies defaults for zero values, so these should succeed.
// We test that the defaults are applied correctly.
b, err := New(tt.cfg)
Expand Down
20 changes: 10 additions & 10 deletions checkpoint/checkpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ type BenchmarkConfig struct {

// SystemMetrics holds server-side and client-side resource metrics.
type SystemMetrics struct {
ServerCPUPercent float64 `json:"server_cpu_percent,omitempty"`
ServerCPUUserPercent float64 `json:"server_cpu_user_percent,omitempty"`
ServerCPUSysPercent float64 `json:"server_cpu_sys_percent,omitempty"`
ServerMemoryRSSMB float64 `json:"server_memory_rss_mb,omitempty"`
ClientCPUPercent float64 `json:"client_cpu_percent,omitempty"`
GCPauses *GCPauseStats `json:"server_gc,omitempty"`
ServerCPUPercent float64 `json:"server_cpu_percent,omitempty"`
ServerCPUUserPercent float64 `json:"server_cpu_user_percent,omitempty"`
ServerCPUSysPercent float64 `json:"server_cpu_sys_percent,omitempty"`
ServerMemoryRSSMB float64 `json:"server_memory_rss_mb,omitempty"`
ClientCPUPercent float64 `json:"client_cpu_percent,omitempty"`
GCPauses *GCPauseStats `json:"server_gc,omitempty"`
Timeseries []loadgen.TimeseriesPoint `json:"timeseries,omitempty"`
}

Expand Down Expand Up @@ -216,14 +216,14 @@ func formatBytes(b float64) string {

func formatFloat(f float64) string {
if f >= 100 {
return java_like_format(f, 0)
return javaLikeFormat(f, 0)
} else if f >= 10 {
return java_like_format(f, 1)
return javaLikeFormat(f, 1)
}
return java_like_format(f, 2)
return javaLikeFormat(f, 2)
}

func java_like_format(f float64, precision int) string {
func javaLikeFormat(f float64, precision int) string {
format := "%." + string('0'+byte(precision)) + "f"
return sprintf(format, f)
}
Expand Down
18 changes: 9 additions & 9 deletions cmd/loadgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ func main() {
}

cfg := loadgen.Config{
URL: *url,
Method: *method,
Body: body,
Duration: *duration,
Warmup: *warmup,
Connections: *connections,
Workers: w,
DisableKeepAlive: *connClose,
HTTP2: *h2,
URL: *url,
Method: *method,
Body: body,
Duration: *duration,
Warmup: *warmup,
Connections: *connections,
Workers: w,
DisableKeepAlive: *connClose,
HTTP2: *h2,
HTTP2Options: loadgen.HTTP2Options{
Connections: *h2Conns,
MaxStreams: *h2Streams,
Expand Down
2 changes: 1 addition & 1 deletion h1client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
// matches wrk's behavior of having many in-flight reconnects per thread.
type h1Client struct {
conns []*h1Conn
connCounters []int // per-worker round-robin counter (no sync needed)
connCounters []int // per-worker round-robin counter (no sync needed)
addr string
reqBuf []byte // pre-formatted request bytes (immutable)
keepAlive bool
Expand Down
Loading