Make WordPress Core


Ignore:
Timestamp:
05/08/2024 10:49:46 PM (11 months ago)
Author:
spacedmonkey
Message:

Query: Improve cache key generation.

Query caching in WP_Query was added in [53941]. When this functionality was added to the WP_Query class, a number of edge cases were missed that would result in redundant duplicate queries. It was possible to pass parameters to WP_Query that would be different but would result in the same database query being generated. As the cache key is generated from a mixture of query arguments and the SQL query, this resulted in different cache keys for the same database query, resulting in unnecessary duplicate queries. In this change, the logic in the generate_cache_key method has been improved to ensure reuse of existing caches. The following edge cases have been considered:

  • Passing post_type as an empty string.
  • Passing post_type as the string any.
  • Passing post_type as array vs. string.
  • Passing post_type as an array in a different order.
  • Not passing orderby.
  • Passing post_status as an array vs. string.
  • Passing post_status as an array in a different order.

This change also fixes an issue where the old SQL query would not match, as the queries had different whitespaces.

Props spacedmonkey, joemcgill, pbearne, peterwilsoncc, rajinsharwar, mukesh27, thekt12, huzaifaalmesbah, rodionov201.
Fixes #59442.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/query/cacheResults.php

    r56925 r58122  
    170170
    171171        $this->assertSame( $cache_key_1, $cache_key_2, 'Cache key differs when using wpdb placeholder.' );
     172    }
     173
     174    /**
     175     * @covers WP_Query::generate_cache_key
     176     * @ticket 59442
     177     */
     178    public function test_generate_cache_key_unregister_post_type() {
     179        global $wpdb;
     180        register_post_type(
     181            'wptests_pt',
     182            array(
     183                'exclude_from_search' => false,
     184            )
     185        );
     186        $query_vars = array(
     187            'post_type' => 'any',
     188        );
     189        $fields     = "{$wpdb->posts}.ID";
     190        $query1     = new WP_Query( $query_vars );
     191        $request1   = str_replace( $fields, "{$wpdb->posts}.*", $query1->request );
     192
     193        $reflection = new ReflectionMethod( $query1, 'generate_cache_key' );
     194        $reflection->setAccessible( true );
     195
     196        $cache_key_1 = $reflection->invoke( $query1, $query_vars, $request1 );
     197        unregister_post_type( 'wptests_pt' );
     198        $cache_key_2 = $reflection->invoke( $query1, $query_vars, $request1 );
     199
     200        $this->assertNotSame( $cache_key_1, $cache_key_2, 'Cache key should differ after unregistering post type.' );
     201    }
     202
     203    /**
     204     * @ticket 59442
     205     *
     206     * @covers WP_Query::generate_cache_key
     207     *
     208     * @dataProvider data_query_cache_duplicate
     209     */
     210    public function test_generate_cache_key_normalize( $query_vars1, $query_vars2 ) {
     211        global $wpdb;
     212
     213        $fields   = "{$wpdb->posts}.ID";
     214        $query1   = new WP_Query( $query_vars1 );
     215        $request1 = str_replace( $fields, "{$wpdb->posts}.*", $query1->request );
     216
     217        $query2   = new WP_Query( $query_vars2 );
     218        $request2 = str_replace( $fields, "{$wpdb->posts}.*", $query2->request );
     219
     220        $reflection = new ReflectionMethod( $query1, 'generate_cache_key' );
     221        $reflection->setAccessible( true );
     222
     223        $this->assertSame( $request1, $request2, 'Queries should match' );
     224
     225        $cache_key_1 = $reflection->invoke( $query1, $query_vars1, $request1 );
     226        $cache_key_2 = $reflection->invoke( $query1, $query_vars2, $request2 );
     227
     228        $this->assertSame( $cache_key_1, $cache_key_2, 'Cache key differs the same paramters.' );
    172229    }
    173230
     
    218275
    219276    /**
     277     * Data provider for test_generate_cache_key_normalize().
     278     *
     279     * @return array[]
     280     */
     281    public function data_query_cache_duplicate() {
     282        return array(
     283            'post type empty'           => array(
     284                'query_vars1' => array( 'post_type' => '' ),
     285                'query_vars2' => array( 'post_type' => 'post' ),
     286            ),
     287            'post type array'           => array(
     288                'query_vars1' => array( 'post_type' => array( 'page' ) ),
     289                'query_vars2' => array( 'post_type' => 'page' ),
     290            ),
     291            'orderby empty'             => array(
     292                'query_vars1' => array( 'orderby' => null ),
     293                'query_vars2' => array( 'orderby' => 'date' ),
     294            ),
     295            'different order parameter' => array(
     296                'query_vars1' => array(
     297                    'post_type'      => 'post',
     298                    'posts_per_page' => 15,
     299                ),
     300                'query_vars2' => array(
     301                    'posts_per_page' => 15,
     302                    'post_type'      => 'post',
     303                ),
     304            ),
     305            'same args'                 => array(
     306                'query_vars1' => array( 'post_type' => 'post' ),
     307                'query_vars2' => array( 'post_type' => 'post' ),
     308            ),
     309            'same args any'             => array(
     310                'query_vars1' => array( 'post_type' => 'any' ),
     311                'query_vars2' => array( 'post_type' => 'any' ),
     312            ),
     313            'any and post types'        => array(
     314                'query_vars1' => array( 'post_type' => 'any' ),
     315                'query_vars2' => array( 'post_type' => array( 'post', 'page', 'attachment' ) ),
     316            ),
     317            'different order post type' => array(
     318                'query_vars1' => array( 'post_type' => array( 'post', 'page' ) ),
     319                'query_vars2' => array( 'post_type' => array( 'page', 'post' ) ),
     320            ),
     321            'post status array'         => array(
     322                'query_vars1' => array( 'post_status' => 'publish' ),
     323                'query_vars2' => array( 'post_status' => array( 'publish' ) ),
     324            ),
     325            'post status order'         => array(
     326                'query_vars1' => array( 'post_status' => array( 'draft', 'publish' ) ),
     327                'query_vars2' => array( 'post_status' => array( 'publish', 'draft' ) ),
     328            ),
     329            'cache parameters'          => array(
     330                'query_vars1' => array(
     331                    'update_post_meta_cache' => true,
     332                    'update_post_term_cache' => true,
     333                    'update_menu_item_cache' => true,
     334                ),
     335                'query_vars2' => array(
     336                    'update_post_meta_cache' => false,
     337                    'update_post_term_cache' => false,
     338                    'update_menu_item_cache' => false,
     339                ),
     340            ),
     341        );
     342    }
     343
     344    /**
    220345     * Data provider.
    221346     *
     
    262387                    'cache_results' => true,
    263388                    'post_type'     => 'page',
     389                ),
     390            ),
     391            'cache true and empty post type'              => array(
     392                'args' => array(
     393                    'cache_results' => true,
     394                    'post_type'     => '',
     395                ),
     396            ),
     397            'cache true and orderby null'                 => array(
     398                'args' => array(
     399                    'cache_results' => true,
     400                    'orderby'       => null,
    264401                ),
    265402            ),
Note: See TracChangeset for help on using the changeset viewer.