Users: Introduce the concept of a large site to single site installations.

Currently in WordPress multisite there is a concept of large networks. The function `wp_is_large_network` is used to determine if a network has a large number of sites or users. If a network is marked as large, then 
expensive queries to calculate user counts are not run on page load but deferred to scheduled events. However there are a number of places in a single site installation where this functionality would also be useful, as 
expensive calls to count users and roles can make screens in the admin extremely slow.

In this change, the `get_user_count` function and related functionality around it is ported to be available in a single site context. This means that expensive calls to the `count_users` function are replaced with 
calls to `get_user_count`. This change also includes a new function called `wp_is_large_user_count` and a filter of the same name, to mark if a site is large.

Props johnbillion, Spacedmonkey, Mista-Flo, lumpysimon, tharsheblows, obenland, miss_jwo, jrchamp, flixos90, macbookandrew, pento, desrosj, johnjamesjacoby, jb510, davidbaumwald, costdev. 
Fixes #38741.


Built from https://develop.svn.wordpress.org/trunk@53011


git-svn-id: http://core.svn.wordpress.org/trunk@52600 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
spacedmonkey 2022-03-29 12:42:13 +00:00
parent c220f23155
commit e2e15f1876
11 changed files with 191 additions and 65 deletions

View File

@ -388,11 +388,6 @@ class WP_Debug_Data {
$site_count += get_blog_count( $network_id ); $site_count += get_blog_count( $network_id );
} }
$info['wp-core']['fields']['user_count'] = array(
'label' => __( 'User count' ),
'value' => get_user_count(),
);
$info['wp-core']['fields']['site_count'] = array( $info['wp-core']['fields']['site_count'] = array(
'label' => __( 'Site count' ), 'label' => __( 'Site count' ),
'value' => $site_count, 'value' => $site_count,
@ -402,15 +397,13 @@ class WP_Debug_Data {
'label' => __( 'Network count' ), 'label' => __( 'Network count' ),
'value' => $network_query->found_networks, 'value' => $network_query->found_networks,
); );
} else {
$user_count = count_users();
$info['wp-core']['fields']['user_count'] = array(
'label' => __( 'User count' ),
'value' => $user_count['total_users'],
);
} }
$info['wp-core']['fields']['user_count'] = array(
'label' => __( 'User count' ),
'value' => get_user_count(),
);
// WordPress features requiring processing. // WordPress features requiring processing.
$wp_dotorg = wp_remote_get( 'https://wordpress.org', array( 'timeout' => 10 ) ); $wp_dotorg = wp_remote_get( 'https://wordpress.org', array( 'timeout' => 10 ) );

View File

@ -1653,7 +1653,7 @@ class WP_Posts_List_Table extends WP_List_Table {
<?php endif; // $bulk ?> <?php endif; // $bulk ?>
<?php <?php
if ( post_type_supports( $screen->post_type, 'author' ) ) { if ( post_type_supports( $screen->post_type, 'author' ) && ! wp_is_large_user_count() ) {
$authors_dropdown = ''; $authors_dropdown = '';
if ( current_user_can( $post_type_object->cap->edit_others_posts ) ) { if ( current_user_can( $post_type_object->cap->edit_others_posts ) ) {

View File

@ -177,28 +177,33 @@ class WP_Users_List_Table extends WP_List_Table {
$wp_roles = wp_roles(); $wp_roles = wp_roles();
$count_users = ! wp_is_large_user_count();
if ( $this->is_site_users ) { if ( $this->is_site_users ) {
$url = 'site-users.php?id=' . $this->site_id; $url = 'site-users.php?id=' . $this->site_id;
switch_to_blog( $this->site_id );
$users_of_blog = count_users( 'time', $this->site_id );
restore_current_blog();
} else { } else {
$url = 'users.php'; $url = 'users.php';
$users_of_blog = count_users();
} }
$total_users = $users_of_blog['total_users']; $role_links = array();
$avail_roles =& $users_of_blog['avail_roles']; $avail_roles = array();
unset( $users_of_blog ); $all_text = __( 'All' );
$current_link_attributes = empty( $role ) ? ' class="current" aria-current="page"' : ''; $current_link_attributes = empty( $role ) ? ' class="current" aria-current="page"' : '';
$role_links = array(); if ( $count_users ) {
$role_links['all'] = sprintf( if ( $this->is_site_users ) {
'<a href="%s"%s>%s</a>', switch_to_blog( $this->site_id );
$url, $users_of_blog = count_users( 'time', $this->site_id );
$current_link_attributes, restore_current_blog();
sprintf( } else {
$users_of_blog = count_users();
}
$total_users = $users_of_blog['total_users'];
$avail_roles =& $users_of_blog['avail_roles'];
unset( $users_of_blog );
$all_text = sprintf(
/* translators: %s: Number of users. */ /* translators: %s: Number of users. */
_nx( _nx(
'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>',
@ -207,11 +212,13 @@ class WP_Users_List_Table extends WP_List_Table {
'users' 'users'
), ),
number_format_i18n( $total_users ) number_format_i18n( $total_users )
) );
); }
$role_links['all'] = sprintf( '<a href="%s"%s>%s</a>', $url, $current_link_attributes, $all_text );
foreach ( $wp_roles->get_names() as $this_role => $name ) { foreach ( $wp_roles->get_names() as $this_role => $name ) {
if ( ! isset( $avail_roles[ $this_role ] ) ) { if ( $count_users && ! isset( $avail_roles[ $this_role ] ) ) {
continue; continue;
} }
@ -222,12 +229,14 @@ class WP_Users_List_Table extends WP_List_Table {
} }
$name = translate_user_role( $name ); $name = translate_user_role( $name );
$name = sprintf( if ( $count_users ) {
/* translators: 1: User role name, 2: Number of users. */ $name = sprintf(
__( '%1$s <span class="count">(%2$s)</span>' ), /* translators: 1: User role name, 2: Number of users. */
$name, __( '%1$s <span class="count">(%2$s)</span>' ),
number_format_i18n( $avail_roles[ $this_role ] ) $name,
); number_format_i18n( $avail_roles[ $this_role ] )
);
}
$role_links[ $this_role ] = "<a href='" . esc_url( add_query_arg( 'role', $this_role, $url ) ) . "'$current_link_attributes>$name</a>"; $role_links[ $this_role ] = "<a href='" . esc_url( add_query_arg( 'role', $this_role, $url ) ) . "'$current_link_attributes>$name</a>";
} }

View File

@ -1267,6 +1267,7 @@ We hope you enjoy your new site. Thanks!
'subdomain_install' => $subdomain_install, 'subdomain_install' => $subdomain_install,
'global_terms_enabled' => global_terms_enabled() ? '1' : '0', 'global_terms_enabled' => global_terms_enabled() ? '1' : '0',
'ms_files_rewriting' => is_multisite() ? get_site_option( 'ms_files_rewriting' ) : '0', 'ms_files_rewriting' => is_multisite() ? get_site_option( 'ms_files_rewriting' ) : '0',
'user_count' => get_site_option( 'user_count' ),
'initial_db_version' => get_option( 'initial_db_version' ), 'initial_db_version' => get_option( 'initial_db_version' ),
'active_sitewide_plugins' => array(), 'active_sitewide_plugins' => array(),
'WPLANG' => get_locale(), 'WPLANG' => get_locale(),

View File

@ -845,6 +845,10 @@ function upgrade_all() {
upgrade_590(); upgrade_590();
} }
if ( $wp_current_db_version < 53011 ) {
upgrade_600();
}
maybe_disable_link_manager(); maybe_disable_link_manager();
maybe_disable_automattic_widgets(); maybe_disable_automattic_widgets();
@ -2282,6 +2286,22 @@ function upgrade_590() {
} }
} }
/**
* Executes changes made in WordPress 6.0.0.
*
* @ignore
* @since 6.0.0
*
* @global int $wp_current_db_version The old (current) database version.
*/
function upgrade_600() {
global $wp_current_db_version;
if ( $wp_current_db_version < 53011 ) {
wp_update_user_counts();
}
}
/** /**
* Executes network-level upgrade routines. * Executes network-level upgrade routines.
* *

View File

@ -98,6 +98,13 @@ add_filter( 'post_mime_type', 'sanitize_mime_type' );
// Meta. // Meta.
add_filter( 'register_meta_args', '_wp_register_meta_args_allowed_list', 10, 2 ); add_filter( 'register_meta_args', '_wp_register_meta_args_allowed_list', 10, 2 );
// Counts.
add_action( 'admin_init', 'wp_schedule_update_user_counts' );
add_action( 'wp_update_user_counts', 'wp_schedule_update_user_counts', 10, 0 );
foreach ( array( 'user_register', 'deleted_user' ) as $action ) {
add_action( $action, 'wp_maybe_update_user_counts', 10, 0 );
}
// Post meta. // Post meta.
add_action( 'added_post_meta', 'wp_cache_set_posts_last_changed' ); add_action( 'added_post_meta', 'wp_cache_set_posts_last_changed' );
add_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed' ); add_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed' );

View File

@ -8418,3 +8418,113 @@ function is_php_version_compatible( $required ) {
function wp_fuzzy_number_match( $expected, $actual, $precision = 1 ) { function wp_fuzzy_number_match( $expected, $actual, $precision = 1 ) {
return abs( (float) $expected - (float) $actual ) <= $precision; return abs( (float) $expected - (float) $actual ) <= $precision;
} }
/**
* Returns the number of active users in your installation.
*
* Note that on a large site the count may be cached and only updated twice daily.
*
* @since MU (3.0.0)
* @since 4.8.0 The `$network_id` parameter has been added.
* @since 6.0.0 Move to wp-includes/functions.php.
*
* @param int|null $network_id ID of the network. Default is the current network.
* @return int Number of active users on the network.
*/
function get_user_count( $network_id = null ) {
if ( ! is_multisite() && null !== $network_id ) {
_doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
}
return (int) get_network_option( $network_id, 'user_count', -1 );
}
/**
* Updates the total count of users on the site if live user counting is enabled.
*
* @since 6.0.0
*
* @param int|null $network_id ID of the network. Default is the current network.
* @return bool Whether the update was successful.
*/
function wp_maybe_update_user_counts( $network_id = null ) {
if ( ! is_multisite() && null !== $network_id ) {
_doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
}
$is_small_network = ! wp_is_large_user_count( $network_id );
/** This filter is documented in wp-includes/ms-functions.php */
if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) {
return false;
}
return wp_update_user_counts( $network_id );
}
/**
* Updates the total count of users on the site.
*
* @global wpdb $wpdb WordPress database abstraction object.
* @since 6.0.0
*
* @param int|null $network_id ID of the network. Default is the current network.
* @return bool Whether the update was successful.
*/
function wp_update_user_counts( $network_id = null ) {
global $wpdb;
if ( ! is_multisite() && null !== $network_id ) {
_doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
}
$query = "SELECT COUNT(ID) as c FROM $wpdb->users";
if ( is_multisite() ) {
$query .= " WHERE spam = '0' AND deleted = '0'";
}
$count = $wpdb->get_var( $query );
return update_network_option( $network_id, 'user_count', $count );
}
/**
* Schedules a recurring recalculation of the total count of users.
*
* @since 6.0.0
*/
function wp_schedule_update_user_counts() {
if ( ! is_main_site() ) {
return;
}
if ( ! wp_next_scheduled( 'wp_update_user_counts' ) && ! wp_installing() ) {
wp_schedule_event( time(), 'twicedaily', 'wp_update_user_counts' );
}
}
/**
* Determines whether the site has a large number of users.
*
* The default criteria for a large site is more than 10,000 users.
*
* @since 6.0.0
*
* @param int|null $network_id ID of the network. Default is the current network.
* @return bool Whether the site has a large number of users.
*/
function wp_is_large_user_count( $network_id = null ) {
if ( ! is_multisite() && null !== $network_id ) {
_doing_it_wrong( __FUNCTION__, __( 'Unable to pass $network_id if not using multisite.' ), '6.0.0' );
}
$count = get_user_count( $network_id );
/**
* Filters whether the site is considered large, based on its number of users.
*
* @since 6.0.0
*
* @param bool $is_large_user_count Whether the site has a large number of users.
* @param int $count The total number of users.
* @param int|null $network_id ID of the network. `null` represents the current network.
*/
return apply_filters( 'wp_is_large_user_count', $count > 10000, $count, $network_id );
}

View File

@ -84,9 +84,14 @@ add_action( 'transition_post_status', '_update_posts_count_on_transition_post_st
// Counts. // Counts.
add_action( 'admin_init', 'wp_schedule_update_network_counts' ); add_action( 'admin_init', 'wp_schedule_update_network_counts' );
add_action( 'update_network_counts', 'wp_update_network_counts', 10, 0 ); add_action( 'update_network_counts', 'wp_update_network_counts', 10, 0 );
foreach ( array( 'user_register', 'deleted_user', 'wpmu_new_user', 'make_spam_user', 'make_ham_user' ) as $action ) { foreach ( array( 'wpmu_new_user', 'make_spam_user', 'make_ham_user' ) as $action ) {
add_action( $action, 'wp_maybe_update_network_user_counts', 10, 0 ); add_action( $action, 'wp_maybe_update_network_user_counts', 10, 0 );
} }
// These counts are handled by wp_update_network_counts() on Multisite:
remove_action( 'admin_init', 'wp_schedule_update_user_counts' );
remove_action( 'wp_update_user_counts', 'wp_schedule_update_user_counts' );
foreach ( array( 'make_spam_blog', 'make_ham_blog', 'archive_blog', 'unarchive_blog', 'make_delete_blog', 'make_undelete_blog' ) as $action ) { foreach ( array( 'make_spam_blog', 'make_ham_blog', 'archive_blog', 'unarchive_blog', 'make_delete_blog', 'make_undelete_blog' ) as $action ) {
add_action( $action, 'wp_maybe_update_network_site_counts', 10, 0 ); add_action( $action, 'wp_maybe_update_network_site_counts', 10, 0 );
} }

View File

@ -100,21 +100,6 @@ function get_active_blog_for_user( $user_id ) {
} }
} }
/**
* The number of active users in your installation.
*
* The count is cached and updated twice daily. This is not a live count.
*
* @since MU (3.0.0)
* @since 4.8.0 The `$network_id` parameter has been added.
*
* @param int|null $network_id ID of the network. Default is the current network.
* @return int Number of active users on the network.
*/
function get_user_count( $network_id = null ) {
return get_network_option( $network_id, 'user_count' );
}
/** /**
* The number of active sites on your installation. * The number of active sites on your installation.
* *
@ -2611,16 +2596,12 @@ function wp_update_network_site_counts( $network_id = null ) {
* *
* @since 3.7.0 * @since 3.7.0
* @since 4.8.0 The `$network_id` parameter has been added. * @since 4.8.0 The `$network_id` parameter has been added.
* * @since 6.0.0 This function is now a wrapper for wp_update_user_counts().
* @global wpdb $wpdb WordPress database abstraction object.
* *
* @param int|null $network_id ID of the network. Default is the current network. * @param int|null $network_id ID of the network. Default is the current network.
*/ */
function wp_update_network_user_counts( $network_id = null ) { function wp_update_network_user_counts( $network_id = null ) {
global $wpdb; wp_update_user_counts( $network_id );
$count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
update_network_option( $network_id, 'user_count', $count );
} }
/** /**
@ -2754,6 +2735,9 @@ function wp_is_large_network( $using = 'sites', $network_id = null ) {
if ( 'users' === $using ) { if ( 'users' === $using ) {
$count = get_user_count( $network_id ); $count = get_user_count( $network_id );
$is_large_network = wp_is_large_user_count( $network_id );
/** /**
* Filters whether the network is considered large. * Filters whether the network is considered large.
* *
@ -2765,7 +2749,7 @@ function wp_is_large_network( $using = 'sites', $network_id = null ) {
* @param int $count The count of items for the component. * @param int $count The count of items for the component.
* @param int $network_id The ID of the network being checked. * @param int $network_id The ID of the network being checked.
*/ */
return apply_filters( 'wp_is_large_network', $count > 10000, 'users', $count, $network_id ); return apply_filters( 'wp_is_large_network', $is_large_network, 'users', $count, $network_id );
} }
$count = get_blog_count( $network_id ); $count = get_blog_count( $network_id );

View File

@ -80,13 +80,10 @@ function wp_version_check( $extra_stats = array(), $force_check = false ) {
} }
if ( is_multisite() ) { if ( is_multisite() ) {
$user_count = get_user_count();
$num_blogs = get_blog_count(); $num_blogs = get_blog_count();
$wp_install = network_site_url(); $wp_install = network_site_url();
$multisite_enabled = 1; $multisite_enabled = 1;
} else { } else {
$user_count = count_users();
$user_count = $user_count['total_users'];
$multisite_enabled = 0; $multisite_enabled = 0;
$num_blogs = 1; $num_blogs = 1;
$wp_install = home_url( '/' ); $wp_install = home_url( '/' );
@ -99,7 +96,7 @@ function wp_version_check( $extra_stats = array(), $force_check = false ) {
'mysql' => $mysql_version, 'mysql' => $mysql_version,
'local_package' => isset( $wp_local_package ) ? $wp_local_package : '', 'local_package' => isset( $wp_local_package ) ? $wp_local_package : '',
'blogs' => $num_blogs, 'blogs' => $num_blogs,
'users' => $user_count, 'users' => get_user_count(),
'multisite_enabled' => $multisite_enabled, 'multisite_enabled' => $multisite_enabled,
'initial_db_version' => get_site_option( 'initial_db_version' ), 'initial_db_version' => get_site_option( 'initial_db_version' ),
); );

View File

@ -16,7 +16,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '6.0-alpha-53010'; $wp_version = '6.0-alpha-53011';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.