Skip to content

[Fix] Avoid loading the response bodies twice in memory when parsing bytes.Buffer#984

Merged
renaudhartert-db merged 6 commits intomainfrom
rh/avoid-body-copy
Jul 17, 2024
Merged

[Fix] Avoid loading the response bodies twice in memory when parsing bytes.Buffer#984
renaudhartert-db merged 6 commits intomainfrom
rh/avoid-body-copy

Conversation

@renaudhartert-db
Copy link
Copy Markdown
Contributor

@renaudhartert-db renaudhartert-db commented Jul 16, 2024

Changes

Prior to this PR, WithResponseUnmarshal was unmarshalling a response body to a bytes.Buffer by:

  1. Reading all the response body in a slice of bytes; and
  2. Writing that slice in the receiving bytes.Buffer.

Step 1 unnecessarily increases the memory footprint as the response body is essentially stored twice in memory during unmarshalling. This PR fixes that problem by loading the body directly in the buffer without passing by an intermediate container.

Tests

Added a few unit test to cover all unmarshalling types.

Memory benchmark:

var longString = strings.Repeat("a", 1<<30) 

func BenchmarkWithResponseUnmarshal_byteBuffer(b *testing.B) {
	b.ReportAllocs()

	client := NewApiClient(ClientConfig{
		Transport: hc(func(r *http.Request) (*http.Response, error) {
			return &http.Response{
				StatusCode: 200,
				Body:       io.NopCloser(strings.NewReader(longString)),
				Request:    r,
			}, nil
		}),
	})

	for i := 0; i < b.N; i++ {
		var got bytes.Buffer
		client.Do(context.Background(), "GET", "/a", WithResponseUnmarshal(&got))
	}
}

Results:

Before:	6810268826 B/op	140 allocs/op
After:	4294972868 B/op	101 allocs/op
  • make test passing
  • make fmt applied
  • relevant integration tests applied

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants