DivvyPayHQ/absinthe_federation

Usage with Dataloader

rewritten opened this issue · 3 comments

The current dataloader integration seems to be breaking a number of assumptions, I wanted to check, before trying a pull-request, if my assumptions are correct.

  1. The function find_relevant_dataloader at https://github.com/DivvyPayHQ/absinthe_federation/blob/v0.3.2/lib/absinthe/federation/schema/entities_field.ex#L299 finds any source that is registered in the dataloader, that has a nonempty (by the way, why nonempty?) map as its batches key. This breaks in two ways,

    1. It pries inside a source structure, which is private (only the Source protocol is guaranteed to exist). In my project we have sources where batches is a list.
    2. A schema might have several registered sources, so the "first one that has batches" might not be the one with the responsibility of resolving the current entity. One may as well have a separate source per each entity for what is worth, and other dataloader sources for completely unrelated data needs.
  2. The call_middleware function only supports a single key, when entities might need a compound key key to be loaded. Or it may declare multiple keys (real case example: a "slug" key that is used to federate from some front-end-facing subgraph, and a separate "uuid" key that is used to federate from other internal subgraphs).

If you agree that this should be solved, I might attempt a pull-request.

After experimenting a bit, I found the solution to the other issues, and that requires having a very specific KV source (or the Ecto one) that responds to a specific interface.

It remains the issue of supporting multiple keys and compound keys, I will have a more specific attempt for it.

Additionally, there is a limitation that I am not completely sure to see the cause. I can make a source per entity, that has this load_function

  def entities(%{__typename: "MyEntity"}, %MapSet{} = keys) do
    # return a map for each key to an {:ok, entity}
  end
  
  # match the initial "marker" batch_key `{:_entities, %{__typename: "MyEntity", ...key}}`
  # it should not be loaded anyways, it is to put a specific item in batches to recognize the source later
  def entities(_, _), do: %{}

but resolving different entities in the same source does not work

  def entities(%{__typename: "MyEntity"}, %MapSet{} = keys) do
    # return a map for each key to an {:ok, entity}
  end

  def entities(%{__typename: "OtherEntity"}, %MapSet{} = keys) do
    # return a map for each key to an {:ok, entity}
  end
  
  # match the initial "marker" batch_key `{:_entities, %{__typename: "MyEntity", ...key}}`
  # it should not be loaded anyways, it is to put a specific item in batches to recognize the source later
  def entities(_, _), do: %{}

, only one entity type is resolved.

I do not have full clarity on how this works, so I'm closing the issue until I can make more experiments.

@SvenW might have some more insight on this aspect, we haven't done too much dataloader with federated subgraphs yet