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

Re-use loaders after they have been performed, but clear on mutation #63

Merged
merged 1 commit into from
Aug 28, 2017

Conversation

dylanahsmith
Copy link
Contributor

Fixes #43

Problem

#56 reverted a change that re-uses loaders after they are loaded so that they aren't re-used after a mutation.

Solution

Add field instrumentation to wrap the resolve proc for mutation fields in order to clear the loaders cache before and after it is called.

Identifying which fields were mutation fields required having the schema in the instrumentation, so I changed the setup modules into classes that take the schema as an initializer. Before #51 we recommended using instrument(:query, GraphQL::Batch::Setup) in the README, so I have added class methods to deprecate that old usage.

@dylanahsmith dylanahsmith requested review from eapache and xuorig August 10, 2017 18:46
@eapache
Copy link
Contributor

eapache commented Aug 10, 2017

This PR does not include all of the changes from the original #53. The missing changes don't seem particularly relevant, but I'm wondering either why they're not here or why you didn't strip them from that PR originally?

@eapache
Copy link
Contributor

eapache commented Aug 10, 2017

I'm not 100% clear on why we need to wrap mutations in both the execution strategy and field instrumentation? It's been a while since I've dug into this gem, do we just have two entirely separate methods for integrating into the graphql-ruby gem?

@eapache
Copy link
Contributor

eapache commented Aug 10, 2017

Just to be clear my understanding is correct, the batch loaders will now be cleared only:

  • before and after each mutation
  • at the end of executing a request (because the executor will be completely reinstantiated)

resolve ->(obj, args, ctx) {
GraphQL::Batch::Executor.current.clear
begin
Promise.sync(old_resolve_proc.call(obj, args, ctx))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the sync necessary here? I didn't think it made sense for a mutation's resolve method to return a promise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a loader were re-used in a mutation to load data to mutate, then it wouldn't be necessary to call sync on the promise. E.g. a callback chain like RecordLoader.for(Model).load(args[:id]).then { ... } could be used. That isn't a pattern that we use, but it doesn't make sense to prevent that considering graphql-ruby provides lazy loading support for mutation fields. So rather than change that behaviour, I just want to try to make sure the cache is actually cleared after any promise callbacks on a returned callback have been called.

@eapache
Copy link
Contributor

eapache commented Aug 10, 2017

A bunch of questions because it's been so long since I've looked at this code, but generally this makes sense.

@dylanahsmith
Copy link
Contributor Author

This PR does not include all of the changes from the original #53. The missing changes don't seem particularly relevant, but I'm wondering either why they're not here or why you didn't strip them from that PR originally?

I added them back in the same PR that reverted them (#56) since they were in support of that change, but didn't cause the regression.

@dylanahsmith
Copy link
Contributor Author

I'm not 100% clear on why we need to wrap mutations in both the execution strategy and field instrumentation? It's been a while since I've dug into this gem, do we just have two entirely separate methods for integrating into the graphql-ruby gem?

Ya, I just hadn't ripped out the old execution strategy code yet. But that would only be needed to support a very old version of graphql-ruby.

if GraphQL::VERSION >= "1.6.0"
schema_defn.instrument(:multiplex, GraphQL::Batch::SetupMultiplex)
instrumentation = GraphQL::Batch::SetupMultiplex.new(schema)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of splitting the multiplex/query and field instrumenters in two different concerns? Setup does a lot right now I find.

Copy link
Contributor Author

@dylanahsmith dylanahsmith Aug 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lib/graphql/batch/setup.rb is only 54 lines long, and only does two things which seem quite related. Also, when we want to break backwards compatibility, we can simplify the code by just having multiplex instrumentation and no need to share code between the two setup classes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense!

old_resolve_proc = field.resolve_proc
field.redefine do
resolve ->(obj, args, ctx) {
GraphQL::Batch::Executor.current.clear
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not totally sure why you need to clear before and after?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clearing afterwards is to avoid using data cached from before the mutation. Clearing before the mutation isn't as important, but just ensures that the field is actually executed serially. However, I could see how it might be desirable to use the already loaded values in the mutation.

Do you think we should clear before the mutation? Or get rid of this one?

@dylanahsmith dylanahsmith merged commit f815fe6 into master Aug 28, 2017
@dylanahsmith dylanahsmith deleted the query-loader-reuse branch August 28, 2017 22:18
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.

Loader cache is short lived
3 participants