Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alter HTTP requests to stop using chunked transfer encoding #1077

Merged
merged 40 commits into from
Jan 20, 2021

Conversation

fractalwrench
Copy link
Contributor

@fractalwrench fractalwrench commented Jan 13, 2021

Goal

Alters HTTP requests so that they no longer use chunked transfer encoding, and instead build the request body up front. This fixes two problems:

  • Proxies such as Privoxy do not support chunked transfer encoding so HTTP requests made by the notifier may always fail on certain versions of Android
  • Creating a buffer that contains the entire request body allows for a SHA-1 digest to be calculated after the payload has been serialized. This avoids the possibility of SHA-1 mismatches caused by payload mutation that could result in some payloads being dropped

Changeset

  • Moved Content-Type header to be included in error/session API DeliveryParams object for consistency with other headers
  • Serialized error/session payload to JSON in a ByteArray, and generated the SHA-1 digest from this ByteArray which circumvents any possible change in the payload
  • Wrote the JSON in the ByteArray to the HTTP request directly, rather than streaming it with chunked transfer encoding
  • Called setFixedLengthStreamingMode to avoid creating an unnecessary buffer within HttpUrlConnection. Testing with unusual unicode characters revealed that this needed to be a ByteArray rather than a String to get the correct body length
  • Read and closed the connection's inputstream to fix an Android platform bug that logs a warning message about connections being leaked, and to log additional information about the request
  • Caught OutOfMemoryError and attempted to persist the payload to disk, as this operation takes a smaller amount of memory and is likely to succeed if memory is constrained for large payloads

Testing

Relied on existing unit and E2E test coverage.

Performance

For the default payload sent by the example app (around 17Kb), there is no discernible difference in performance for this changeset. Both v5.5.0 and the current changeset use around 0.2Mb to deliver one payload.

Datadog has confirmed that the average payload size received by the pipeline is 16-24Kb, so for the majority of use cases this change will have no performance impact.

Additional load testing

To perform stress testing, v5.5.0 and the current changeset were load tested using the following snippet which generates a 900Kb payload containing a very large amount of metadata:

repeat(1000) { a ->
    repeat(10) { z ->
        Bugsnag.addMetadata(
            "custom",
            "a long long long long long long key: $a $z",
            "a long long long long long long value: $a $z"
        )
    }
}

The additional JVM memory required to deliver one payload was as follows:

Before changeset: 1.3Mb memory usage
With changeset: 2.9Mb memory usage

Further stress testing generated 20 of these payloads simultaneously on 5 different threads (an unrealistic scenario):

Before changeset: 12.5Mb memory usage
With changeset: 18.2Mb memory usage

The majority of allocations for both changesets were for java/lang/String and java/util/concurrent/ConcurrentHashMap$Node, so it seems that JSON serialization is the dominating factor for memory usage rather than buffering the payload. It’s worth noting that manually triggering GC freed up short-lived references created during JSON serialization, so the JVM would trigger GC calls if the app was running low on memory when attempting to allocate a buffer.

It's also worth noting that the example app's payload size is around 17Kb, which is effectively the same size as the default buffer (16kb) in the previous implementation that uses streams. This suggests that for regular use without large amounts of metadata there should be no performance impact.

@bugsnagbot
Copy link
Collaborator

bugsnagbot commented Jan 13, 2021

Android notifier sizes

Format Size impact of Bugsnag (kB) Size impact of Bugsnag when Minified (kB)
APK 1459.74 1374.4
arm64_v8a 377.51 291.5
armeabi 357.03 266.91
armeabi_v7a 340.65 250.54
x86 414.36 328.34
x86_64 397.98 311.97

Generated by 🚫 Danger

@fractalwrench fractalwrench force-pushed the PLAT-5698/chunked-transfer-encoding branch 3 times, most recently from b8399a7 to 629a937 Compare January 13, 2021 15:12
@fractalwrench fractalwrench changed the title Use standard HTTP connection rather than chunked transfer encoding Alter HTTP requests to stop using chunked transfer encoding Jan 14, 2021
@fractalwrench fractalwrench force-pushed the PLAT-5698/chunked-transfer-encoding branch from 629a937 to d4ba501 Compare January 14, 2021 15:15
@fractalwrench
Copy link
Contributor Author

@tomlongridge @twometresteve I've tagged for a preliminary review - I'm planning on doing some more detailed performance testing on the changeset, but it'd be good to resolve any code changes before that gets too far along.

@tomlongridge
Copy link
Contributor

Changes LGTM - we just need to conclude discussions based on performance.

@twometresteve
Copy link
Contributor

twometresteve commented Jan 14, 2021

Also LGTM.

Scenarios ring fenced as being "apparently flaky" need to be reviewed quickly after this change, or possibly even as part of it. I already have a ticket to do that myself, so just a heads-up.

Edit: The struck-through point has now been implemented in #1081.

Copy link
Contributor

@twometresteve twometresteve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See 1 comment above.

@fractalwrench fractalwrench force-pushed the PLAT-5698/chunked-transfer-encoding branch from d4ba501 to 52d6a30 Compare January 15, 2021 09:29
@fractalwrench fractalwrench force-pushed the PLAT-5698/chunked-transfer-encoding branch from 52d6a30 to d0b3b50 Compare January 15, 2021 14:17
@fractalwrench fractalwrench marked this pull request as ready for review January 15, 2021 14:18
@fractalwrench
Copy link
Contributor Author

fractalwrench commented Jan 15, 2021

Updated PR description with details of perf testing - this is now ready for full review.

Copy link
Contributor

@twometresteve twometresteve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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.

4 participants