From 35a678b0e60837c85a16005ec27b1bc867f5dd53 Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Tue, 7 Feb 2023 12:07:46 +0000 Subject: [PATCH] Taxonomy: Implement `wp_cache_get_multiple` in `wp_queue_posts_for_term_meta_lazyload`. In [47938] the `wp_cache_get_multiple` function was added to core. This function allows for multiple cache keys to be received from cache in a single function call. `wp_queue_posts_for_term_meta_lazyload` function does many calls to cache. To get taxonomy relationship for multiple posts and get all terms. Replace calls to `get_object_term_cache` with calls to `wp_cache_get_multiple` and `_prime_term_caches`. This improves performance on sites that implement the `wp_cache_get_multiple` in their object caching drop-in. Props spacedmonkey, ocean90, SergeyBiryukov, costdev, flixos90, joemcgill, 10upsimon. Fixes #57150. git-svn-id: https://develop.svn.wordpress.org/trunk@55252 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 2 + src/wp-includes/post.php | 50 ++++++- tests/phpunit/includes/abstract-testcase.php | 4 + .../phpunit/tests/query/lazyLoadTermMeta.php | 138 ++++++++++++++++++ 4 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 tests/phpunit/tests/query/lazyLoadTermMeta.php diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 2e1f2f8b57..e33fa7904f 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -1948,6 +1948,8 @@ class WP_Query { if ( ! isset( $q['lazy_load_term_meta'] ) ) { $q['lazy_load_term_meta'] = $q['update_post_term_cache']; + } elseif ( $q['lazy_load_term_meta'] ) { // Lazy loading term meta only works if term caches are primed. + $q['update_post_term_cache'] = true; } if ( ! isset( $q['update_post_meta_cache'] ) ) { diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index d9460d7838..f4423dcd66 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -7779,7 +7779,7 @@ function wp_delete_auto_drafts() { */ function wp_queue_posts_for_term_meta_lazyload( $posts ) { $post_type_taxonomies = array(); - $term_ids = array(); + $prime_post_terms = array(); foreach ( $posts as $post ) { if ( ! ( $post instanceof WP_Post ) ) { continue; @@ -7790,16 +7790,52 @@ function wp_queue_posts_for_term_meta_lazyload( $posts ) { } foreach ( $post_type_taxonomies[ $post->post_type ] as $taxonomy ) { - // Term cache should already be primed by `update_post_term_cache()`. - $terms = get_object_term_cache( $post->ID, $taxonomy ); - if ( false !== $terms ) { - foreach ( $terms as $term ) { - if ( ! in_array( $term->term_id, $term_ids, true ) ) { - $term_ids[] = $term->term_id; + $prime_post_terms[ $taxonomy ][] = $post->ID; + } + } + + $term_ids = array(); + if ( $prime_post_terms ) { + $prime_term_ids = array(); + $prime_taxonomy_ids = array(); + foreach ( $prime_post_terms as $taxonomy => $post_ids ) { + $cached_term_ids = wp_cache_get_multiple( $post_ids, "{$taxonomy}_relationships" ); + if ( is_array( $cached_term_ids ) ) { + $cached_term_ids = array_filter( $cached_term_ids ); + foreach ( $cached_term_ids as $term_ids ) { + // Backward compatibility for if a plugin is putting objects into the cache, rather than IDs. + foreach ( $term_ids as $term_id ) { + if ( is_numeric( $term_id ) ) { + $prime_term_ids[] = (int) $term_id; + $prime_taxonomy_ids[ $taxonomy ][] = (int) $term_id; + } elseif ( isset( $term_id->term_id ) ) { + $prime_taxonomy_ids[ $taxonomy ][] = (int) $term_id->term_id; + $prime_term_ids[] = (int) $term_id->term_id; + } } } } } + + if ( $prime_term_ids ) { + $prime_term_ids = array_unique( $prime_term_ids ); + // Do not prime term meta at this point, let the lazy loader take care of that. + _prime_term_caches( $prime_term_ids, false ); + + foreach ( $prime_taxonomy_ids as $taxonomy => $_term_ids ) { + foreach ( $_term_ids as $term_id ) { + if ( in_array( $term_id, $term_ids, true ) ) { + continue; + } + $term = get_term( $term_id, $taxonomy ); + if ( is_wp_error( $term ) ) { + continue; + } + + $term_ids[] = $term_id; + } + } + } } if ( $term_ids ) { diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index be96f06e31..9c189bd96d 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -194,6 +194,10 @@ abstract class WP_UnitTestCase_Base extends PHPUnit_Adapter_TestCase { remove_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) ); $this->_restore_hooks(); wp_set_current_user( 0 ); + + $lazyloader = wp_metadata_lazyloader(); + $lazyloader->reset_queue( 'term' ); + $lazyloader->reset_queue( 'comment' ); } /** diff --git a/tests/phpunit/tests/query/lazyLoadTermMeta.php b/tests/phpunit/tests/query/lazyLoadTermMeta.php new file mode 100644 index 0000000000..0677a2d0bd --- /dev/null +++ b/tests/phpunit/tests/query/lazyLoadTermMeta.php @@ -0,0 +1,138 @@ +post->create_many( + 3, + array( + 'post_type' => $post_type, + 'post_status' => 'publish', + ) + ); + $taxonomies = get_object_taxonomies( $post_type, 'object' ); + foreach ( self::$post_ids as $post_id ) { + foreach ( $taxonomies as $taxonomy ) { + if ( ! $taxonomy->_builtin ) { + continue; + } + $terms = $factory->term->create_many( 3, array( 'taxonomy' => $taxonomy->name ) ); + self::$term_ids = array_merge( self::$term_ids, $terms ); + foreach ( $terms as $term ) { + add_term_meta( $term, wp_rand(), 'test' ); + } + wp_set_object_terms( $post_id, $terms, $taxonomy->name ); + } + } + } + + /** + * @ticket 57150 + * @covers ::wp_queue_posts_for_term_meta_lazyload + */ + public function test_wp_queue_posts_for_term_meta_lazyload() { + $filter = new MockAction(); + add_filter( 'update_term_metadata_cache', array( $filter, 'filter' ), 10, 2 ); + new WP_Query( + array( + 'post__in' => self::$post_ids, + 'lazy_load_term_meta' => true, + ) + ); + + get_term_meta( end( self::$term_ids ) ); + + $args = $filter->get_args(); + $first = reset( $args ); + $term_ids = end( $first ); + $this->assertSameSets( $term_ids, self::$term_ids ); + } + + /** + * @ticket 57150 + * @covers ::wp_queue_posts_for_term_meta_lazyload + */ + public function test_wp_queue_posts_for_term_meta_lazyload_update_post_term_cache() { + $filter = new MockAction(); + add_filter( 'update_term_metadata_cache', array( $filter, 'filter' ), 10, 2 ); + new WP_Query( + array( + 'post__in' => self::$post_ids, + 'lazy_load_term_meta' => true, + 'update_post_term_cache' => false, + ) + ); + + get_term_meta( end( self::$term_ids ) ); + + $args = $filter->get_args(); + $first = reset( $args ); + $term_ids = end( $first ); + $this->assertSameSets( $term_ids, self::$term_ids ); + } + + /** + * @ticket 57150 + * @covers ::wp_queue_posts_for_term_meta_lazyload + */ + public function test_wp_queue_posts_for_term_meta_lazyload_false() { + $filter = new MockAction(); + add_filter( 'update_term_metadata_cache', array( $filter, 'filter' ), 10, 2 ); + new WP_Query( + array( + 'post__in' => self::$post_ids, + 'lazy_load_term_meta' => false, + ) + ); + + $term_id = end( self::$term_ids ); + get_term_meta( $term_id ); + + $args = $filter->get_args(); + $first = reset( $args ); + $term_ids = end( $first ); + $this->assertSameSets( $term_ids, array( $term_id ) ); + } + + /** + * @ticket 57150 + * @covers ::wp_queue_posts_for_term_meta_lazyload + */ + public function test_wp_queue_posts_for_term_meta_lazyload_delete_term() { + $filter = new MockAction(); + add_filter( 'update_term_metadata_cache', array( $filter, 'filter' ), 10, 2 ); + + $remove_term_id = end( self::$term_ids ); + $term = get_term( $remove_term_id ); + wp_delete_term( $remove_term_id, $term->taxonomy ); + + new WP_Query( + array( + 'post__in' => self::$post_ids, + 'lazy_load_term_meta' => true, + ) + ); + + $term_id = end( self::$term_ids ); + get_term_meta( $term_id ); + + $args = $filter->get_args(); + $first = reset( $args ); + $term_ids = end( $first ); + $this->assertNotContains( $remove_term_id, $term_ids ); + } +}