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

fix(groupBy): unsubscribe & cleanup GroupDurationSubscriber #2662

Merged
merged 6 commits into from
Jun 14, 2017

Conversation

hermanbanken
Copy link
Contributor

@hermanbanken hermanbanken commented Jun 13, 2017

The groupBy operator has a durationSelector argument that we can use to specify additional closings for the produced groups. However, the durationSelector produces Observables that were never unsubscribed. (#2660)

Furthermore even if the durationSelector Observable was auto-unsubscribed (due to for example a take(1) in that Observable) its subscription would not be removed from the GroupBySubscriber, which causes memory usage to grow and eventually causes a OOM exception in @crunchie84's application. (#2661)

This PR solves both problems by explicitly unsubscribing & removing the subscription from the GroupBySubscriber. I also added a test for the unsubscribing part, but could not easily create a test in the same style as the other groupBy-tests without poking into the subscription.

hermanbanken and others added 3 commits June 13, 2017 14:26
Duration selectors where not disposed when the GroupDurationSubscriber's completed
…e group

The Groups are disposed by the GroupDurationSelector, however the
GroupDurationSubscriber can be subscribed to a different observable
than the group itself. To prevent any unwanted subscriptions to
accumulate over time we need to explicitly unsubscribe after the
first event in GroupDurationSubscriber closes the group.

Fixes ReactiveX#2660
The subscriptions to the durationSelector would pile up in the
internal subscription list of the GroupBySubscriber. By removing
the GroupDurationSubscriber explicitly from the GroupBySubscriber
we prevent potential OOM exceptions.

Fixes ReactiveX#2661
@hermanbanken hermanbanken changed the title Unsubscribe & cleanup GroupDurationSubscriber fix(groupBy): unsubscribe & cleanup GroupDurationSubscriber Jun 13, 2017
@coveralls
Copy link

Coverage Status

Coverage increased (+0.001%) to 97.736% when pulling 19c5f86 on hermanbanken:fix2661 into 3005bfe on ReactiveX:master.

@@ -240,6 +240,8 @@ class GroupDurationSubscriber<K, T> extends Subscriber<T> {
group.error(err);
}
this.parent.removeGroup(this.key);
this.unsubscribe();
this.parent.remove(this);
Copy link
Member

Choose a reason for hiding this comment

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

wouldn't it be better GroupBySubscriber::removeGroup manages subscription as well when it's specified to remove?

Copy link
Member

Choose a reason for hiding this comment

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

@kwonoj if anything, the GroupDurationSubscriber should be a pass-through to the group Subject. If we did that, the base Subscriber _error and _complete implementations would call unsubscribe, and the Subscription's unsubscribe would automatically call parent.remove()

Copy link
Member

@trxcllnt trxcllnt left a comment

Choose a reason for hiding this comment

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

@hermanbanken I've submitted hermanbanken#1 to your branch with my proposed changes. If you accept the PR there, it should reflect in this PR here.

@@ -240,6 +240,8 @@ class GroupDurationSubscriber<K, T> extends Subscriber<T> {
group.error(err);
}
this.parent.removeGroup(this.key);
this.unsubscribe();
this.parent.remove(this);
Copy link
Member

Choose a reason for hiding this comment

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

@kwonoj if anything, the GroupDurationSubscriber should be a pass-through to the group Subject. If we did that, the base Subscriber _error and _complete implementations would call unsubscribe, and the Subscription's unsubscribe would automatically call parent.remove()

refactor(groupBy): Refactor GroupDurationSubscriber to pass-through to underlying group Subject
@hermanbanken
Copy link
Contributor Author

hermanbanken commented Jun 13, 2017

With the change of @trxcllnt we no longer need to do this.parent.remove(this) manually (as in the commit of @crunchie84 and me).

It took me a while to understand why his change was enough. Let me refrase the explanation, maybe it will help anyone understand if it is not clear already:

During unsubscribe the Subscription class makes sure to remove itself from the parents list of active subscriptions, which was already possible as this._parent of the GroupDurationSubscriber always correctly linked to the GroupBySubscriber, but due to overriding complete and error the unsubscribe that is normally called there was not called. This caused the GroupDurationSubscribers to build up in GroupBySubscriber.

Previously

Rx.Observable.range(0, 10000)
  .concat(Rx.Observable.never())
  .groupBy(x => x, x => x, group => Rx.Observable.empty())
  .subscribe()

would result in 10000 GroupDurationSubscribers retained in GroupBySubscriber but with this PR that is no longer true.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.002%) to 97.733% when pulling 20f439c on hermanbanken:fix2661 into 3005bfe on ReactiveX:master.

@arjenvanderende
Copy link

@crunchie84 and I have retested our application against these changes. We can happily confirm that it fixes both #2660 and #2661. Thanks for your work on this PR! 👍

let unsubs = [];
for (let i = 0; i < gr.length; i++) {
if (gr[i] !== '-' && gr[i] !== '|') {
unsubs.push(empty.slice(0, i) + '^' + durationMarble.slice(1, -1) + '!');
Copy link
Member

Choose a reason for hiding this comment

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

This is arguably less readable than just using the strings:

const unsubs = [
  '^------!',
  '-^------!',
  '--^------!,
 /* etc */
];

It's more busy work, sure, but tests are about readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes true. How about this [incoming].

@coveralls
Copy link

coveralls commented Jun 14, 2017

Coverage Status

Coverage increased (+0.0003%) to 97.735% when pulling 8a954b0 on hermanbanken:fix2661 into 3005bfe on ReactiveX:master.

'-^--!',
'---^--!',
'------------^-!',
];
Copy link
Member

Choose a reason for hiding this comment

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

haha... so much cleaner than the for loop! 😄

@benlesh benlesh merged commit ab92083 into ReactiveX:master Jun 14, 2017
@hermanbanken hermanbanken deleted the fix2661 branch June 14, 2017 21:33
@lock
Copy link

lock bot commented Jun 6, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jun 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants