Skip to content

Commit fc06510

Browse files
committed
benchmark on AddHeader vs AddHeaders
1 parent 48c3d27 commit fc06510

File tree

8 files changed

+170
-16
lines changed

8 files changed

+170
-16
lines changed

transport/grpc/headers.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,22 @@ func metadataToApplicationErrorMeta(responseMD metadata.MD) *transport.Applicati
184184
// addApplicationHeaders adds the headers to md.
185185
func addApplicationHeaders(md metadata.MD, headers transport.Headers) error {
186186
for header, value := range headers.Items() {
187-
header = transport.CanonicalizeHeaderKey(header)
188-
if isReserved(header) {
189-
return yarpcerrors.InvalidArgumentErrorf("cannot use reserved header in application headers: %s", header)
190-
}
191-
if err := addToMetadata(md, header, value); err != nil {
187+
if err := addApplicationHeader(md, header, value); err != nil {
192188
return err
193189
}
194190
}
195191
return nil
196192
}
197193

194+
// addApplicationHeaders adds the header pair 'key': 'value' to 'md'.
195+
func addApplicationHeader(md metadata.MD, key, value string) error {
196+
key = transport.CanonicalizeHeaderKey(key)
197+
if isReserved(key) {
198+
return yarpcerrors.InvalidArgumentErrorf("cannot use reserved header in application headers: %s", key)
199+
}
200+
return addToMetadata(md, key, value)
201+
}
202+
198203
// getApplicationHeaders returns the headers from md without any reserved headers.
199204
func getApplicationHeaders(md metadata.MD) (transport.Headers, error) {
200205
if len(md) == 0 {

transport/grpc/response_writer.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ func (r *responseWriter) AddHeaders(headers transport.Headers) {
6161
r.headerErr = multierr.Combine(r.headerErr, addApplicationHeaders(r.md, headers))
6262
}
6363

64+
func (r *responseWriter) AddHeader(key, value string) {
65+
if r.md == nil {
66+
r.md = metadata.New(nil)
67+
}
68+
r.headerErr = multierr.Combine(r.headerErr, addApplicationHeader(r.md, key, value))
69+
}
70+
6471
func (r *responseWriter) SetApplicationError() {
6572
r.AddSystemHeader(ApplicationErrorHeader, ApplicationErrorHeaderValue)
6673
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package grpc
2+
3+
import (
4+
"testing"
5+
6+
"go.uber.org/yarpc/api/transport"
7+
)
8+
9+
func Benchmark_ResponseWriter_AddHeaders(b *testing.B) {
10+
b.Run("lowercase", func(b *testing.B) {
11+
rw := newResponseWriter()
12+
for i := 0; i < b.N; i++ {
13+
rw.AddHeaders(transport.NewHeadersWithCapacity(1).With(
14+
"abc", "",
15+
))
16+
}
17+
})
18+
19+
b.Run("titlecase", func(b *testing.B) {
20+
rw := newResponseWriter()
21+
for i := 0; i < b.N; i++ {
22+
rw.AddHeaders(transport.NewHeadersWithCapacity(1).With(
23+
"Abc", "",
24+
))
25+
}
26+
})
27+
}
28+
29+
func Benchmark_ResponseWriter_AddHeader(b *testing.B) {
30+
b.Run("lowercase", func(b *testing.B) {
31+
rw := newResponseWriter()
32+
for i := 0; i < b.N; i++ {
33+
rw.AddHeader("abc", "")
34+
}
35+
})
36+
37+
b.Run("titlecase", func(b *testing.B) {
38+
rw := newResponseWriter()
39+
for i := 0; i < b.N; i++ {
40+
rw.AddHeader("Abc", "")
41+
}
42+
})
43+
}

transport/http/handler.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ func (rw *responseWriter) AddHeaders(h transport.Headers) {
270270
applicationHeaders.ToHTTPHeaders(h, rw.w.Header())
271271
}
272272

273+
func (rw *responseWriter) AddHeader(k, v string) {
274+
applicationHeaders.ToHTTPHeader(rw.w.Header(), k, v)
275+
}
276+
273277
func (rw *responseWriter) SetApplicationError() {
274278
rw.w.Header().Set(ApplicationStatusHeader, ApplicationErrorStatus)
275279
}

transport/http/handler_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,46 @@ func TestResponseWriter(t *testing.T) {
454454
assert.Equal(t, strconv.Itoa(int(appErrCode)), recorder.Header().Get(_applicationErrorCodeHeader))
455455
}
456456

457+
func Benchmark_ResponseWriter_AddHeaders(b *testing.B) {
458+
b.Run("lowercase", func(b *testing.B) {
459+
recorder := httptest.NewRecorder()
460+
rw := newResponseWriter(recorder)
461+
for i := 0; i < b.N; i++ {
462+
rw.AddHeaders(transport.NewHeadersWithCapacity(1).With(
463+
"abc", "",
464+
))
465+
}
466+
})
467+
468+
b.Run("titlecase", func(b *testing.B) {
469+
recorder := httptest.NewRecorder()
470+
rw := newResponseWriter(recorder)
471+
for i := 0; i < b.N; i++ {
472+
rw.AddHeaders(transport.NewHeadersWithCapacity(1).With(
473+
"Abc", "",
474+
))
475+
}
476+
})
477+
}
478+
479+
func Benchmark_ResponseWriter_AddHeader(b *testing.B) {
480+
b.Run("lowercase", func(b *testing.B) {
481+
recorder := httptest.NewRecorder()
482+
rw := newResponseWriter(recorder)
483+
for i := 0; i < b.N; i++ {
484+
rw.AddHeader("abc", "")
485+
}
486+
})
487+
488+
b.Run("titlecase", func(b *testing.B) {
489+
recorder := httptest.NewRecorder()
490+
rw := newResponseWriter(recorder)
491+
for i := 0; i < b.N; i++ {
492+
rw.AddHeader("Abc", "")
493+
}
494+
})
495+
}
496+
457497
func TestTruncatedHeader(t *testing.T) {
458498
tests := []struct {
459499
name string

transport/http/header.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ func (hm headerMapper) ToHTTPHeaders(from transport.Headers, to http.Header) htt
5050
return to
5151
}
5252

53+
// toHTTPHeader adds a key, value header pair into a transport header.
54+
//
55+
// The header pair: 'key': 'value' is written to 'to'. The final header collection
56+
// is returned.
57+
//
58+
// If 'to' is nil, a new map will be assigned.
59+
func (hm headerMapper) ToHTTPHeader(to http.Header, key, value string) http.Header {
60+
if to == nil {
61+
to = make(http.Header, 1)
62+
}
63+
to.Add(hm.Prefix+transport.CanonicalizeHeaderKey(key), value)
64+
return to
65+
}
66+
5367
// fromHTTPHeaders converts HTTP headers to application headers.
5468
//
5569
// Headers are read from 'from' and written to 'to'. The final header collection

transport/tchannel/handler.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type inboundCallResponse interface {
7878
type responseWriter interface {
7979
AddHeaders(h transport.Headers)
8080
AddHeader(key string, value string)
81+
addHeader(key string, value string)
8182
Close() error
8283
ReleaseBuffer()
8384
IsApplicationError() bool
@@ -118,7 +119,7 @@ func (h handler) handle(ctx context.Context, call inboundCall) {
118119

119120
if !h.excludeServiceHeaderInResponse {
120121
// echo accepted rpc-service in response header
121-
responseWriter.AddHeader(ServiceHeaderKey, call.ServiceName())
122+
responseWriter.addHeader(ServiceHeaderKey, call.ServiceName())
122123
}
123124

124125
err := h.callHandler(ctx, call, responseWriter)
@@ -147,12 +148,12 @@ func (h handler) handle(ctx context.Context, call inboundCall) {
147148
// TODO: what to do with error? we could have a whole complicated scheme to
148149
// return a SystemError here, might want to do that
149150
text, _ := status.Code().MarshalText()
150-
responseWriter.AddHeader(ErrorCodeHeaderKey, string(text))
151+
responseWriter.addHeader(ErrorCodeHeaderKey, string(text))
151152
if status.Name() != "" {
152-
responseWriter.AddHeader(ErrorNameHeaderKey, status.Name())
153+
responseWriter.addHeader(ErrorNameHeaderKey, status.Name())
153154
}
154155
if status.Message() != "" {
155-
responseWriter.AddHeader(ErrorMessageHeaderKey, status.Message())
156+
responseWriter.addHeader(ErrorMessageHeaderKey, status.Message())
156157
}
157158
}
158159
if reswErr := responseWriter.Close(); reswErr != nil && !clientTimedOut {
@@ -271,15 +272,19 @@ func newHandlerWriter(response inboundCallResponse, format tchannel.Format, head
271272

272273
func (hw *handlerWriter) AddHeaders(h transport.Headers) {
273274
for k, v := range h.OriginalItems() {
274-
if isReservedHeaderKey(k) {
275-
hw.failedWith = appendError(hw.failedWith, fmt.Errorf("cannot use reserved header key: %s", k))
276-
return
277-
}
278275
hw.AddHeader(k, v)
279276
}
280277
}
281278

282279
func (hw *handlerWriter) AddHeader(key string, value string) {
280+
if isReservedHeaderKey(key) {
281+
hw.failedWith = appendError(hw.failedWith, fmt.Errorf("cannot use reserved header key: %s", key))
282+
return
283+
}
284+
hw.addHeader(key, value)
285+
}
286+
287+
func (hw *handlerWriter) addHeader(key string, value string) {
283288
hw.headers = hw.headers.With(key, value)
284289
}
285290

@@ -292,13 +297,13 @@ func (hw *handlerWriter) SetApplicationErrorMeta(applicationErrorMeta *transport
292297
return
293298
}
294299
if applicationErrorMeta.Code != nil {
295-
hw.AddHeader(ApplicationErrorCodeHeaderKey, strconv.Itoa(int(*applicationErrorMeta.Code)))
300+
hw.addHeader(ApplicationErrorCodeHeaderKey, strconv.Itoa(int(*applicationErrorMeta.Code)))
296301
}
297302
if applicationErrorMeta.Name != "" {
298-
hw.AddHeader(ApplicationErrorNameHeaderKey, applicationErrorMeta.Name)
303+
hw.addHeader(ApplicationErrorNameHeaderKey, applicationErrorMeta.Name)
299304
}
300305
if applicationErrorMeta.Details != "" {
301-
hw.AddHeader(ApplicationErrorDetailsHeaderKey, truncateAppErrDetails(applicationErrorMeta.Details))
306+
hw.addHeader(ApplicationErrorDetailsHeaderKey, truncateAppErrDetails(applicationErrorMeta.Details))
302307
}
303308
}
304309

transport/tchannel/handler_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,42 @@ func TestResponseWriterEmptyBodyHeaders(t *testing.T) {
648648
assert.False(t, res.applicationError, "application error must be false")
649649
}
650650

651+
func Benchmark_ResponseWriter_AddHeaders(b *testing.B) {
652+
b.Run("lowercase", func(b *testing.B) {
653+
rw := newHandlerWriter(nil, tchannel.Raw, canonicalizedHeaderCase)
654+
for i := 0; i < b.N; i++ {
655+
rw.AddHeaders(transport.NewHeadersWithCapacity(1).With(
656+
"abc", "",
657+
))
658+
}
659+
})
660+
661+
b.Run("titlecase", func(b *testing.B) {
662+
rw := newHandlerWriter(nil, tchannel.Raw, canonicalizedHeaderCase)
663+
for i := 0; i < b.N; i++ {
664+
rw.AddHeaders(transport.NewHeadersWithCapacity(1).With(
665+
"Abc", "",
666+
))
667+
}
668+
})
669+
}
670+
671+
func Benchmark_ResponseWriter_AddHeader(b *testing.B) {
672+
b.Run("lowercase", func(b *testing.B) {
673+
rw := newHandlerWriter(nil, tchannel.Raw, canonicalizedHeaderCase)
674+
for i := 0; i < b.N; i++ {
675+
rw.AddHeader("abc", "")
676+
}
677+
})
678+
679+
b.Run("titlecase", func(b *testing.B) {
680+
rw := newHandlerWriter(nil, tchannel.Raw, canonicalizedHeaderCase)
681+
for i := 0; i < b.N; i++ {
682+
rw.AddHeader("Abc", "")
683+
}
684+
})
685+
}
686+
651687
func TestGetSystemError(t *testing.T) {
652688
tests := []struct {
653689
giveErr error

0 commit comments

Comments
 (0)