Make WordPress Core


Ignore:
Timestamp:
02/17/2016 10:57:33 PM (9 years ago)
Author:
boonebgorges
Message:

More performance improvements to metadata lazyloading.

Comment and term meta lazyloading for WP_Query loops, introduced in 4.4,
depended on filter callback methods belonging to WP_Query objects. This meant
storing WP_Query objects in the $wp_filter global (via add_filter()),
requiring that PHP retain the objects in memory, even when the local variables
would typically be expunged during normal garbage collection. In cases where a
large number of WP_Query objects were instantiated on a single pageload,
and/or where the contents of the WP_Query objects were quite large, serious
performance issues could result.

We skirt this problem by moving metadata lazyloading out of WP_Query. The
new WP_Metadata_Lazyloader class acts as a lazyload queue. Query instances
register items whose metadata should be lazyloaded - such as post terms, or
comments - and a WP_Metadata_Lazyloader method will intercept comment and
term meta requests to perform the cache priming. Since WP_Metadata_Lazyloader
instances are far smaller than WP_Query (containing only object IDs), and
clean up after themselves far better than the previous WP_Query methods (bp
only running their callbacks a single time for a given set of queued objects),
the resource use is decreased dramatically.

See [36525] for an earlier step in this direction.

Props lpawlik, stevegrunwell, boonebgorges.
Fixes #35816.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/query.php

    r36524 r36566  
    36063606            $this->posts = array_map( 'get_post', $this->posts );
    36073607
    3608 
    3609         if ( $q['update_post_term_cache'] ) {
    3610             add_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
    3611         }
    3612 
    36133608        if ( ! $q['suppress_filters'] ) {
    36143609            /**
     
    37393734        // If comments have been fetched as part of the query, make sure comment meta lazy-loading is set up.
    37403735        if ( ! empty( $this->comments ) ) {
    3741             add_filter( 'get_comment_metadata', array( $this, 'lazyload_comment_meta' ), 10, 2 );
     3736            wp_queue_comments_for_comment_meta_lazyload( $this->comments );
    37423737        }
    37433738
     
    37693764            $this->post_count = 0;
    37703765            $this->posts = array();
     3766        }
     3767
     3768        if ( $q['update_post_term_cache'] ) {
     3769            wp_queue_posts_for_term_meta_lazyload( $this->posts );
    37713770        }
    37723771
     
    48354834
    48364835    /**
    4837      * Lazy-loads termmeta for located posts.
    4838      *
    4839      * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
    4840      * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
    4841      * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
    4842      * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
    4843      * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
    4844      * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
    4845      * with a single database query.
    4846      *
    4847      * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
    4848      * directly, from either inside or outside the `WP_Query` object.
     4836     * Lazyload term meta for posts in the loop.
    48494837     *
    48504838     * @since 4.4.0
    4851      * @access public
    4852      *
    4853      * @param mixed $check  The `$check` param passed from the 'get_term_metadata' hook.
    4854      * @param int  $term_id ID of the term whose metadata is being cached.
    4855      * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
    4856      *               another value if filtered by a plugin.
     4839     * @deprecated 4.5.0 See wp_queue_posts_for_term_meta_lazyload().
     4840     *
     4841     * @param mixed $check
     4842     * @param int   $term_id
     4843     * @return mixed
    48574844     */
    48584845    public function lazyload_term_meta( $check, $term_id ) {
    4859         // We can only lazyload if the entire post object is present.
    4860         $posts = array();
    4861         foreach ( $this->posts as $post ) {
    4862             if ( $post instanceof WP_Post ) {
    4863                 $posts[] = $post;
    4864             }
    4865         }
    4866 
    4867         if ( ! empty( $posts ) ) {
    4868             // Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
    4869             $term_ids = array();
    4870             foreach ( $posts as $post ) {
    4871                 $taxonomies = get_object_taxonomies( $post->post_type );
    4872                 foreach ( $taxonomies as $taxonomy ) {
    4873                     // Term cache should already be primed by 'update_post_term_cache'.
    4874                     $terms = get_object_term_cache( $post->ID, $taxonomy );
    4875                     if ( false !== $terms ) {
    4876                         foreach ( $terms as $term ) {
    4877                             if ( ! isset( $term_ids[ $term->term_id ] ) ) {
    4878                                 $term_ids[ $term->term_id ] = 1;
    4879                             }
    4880                         }
    4881                     }
    4882                 }
    4883             }
    4884 
    4885             /*
    4886              * Only update the metadata cache for terms belonging to these posts if the term_id passed
    4887              * to `get_term_meta()` matches one of those terms. This prevents a single call to
    4888              * `get_term_meta()` from priming metadata for all `WP_Query` objects.
    4889              */
    4890             if ( isset( $term_ids[ $term_id ] ) ) {
    4891                 update_termmeta_cache( array_keys( $term_ids ) );
    4892                 remove_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
    4893             }
    4894         }
    4895 
    4896         // If no terms were found, there's no need to run this again.
    4897         if ( empty( $term_ids ) ) {
    4898             remove_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
    4899         }
    4900 
     4846        _deprecated_function( __METHOD__, '4.5.0' );
    49014847        return $check;
    49024848    }
    49034849
    49044850    /**
    4905      * Lazy-load comment meta when inside of a `WP_Query` loop.
    4906      *
    4907      * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
    4908      * directly, from either inside or outside the `WP_Query` object.
     4851     * Lazyload comment meta for comments in the loop.
    49094852     *
    49104853     * @since 4.4.0
    4911      *
    4912      * @param mixed $check     The `$check` param passed from the 'get_comment_metadata' hook.
    4913      * @param int  $comment_id ID of the comment whose metadata is being cached.
    4914      * @return mixed The original value of `$check`, to not affect 'get_comment_metadata'.
     4854     * @deprecated 4.5.0 See wp_queue_comments_for_comment_meta_lazyload().
     4855     *
     4856     * @param mixed $check
     4857     * @param int   $comment_id
     4858     * @return mixed
    49154859     */
    49164860    public function lazyload_comment_meta( $check, $comment_id ) {
    4917         // Don't use `wp_list_pluck()` to avoid by-reference manipulation.
    4918         $comment_ids = array();
    4919         if ( is_array( $this->comments ) ) {
    4920             foreach ( $this->comments as $comment ) {
    4921                 $comment_ids[] = $comment->comment_ID;
    4922             }
    4923         }
    4924 
    4925         /*
    4926          * Only update the metadata cache for comments belonging to these posts if the comment_id passed
    4927          * to `get_comment_meta()` matches one of those comments. This prevents a single call to
    4928          * `get_comment_meta()` from priming metadata for all `WP_Query` objects.
    4929          */
    4930         if ( in_array( $comment_id, $comment_ids ) ) {
    4931             update_meta_cache( 'comment', $comment_ids );
    4932             remove_filter( 'get_comment_metadata', array( $this, 'lazyload_comment_meta' ), 10, 2 );
    4933         } elseif ( empty( $comment_ids ) ) {
    4934             remove_filter( 'get_comment_metadata', array( $this, 'lazyload_comment_meta' ), 10, 2 );
    4935         }
    4936 
     4861        _deprecated_function( __METHOD__, '4.5.0' );
    49374862        return $check;
    49384863    }
Note: See TracChangeset for help on using the changeset viewer.