-
Notifications
You must be signed in to change notification settings - Fork 139
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
Take operator doesn't dispose upstream observable #209
base: master
Are you sure you want to change the base?
Take operator doesn't dispose upstream observable #209
Conversation
If there is an active polling observable upstream it will keep polling as long as it isn't disposed. By disposing when we reach the desired amount of items, the possible upstream polling is also stopped.
Could you add test for this case? Ideally calling dispose both in the operator and from the outside to make sure things still work. I'm not 100% convinced this is the right thing to do yet, but on the surface it seems sensible. I don't see language implementations dispose the source inside |
|
@asm89 Sure, here is the full use case, I want to consume 13 items from Observable::defer(
fn (): Observable => Observable::fromPromise(resolve($this->redis->blpop(
$key,
1
)))
->flatMap(
static fn (?array $event): Observable => $event === null ? Observable::error(new Exception()) : observableFromArray([$event[1]])
),
$this->asyncScheduler
)
->retryWhen(static fn (Observable $errors): Observable => $errors->delay(0))
->repeatWhen(static fn (Observable $errors): Observable => $errors->delay(0))
->map(
static fn (string $event): array => json_decode($event, true)
)->take(13)->map(
// Mainly preventing a memory leak here
static fn (): bool => true,
)->toArray()->toPromise() |
I'm not sure that this is an issue with the |
@davidwdan Will have a try and report back 👍 . So you're suggesting it's a race condition? |
I don't think it's a race condition. On some of the other operators, we schedule the subscriptions so they happen in the next tick. We might have to do something like that with |
@davidwdan @asm89 Changed the |
@WyriHaximus That is definitely not the long term solution. In almost all of the I believe the correct way to resolve this issue would be to schedule the This change would require the addition of a scheduler argument to This change would also break Here is what I was thinking: public static function toObservable(PromiseInterface $promise, SchedulerInterface $scheduler = null): Observable
{
$scheduler = $scheduler ?: Scheduler::getDefault();
return new AnonymousObservable(function ($observer) use ($promise, $scheduler) {
$subject = new AsyncSubject();
$disp = $scheduler->schedule(function () use ($subject, $promise) {
$promise->then(
function ($value) use ($subject) {
$subject->onNext($value);
$subject->onCompleted();
},
function ($error) use ($subject) {
$error = $error instanceof \Exception ? $error : new RejectedPromiseException($error);
$subject->onError($error);
}
);
});
return new CompositeDisposable([
$subject->subscribe($observer),
$disp,
new CallbackDisposable(function () use ($promise) {
if ($promise instanceof CancellablePromiseInterface) {
$promise->cancel();
}
})
]);
});
} |
If there is an active polling observable upstream it will keep polling as long as it isn't disposed. By disposing when we reach the desired amount of items, the possible upstream polling is also stopped.