Skip to content

Commit

Permalink
feat(instrumentation-fetch)!: passthrough original response to `apply…
Browse files Browse the repository at this point in the history
…CustomAttributes` hook

Previously, the fetch instrumentation code unconditionally clones
every `fetch()` response in order to preserve the ability for the
`applyCustomAttributes` hook to consume the response body. This is
fundamentally unsound, as it forces the browser to buffer and
retain the response body until it is fully received and read, which
crates unnecessary memory pressure on large or long-running response
streams. In extreme cases, this is effectively a memory leak and can
cause the browser tab to crash.

Fixes #4888
  • Loading branch information
chancancode committed Dec 17, 2024
1 parent e188496 commit 541dbe5
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 18 deletions.
2 changes: 2 additions & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ All notable changes to experimental packages in this project will be documented

### :boom: Breaking Change

* feat(instrumentation-fetch)!: passthrough original response to `applyCustomAttributes` hook [#5281](https://github.com/open-telemetry/opentelemetry-js/pull/5281) @chancancode
* Previously, the fetch instrumentation code unconditionally clones every `fetch()` response in order to preserve the ability for the `applyCustomAttributes` hook to consume the response body. This is fundamentally unsound, as it forces the browser to buffer and retain the response body until it is fully received and read, which crates unnecessary memory pressure on large or long-running response streams. In extreme cases, this is effectively a memory leak and can cause the browser tab to crash. If your use case for `applyCustomAttributes` requires access to the response body, TODO...
* feat(otlp-exporter-base)!: collapse base classes into one [#5031](https://github.com/open-telemetry/opentelemetry-js/pull/5031) @pichlermarc
* `OTLPExporterNodeBase` has been removed in favor of a platform-agnostic implementation (`OTLPExporterBase`)
* `OTLPExporterBrowserBase` has been removed in favor of a platform-agnostic implementation (`OTLPExporterBase`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,15 +368,14 @@ export class FetchInstrumentation extends InstrumentationBase<FetchInstrumentati
): void {
try {
const resClone = response.clone();
const resClone4Hook = response.clone();
const body = resClone.body;
if (body) {
const reader = body.getReader();
const read = (): void => {
reader.read().then(
({ done }) => {
if (done) {
endSpanOnSuccess(span, resClone4Hook);
endSpanOnSuccess(span, response);
} else {
read();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -931,24 +931,33 @@ describe('fetch', () => {

await prepare(url, applyCustomAttributes);
assert.ok(request.method === 'GET');
assert.ok(response.status === 200);
});

it('get response body from callback arguments response', async () => {
let response: any;
const applyCustomAttributes: FetchCustomAttributeFunction = async (
span,
req,
res
) => {
if (res instanceof Response) {
response = res;
}
};
assert.ok(lastResponse !== undefined);
assert.strictEqual(response, lastResponse);
assert.ok(response.status === 200);

await prepare(url, applyCustomAttributes);
const rsp = await response.json();
assert.strictEqual(rsp.isServerResponse, true);
/*
Note: this confirms that nothing *in the instrumentation code*
consumed the response body; it doesn't guarantee that the response
object passed to the `applyCustomAttributes` hook will always have
a consumable body – in fact, this is typically *not* the case:
```js
// user code:
let response = await fetch("foo");
let json = await response.json(); // <- user code consumes the body on `response`
// ...
{
// ...this is called sometime later...
applyCustomAttributes(span, request, response) {
// too late!
response.bodyUsed // => true
}
}
```
*/
assert.strictEqual(response.bodyUsed, false);
});
});

Expand Down

0 comments on commit 541dbe5

Please sign in to comment.