relay: Duplicate RelayQueryTracker trackedNodes generating giant mutation fat query fragment

I’m tracking down an issue where Relay generated a mutation request with ~15k instances of an “id” field lookup within the same fragment.

Our application shows some near-realtime data that we’ve been getting by calling forceFetch on a timer within our component.

What I think is happening is that each of the responses to that causes a new entry to be added to the RelayQueryTracker. When a mutation for the same node is run, Relay compares these children to the fatQuery. While the fragment that was forceFetch’d is not matched, the lookup for the id field is. Since these field references are not de-duped in the mutation query, the generated GraphQL has “id” once for each forceFetch request that was made.

I think that the right solution is to try and reduce the buildup in the tracker by de-duping, at least as merge time but possibly when the number of children grows to a certain size? I’m willing to put some work into fixing this, but I’d like to know what a recommended approach would be.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 17 (12 by maintainers)

Most upvoted comments

It looks like things work with:

// From https://github.com/facebook/relay/issues/1098:

import flattenRelayQuery from 'react-relay/lib/flattenRelayQuery';
import RelayNodeInterface from 'react-relay/lib/RelayNodeInterface';
import RelayQuery from 'react-relay/lib/RelayQuery';
import RelayQueryTracker from 'react-relay/lib/RelayQueryTracker';

const baseTrackNodeForID = RelayQueryTracker.prototype.trackNodeForID;

function trackNodeForID(node, id) {
  baseTrackNodeForID.call(this, node, id);

  /* eslint-disable no-underscore-dangle */
  const nodes = this._trackedNodesByID[id];
  /* eslint-enable no-underscore-dangle */

  if (nodes.isMerged) {
    return;
  }

  const children = [];
  nodes.trackedNodes.forEach(trackedNode => {
    children.push(...trackedNode.getChildren());
  });

  nodes.isMerged = true;
  nodes.trackedNodes.length = 0;

  const containerNode = RelayQuery.Fragment.build(
    'patchRelay', RelayNodeInterface.NODE_TYPE, children
  );
  if (containerNode) {
    nodes.trackedNodes.push(flattenRelayQuery(containerNode));
  }
}

RelayQueryTracker.prototype.trackNodeForID = trackNodeForID;

I’m well aware this voids my warranty, but it seems to work.

I just verified that this looks good on my end now without the monkey patch. Thanks!