Skip to content

Commit cba3bd7

Browse files
committed
feat: added integration test and some benchmark info
1 parent 8810fa6 commit cba3bd7

File tree

4 files changed

+473
-32
lines changed

4 files changed

+473
-32
lines changed

README.md

+87
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ gRoxy is a gRPC mocking server that allows you to mock gRPC services and respons
2020
- [enums](#enums)
2121
- [repeated fields](#repeated-fields)
2222
- [maps](#maps)
23+
- [benchmark](#benchmark)
2324
- [project status](#status)
2425

2526
## todos
@@ -243,5 +244,91 @@ message StubResponse {
243244
}
244245
```
245246

247+
## benchmark
248+
249+
all benchmarks were performed on a MacBook Pro 2021 with M1 Pro chip with 16GB of RAM.
250+
251+
### mocker
252+
253+
single rule on mock
254+
255+
```shell
256+
$ ghz --insecure --call 'grpc_echo/v1/EchoService/Echo' -d '{"ping": "Hello, world!"}' -c 1 --total 10000 localhost:8080
257+
258+
Summary:
259+
Count: 10000
260+
Total: 3.42 s
261+
Slowest: 8.03 ms
262+
Fastest: 0.12 ms
263+
Average: 0.24 ms
264+
Requests/sec: 2923.93
265+
266+
Response time histogram:
267+
0.119 [1] |
268+
0.909 [9965] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
269+
1.700 [30] |
270+
2.491 [0] |
271+
3.282 [2] |
272+
4.072 [1] |
273+
4.863 [0] |
274+
5.654 [0] |
275+
6.444 [0] |
276+
7.235 [0] |
277+
8.026 [1] |
278+
279+
Latency distribution:
280+
10 % in 0.17 ms
281+
25 % in 0.18 ms
282+
50 % in 0.21 ms
283+
75 % in 0.26 ms
284+
90 % in 0.35 ms
285+
95 % in 0.42 ms
286+
99 % in 0.66 ms
287+
288+
Status code distribution:
289+
[OK] 10000 responses
290+
```
291+
292+
### reverse-proxy
293+
294+
performed with the use of [grpc-echo](https://github.com/Semior001/grpc-echo), single rule on upstream
295+
296+
```shell
297+
$ ghz --insecure --call 'grpc_echo/v1/EchoService/Echo' -d '{"ping": "Hello, world!"}' -c 1 --total 10000 localhost:8080
298+
299+
Summary:
300+
Count: 10000
301+
Total: 10.16 s
302+
Slowest: 43.56 ms
303+
Fastest: 0.46 ms
304+
Average: 0.90 ms
305+
Requests/sec: 984.55
306+
307+
Response time histogram:
308+
0.456 [1] |
309+
4.767 [9980] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
310+
9.078 [16] |
311+
13.389 [0] |
312+
17.700 [0] |
313+
22.010 [1] |
314+
26.321 [1] |
315+
30.632 [0] |
316+
34.943 [0] |
317+
39.254 [0] |
318+
43.564 [1] |
319+
320+
Latency distribution:
321+
10 % in 0.61 ms
322+
25 % in 0.67 ms
323+
50 % in 0.78 ms
324+
75 % in 0.97 ms
325+
90 % in 1.25 ms
326+
95 % in 1.53 ms
327+
99 % in 2.57 ms
328+
329+
Status code distribution:
330+
[OK] 10000 responses
331+
```
332+
246333
## status
247334
The project is currently in active development, and breaking changes may occur until the release of version 1.0. However, we strive to minimize disruptions and will only introduce breaking changes when there is a compelling reason to do so.

cmd/groxy/main_test.go

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
"net"
6+
"github.com/stretchr/testify/require"
7+
"fmt"
8+
"math/rand"
9+
"os"
10+
"syscall"
11+
"github.com/testcontainers/testcontainers-go"
12+
"github.com/testcontainers/testcontainers-go/wait"
13+
"context"
14+
"google.golang.org/grpc"
15+
healthpb "google.golang.org/grpc/health/grpc_health_v1"
16+
"time"
17+
"google.golang.org/grpc/credentials/insecure"
18+
"strings"
19+
20+
"github.com/Semior001/grpc-echo/echopb"
21+
)
22+
23+
func TestMain_ReverseProxy(t *testing.T) {
24+
echoAddr := startEchoServer(t)
25+
_, conn := setup(t, fmt.Sprintf(`
26+
version: 1
27+
upstreams: { benchmark: { address: "%s" } }
28+
rules: [{ match: { uri: "(.*)" }, forward: { upstream: "benchmark" } }]`, echoAddr))
29+
waitForServerUp(t, conn)
30+
31+
client := echopb.NewEchoServiceClient(conn)
32+
33+
resp, err := client.Echo(context.Background(), &echopb.EchoRequest{Ping: "hello"})
34+
require.NoError(t, err)
35+
36+
t.Logf("%+v", resp)
37+
}
38+
39+
func setup(tb testing.TB, config string, flags ...string) (port int, conn *grpc.ClientConn) {
40+
tb.Helper()
41+
42+
f, err := os.CreateTemp("", "groxy-test")
43+
require.NoError(tb, err)
44+
tb.Cleanup(func() { require.NoError(tb, os.Remove(f.Name())) })
45+
46+
tb.Logf("writing config to %s", f.Name())
47+
48+
_, err = f.WriteString(config)
49+
require.NoError(tb, err)
50+
require.NoError(tb, f.Close())
51+
52+
port = 40000 + int(rand.Int31n(10000))
53+
os.Args = append([]string{"test", "--file.name=" + f.Name(), "--addr=:" + fmt.Sprint(port)}, flags...)
54+
55+
done := make(chan struct{})
56+
go func() {
57+
<-done
58+
e := syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
59+
require.NoError(tb, e)
60+
}()
61+
62+
started, finished := make(chan struct{}), make(chan struct{})
63+
go func() {
64+
tb.Logf("running server on port %d", port)
65+
close(started)
66+
main()
67+
close(finished)
68+
}()
69+
70+
tb.Cleanup(func() {
71+
close(done)
72+
<-finished
73+
})
74+
75+
<-started
76+
time.Sleep(time.Millisecond * 50) // do not start right away
77+
conn, err = grpc.NewClient(fmt.Sprintf("localhost:%d", port),
78+
grpc.WithTransportCredentials(insecure.NewCredentials()),
79+
grpc.WithUserAgent("groxy-test-ua"))
80+
require.NoError(tb, err)
81+
82+
tb.Cleanup(func() {
83+
if err := conn.Close(); err != nil &&
84+
!strings.Contains(err.Error(), "grpc: the client connection is closing") {
85+
tb.Errorf("failed to close connection: %v", err)
86+
}
87+
})
88+
89+
tb.Logf("server started on port %d", port)
90+
return port, conn
91+
}
92+
93+
func waitForServerUp(tb testing.TB, conn *grpc.ClientConn) {
94+
tb.Helper()
95+
96+
healthClient := healthpb.NewHealthClient(conn)
97+
for i := 0; i < 100; i++ {
98+
time.Sleep(time.Millisecond * 100)
99+
st, err := healthClient.Check(context.Background(), &healthpb.HealthCheckRequest{})
100+
if err == nil && st.Status == healthpb.HealthCheckResponse_SERVING {
101+
tb.Logf("server is up")
102+
return
103+
}
104+
}
105+
tb.Fatal("server is not up")
106+
}
107+
108+
func startEchoServer(tb testing.TB) (addr string) {
109+
tb.Helper()
110+
ctx := context.Background()
111+
112+
provider, err := testcontainers.ProviderDocker.GetProvider()
113+
if err != nil {
114+
tb.Skipf("docker not available, skipping test: %s", err)
115+
}
116+
if err = provider.Health(ctx); err != nil {
117+
tb.Skipf("docker not healthy, skipping test: %s", err)
118+
}
119+
120+
echoReq := testcontainers.GenericContainerRequest{
121+
ContainerRequest: testcontainers.ContainerRequest{
122+
Image: "semior/grpc-echo:latest",
123+
ExposedPorts: []string{"8080/tcp"},
124+
WaitingFor: wait.ForLog("listening gRPC"),
125+
},
126+
Started: true,
127+
}
128+
echo, err := testcontainers.GenericContainer(ctx, echoReq)
129+
testcontainers.CleanupContainer(tb, echo)
130+
require.NoError(tb, err)
131+
132+
echoIP, err := echo.Host(ctx)
133+
require.NoError(tb, err)
134+
135+
echoPort, err := echo.MappedPort(ctx, "8080")
136+
require.NoError(tb, err)
137+
138+
addr = net.JoinHostPort(echoIP, echoPort.Port())
139+
tb.Logf("started echo server at %s", addr)
140+
return addr
141+
}

go.mod

+55-10
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,74 @@
11
module github.com/Semior001/groxy
22

3-
go 1.22.0
3+
go 1.23.4
44

55
require (
6+
github.com/Semior001/grpc-echo/echopb v0.1.0
67
github.com/bufbuild/protocompile v0.8.0
78
github.com/cappuccinotm/slogx v1.3.0
89
github.com/jessevdk/go-flags v1.5.0
910
github.com/jhump/protoreflect v1.15.6
1011
github.com/lmittmann/tint v1.0.4
1112
github.com/mattn/go-isatty v0.0.20
1213
github.com/samber/lo v1.39.0
13-
github.com/stretchr/testify v1.8.4
14-
golang.org/x/sync v0.6.0
15-
google.golang.org/grpc v1.62.0
16-
google.golang.org/protobuf v1.32.0
14+
github.com/stretchr/testify v1.9.0
15+
github.com/testcontainers/testcontainers-go v0.34.0
16+
golang.org/x/sync v0.8.0
17+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1
18+
google.golang.org/grpc v1.68.1
19+
google.golang.org/protobuf v1.35.2
1720
gopkg.in/yaml.v3 v3.0.1
1821
)
1922

2023
require (
24+
dario.cat/mergo v1.0.0 // indirect
25+
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
26+
github.com/Microsoft/go-winio v0.6.2 // indirect
27+
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
28+
github.com/containerd/containerd v1.7.18 // indirect
29+
github.com/containerd/log v0.1.0 // indirect
30+
github.com/containerd/platforms v0.2.1 // indirect
31+
github.com/cpuguy83/dockercfg v0.3.2 // indirect
2132
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
22-
github.com/golang/protobuf v1.5.3 // indirect
33+
github.com/distribution/reference v0.6.0 // indirect
34+
github.com/docker/docker v27.1.1+incompatible // indirect
35+
github.com/docker/go-connections v0.5.0 // indirect
36+
github.com/docker/go-units v0.5.0 // indirect
37+
github.com/felixge/httpsnoop v1.0.4 // indirect
38+
github.com/go-logr/logr v1.4.1 // indirect
39+
github.com/go-logr/stdr v1.2.2 // indirect
40+
github.com/go-ole/go-ole v1.2.6 // indirect
41+
github.com/gogo/protobuf v1.3.2 // indirect
42+
github.com/golang/protobuf v1.5.4 // indirect
43+
github.com/google/uuid v1.6.0 // indirect
44+
github.com/klauspost/compress v1.17.4 // indirect
45+
github.com/kr/text v0.2.0 // indirect
46+
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
47+
github.com/magiconair/properties v1.8.7 // indirect
48+
github.com/moby/docker-image-spec v1.3.1 // indirect
49+
github.com/moby/patternmatcher v0.6.0 // indirect
50+
github.com/moby/sys/sequential v0.5.0 // indirect
51+
github.com/moby/sys/user v0.1.0 // indirect
52+
github.com/moby/term v0.5.0 // indirect
53+
github.com/morikuni/aec v1.0.0 // indirect
54+
github.com/opencontainers/go-digest v1.0.0 // indirect
55+
github.com/opencontainers/image-spec v1.1.0 // indirect
56+
github.com/pkg/errors v0.9.1 // indirect
2357
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
58+
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
59+
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
60+
github.com/shoenig/go-m1cpu v0.1.6 // indirect
61+
github.com/sirupsen/logrus v1.9.3 // indirect
62+
github.com/tklauser/go-sysconf v0.3.12 // indirect
63+
github.com/tklauser/numcpus v0.6.1 // indirect
64+
github.com/yusufpapurcu/wmi v1.2.3 // indirect
65+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
66+
go.opentelemetry.io/otel v1.24.0 // indirect
67+
go.opentelemetry.io/otel/metric v1.24.0 // indirect
68+
go.opentelemetry.io/otel/trace v1.24.0 // indirect
69+
golang.org/x/crypto v0.27.0 // indirect
2470
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
25-
golang.org/x/net v0.21.0 // indirect
26-
golang.org/x/sys v0.17.0 // indirect
27-
golang.org/x/text v0.14.0 // indirect
28-
google.golang.org/genproto/googleapis/rpc v0.0.0-20240228224816-df926f6c8641 // indirect
71+
golang.org/x/net v0.29.0 // indirect
72+
golang.org/x/sys v0.25.0 // indirect
73+
golang.org/x/text v0.18.0 // indirect
2974
)

0 commit comments

Comments
 (0)