Make WordPress Core

Changeset 53955


Ignore:
Timestamp:
08/29/2022 04:52:12 PM (4 years ago)
Author:
flixos90
Message:

Site Health: Introduce persistent object cache check.

This changeset adds a new persistent_object_cache check which determines whether the site uses a persistent object cache, and if not, recommends it if it is beneficial for the site. A support resource to learn more about object caching has been created and is linked in the check.

A few filters are included for customization of the check, aimed primarily at hosting providers to provide more specific information in regards to their environment:

  • site_status_persistent_object_cache_url filters the URL to learn more about object caching, so that e.g. a hosting-specific object caching support resource could be linked.
  • site_status_persistent_object_cache_notes filters the notes added to the check description, so that more fine tuned information on object caching based on the environment can be provided.
  • site_status_should_suggest_persistent_object_cache is a short-circuit filter which allows using entirely custom logic to determine whether a persistent object cache would make sense for the site.
  • site_status_persistent_object_cache_thresholds filters the thresholds in the default logic to determine whether a persistent object cache would make sense for the site, which is based on the amount of data in the database.

Note that due to the nature of this check it is only run in production environments.

Props furi3r, tillkruss, spacedmonkey, audrasjb, Clorith.
Fixes #56040.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-site-health.php

    r53817 r53955  
    22602260                        );
    22612261                }
     2262
     2263                return $result;
     2264        }
     2265
     2266        /**
     2267         * Tests if sites uses persistent object cache.
     2268         *
     2269         * Checks if site uses persistent object cache or recommends to use it if not.
     2270         *
     2271         * @since 6.1.0
     2272         *
     2273         * @return array The test result.
     2274         */
     2275        public function get_test_persistent_object_cache() {
     2276                /**
     2277                 * Filters the action URL for the persistent object cache health check.
     2278                 *
     2279                 * @since 6.1.0
     2280                 *
     2281                 * @param string $action_url Learn more link for persistent object cache health check.
     2282                 */
     2283                $action_url = apply_filters(
     2284                        'site_status_persistent_object_cache_url',
     2285                        /* translators: Localized Support reference. */
     2286                        __( 'https://wordpress.org/support/article/optimization/#object-caching' )
     2287                );
     2288
     2289                $result = array(
     2290                        'test'        => 'persistent_object_cache',
     2291                        'status'      => 'good',
     2292                        'badge'       => array(
     2293                                'label' => __( 'Performance' ),
     2294                                'color' => 'blue',
     2295                        ),
     2296                        'label'       => __( 'A persistent object cache is being used' ),
     2297                        'description' => sprintf(
     2298                                '<p>%s</p>',
     2299                                __( "A persistent object cache makes your site's database more efficient, resulting in faster load times because WordPress can retrieve your site's content and settings much more quickly." )
     2300                        ),
     2301                        'actions'     => sprintf(
     2302                                '<p><a href="%s" target="_blank" rel="noopener">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
     2303                                esc_url( $action_url ),
     2304                                __( 'Learn more about persistent object caching.' ),
     2305                                /* translators: Accessibility text. */
     2306                                __( '(opens in a new tab)' )
     2307                        ),
     2308                );
     2309
     2310                if ( wp_using_ext_object_cache() ) {
     2311                        return $result;
     2312                }
     2313
     2314                if ( ! $this->should_suggest_persistent_object_cache() ) {
     2315                        $result['label'] = __( 'A persistent object cache is not required' );
     2316
     2317                        return $result;
     2318                }
     2319
     2320                $available_services = $this->available_object_cache_services();
     2321
     2322                $notes = __( 'Your hosting provider can tell you if a persistent object cache can be enabled on your site.' );
     2323
     2324                if ( ! empty( $available_services ) ) {
     2325                        $notes .= ' ' . sprintf(
     2326                                /* translators: Available object caching services. */
     2327                                __( 'Your host appears to support the following object caching services: %s.' ),
     2328                                implode( ', ', $available_services )
     2329                        );
     2330                }
     2331
     2332                /**
     2333                 * Filters the second paragraph of the health check's description
     2334                 * when suggesting the use of a persistent object cache.
     2335                 *
     2336                 * Hosts may want to replace the notes to recommend their preferred object caching solution.
     2337                 *
     2338                 * Plugin authors may want to append notes (not replace) on why object caching is recommended for their plugin.
     2339                 *
     2340                 * @since 6.1.0
     2341                 *
     2342                 * @param string $notes              The notes appended to the health check description.
     2343                 * @param array  $available_services The list of available persistent object cache services.
     2344                 */
     2345                $notes = apply_filters( 'site_status_persistent_object_cache_notes', $notes, $available_services );
     2346
     2347                $result['status']       = 'recommended';
     2348                $result['label']        = __( 'You should use a persistent object cache' );
     2349                $result['description'] .= sprintf(
     2350                        '<p>%s</p>',
     2351                        wp_kses(
     2352                                $notes,
     2353                                array(
     2354                                        'a'      => array( 'href' => true ),
     2355                                        'code'   => true,
     2356                                        'em'     => true,
     2357                                        'strong' => true,
     2358                                )
     2359                        )
     2360                );
    22622361
    22632362                return $result;
     
    23842483                }
    23852484
     2485                // Only check for a persistent object cache in production environments to not unnecessarily promote complicated setups.
     2486                if ( 'production' === wp_get_environment_type() ) {
     2487                        $tests['direct']['persistent_object_cache'] = array(
     2488                                'label' => __( 'Persistent object cache' ),
     2489                                'test'  => 'persistent_object_cache',
     2490                        );
     2491                }
     2492
    23862493                /**
    23872494                 * Add or modify which site status tests are run on a site.
     
    28592966        }
    28602967
     2968        /**
     2969         * Determines whether to suggest using a persistent object cache.
     2970         *
     2971         * @since 6.1.0
     2972         *
     2973         * @global wpdb $wpdb WordPress database abstraction object.
     2974         *
     2975         * @return bool Whether to suggest using a persistent object cache.
     2976         */
     2977        public function should_suggest_persistent_object_cache() {
     2978                global $wpdb;
     2979
     2980                if ( is_multisite() ) {
     2981                        return true;
     2982                }
     2983
     2984                /**
     2985                 * Filters whether to suggest use of a persistent object cache and bypass default threshold checks.
     2986                 *
     2987                 * Using this filter allows to override the default logic, effectively short-circuiting the method.
     2988                 *
     2989                 * @since 6.1.0
     2990                 *
     2991                 * @param bool|null $suggest Boolean to short-circuit, for whether to suggest using a persistent object cache.
     2992                 *                           Default null.
     2993                 */
     2994                $short_circuit = apply_filters( 'site_status_should_suggest_persistent_object_cache', null );
     2995                if ( is_bool( $short_circuit ) ) {
     2996                        return $short_circuit;
     2997                }
     2998
     2999                /**
     3000                 * Filters the thresholds used to determine whether to suggest the use of a persistent object cache.
     3001                 *
     3002                 * @since 6.1.0
     3003                 *
     3004                 * @param array $thresholds The list of threshold names and numbers.
     3005                 */
     3006                $thresholds = apply_filters(
     3007                        'site_status_persistent_object_cache_thresholds',
     3008                        array(
     3009                                'alloptions_count' => 500,
     3010                                'alloptions_bytes' => 100000,
     3011                                'comments_count'   => 1000,
     3012                                'options_count'    => 1000,
     3013                                'posts_count'      => 1000,
     3014                                'terms_count'      => 1000,
     3015                                'users_count'      => 1000,
     3016                        )
     3017                );
     3018
     3019                $alloptions = wp_load_alloptions();
     3020
     3021                if ( $thresholds['alloptions_count'] < count( $alloptions ) ) {
     3022                        return true;
     3023                }
     3024
     3025                if ( $thresholds['alloptions_bytes'] < strlen( serialize( $alloptions ) ) ) {
     3026                        return true;
     3027                }
     3028
     3029                $table_names = implode( "','", array( $wpdb->comments, $wpdb->options, $wpdb->posts, $wpdb->terms, $wpdb->users ) );
     3030
     3031                // With InnoDB the `TABLE_ROWS` are estimates, which are accurate enough and faster to retrieve than individual `COUNT()` queries.
     3032                $results = $wpdb->get_results(
     3033                        $wpdb->prepare(
     3034                                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- This query cannot use interpolation.
     3035                                "SELECT TABLE_NAME AS 'table', TABLE_ROWS AS 'rows', SUM(data_length + index_length) as 'bytes' FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s AND TABLE_NAME IN ('$table_names') GROUP BY TABLE_NAME;",
     3036                                DB_NAME
     3037                        ),
     3038                        OBJECT_K
     3039                );
     3040
     3041                $threshold_map = array(
     3042                        'comments_count' => $wpdb->comments,
     3043                        'options_count'  => $wpdb->options,
     3044                        'posts_count'    => $wpdb->posts,
     3045                        'terms_count'    => $wpdb->terms,
     3046                        'users_count'    => $wpdb->users,
     3047                );
     3048
     3049                foreach ( $threshold_map as $threshold => $table ) {
     3050                        if ( $thresholds[ $threshold ] <= $results[ $table ]->rows ) {
     3051                                return true;
     3052                        }
     3053                }
     3054
     3055                return false;
     3056        }
     3057
     3058        /**
     3059         * Returns a list of available persistent object cache services.
     3060         *
     3061         * @since 6.1.0
     3062         *
     3063         * @return array The list of available persistent object cache services.
     3064         */
     3065        private function available_object_cache_services() {
     3066                $extensions = array_map(
     3067                        'extension_loaded',
     3068                        array(
     3069                                'APCu'      => 'apcu',
     3070                                'Redis'     => 'redis',
     3071                                'Relay'     => 'relay',
     3072                                'Memcache'  => 'memcache',
     3073                                'Memcached' => 'memcached',
     3074                        )
     3075                );
     3076
     3077                $services = array_keys( array_filter( $extensions ) );
     3078
     3079                /**
     3080                 * Filters the persistent object cache services available to the user.
     3081                 *
     3082                 * This can be useful to hide or add services not included in the defaults.
     3083                 *
     3084                 * @since 6.1.0
     3085                 *
     3086                 * @param array $services The list of available persistent object cache services.
     3087                 */
     3088                return apply_filters( 'site_status_available_object_cache_services', $services );
     3089        }
     3090
    28613091}
  • trunk/tests/phpunit/tests/site-health.php

    r52010 r53955  
    108108                );
    109109        }
     110
     111        /**
     112         * @group ms-excluded
     113         * @ticket 56040
     114         */
     115        public function test_object_cache_default_thresholds() {
     116                $wp_site_health = new WP_Site_Health();
     117
     118                $this->assertFalse(
     119                        $wp_site_health->should_suggest_persistent_object_cache()
     120                );
     121        }
     122
     123
     124        /**
     125         * @group ms-required
     126         * @ticket 56040
     127         */
     128        public function test_object_cache_default_thresholds_on_multisite() {
     129                $wp_site_health = new WP_Site_Health();
     130                $this->assertTrue(
     131                        $wp_site_health->should_suggest_persistent_object_cache()
     132                );
     133        }
     134
     135        /**
     136         * @ticket 56040
     137         */
     138        public function test_object_cache_thresholds_check_can_be_bypassed() {
     139                $wp_site_health = new WP_Site_Health();
     140                add_filter( 'site_status_should_suggest_persistent_object_cache', '__return_true' );
     141
     142                $this->assertTrue(
     143                        $wp_site_health->should_suggest_persistent_object_cache()
     144                );
     145        }
     146
     147        /**
     148         * @dataProvider thresholds
     149         * @ticket 56040
     150         */
     151        public function test_object_cache_thresholds( $threshold, $count ) {
     152                $wp_site_health = new WP_Site_Health();
     153                add_filter(
     154                        'site_status_persistent_object_cache_thresholds',
     155                        function ( $thresholds ) use ( $threshold, $count ) {
     156                                return array_merge( $thresholds, array( $threshold => $count ) );
     157                        }
     158                );
     159
     160                $this->assertTrue(
     161                        $wp_site_health->should_suggest_persistent_object_cache()
     162                );
     163        }
     164
     165        /**
     166         * Data provider.
     167         *
     168         * @ticket 56040
     169         */
     170        public function thresholds() {
     171                return array(
     172                        array( 'comments_count', 0 ),
     173                        array( 'posts_count', 0 ),
     174                        array( 'terms_count', 1 ),
     175                        array( 'options_count', 100 ),
     176                        array( 'users_count', 0 ),
     177                        array( 'alloptions_count', 100 ),
     178                        array( 'alloptions_bytes', 1000 ),
     179                );
     180        }
    110181}
Note: See TracChangeset for help on using the changeset viewer.