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

timeseries: fix memory leaks involving CardObserver and shareReplay #5254

Merged
merged 1 commit into from
Aug 18, 2021

Conversation

psybuzz
Copy link
Contributor

@psybuzz psybuzz commented Aug 18, 2021

This addresses 2 non-trivial sources of memory leaks observed when
filtering tags in the Time Series dashboard.

a) CardObserver used to track Elements that were destroyed via
ngOnDestroy and stored them in a Set<Element>. Now with a WeakSet
instead, we cover cases when the browser's IntersectionObserverEntry
is not handled for some reason.

b) The rxjs operator "shareReplay" has some known gotchas [1], [2].
When more observables subscribe to a source containing
shareReplay(1) in its operator chain, the source remains subscribed
even after the other observables unsubscribe. This change uses the
takeUntil(ngUnsubscribe$) pattern to ensure any component using a
shareReplay will not keep it subscribed after the component is gone.
Alternatively we could use refCount, but this opts for the takeUntil
pattern already used widely in the codebase.

Manually checked that the dashboard still operates properly, and in
Angular's dev mode, using a specific demo logdir, adding and clearing a
"." tag filter has these characteristics:

Before this change: 5.8 MB, 5.5K DOM nodes added each cycle
After this change: 0.05 MB, <10 DOM nodes added each cycle

Googlers, see b/196996936

[1] ReactiveX/rxjs#5034
[2] ReactiveX/rxjs#5931

@google-cla google-cla bot added the cla: yes label Aug 18, 2021
@psybuzz psybuzz marked this pull request as ready for review August 18, 2021 02:29
@psybuzz psybuzz requested a review from bmd3k August 18, 2021 02:29
@@ -305,7 +306,7 @@ export class RunsTableContainer implements OnInit, OnDestroy {

const getFilteredItems$ = this.getFilteredItems$(
this.allUnsortedRunTableItems$
).pipe(shareReplay(1));
).pipe(takeUntil(this.ngUnsubscribe), shareReplay(1));
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you mind putting a comma after shareReplay(1) here so it formats on multiple lines?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just tried, but unfortunately Prettier seems to undo the comma and remove the multiline, I think because this.getFilteredItems$(this.allUnsortedRunTableItems$) did not fit onto one line.

@psybuzz
Copy link
Contributor Author

psybuzz commented Aug 18, 2021

Thanks for the review!

@psybuzz psybuzz merged commit f44e0b6 into tensorflow:master Aug 18, 2021
yatbear pushed a commit to yatbear/tensorboard that referenced this pull request Mar 27, 2023
…ensorflow#5254)

This addresses 2 non-trivial sources of memory leaks observed when
filtering tags in the Time Series dashboard.

a) CardObserver used to track Elements that were destroyed via
ngOnDestroy and stored them in a `Set<Element>`. Now with a WeakSet
instead, we cover cases when the browser's IntersectionObserverEntry
is not handled for some reason.

b) The rxjs operator "shareReplay" has some known gotchas [1], [2].
When more observables subscribe to a source containing
`shareReplay(1)` in its operator chain, the source remains subscribed
even after the other observables unsubscribe. This change uses the
`takeUntil(ngUnsubscribe$)` pattern to ensure any component using a
`shareReplay` will not keep it subscribed after the component is gone.
Alternatively we could use `refCount`, but this opts for the `takeUntil`
pattern already used widely in the codebase.

Manually checked that the dashboard still operates properly, and in
Angular's dev mode, using a specific demo logdir, adding and clearing a
"." tag filter has these characteristics:

Before this change: 5.8 MB, 5.5K DOM nodes added each cycle
After this change: 0.05 MB, <10 DOM nodes added each cycle

Googlers, see b/196996936

[1] ReactiveX/rxjs#5034
[2] ReactiveX/rxjs#5931
dna2github pushed a commit to dna2fork/tensorboard that referenced this pull request May 1, 2023
…ensorflow#5254)

This addresses 2 non-trivial sources of memory leaks observed when
filtering tags in the Time Series dashboard.

a) CardObserver used to track Elements that were destroyed via
ngOnDestroy and stored them in a `Set<Element>`. Now with a WeakSet
instead, we cover cases when the browser's IntersectionObserverEntry
is not handled for some reason.

b) The rxjs operator "shareReplay" has some known gotchas [1], [2].
When more observables subscribe to a source containing
`shareReplay(1)` in its operator chain, the source remains subscribed
even after the other observables unsubscribe. This change uses the
`takeUntil(ngUnsubscribe$)` pattern to ensure any component using a
`shareReplay` will not keep it subscribed after the component is gone.
Alternatively we could use `refCount`, but this opts for the `takeUntil`
pattern already used widely in the codebase.

Manually checked that the dashboard still operates properly, and in
Angular's dev mode, using a specific demo logdir, adding and clearing a
"." tag filter has these characteristics:

Before this change: 5.8 MB, 5.5K DOM nodes added each cycle
After this change: 0.05 MB, <10 DOM nodes added each cycle

Googlers, see b/196996936

[1] ReactiveX/rxjs#5034
[2] ReactiveX/rxjs#5931
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants