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 user visibility in UserLoader #1684

Conversation

chriszarate
Copy link
Collaborator

@chriszarate chriszarate commented Jan 17, 2021

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.

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:
#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:

#962
#972
#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. This is not an efficient query, apologies. Working on a better one. 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.

Looking forward to getting this bug squashed and happy to jump on any feedback. Also paging @abhijitrakas and @zacscott since they seem to have been negatively impacted by this issue as well.

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?

Where has this been tested?

Operating System: Linux

WordPress Version: 5.3.2

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.
@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) to 79.706% when pulling ac07c0d on Quartz:bug/#961/add-user-visibility-to-user-loader into 91e1249 on wp-graphql:develop.

@jasonbahl
Copy link
Collaborator

@chriszarate I want to get this tackled. Went to try and get this branch updated to current version of WPGraphQL. Typically I can push to forks of WPGraphQL but seems like Quartz fork is locked down.

I pushed up the branch to this repo and opened #2031 where we can iterate and get this resolved.

I'll close this PR and we can use that PR/branch to get this over the finish line.

@jasonbahl jasonbahl closed this Jul 23, 2021
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.

Negative performance impact in the User Model
3 participants