Make WordPress Core

Opened 7 years ago

Last modified 24 minutes ago

#46388 new enhancement

WP_User::get_data_by(): Cache non-existent users to prevent triggering multiple queries

Reported by: asgaros's profile Asgaros Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: 5.2
Component: Cache API Keywords: reporter-feedback has-patch has-unit-tests
Focuses: performance Cc:

Description

Assume you use the WP_User::get_data_by() function to get the main user fields queried against an ID:

WP_User::get_data_by('ID', 1337);

If you call this function multiple times - by calling the function directly or indirectly by using functions like get_userdata or get_avatar - and an user with the given ID does not exist, the database-query is getting executed multiple times resulting in duplicate queries:

SELECT * FROM wp_users WHERE ID = '1337'

To prevent triggering multiple queries, non-existing users should get stored inside the WP Object Cache similar as in the get_option() function for non-existing options.

The attached patch checks if the user ID exists in the WP Object Cache inside of the notusers-array so the WP_User::get_data_by() function returns FALSE if this is the case:

// Prevent non-existent users from triggering multiple queries
$notusers = wp_cache_get( 'notusers', 'users' );
if ( isset( $notusers[ $user_id ] ) ) {
	return false;
}

If the user is not existent in the notusers-array but he also does not exist inside of the database, the user gets added to the WP Object Cache before the WP_User:get_data_by() function returns FALSE:

// User does not exist, so we must cache its non-existence
if ( ! is_array( $notusers ) ) {
	$notusers = array();
}

$notusers[ $user_id ] = true;
wp_cache_set( 'notusers', $notusers, 'users' );

Attachments (1)

46388.diff (851 bytes) - added by Asgaros 7 years ago.
Add patch file for caching of non-existent users

Download all attachments as: .zip

Change History (4)

@Asgaros
7 years ago

Add patch file for caching of non-existent users

#1 follow-up: @andraganescu
5 years ago

  • Keywords reporter-feedback added

Can we find ourselves in a situation where the ID is *created* after it's cached as non existent? If yes that operation should invalidate the cache.

#2 in reply to: ↑ 1 @azaozz
5 years ago

  • Keywords needs-patch added; has-patch removed
  • Milestone changed from Awaiting Review to Future Release

Replying to andraganescu:

Right, this ticket is a good idea but will need the "full" cache handling including cache busting or resetting on creating new user, etc.

This ticket was mentioned in PR #11632 on WordPress/wordpress-develop by @MythThrazz.


24 minutes ago
#3

  • Keywords has-patch has-unit-tests added; needs-patch removed

Fixes https://core.trac.wordpress.org/ticket/46388

## Problem

When get_userdata() (or WP_User::get_data_by('id', $id)) is called for a non-existent user ID, it always executes a database query. If the same non-existent ID is looked up multiple times — for example, through repeated calls to get_userdata(), get_avatar(), or other functions that resolve user data — each call results in a duplicate SELECT * FROM wp_users WHERE ID = ... query.

## Solution

Cache non-existent user IDs in a notusers array within the users object cache group, following the established notoptions pattern from the options API (get_option()).

WP_User::get_data_by() (when $field === 'id'):

  1. Before querying the database, check the notusers cache. Return false immediately if the ID is cached as non-existent.
  2. After a database miss, add the ID to the notusers cache.

update_user_caches():

  • When a user is created or updated, remove their ID from the notusers cache to ensure stale negative-cache entries are invalidated. This addresses the concern raised in comment:1 about cache invalidation on user creation.

The users cache group is already registered as a global group in multisite (wp_cache_add_global_groups()), so the notusers key inherits that scope correctly.

## Tests

Four new tests in tests/phpunit/tests/user/getDataBy.php:

  • Verifies second call for a non-existent user triggers 0 DB queries.
  • Verifies non-existent user ID is added to notusers cache after first miss.
  • Verifies notusers cache is invalidated when update_user_caches() is called.
  • Verifies existing user IDs are never added to notusers.

---

AI assistance: Yes
Tool(s): Claude Code (Anthropic)
Model(s): Claude Sonnet 4.6
Used for: Implementation and test authorship; reviewed and verified by contributor.

Note: See TracTickets for help on using tickets.