-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
fix potential memory leak in Concast
, add tests
#11358
Conversation
🦋 Changeset detectedLatest commit: 1bbd815 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
size-limit report 📦
|
|
.changeset/forty-cups-shop.md
Outdated
"@apollo/client": patch | ||
--- | ||
|
||
Fixes a potential memory leak (that was not triggered by ApolloClient until now) in `Concast`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional context: this leak never occurred in our runtime code, but could present a problem for different usages of Concast
(that do not appear in our codebase). Since Concast
is exported from our library, we should fix it as good citizens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alessbell that additional context is super helpful as I was a bit confused at what the "until now" meant.
@phryneas perhaps you could word this as such to make it more clear?
Fixes a potential memory leak in
Concast
whenConcast
was used outside of Apollo Client.
This gives a bit better context on when someone could expect to see the issue arise.
src/testing/matchers/index.d.ts
Outdated
@@ -10,6 +10,11 @@ import { | |||
ProfiledHook, | |||
} from "../internal/index.js"; | |||
|
|||
declare class WeakRef<T extends WeakKey> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove this since we're including it in our tsconfig.
@@ -0,0 +1,57 @@ | |||
import type { MatcherFunction } from "expect"; | |||
|
|||
declare class WeakRef<T extends WeakKey> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also remove this declaration since we're including it in our tsconfig.
@@ -5,7 +5,7 @@ | |||
{ | |||
"compilerOptions": { | |||
"noEmit": true, | |||
"lib": ["es2015", "esnext.asynciterable", "dom"], | |||
"lib": ["es2015", "esnext.asynciterable", "dom", "ES2021.WeakRef"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With our two tsconfigs now, the one extending the outer config which is used for builds, this change will not display an error in our editors if we use WeakRef
s but tsc would fail at build time.
return { | ||
pass, | ||
message: () => { | ||
if (pass) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note on how matchers work: if the user is expecting a pass and pass is true, no message is shown. So the two return cases here cover if the user gets a result that is unexpected, both in the affirmative and negative.
}); | ||
|
||
function deferred<X>() { | ||
let resolve!: (v: X) => void, reject!: (e: any) => void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit: declaring variables on separate lines for readability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know why prettier let me down here ^^
const concast = new Concast<number>([ | ||
Observable.of(1, 2), | ||
promise, | ||
Observable.of(3, 5), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may not be load-bearing in this test, but initializing our Concast with a third item after our promise-wrapped Observable which will be rejected gives us additional signal that the promise rejects with an error even though there are additional observables coming after it.
}); | ||
|
||
it("rejecting a source of a concast frees all observer references on `this.observers`", async () => { | ||
const observers: Observer<any>[] = [{ next() {}, error() {} }]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const observers: Observer<any>[] = [{ next() {}, error() {} }]; | |
const subscribingObservers: Observer<any>[] = [{ next() {}, error() {} }]; |
const observers: Observer<any>[] = [{ next() {}, error() {} }]; | ||
const observerRefs = observers.map((observer) => new WeakRef(observer)); | ||
|
||
let observer!: Observer<number>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let observer!: Observer<number>; | |
let sourceObserver!: Observer<number>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just went through this together - thanks for walking through it! A few small comments in the course of our pairing but LGTM ✅
This adds a bunch of memory-related tests to
Concast
and also fixes two scenarios (that were not triggered in our codebase) where an actual memory leak would be created.You can see the "before->after" in this commit.
Checklist: