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
cds-icon causes memory leak and slow Angular v13 unit tests #61
Comments
ensure the component is connected before subscribing to the globalstateservice Fixes vmware-clarity#61 Signed-off-by: Ashley Ryan <asryan@vmware.com>
ensure the component is connected before subscribing to the globalstateservice Fixes vmware-clarity#61 Signed-off-by: Ashley Ryan <asryan@vmware.com>
ensure the component is connected before subscribing to the globalstateservice Fixes #61 Signed-off-by: Ashley Ryan <asryan@vmware.com>
ensure the component is connected before subscribing to the globalstateservice Fixes #61 Signed-off-by: Ashley Ryan <asryan@vmware.com>
🎉 This issue has been resolved in version 5.7.3 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
🎉 This issue has been resolved in version 6.0.1 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Hi there 👋, this is an automated message. To help Clarity keep track of discussions, we automatically lock closed issues after 14 days. Please look for another open issue or open a new issue with updated details and reference this one as necessary. |
cds-icon
subscribes to state updates observableGlobalStateService.stateUpdates
in itsfirstUpdated
callback.The unsubscribe logic for
GlobalStateService.stateUpdates
is placed in thedisconnectCallback
.The memory leak happens because it's not guaranteed that
disconnectCallback
will be called afterfirstUpdated
in terms of callback execution order. There are situations wheredisconnectCallback
is called first and thenfirstUpdated
is called. When this happens anddisconnectCallback
is called first, thecds-icon
will 'leak' subscribers to theGlobalStateService.stateUpdates
observable. These leaked subscribers are never unsubscribed and this leads to the memory leak.The reason why
disconnectCallback
is sometimes called beforefirstUpdated
is related to the wayLitElement.requestUpdated()
works - this is the code https://github.com/lit/lit/blob/93d671feab82688a79fc60ba22cf204fa4ca02ec/packages/reactive-element/src/reactive-element.ts#L1150.In short,
requestUpdated()
works asynchronously which means thefirstUpdated
callback will be called asynchronously. SincedisconnectCallback
works synchronously there are situations wherefirstUpdated
will be called afterdisconnectCallback
.How to reproduce
To reproduce the problem, you can run unit test spec file using Angular 13 with testing module
teardown.destroyAfterEach
enabled (teardown is enabled by default in Angular 13).In the spec below, every
it
will leak 1 subscriber to theGlobalStateService.stateUpdates
, that's why the expectations (expect
) in the consecutiveit(...)
are for 1, 2, 3 and 4 subscribers respectively.NOTE: if you run the spec file, make sure the spec randomized execution is disabled in your
karma.conf.js
. Also, you must use Angular 13 with teardown enabled. By doing this, thedisconnectCallback
will be called beforefirstUpdated
consistently. In Angular 13 projects (with teardown enabled) that have a lot of unit tests, these leaked subscribers cause significant test slowdown as the test execution progresses.SPEC FILE CODE
How to reproduce in production code
The easiest way to reproduce the issue is with the following code:
Creating an icon element, attaching it to the DOM and then immediately removing it will result in leaked subscribers.
Here is a stackblitz that uses the code above to leak 1000 subscriptions https://stackblitz.com/edit/clarity-dark-theme-clr13-cds6-duukxq?file=src/app/app.component.html
Expected behavior
Adding and removing cds-icon from the DOM should not leak subscribers to
GlobalStateService.stateUpdates
.It seems
firstUpdated()
callback is not the place where cds-icon (and possibly other components from Clarity Core) should subscribe toGlobalStateService.stateUpdates
.Probably, the
connectedCallback()
is the place where the subscribe logic should be placed.Versions
Clarity project:
Clarity version:
Framework:
Framework version:
Angular 13
Device:
The text was updated successfully, but these errors were encountered: