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

Allow developers to short circuit potentially expensive is_private ch… #972

Merged

Conversation

CodeProKid
Copy link
Member

…eck in model layer

Your checklist for this pull request

Thanks for sending a pull request! Please make sure you click the link above to view the contribution guidelines, then fill out the blanks below.

🚨Please review the guidelines for contributing to this repository.

  • Make sure you are making a pull request against the develop branch (left side). Also you should start your branch off our develop.
  • Make sure you are requesting to pull request from a topic/feature/bugfix branch (right side). Don't pull request from your master!

What does this implement/fix? Explain your changes.

Makes the graphql_data_is_private filter a short circuit filter by using null as the default value. A callback could then override this with true or false and that will prevent the default is_private() check running for the model.

Does this close any currently open issues?

Fixes #961

Any relevant logs, error output, GraphiQL screenshots, etc?

(If it’s long, please paste to https://ghostbin.com/ and insert the link here.)

Any other comments?

Technically this is a breaking change since the filter signature is changing, but I don't really consider it to be a breaking change since the previous true or false context passes as the first param from the is_private() method probably wasn't really useful in the callback anyways, other than returning it as the default value if certain conditions weren't met, and that functionality still works as it did.

Where has this been tested?

Operating System:

WordPress Version:

@jasonbahl
Copy link
Collaborator

@CodeProKid this is breaking tests 🤔

src/Model/Model.php Outdated Show resolved Hide resolved
@esamattis
Copy link
Collaborator

WP core seems to use pre_ style filters for this kind of short circuiting for the actually implemtations when needed.

Fox example here's how option filtering is implemented

Pre skip:

https://github.com/WordPress/WordPress/blob/c67763478840fabf689ecbd175dc682da1623069/wp-includes/option.php#L58-L62

Actual value filtering:

https://github.com/WordPress/WordPress/blob/c67763478840fabf689ecbd175dc682da1623069/wp-includes/option.php#L152

Maybe we should follow the same convention? Now this PR removes the possibility to reference the original private value.

## Bugfixes

- MenuItems are stored in the post table and should use the post loader. To use the post loader properly, the menu item IDs should be encoded as `post:$id` but were encoded as `nav_menu_item:$id` which breaks the principles of the Relay spec. This fixes that. MenuItems now output ids encoded as `post:$id`
chriszarate added a commit to Quartz/wp-graphql that referenced this pull request Jan 17, 2021
There has been a long-standing performance issue with the is_private
method of the User model, because it attempts to count the number of
published posts for each user, a potentially very expensive operation.
And since the operation is uncached, this can very quickly overwhelm the
database.

More discussion of the issue:
wp-graphql#961

At Quartz, this has prevented us from upgrading WPGraphQL, and it
appears to have affected other users as well. A few different PRs exist
to address this issue:

wp-graphql#962
wp-graphql#972
wp-graphql#1677

This PR is my attempt to fix this issue the way that Jason and others
have described as ideal: using DataLoader.

Instead of creating a new loader (e.g., "UserVisibilityLoader"), I am
including this in UserLoader. The reason is that they seem tightly
coupled and making them separate would just introduce unnecessary
complexity. You'd need to remember to use UserVisibilityLoader
everywhere you used UserLoader or risk losing the performance benefit
it provides.

My fix simply plugs into the existing "loadKeys" implementation and
produces a single query that determines visibility for each user. I'd
love feedback on the efficiency of this query; I hope that this is
good enough to sidestep the need for caching. If its not possible to
generate a single efficient query, generating an extra query for each
user along the lines of `SELECT id FROM wp_posts WHERE post_author = X
LIMIT 1` seems like the next best approach.

Finally, we need a way to provide the visibility to the User model.
Luckily, WP_User is not a final class and provides a setter:

https://developer.wordpress.org/reference/classes/wp_user/__set/

This allows us to set an "is_private" property on the WP_User instance,
which is then available to the model via "$this->data". This seems to
be done widely, but we could implement a wrapper around WP_User if we
wanted to avoid mutating WP_User instances.

From there, it's a very simple matter of having the User model check
this property.
jasonbahl
jasonbahl previously approved these changes Jan 21, 2021
jasonbahl
jasonbahl previously approved these changes Jan 22, 2021
@jasonbahl
Copy link
Collaborator

WP core seems to use pre_ style filters for this kind of short circuiting for the actually implemtations when needed.

Fox example here's how option filtering is implemented

Pre skip:

https://github.com/WordPress/WordPress/blob/c67763478840fabf689ecbd175dc682da1623069/wp-includes/option.php#L58-L62

Actual value filtering:

https://github.com/WordPress/WordPress/blob/c67763478840fabf689ecbd175dc682da1623069/wp-includes/option.php#L152

Maybe we should follow the same convention? Now this PR removes the possibility to reference the original private value.

@jasonbahl jasonbahl closed this Jan 22, 2021
@jasonbahl jasonbahl reopened this Jan 22, 2021
@jasonbahl
Copy link
Collaborator

@zacscott I got this PR updated and passing tests.

This should help with the issue you were facing. @imranhsayed @chriszarate @benmay this should help with what ya'll were facing as well?

I still plan to fix the SQL check as well that checks for published authors (there are a few open PRs for that, but this should unblock you until I review those PRs and make some final decisions on that).

@jasonbahl jasonbahl merged commit f15bbb5 into wp-graphql:develop Jan 22, 2021
@jasonbahl jasonbahl self-assigned this Jan 22, 2021
@coveralls
Copy link

coveralls commented Jan 22, 2021

Coverage Status

Coverage decreased (-0.02%) to 79.646% when pulling 1df226e on CodeProKid:bug/961/many-queries-from-model-layer into 91e1249 on wp-graphql:develop.

@jasonbahl jasonbahl mentioned this pull request Jan 22, 2021
@imranhsayed
Copy link

Thank you @jasonbahl
Appreciate it 👍

@zacscott
Copy link

Thank you @jasonbahl 🙌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Negative performance impact in the User Model
7 participants