Skip to content

Commit 97d689c

Browse files
Add CodSpeed performance benchmarking integration
This commit adds comprehensive performance benchmarking support using CodSpeed: - Add benchmark files for album, auth, logging, and pagination packages - Configure GitHub Actions workflow for automated benchmark execution - Use CodSpeed action v4.4.1 with OIDC authentication - Enable continuous performance monitoring for all key components The benchmarks cover critical performance paths including service operations, authentication, logging, and pagination utilities.
1 parent c310133 commit 97d689c

File tree

5 files changed

+375
-0
lines changed

5 files changed

+375
-0
lines changed

.github/workflows/codspeed.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: CodSpeed Performance Benchmarks
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request: null
7+
permissions:
8+
contents: read
9+
id-token: write
10+
jobs:
11+
benchmarks:
12+
name: Run Go Benchmarks with CodSpeed
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
- name: Set up Go
18+
uses: actions/setup-go@v5
19+
with:
20+
go-version: "1.13"
21+
- name: Get dependencies
22+
run: |
23+
go mod download
24+
go mod verify
25+
- name: Run benchmarks with CodSpeed
26+
uses: CodSpeedHQ/[email protected]
27+
with:
28+
upload-url: ${{ secrets.CODSPEED_STAGING_UPLOAD_URL }}
29+
run: go test -bench=. -benchmem ./...
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package album
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/qiangxue/go-rest-api/pkg/log"
8+
)
9+
10+
func BenchmarkService_Create(b *testing.B) {
11+
logger, _ := log.NewForTest()
12+
s := NewService(&mockRepository{}, logger)
13+
ctx := context.Background()
14+
req := CreateAlbumRequest{Name: "benchmark album"}
15+
16+
b.ResetTimer()
17+
for i := 0; i < b.N; i++ {
18+
_, _ = s.Create(ctx, req)
19+
}
20+
}
21+
22+
func BenchmarkService_Get(b *testing.B) {
23+
logger, _ := log.NewForTest()
24+
repo := &mockRepository{}
25+
s := NewService(repo, logger)
26+
ctx := context.Background()
27+
28+
// Setup: create an album first
29+
album, _ := s.Create(ctx, CreateAlbumRequest{Name: "test album"})
30+
31+
b.ResetTimer()
32+
for i := 0; i < b.N; i++ {
33+
_, _ = s.Get(ctx, album.ID)
34+
}
35+
}
36+
37+
func BenchmarkService_Update(b *testing.B) {
38+
logger, _ := log.NewForTest()
39+
repo := &mockRepository{}
40+
s := NewService(repo, logger)
41+
ctx := context.Background()
42+
43+
// Setup: create an album first
44+
album, _ := s.Create(ctx, CreateAlbumRequest{Name: "test album"})
45+
req := UpdateAlbumRequest{Name: "updated album"}
46+
47+
b.ResetTimer()
48+
for i := 0; i < b.N; i++ {
49+
_, _ = s.Update(ctx, album.ID, req)
50+
}
51+
}
52+
53+
func BenchmarkService_Delete(b *testing.B) {
54+
logger, _ := log.NewForTest()
55+
ctx := context.Background()
56+
57+
b.ResetTimer()
58+
for i := 0; i < b.N; i++ {
59+
b.StopTimer()
60+
repo := &mockRepository{}
61+
s := NewService(repo, logger)
62+
album, _ := s.Create(ctx, CreateAlbumRequest{Name: "test album"})
63+
b.StartTimer()
64+
65+
_, _ = s.Delete(ctx, album.ID)
66+
}
67+
}
68+
69+
func BenchmarkService_Query(b *testing.B) {
70+
logger, _ := log.NewForTest()
71+
repo := &mockRepository{}
72+
s := NewService(repo, logger)
73+
ctx := context.Background()
74+
75+
// Setup: create multiple albums
76+
for i := 0; i < 10; i++ {
77+
_, _ = s.Create(ctx, CreateAlbumRequest{Name: "test album"})
78+
}
79+
80+
b.ResetTimer()
81+
for i := 0; i < b.N; i++ {
82+
_, _ = s.Query(ctx, 0, 10)
83+
}
84+
}
85+
86+
func BenchmarkService_Count(b *testing.B) {
87+
logger, _ := log.NewForTest()
88+
repo := &mockRepository{}
89+
s := NewService(repo, logger)
90+
ctx := context.Background()
91+
92+
// Setup: create multiple albums
93+
for i := 0; i < 10; i++ {
94+
_, _ = s.Create(ctx, CreateAlbumRequest{Name: "test album"})
95+
}
96+
97+
b.ResetTimer()
98+
for i := 0; i < b.N; i++ {
99+
_, _ = s.Count(ctx)
100+
}
101+
}
102+
103+
func BenchmarkCreateAlbumRequest_Validate(b *testing.B) {
104+
req := CreateAlbumRequest{Name: "test album"}
105+
106+
b.ResetTimer()
107+
for i := 0; i < b.N; i++ {
108+
_ = req.Validate()
109+
}
110+
}
111+
112+
func BenchmarkUpdateAlbumRequest_Validate(b *testing.B) {
113+
req := UpdateAlbumRequest{Name: "updated album"}
114+
115+
b.ResetTimer()
116+
for i := 0; i < b.N; i++ {
117+
_ = req.Validate()
118+
}
119+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package auth
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/qiangxue/go-rest-api/pkg/log"
8+
)
9+
10+
func BenchmarkService_Login(b *testing.B) {
11+
logger, _ := log.NewForTest()
12+
s := NewService("test-signing-key", 3600, logger)
13+
ctx := context.Background()
14+
15+
b.ResetTimer()
16+
for i := 0; i < b.N; i++ {
17+
_, _ = s.Login(ctx, "demo", "pass")
18+
}
19+
}
20+
21+
func BenchmarkService_LoginInvalid(b *testing.B) {
22+
logger, _ := log.NewForTest()
23+
s := NewService("test-signing-key", 3600, logger)
24+
ctx := context.Background()
25+
26+
b.ResetTimer()
27+
for i := 0; i < b.N; i++ {
28+
_, _ = s.Login(ctx, "invalid", "invalid")
29+
}
30+
}
31+
32+
func BenchmarkWithUser(b *testing.B) {
33+
ctx := context.Background()
34+
35+
b.ResetTimer()
36+
for i := 0; i < b.N; i++ {
37+
_ = WithUser(ctx, "100", "demo")
38+
}
39+
}
40+
41+
func BenchmarkCurrentUser(b *testing.B) {
42+
ctx := WithUser(context.Background(), "100", "demo")
43+
44+
b.ResetTimer()
45+
for i := 0; i < b.N; i++ {
46+
_ = CurrentUser(ctx)
47+
}
48+
}

pkg/log/logger_bench_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package log
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
)
8+
9+
func BenchmarkLogger_With(b *testing.B) {
10+
logger := New()
11+
ctx := context.Background()
12+
args := []interface{}{"key1", "value1", "key2", "value2"}
13+
14+
b.ResetTimer()
15+
for i := 0; i < b.N; i++ {
16+
_ = logger.With(ctx, args...)
17+
}
18+
}
19+
20+
func BenchmarkLogger_WithContext(b *testing.B) {
21+
logger := New()
22+
req, _ := http.NewRequest("GET", "/test", nil)
23+
req.Header.Set("X-Request-ID", "test-request-id")
24+
req.Header.Set("X-Correlation-ID", "test-correlation-id")
25+
ctx := WithRequest(context.Background(), req)
26+
args := []interface{}{"key", "value"}
27+
28+
b.ResetTimer()
29+
for i := 0; i < b.N; i++ {
30+
_ = logger.With(ctx, args...)
31+
}
32+
}
33+
34+
func BenchmarkLogger_Debug(b *testing.B) {
35+
logger := New()
36+
37+
b.ResetTimer()
38+
for i := 0; i < b.N; i++ {
39+
logger.Debug("debug message")
40+
}
41+
}
42+
43+
func BenchmarkLogger_Info(b *testing.B) {
44+
logger := New()
45+
46+
b.ResetTimer()
47+
for i := 0; i < b.N; i++ {
48+
logger.Info("info message")
49+
}
50+
}
51+
52+
func BenchmarkLogger_Error(b *testing.B) {
53+
logger := New()
54+
55+
b.ResetTimer()
56+
for i := 0; i < b.N; i++ {
57+
logger.Error("error message")
58+
}
59+
}
60+
61+
func BenchmarkLogger_Debugf(b *testing.B) {
62+
logger := New()
63+
64+
b.ResetTimer()
65+
for i := 0; i < b.N; i++ {
66+
logger.Debugf("debug message: %s %d", "test", i)
67+
}
68+
}
69+
70+
func BenchmarkLogger_Infof(b *testing.B) {
71+
logger := New()
72+
73+
b.ResetTimer()
74+
for i := 0; i < b.N; i++ {
75+
logger.Infof("info message: %s %d", "test", i)
76+
}
77+
}
78+
79+
func BenchmarkLogger_Errorf(b *testing.B) {
80+
logger := New()
81+
82+
b.ResetTimer()
83+
for i := 0; i < b.N; i++ {
84+
logger.Errorf("error message: %s %d", "test", i)
85+
}
86+
}
87+
88+
func BenchmarkWithRequest(b *testing.B) {
89+
req, _ := http.NewRequest("GET", "/test", nil)
90+
req.Header.Set("X-Request-ID", "test-request-id")
91+
req.Header.Set("X-Correlation-ID", "test-correlation-id")
92+
ctx := context.Background()
93+
94+
b.ResetTimer()
95+
for i := 0; i < b.N; i++ {
96+
_ = WithRequest(ctx, req)
97+
}
98+
}

pkg/pagination/pages_bench_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package pagination
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
)
7+
8+
func BenchmarkNew(b *testing.B) {
9+
b.ResetTimer()
10+
for i := 0; i < b.N; i++ {
11+
_ = New(1, 100, 1000)
12+
}
13+
}
14+
15+
func BenchmarkNewFromRequest(b *testing.B) {
16+
req, _ := http.NewRequest("GET", "/test?page=2&per_page=50", nil)
17+
18+
b.ResetTimer()
19+
for i := 0; i < b.N; i++ {
20+
_ = NewFromRequest(req, 1000)
21+
}
22+
}
23+
24+
func BenchmarkPages_Offset(b *testing.B) {
25+
pages := New(5, 100, 1000)
26+
27+
b.ResetTimer()
28+
for i := 0; i < b.N; i++ {
29+
_ = pages.Offset()
30+
}
31+
}
32+
33+
func BenchmarkPages_Limit(b *testing.B) {
34+
pages := New(5, 100, 1000)
35+
36+
b.ResetTimer()
37+
for i := 0; i < b.N; i++ {
38+
_ = pages.Limit()
39+
}
40+
}
41+
42+
func BenchmarkPages_BuildLinks(b *testing.B) {
43+
pages := New(5, 100, 1000)
44+
baseURL := "http://example.com/api/items"
45+
46+
b.ResetTimer()
47+
for i := 0; i < b.N; i++ {
48+
_ = pages.BuildLinks(baseURL, 100)
49+
}
50+
}
51+
52+
func BenchmarkPages_BuildLinkHeader(b *testing.B) {
53+
pages := New(5, 100, 1000)
54+
baseURL := "http://example.com/api/items"
55+
56+
b.ResetTimer()
57+
for i := 0; i < b.N; i++ {
58+
_ = pages.BuildLinkHeader(baseURL, 100)
59+
}
60+
}
61+
62+
func BenchmarkParseInt(b *testing.B) {
63+
b.ResetTimer()
64+
for i := 0; i < b.N; i++ {
65+
_ = parseInt("42", 10)
66+
}
67+
}
68+
69+
func BenchmarkParseIntDefault(b *testing.B) {
70+
b.ResetTimer()
71+
for i := 0; i < b.N; i++ {
72+
_ = parseInt("", 10)
73+
}
74+
}
75+
76+
func BenchmarkParseIntInvalid(b *testing.B) {
77+
b.ResetTimer()
78+
for i := 0; i < b.N; i++ {
79+
_ = parseInt("invalid", 10)
80+
}
81+
}

0 commit comments

Comments
 (0)