Skip to content

Commit

Permalink
Add otelhttp Handler.ServeHTTP and Transport.RoundTrip benchmarks (#5681
Browse files Browse the repository at this point in the history
)

This adds high-level benchmarks for `otelhttp.Handler.ServeHTTP` and
`otelhttp.Transport.RoundTrip`, as well as a benchmark comparison
without those wrappers.

Related: #5664.

Current results on my machine:

```
goos: darwin
goarch: arm64
pkg: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/test
BenchmarkHandlerServeHTTP/without_the_otelhttp_handler-10               37971928                31.35 ns/op           28 B/op          0 allocs/op
BenchmarkHandlerServeHTTP/with_the_otelhttp_handler-10                    435142              2723 ns/op            5585 B/op         35 allocs/op
BenchmarkTransportRoundTrip/without_the_otelhttp_transport-10              11430            401735 ns/op           24436 B/op        117 allocs/op
BenchmarkTransportRoundTrip/with_the_otelhttp_transport-10                   278           4367998 ns/op            5416 B/op         85 allocs/op
PASS
ok      go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/test      10.056s
```

---------

Co-authored-by: Tyler Yahn <[email protected]>
  • Loading branch information
dmathieu and MrAlias committed May 30, 2024
1 parent db38424 commit 59a2c1d
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 16 deletions.
51 changes: 46 additions & 5 deletions instrumentation/net/http/otelhttp/test/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
Expand Down Expand Up @@ -80,8 +80,8 @@ func TestHandlerBasics(t *testing.T) {
spanRecorder := tracetest.NewSpanRecorder()
provider := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(spanRecorder))

reader := metric.NewManualReader()
meterProvider := metric.NewMeterProvider(metric.WithReader(reader))
reader := sdkmetric.NewManualReader()
meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))

h := otelhttp.NewHandler(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -469,8 +469,8 @@ func TestWithRouteTag(t *testing.T) {
tracerProvider := sdktrace.NewTracerProvider()
tracerProvider.RegisterSpanProcessor(spanRecorder)

metricReader := metric.NewManualReader()
meterProvider := metric.NewMeterProvider(metric.WithReader(metricReader))
metricReader := sdkmetric.NewManualReader()
meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(metricReader))

h := otelhttp.NewHandler(
otelhttp.WithRouteTag(
Expand Down Expand Up @@ -528,3 +528,44 @@ func TestWithRouteTag(t *testing.T) {
}
}
}

func BenchmarkHandlerServeHTTP(b *testing.B) {
tp := sdktrace.NewTracerProvider()
mp := sdkmetric.NewMeterProvider()

r, err := http.NewRequest(http.MethodGet, "http://localhost/", nil)
require.NoError(b, err)

for _, bb := range []struct {
name string
handler http.Handler
}{
{
name: "without the otelhttp handler",
handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
}),
},
{
name: "with the otelhttp handler",
handler: otelhttp.NewHandler(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
}),
"test_handler",
otelhttp.WithTracerProvider(tp),
otelhttp.WithMeterProvider(mp),
),
},
} {
b.Run(bb.name, func(b *testing.B) {
rr := httptest.NewRecorder()

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
bb.handler.ServeHTTP(rr, r)
}
})
}
}
64 changes: 53 additions & 11 deletions instrumentation/net/http/otelhttp/test/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package test
import (
"bytes"
"context"
"fmt"
"io"
"net"
"net/http"
Expand All @@ -16,22 +17,22 @@ import (
"strings"
"testing"

"go.opentelemetry.io/otel/sdk/metric"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/instrumentation"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"

"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
"go.opentelemetry.io/otel/trace"
)

Expand Down Expand Up @@ -244,8 +245,8 @@ func TestTransportMetrics(t *testing.T) {
responseBody := []byte("Hello, world!")

t.Run("make http request and read entire response at once", func(t *testing.T) {
reader := metric.NewManualReader()
meterProvider := metric.NewMeterProvider(metric.WithReader(reader))
reader := sdkmetric.NewManualReader()
meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
Expand Down Expand Up @@ -302,8 +303,8 @@ func TestTransportMetrics(t *testing.T) {
})

t.Run("make http request and buffer response", func(t *testing.T) {
reader := metric.NewManualReader()
meterProvider := metric.NewMeterProvider(metric.WithReader(reader))
reader := sdkmetric.NewManualReader()
meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
Expand Down Expand Up @@ -370,8 +371,8 @@ func TestTransportMetrics(t *testing.T) {
})

t.Run("make http request and close body before reading completely", func(t *testing.T) {
reader := metric.NewManualReader()
meterProvider := metric.NewMeterProvider(metric.WithReader(reader))
reader := sdkmetric.NewManualReader()
meterProvider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
Expand Down Expand Up @@ -484,8 +485,8 @@ func TestCustomAttributesHandling(t *testing.T) {
clientDuration = "http.client.duration"
)
ctx := context.TODO()
reader := metric.NewManualReader()
provider := metric.NewMeterProvider(metric.WithReader(reader))
reader := sdkmetric.NewManualReader()
provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader))
defer func() {
err := provider.Shutdown(ctx)
if err != nil {
Expand Down Expand Up @@ -550,3 +551,44 @@ func TestCustomAttributesHandling(t *testing.T) {
}
}
}

func BenchmarkTransportRoundTrip(b *testing.B) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
}))
defer ts.Close()

tp := sdktrace.NewTracerProvider()
mp := sdkmetric.NewMeterProvider()

r, err := http.NewRequest(http.MethodGet, ts.URL, nil)
require.NoError(b, err)

for _, bb := range []struct {
name string
transport http.RoundTripper
}{
{
name: "without the otelhttp transport",
transport: http.DefaultTransport,
},
{
name: "with the otelhttp transport",
transport: otelhttp.NewTransport(
http.DefaultTransport,
otelhttp.WithTracerProvider(tp),
otelhttp.WithMeterProvider(mp),
),
},
} {
b.Run(bb.name, func(b *testing.B) {
c := http.Client{Transport: bb.transport}

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = c.Do(r)
}
})
}
}

0 comments on commit 59a2c1d

Please sign in to comment.