Skip to content
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

Resolve multiple federated entities in a single entityResolve call #1709

Merged
merged 3 commits into from
Nov 13, 2021

Conversation

MiguelCastillo
Copy link
Collaborator

@MiguelCastillo MiguelCastillo commented Nov 12, 2021

Entity resolver functions can only process one entity at a time. But often we want to resolve all the entities at once so that we can optimize things like database calls. And to do that you need to add you'd need to add batching with abstractions like dataloadgen or batchloader. The drawback here is that the resolver code (the domain logic) gets more complex to implement, test, and debug.

An alternative is to have entity resolvers that can process all the representations in a single call so that domain logic can have access to all the representations up front, which is what Im adding in this PR.

There are a few moving pieces here:

  1. We need to define the directive directive @entityResolver(multi: Boolean) on OBJECT.
  2. Then federated entities need to be annotated to enable the functionality. E.g. type MultiHello @key(fields: "name") @entityResolver(multi: true)
  3. When that's configured, the federation plugin will create an entity resolver that will take a list of representations.

Please note that this is very specific to federation and entity resolvers. This does not add support for resolving fields in an entity.

Some of the implementation details worth noting. In order to efficiently process batches of entities, I group them by type so that we can process groups of entities at the same time. The resolution of groups of entities run concurrently in Go routines. If there is only one type, then that's just processed without concurrency. Entities that don't have multiget enabled will still continue to resolve concurrently with Go routines, and entities that have multiget enabled just get the entire list of representations.

The list of representations that are passed to entity resolvers are strongly types, and the type is generated for you.

There are lots of new tests to ensure that there are no regressions and that the new functionality still functions as expected. To test:

  1. Go to plugin/federation
  2. Generate files with go run github.com/99designs/gqlgen --config testdata/entityresolver/gqlgen.yml
  3. And run go test ./.... Verify they all pass.

You can look at the federated code in plugin/federation/testdata/entityresolver/gederated/federation.go

Describe your PR and link to any relevant issues.

I have:

  • Added tests covering the bug / feature (see testing)
  • Updated any relevant documentation (see docs)

#1686

Entity resolver functions can only process one entity at a time. But often we want to resolve all the entities at once so that we can optimize things like database calls. And to do that you need to add you'd need to add batching with abstractions like dataloadgen or batchloader. The drawback here is that the resolver code (the domain logic) gets more complex to implement, test, and debug.

An alternative is to have entity resolvers that can process all the representations in a single call so that domain logic can have access to all the representations up front, which is what Im adding in this PR.

There are a few moving pieces here:
1. We need to define the directive `directive @entityResolver(multi: Boolean) on OBJECT`.
2. Then federated entities need to be annotated to enable the functionality.  E.g. `type MultiHello @key(fields: "name") @entityResolver(multi: true)`
3. When that's configured, the federation plugin will create an entity resolver that will take a list of representations.

Please note that this is very specific to federation and entity resolvers. This does not add support for resolving fields in an entity.

Some of the implementation details worth noting. In order to efficiently process batches of entities, I group them by type so that we can process groups of entities at the same time. The resolution of groups of entities run concurrently in Go routines.  If there is _only_ one type, then that's just processed without concurrency. Entities that don't have multiget enabled will still continue to resolve concurrently with Go routines, and entities that have multiget enabled just get the entire list of representations.

The list of representations that are passed to entity resolvers are strongly types, and the type is generated for you.

There are lots of new tests to ensure that there are no regressions and that the new functionality still functions as expected. To test:
1. Go to `plugin/federation`
2. Generate files with `go run github.com/99designs/gqlgen --config testdata/entityresolver/gqlgen.yml`
3. And run `go test ./...`. Verify they all pass.

You can look at the federated code in `plugin/federation/testdata/entityresolver/gederated/federation.go`
@coveralls
Copy link

coveralls commented Nov 12, 2021

Coverage Status

Coverage decreased (-0.05%) to 67.34% when pulling 61cf087 on Khan:multiget into 80713b8 on 99designs:master.

MiguelCastillo and others added 2 commits November 13, 2021 08:46
Signed-off-by: Steve Coffman <steve@khanacademy.org>
@StevenACoffman StevenACoffman merged commit 50292e9 into 99designs:master Nov 13, 2021
@MiguelCastillo MiguelCastillo deleted the multiget branch November 15, 2021 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants