Skip to content
This repository has been archived by the owner on Mar 9, 2024. It is now read-only.

WP-r55657: Users: Cache database queries within WP_User_Query class #267

Merged
merged 5 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 98 additions & 20 deletions src/wp-includes/class-wp-user-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ public static function fill_query_vars( $args ) {
'login' => '',
'login__in' => array(),
'login__not_in' => array(),
'cache_results' => true,
);

return wp_parse_args( $args, $defaults );
Expand All @@ -140,6 +141,7 @@ public static function fill_query_vars( $args ) {
* @since 5.1.0 Introduced the 'meta_compare_key' parameter.
* @since 5.3.0 Introduced the 'meta_type_key' parameter.
* @since 5.9.0 Added 'capability', 'capability__in', and 'capability__not_in' parameters.
* @since 6.3.0 Added 'cache_results' parameter.
*
* @global wpdb $wpdb WordPress database abstraction object.
* @global WP_Roles $wp_roles WordPress role management object.
Expand Down Expand Up @@ -254,6 +256,7 @@ public static function fill_query_vars( $args ) {
* logins will be included in results. Default empty array.
* @type string[] $login__not_in An array of logins to exclude. Users matching one of these
* logins will not be included in results. Default empty array.
* @type bool $cache_results Whether to cache user information. Default true.
* }
*/
public function prepare_query( $query = array() ) {
Expand Down Expand Up @@ -790,6 +793,11 @@ public function query() {

$qv =& $this->query_vars;

// Do not cache results if more than 3 fields are requested.
if ( is_array( $qv['fields'] ) && count( $qv['fields'] ) > 3 ) {
$qv['cache_results'] = false;
}

/**
* Filters the users array before the query takes place.
*
Expand All @@ -816,28 +824,47 @@ public function query() {
{$this->query_orderby}
{$this->query_limit}
";

if ( is_array( $qv['fields'] ) ) {
$this->results = $wpdb->get_results( $this->request );
} else {
$this->results = $wpdb->get_col( $this->request );
$cache_value = false;
$cache_key = $this->generate_cache_key( $qv, $this->request );
$cache_group = 'user-queries';
if ( $qv['cache_results'] ) {
$cache_value = wp_cache_get( $cache_key, $cache_group );
}
if ( false !== $cache_value ) {
$this->results = $cache_value['user_data'];
$this->total_users = $cache_value['total_users'];
} else {

if ( is_array( $qv['fields'] ) ) {
$this->results = $wpdb->get_results( $this->request );
} else {
$this->results = $wpdb->get_col( $this->request );
}

if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
/**
* Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance.
*
* @since 3.2.0
* @since 5.1.0 Added the `$this` parameter.
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query.
* @param WP_User_Query $query The current WP_User_Query instance.
*/
$found_users_query = apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()', $this );

$this->total_users = (int) $wpdb->get_var( $found_users_query );
}

if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
/**
* Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance.
*
* @since 3.2.0
* @since 5.1.0 Added the `$this` parameter.
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query.
* @param WP_User_Query $query The current WP_User_Query instance.
*/
$found_users_query = apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()', $this );

$this->total_users = (int) $wpdb->get_var( $found_users_query );
if ( $qv['cache_results'] ) {
$cache_value = array(
'user_data' => $this->results,
'total_users' => $this->total_users,
);
wp_cache_add( $cache_key, $cache_value, $cache_group );
}
}
}

Expand Down Expand Up @@ -1010,6 +1037,57 @@ protected function parse_orderby( $orderby ) {
return $_orderby;
}

/**
* Generate cache key.
*
* @since 6.3.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param array $args Query arguments.
* @param string $sql SQL statement.
* @return string Cache key.
*/
protected function generate_cache_key( array $args, $sql ) {
global $wpdb;

// Replace wpdb placeholder in the SQL statement used by the cache key.
$sql = $wpdb->remove_placeholder_escape( $sql );

$key = md5( $sql );
$last_changed = wp_cache_get_last_changed( 'users' );

if ( empty( $args['orderby'] ) ) {
// Default order is by 'user_login'.
$ordersby = array( 'user_login' => '' );
} elseif ( is_array( $args['orderby'] ) ) {
$ordersby = $args['orderby'];
} else {
// 'orderby' values may be a comma- or space-separated list.
$ordersby = preg_split( '/[,\s]+/', $args['orderby'] );
}

$blog_id = 0;
if ( isset( $args['blog_id'] ) ) {
$blog_id = absint( $args['blog_id'] );
}

if ( $args['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) {
$switch = $blog_id && get_current_blog_id() !== $blog_id;
if ( $switch ) {
switch_to_blog( $blog_id );
}

$last_changed .= wp_cache_get_last_changed( 'posts' );

if ( $switch ) {
restore_current_blog();
}
}

return "get_users:$key:$last_changed";
}

/**
* Parses an 'order' query variable and casts it to ASC or DESC as necessary.
*
Expand Down
8 changes: 8 additions & 0 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@
add_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed' );
add_action( 'deleted_post_meta', 'wp_cache_set_posts_last_changed' );

// User meta.
add_action( 'added_user_meta', 'wp_cache_set_users_last_changed' );
add_action( 'updated_user_meta', 'wp_cache_set_users_last_changed' );
add_action( 'deleted_user_meta', 'wp_cache_set_users_last_changed' );
add_action( 'add_user_role', 'wp_cache_set_users_last_changed' );
add_action( 'set_user_role', 'wp_cache_set_users_last_changed' );
add_action( 'remove_user_role', 'wp_cache_set_users_last_changed' );

// Term meta.
add_action( 'added_term_meta', 'wp_cache_set_terms_last_changed' );
add_action( 'updated_term_meta', 'wp_cache_set_terms_last_changed' );
Expand Down
1 change: 1 addition & 0 deletions src/wp-includes/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ function wp_start_object_cache() {
'site-transient',
'rss',
'users',
'user-queries',
'useremail',
'userlogins',
'usermeta',
Expand Down
2 changes: 2 additions & 0 deletions src/wp-includes/ms-blogs.php
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ function switch_to_blog( $new_blog_id, $deprecated = null ) {
'site-transient',
'rss',
'users',
'user-queries',
'useremail',
'userlogins',
'usermeta',
Expand Down Expand Up @@ -657,6 +658,7 @@ function restore_current_blog() {
'site-transient',
'rss',
'users',
'user-queries',
'useremail',
'userlogins',
'usermeta',
Expand Down
1 change: 1 addition & 0 deletions src/wp-includes/ms-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ function remove_user_from_blog( $user_id, $blog_id = 0, $reassign = 0 ) {
}
}

clean_user_cache( $user_id );
restore_current_blog();

return true;
Expand Down
10 changes: 10 additions & 0 deletions src/wp-includes/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,7 @@ function clean_user_cache( $user ) {
}

wp_cache_delete( $user->ID, 'user_meta' );
wp_cache_set_users_last_changed();

/**
* Fires immediately after the given user's cache is cleaned.
Expand Down Expand Up @@ -4969,3 +4970,12 @@ function wp_is_application_passwords_available_for_user( $user ) {
*/
return apply_filters( 'wp_is_application_passwords_available_for_user', true, $user );
}

/**
* Sets the last changed time for the 'users' cache group.
*
* @since 6.3.0
*/
function wp_cache_set_users_last_changed() {
wp_cache_set( 'last_changed', microtime(), 'users' );
}
1 change: 1 addition & 0 deletions tests/phpunit/includes/abstract-testcase.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ public static function flush_cache() {
'site-transient',
'rss',
'users',
'user-queries',
'useremail',
'userlogins',
'usermeta',
Expand Down
Loading
Loading