Make WordPress Core

Changeset 53955


Ignore:
Timestamp:
08/29/2022 04:52:12 PM (22 months 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.