Relay: [Modern] cacheConfig.poll setting does not poll

Created on 28 Oct 2017  路  16Comments  路  Source: facebook/relay

If passing cacheConfig to QueryRenderer with a poll interval set, the function passed to Network.create does not execute on each poll interval. I believe this is a regression from v1.1.0 and is related to the refactoring of RelayEnvironment to use observables.

Reproducing

To demonstrate this is a regression, I鈥檝e created two branches on a forked version of relay-examples. In polling-1.1, polling works as expected and in not-polling-1.4, polling does not work. Additionally, I confirmed when upgrading to v1.2.0, polling stops working. The only changes in these examples are modifications of relay library versions and console.logs.

Potential Causes

From diffing v1.1.0 and v1.2.0, I think observables were introduced in 1.2. From a few attempts at debugging, it appears to me this may be related to ConvertToExecuteFunction.js. I am not that familiar with observables, but the poll function on the observable is not invoking the fetchFn passed to Network.create.

Thanks for a great open source library, and would love to help on this one if it seems like a good beginner issue.

Most helpful comment

I've managed to implemented it

It's open source here: https://github.com/sibelius/relay-modern-network-deep-dive

There is also a medium post about it: https://medium.com/@sibelius/relay-modern-network-deep-dive-ec187629dfd3

now we need to improve Relay official docs

All 16 comments

Hi @BenBrostoff
Have you been able to figure out what is wrong in the polling mechanism or how should it be used in case nothing is wrong?
Thanks

I just spent some time digging into this as well and found a couple of things.

Polling current polls from the previous RelayObservable state rather than using the network

After setting up one of my QueryRenders to include the prop cacheConfig={{ poll: 1000 }}, I could see in my RelayNetworkLogger that it looked like requests were happening and data was being returned. However, the requests always seemed to return the same data as the initial request, and subsequent polling requests returned in sub-millisecond time. Taking a look at my server logs, it was clear that Relay wasn't actually making the request, but rather just returning the initial response from the first network request.

I tried setting some breakpoints to see why this was happening, but I couldn't find a clear-cut place in Relay where the result of the previous result from execute was being returned. For context, my Network fetch function just returns a Promise from fetch.

Using Network.create to do Live Queries (aka polling)

Tangentially, in #1655, @leebyron mentioned that you can create your own "live query" ability using Network.create with a fetch function that returns an observable: https://github.com/facebook/relay/issues/1655#issuecomment-328643463

However, if we supply some value for cacheConfig to a QueryRenderer, like cacheConfig={{ poll: 1000 }}, our Network function will never receive this poll value, because we're hardcoding the cacheConfig parameter that is passed to our Network fetch function to always be { force: true }: https://github.com/facebook/relay/blob/0a4027d4c2328549e2d314dbb5eb8ad1eeaeb051/packages/relay-runtime/network/RelayNetwork.js#L71

This seems a little bit related to this issue, but it seems as though we should pass through the cacheConfig prop including force: true to Network.execute rather than override cacheConfig when polling is set.

I also spent some time looking at why polling was not working for me. As @eliperkins mentioned, Relay is actually doing polling. It didn't work when fetch function returns a Promise. But it has been working with fetch function returning RelayObservable.

When your fetch function is a Promise, it is called only once and then RelayObservable is created from the result. So only the initial result it being polled.

On the other hand when fetch function returns RelayObservable, then its Source function is being polled as expected.

@timobetina @eliperkins @BenBrostoff just ran into this myself, tried returning RelayObservable from fetch but still doesn't work, could any of you paste a working, polling fetch for this simpleton, please :P ?

Hi @Angry-Potato
This is a simplified example of my relay environment:

import { graphql } from "graphql";
import { Environment, Network, RecordSource, Store, Observable } from "relay-runtime";
import Schema from "./Schema";

function createFetch(schema) {
  return function fetch(operation) {
    return new Observable(sink => {
      graphql(
        schema,
        operation.text,
      ).then((payload) => {
        if (payload.errors) {
          /* Handle errors */
          sink.error(payload.errors);
        } else {
          /* Handle payload */
          sink.next(payload);
          sink.complete();
        }
      });
    });
  }
}

function createEnvironment() {
  return new Environment({
    network: Network.create(createFetch(Schema)),
    store: new Store(new RecordSource()),
  });
}

Thanks, I ended up just using subscriptions instead :P

Is this issue still relevant? Should there be updates to documentation and, if so, could one of you make a PR for that?

I think we can provide an example of fetch for network using Observable, and explain that this is needed to make poll work

I've managed to implemented it

It's open source here: https://github.com/sibelius/relay-modern-network-deep-dive

There is also a medium post about it: https://medium.com/@sibelius/relay-modern-network-deep-dive-ec187629dfd3

now we need to improve Relay official docs

Great stuff @sibelius 馃憦

@timobetina @sibelius it worked for relay modern v1 but not working for v3. Graphql query is returned but response not passed to the components when using Observable

Can you share your network layer implementation?

@sibelius Thanks for quick response. I implemented the one from your repo (4-observables) with out uploadables.

https://github.com/sibelius/relay-modern-network-deep-dive/tree/master/src/4-observables

@sibelius I found the issue.

This is not working

  sink.next({
    operation: request.operation,
    variables,
    response: data,
  });

This is working
sink.next(data);

this was a breaking change on this on v2

can we close this?

Yes please and sorry for the trouble

Was this page helpful?
0 / 5 - 0 ratings