apollographql / apollographql/federation
Enhance handling of entity fetches to reduce traffic
- Dominant language
- TypeScript
- Stars
- 725
- Forks
- 276
- Avg merge
- 1h 47m
- Merged PRs (30d)
- 1
Description
Currently, anytime a reference occurs where Federation must invoke an entity/representation query, it calls the underlying service with the list of representations. This happens within https://github.com/apollographql/federation/blob/main/gateway-js/src/executeQueryPlan.ts#L231-L277.
However, there are a few issues that occur as part of this that we've seen in practice with some more complex queries.
1. At times, when running over a list of references, the references are `null` meaning no reference exist at that index. This results in an empty set of representations. As such an entity query of `{ representations : [ ] }` is passed to the service. This is essentially a no-op and unnecessary. Instead, a check could be done after the `forEach` array to check if any `representations` were determined. If not, simply `return`. Otherwise, continue as normal.
2. This one is far more complex. Essentially, while invoking a request it is possible that either a duplicate reference exists within the same operation or multiple operations within the same request occur and the same entity exists between them. This results in lots of unnecessary traffic since the data already exists. In a way this is like the data loader problem/solution where an entity is fetched multiple times. The data loader maintains a local cache unique per request to help offset those fetches. A similar principle may be useful within the above code. First, we should flatten the list into a set to ensure only unique entities exist and then copy the resulting entity back into the entities. Second, we should maintain a cache in the context (or elsewhere) of representation to promise/data. Often times, the invocations happen in parallel, so treating the cache as a promise would make the parallelism and cache more effective. While determining the representations, the cache should be checked to see if a promise or result exists for the representation already. If so, a resolver should be wired to copy the result over once the promise completes. If not, the representation should be passed to the underlying service and the promise cached for future calls. Once the sendOperation completes, the promises may be resolved (or rejected) allowing all items waiting on cached promises to finish.
The combination of these two will greatly improve the effectiveness and traffic to core systems.
If desired, I can help propose/produce more concrete code if there is a desire to take up this work. I already have some hack'd up code to test and validate some of my theories. I can improve upon that if needed.
Contributor guide
Research direction
Start in gateway-js/src/executeQueryPlan.ts at lines 231-277, where entity and representation queries are invoked. Trace how null references and repeated entities are handled across operations, then define completion as avoiding empty entity requests and reusing in-flight or completed entity results without changing the returned entities.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, typescript
- Domain
- backend-api-design, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100