From 278ef2f53b7f890f6b7fe0100259928f0b47dac4 Mon Sep 17 00:00:00 2001 From: Jonny Harrus Date: Thu, 4 Jun 2026 14:04:32 +0200 Subject: [PATCH] Update cache invalidation for meta and query groups across objects --- src/wp-includes/class-wp-comment-query.php | 7 ++++-- src/wp-includes/class-wp-query.php | 14 +++++++---- src/wp-includes/class-wp-site-query.php | 5 +++- src/wp-includes/class-wp-term-query.php | 7 ++++-- src/wp-includes/class-wp-user-query.php | 27 ++++++++++++++++++---- src/wp-includes/comment.php | 6 +++++ src/wp-includes/general-template.php | 6 ++++- src/wp-includes/link-template.php | 7 ++++-- src/wp-includes/ms-site.php | 6 +++++ src/wp-includes/post.php | 8 ++++++- src/wp-includes/query.php | 4 ++-- src/wp-includes/taxonomy.php | 8 ++++++- src/wp-includes/user.php | 21 ++++++++++++++--- 13 files changed, 101 insertions(+), 25 deletions(-) diff --git a/src/wp-includes/class-wp-comment-query.php b/src/wp-includes/class-wp-comment-query.php index cfabfd7e6b964..05cd8d4afdc35 100644 --- a/src/wp-includes/class-wp-comment-query.php +++ b/src/wp-includes/class-wp-comment-query.php @@ -449,7 +449,10 @@ public function get_comments() { unset( $_args['fields'], $_args['update_comment_meta_cache'], $_args['update_comment_post_cache'] ); $key = md5( serialize( $_args ) ); - $last_changed = wp_cache_get_last_changed( 'comment' ); + $last_changed = (array) wp_cache_get_last_changed( 'comment-queries' ); + if ( ! empty( $this->meta_query->queries ) || str_contains( $this->request, $wpdb->commentmeta ) ) { + $last_changed[] = wp_cache_get_last_changed( 'comment-meta' ); + } $cache_key = "get_comments:$key"; $cache_value = wp_cache_get_salted( $cache_key, 'comment-queries', $last_changed ); @@ -1041,7 +1044,7 @@ protected function fill_descendants( $comments ) { ); $key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) ); - $last_changed = wp_cache_get_last_changed( 'comment' ); + $last_changed = wp_cache_get_last_changed( 'comment-queries' ); // Fetch an entire level of the descendant tree at a time. $level = 0; diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index c9bf901ae1576..d235af890f984 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -2879,8 +2879,8 @@ public function get_posts() { $key = md5( $comments_request ); $last_changed = array( - wp_cache_get_last_changed( 'comment' ), - wp_cache_get_last_changed( 'posts' ), + wp_cache_get_last_changed( 'comment-queries' ), + wp_cache_get_last_changed( 'post-queries' ), ); $cache_key = "comment_feed:$key"; @@ -3245,9 +3245,13 @@ public function get_posts() { $id_query_is_cacheable = false; } - $last_changed = (array) wp_cache_get_last_changed( 'posts' ); + $last_changed = (array) wp_cache_get_last_changed( 'post-queries' ); if ( ! empty( $this->tax_query->queries ) ) { - $last_changed[] = wp_cache_get_last_changed( 'terms' ); + $last_changed[] = wp_cache_get_last_changed( 'term-queries' ); + } + + if ( ! empty( $this->meta_query->queries ) || str_contains( $this->request, $wpdb->postmeta ) ) { + $last_changed[] = wp_cache_get_last_changed( 'post-meta' ); } if ( $query_vars['cache_results'] && $id_query_is_cacheable ) { @@ -3489,7 +3493,7 @@ public function get_posts() { $comments_request = "SELECT {$wpdb->comments}.comment_ID FROM {$wpdb->comments} $cjoin $cwhere $cgroupby $corderby $climits"; $comment_key = md5( $comments_request ); - $comment_last_changed = wp_cache_get_last_changed( 'comment' ); + $comment_last_changed = wp_cache_get_last_changed( 'comment-queries' ); $comment_cache_key = "comment_feed:$comment_key"; $comment_ids = wp_cache_get_salted( $comment_cache_key, 'comment-queries', $comment_last_changed ); diff --git a/src/wp-includes/class-wp-site-query.php b/src/wp-includes/class-wp-site-query.php index 52ae228d90af0..35c81302d2a78 100644 --- a/src/wp-includes/class-wp-site-query.php +++ b/src/wp-includes/class-wp-site-query.php @@ -355,7 +355,10 @@ public function get_sites() { unset( $_args['fields'], $_args['update_site_cache'], $_args['update_site_meta_cache'] ); $key = md5( serialize( $_args ) ); - $last_changed = wp_cache_get_last_changed( 'sites' ); + $last_changed = (array) wp_cache_get_last_changed( 'site-queries' ); + if ( ! empty( $this->meta_query->queries ) || str_contains( $this->request, $wpdb->sitemeta ) ) { + $last_changed[] = wp_cache_get_last_changed( 'blog-meta' ); + } $cache_key = "get_sites:$key"; $cache_value = wp_cache_get_salted( $cache_key, 'site-queries', $last_changed ); diff --git a/src/wp-includes/class-wp-term-query.php b/src/wp-includes/class-wp-term-query.php index a30d887aa56d1..d9f5897f06fc1 100644 --- a/src/wp-includes/class-wp-term-query.php +++ b/src/wp-includes/class-wp-term-query.php @@ -775,8 +775,11 @@ public function get_terms() { if ( $args['cache_results'] ) { $cache_key = $this->generate_cache_key( $args, $this->request ); - $last_changed = wp_cache_get_last_changed( 'terms' ); - $cache = wp_cache_get_salted( $cache_key, 'term-queries', $last_changed ); + $last_changed = (array) wp_cache_get_last_changed( 'term-queries' ); + if ( ! empty( $this->meta_query->queries ) || str_contains( $this->request, $wpdb->termmeta ) ) { + $last_changed[] = wp_cache_get_last_changed( 'term-meta' ); + } + $cache = wp_cache_get_salted( $cache_key, 'term-queries', $last_changed ); if ( false !== $cache ) { if ( 'ids' === $_fields ) { diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index 3815023924489..b3de6ace88acb 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -830,7 +830,7 @@ public function query() { $cache_value = false; $cache_key = $this->generate_cache_key( $qv, $this->request ); $cache_group = 'user-queries'; - $last_changed = $this->get_cache_last_changed( $qv ); + $last_changed = $this->get_cache_last_changed( $qv, $this->request ); if ( $qv['cache_results'] ) { $cache_value = wp_cache_get_salted( $cache_key, $cache_group, $last_changed ); @@ -1064,12 +1064,19 @@ protected function generate_cache_key( array $deprecated, $sql ) { * Retrieves the last changed cache timestamp for users and optionally posts. * * @since 6.9.0 + * @since x.x.x Add SQL parameter to get last changed. * * @param array $args Query arguments. + * @param string $sql SQL statement. * @return string[] The last changed timestamp string for the relevant cache groups. */ - protected function get_cache_last_changed( array $args ) { - $last_changed = (array) wp_cache_get_last_changed( 'users' ); + protected function get_cache_last_changed( array $args, string $sql ) { + global $wpdb; + $last_changed = (array) wp_cache_get_last_changed( 'user-queries' ); + + if ( str_contains( $sql, $wpdb->usermeta ) ) { + $last_changed[] = wp_cache_get_last_changed( 'user-meta' ); + } if ( empty( $args['orderby'] ) ) { // Default order is by 'user_login'. @@ -1086,19 +1093,29 @@ protected function get_cache_last_changed( array $args ) { $blog_id = absint( $args['blog_id'] ); } + $switch = $blog_id && get_current_blog_id() !== $blog_id; if ( $args['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { - $switch = $blog_id && get_current_blog_id() !== $blog_id; if ( $switch ) { switch_to_blog( $blog_id ); } - $last_changed[] = wp_cache_get_last_changed( 'posts' ); + $last_changed[] = wp_cache_get_last_changed( 'post-queries' ); if ( $switch ) { restore_current_blog(); } } + if ( str_contains( $sql, $wpdb->postmeta ) ) { + if ( $switch ) { + switch_to_blog( $blog_id ); + } + $last_changed[] = wp_cache_get_last_changed( 'post-meta' ); + if ( $switch ) { + restore_current_blog(); + } + } + return $last_changed; } diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 70d5c03b378f4..0c3842c9a017e 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -4147,6 +4147,12 @@ function wp_comments_personal_data_eraser( $email_address, $page = 1 ) { */ function wp_cache_set_comments_last_changed() { wp_cache_set_last_changed( 'comment' ); + $current_action = current_action(); + if ( in_array( $current_action, array( 'added_comment_meta', 'updated_comment_meta', 'deleted_comment_meta' ), true ) ) { + wp_cache_set_last_changed( 'comment-meta' ); + } else { + wp_cache_set_last_changed( 'comment-queries' ); + } } /** diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 01aefa370b5f1..27055fa15e942 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -2079,7 +2079,11 @@ function wp_get_archives( $args = '' ) { $output = ''; - $last_changed = wp_cache_get_last_changed( 'posts' ); + $last_changed = (array) wp_cache_get_last_changed( 'post-queries' ); + + if ( str_contains( $join, $wpdb->postmeta ) ) { + $last_changed[] = wp_cache_get_last_changed( 'post-meta' ); + } $limit = $parsed_args['limit']; diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php index 54b78a028d745..1215453ca2440 100644 --- a/src/wp-includes/link-template.php +++ b/src/wp-includes/link-template.php @@ -2013,9 +2013,12 @@ function get_adjacent_post( $in_same_term = false, $excluded_terms = '', $previo $query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort"; $key = md5( $query ); - $last_changed = (array) wp_cache_get_last_changed( 'posts' ); + $last_changed = (array) wp_cache_get_last_changed( 'post-queries' ); if ( $in_same_term || ! empty( $excluded_terms ) ) { - $last_changed[] = wp_cache_get_last_changed( 'terms' ); + $last_changed[] = wp_cache_get_last_changed( 'term-queries' ); + } + if ( str_contains( $query, $wpdb->postmeta ) ) { + $last_changed[] = wp_cache_get_last_changed( 'post-meta' ); } $cache_key = "adjacent_post:$key"; diff --git a/src/wp-includes/ms-site.php b/src/wp-includes/ms-site.php index 6399dab72e881..9af52096bccf3 100644 --- a/src/wp-includes/ms-site.php +++ b/src/wp-includes/ms-site.php @@ -1318,6 +1318,12 @@ function wp_update_blog_public_option_on_site_update( $site_id, $is_public ) { */ function wp_cache_set_sites_last_changed() { wp_cache_set_last_changed( 'sites' ); + $current_action = current_action(); + if ( in_array( $current_action, array( 'added_blog_meta', 'updated_blog_meta', 'deleted_blog_meta' ), true ) ) { + wp_cache_set_last_changed( 'blog-meta' ); + } else { + wp_cache_set_last_changed( 'site-queries' ); + } } /** diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 005ccadd62e34..08ecaef9f6241 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6154,7 +6154,7 @@ function get_page( $page, $output = OBJECT, $filter = 'raw' ) { function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { global $wpdb; - $last_changed = wp_cache_get_last_changed( 'posts' ); + $last_changed = wp_cache_get_last_changed( 'post-queries' ); $hash = md5( $page_path . serialize( $post_type ) ); $cache_key = "get_page_by_path:$hash"; @@ -8412,6 +8412,12 @@ function wp_add_trashed_suffix_to_post_name_for_post( $post ) { */ function wp_cache_set_posts_last_changed() { wp_cache_set_last_changed( 'posts' ); + $current_action = current_action(); + if ( in_array( $current_action, array( 'added_post_meta', 'updated_post_meta', 'deleted_post_meta' ), true ) ) { + wp_cache_set_last_changed( 'blog-meta' ); + } else { + wp_cache_set_last_changed( 'blog-queries' ); + } } /** diff --git a/src/wp-includes/query.php b/src/wp-includes/query.php index 592e70e0290a3..673cc0d0bd2c4 100644 --- a/src/wp-includes/query.php +++ b/src/wp-includes/query.php @@ -1154,7 +1154,7 @@ function _find_post_by_old_slug( $post_type ) { } $key = md5( $query ); - $last_changed = wp_cache_get_last_changed( 'posts' ); + $last_changed = array( wp_cache_get_last_changed( 'post-queries' ), wp_cache_get_last_changed( 'post-meta' ) ); $cache_key = "find_post_by_old_slug:$key"; $cache = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false !== $cache ) { @@ -1197,7 +1197,7 @@ function _find_post_by_old_date( $post_type ) { if ( $date_query ) { $query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta AS pm_date, $wpdb->posts WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_date' AND post_name = %s" . $date_query, $post_type, get_query_var( 'name' ) ); $key = md5( $query ); - $last_changed = wp_cache_get_last_changed( 'posts' ); + $last_changed = array( wp_cache_get_last_changed( 'post-queries' ), wp_cache_get_last_changed( 'post-meta' ) ); $cache_key = "find_post_by_old_date:$key"; $cache = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false !== $cache ) { diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 80f457de0e6f7..6f6b9583556d5 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -895,7 +895,7 @@ function get_objects_in_term( $term_ids, $taxonomies, $args = array() ) { $sql = "SELECT tr.object_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tt.term_id IN ($term_ids) ORDER BY tr.object_id $order"; - $last_changed = wp_cache_get_last_changed( 'terms' ); + $last_changed = wp_cache_get_last_changed( 'term-queries' ); $cache_key = 'get_objects_in_term:' . md5( $sql ); $cache = wp_cache_get_salted( $cache_key, 'term-queries', $last_changed ); if ( false === $cache ) { @@ -5140,6 +5140,12 @@ function is_term_publicly_viewable( $term ) { */ function wp_cache_set_terms_last_changed() { wp_cache_set_last_changed( 'terms' ); + $current_action = current_action(); + if ( in_array( $current_action, array( 'added_term_meta', 'updated_term_meta', 'deleted_term_meta' ), true ) ) { + wp_cache_set_last_changed( 'term-meta' ); + } else { + wp_cache_set_last_changed( 'term-queries' ); + } } /** diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 7c856f0963634..d97875cae0fc7 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -622,7 +622,7 @@ function count_user_posts( $userid, $post_type = 'post', $public_only = false ) $where = get_posts_by_author_sql( $post_type, true, $userid, $public_only ); $query = "SELECT COUNT(*) FROM $wpdb->posts $where"; - $last_changed = wp_cache_get_last_changed( 'posts' ); + $last_changed = wp_cache_get_last_changed( 'post-queries' ); $cache_key = 'count_user_posts:' . md5( $query ); $count = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed ); if ( false === $count ) { @@ -695,8 +695,17 @@ function count_many_users_posts( $users, $post_type = 'post', $public_only = fal $where = get_posts_by_author_sql( $post_type, true, null, $public_only ); $query = "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author"; $cache_key = 'count_many_users_posts:' . md5( $query ); - $cache_salts = array( wp_cache_get_last_changed( 'posts' ), wp_cache_get_last_changed( 'users' ) ); - $count = wp_cache_get_salted( $cache_key, 'post-queries', $cache_salts ); + $cache_salts = array( wp_cache_get_last_changed( 'post-queries' ), wp_cache_get_last_changed( 'user-queries' ) ); + + if ( str_contains( $query, $wpdb->postmeta ) ) { + $cache_salts[] = wp_cache_get_last_changed( 'post-meta' ); + } + + if ( str_contains( $query, $wpdb->usermeta ) ) { + $cache_salts[] = wp_cache_get_last_changed( 'user-meta' ); + } + + $count = wp_cache_get_salted( $cache_key, 'post-queries', $cache_salts ); if ( false === $count ) { $result = $wpdb->get_results( $query, ARRAY_N ); @@ -5253,6 +5262,12 @@ function wp_register_persisted_preferences_meta() { */ function wp_cache_set_users_last_changed() { wp_cache_set_last_changed( 'users' ); + $current_action = current_action(); + if ( in_array( $current_action, array( 'added_user_meta', 'updated_user_meta', 'deleted_user_meta' ), true ) ) { + wp_cache_set_last_changed( 'user-meta' ); + } else { + wp_cache_set_last_changed( 'user-queries' ); + } } /**