Resolve user visibility in UserLoader #1684
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
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 ofSELECT 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