apollo-client icon indicating copy to clipboard operation
apollo-client copied to clipboard

Polling stops when all sububscriptions are unsubscribed and does not restart on new subscriptions

Open Yogu opened this issue 3 years ago • 4 comments

Intended outcome:

I want to be able to configure pollInterval on a query, then pass the valueChanges observable to be used by other components. I want these components to be able to subscribe and unsubscribe like they please, and I want the polling to work while there is at least one active subscription.

Also, I want to be able to use the switchMap operator with the valueChanges observable without worrying about the polling breaking.

Actual outcome:

The query starts to poll when the first subscription is active, and it stops when all subscriptions are unsubscribed. Afterwards, the polling stays disabled and is not started again when there is a new subscription.

When I use switchMap and return valueChanges of the same query on multiple subsequent calls of the map callback, switchMap always first unsubscribes from the old observable and then subscribes on the new (= old) observable, so there is a brief moment without any active subscriptions, and the polling permanently stops.

How to reproduce the issue:

I created an interactive stackblitz

https://stackblitz.com/edit/angular-ivy-egtyv6?file=src%2Fapp%2Fapp.component.ts

If you click Subscribe, then Unsubscribe, and then Subscribe again, the polling stops working.

If you click Subscribe with switchMap , then click Emit switch map subject, the polling works. But if you click Emit switch map subject again, the polling stops working.

Versions

Can't run this command in stackblitz, but see the versions there

image

Yogu avatar Sep 30 '22 14:09 Yogu

I'm having the same issue when the observable is on a provided-in-root service. When testing it providing the service at component level the polling is working without issues. Also, even on a singleton service, when reassigning the observable, the polling is able to continue.

moi-j avatar Mar 14 '23 15:03 moi-j

luckily this is under investigation?

jm-ra avatar Mar 31 '23 20:03 jm-ra

I've done some digging and I think I've found the cause of this.

  1. When all subscriptions are unsubscribed, the tearDownQuery() is called.
  2. It calls stopPolling(), which sets this.options.pollInterval = 0.
  3. Later when you subscribe again, the pollInterval option is still 0, so it only does one query, but does not poll.

So the root cause of this is that this is because the original pollInterval option is lost when stopPolling is called by tearDownQuery...

MiniGod avatar Jul 17 '23 14:07 MiniGod

How could a fix look like? To me it looks like the main issue is that every trace of poll information gets deleted after the last subscriber unsubscribed (as described by you @MiniGod). There is no chance, after subscriptions got cleared, to know someone created that query with pollInterval: x

If the initial value (and all upcoming changes to pollInterval from user-land) would be cached, the next subscriber could start to poll again, right?

What I don't know is, if there need further checks to not clear everything and set isTornDown = true

  private tearDownQuery() {
    if (this.isTornDown) return;
    if (this.concast && this.observer) {
      this.concast.removeObserver(this.observer);
      delete this.concast;
      delete this.observer;
    }

    this.stopPolling();
    // stop all active GraphQL subscriptions
    this.subscriptions.forEach((sub) => sub.unsubscribe());
    this.subscriptions.clear();
    this.queryManager.stopQuery(this.queryId);
    this.observers.clear();
    this.isTornDown = true;
  }

kroeder avatar Jun 12 '25 06:06 kroeder