Skip to content

Commit 86bfd1e

Browse files
ellatrixclaude
andcommitted
Meta: Allow registering meta keys that skip query cache invalidation
Add `invalidates_query_cache` parameter to `register_meta()`. Meta keys registered with this set to `false` will not bump the `last_changed` timestamp for the parent object's cache group when added, updated, or deleted. This prevents high-frequency meta writes from invalidating query caches site-wide. Meta keys registered as non-query-cacheable are also blocked from use in `WP_Meta_Query` clauses, since skipping cache invalidation would lead to stale query results. Registers the real-time collaboration meta keys (`wp_sync_awareness` and `wp_sync_update`) with this flag to resolve the cache invalidation issue described in https://core.trac.wordpress.org/ticket/64696. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent 14fbb49 commit 86bfd1e

4 files changed

Lines changed: 113 additions & 14 deletions

File tree

src/wp-includes/class-wp-meta-query.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ class WP_Meta_Query {
9595
*/
9696
protected $has_or_relation = false;
9797

98+
/**
99+
* The type of object metadata is for (e.g. 'post', 'user', 'term').
100+
*
101+
* @since 7.0.0
102+
* @var string
103+
*/
104+
protected $object_type = '';
105+
98106
/**
99107
* Constructor.
100108
*
@@ -364,6 +372,7 @@ public function get_sql( $type, $primary_table, $primary_id_column, $context = n
364372

365373
$this->meta_table = $meta_table;
366374
$this->meta_id_column = sanitize_key( $type . '_id' );
375+
$this->object_type = $type;
367376

368377
$this->primary_table = $primary_table;
369378
$this->primary_id_column = $primary_id_column;
@@ -533,6 +542,28 @@ protected function get_sql_for_query( &$query, $depth = 0 ) {
533542
public function get_sql_for_clause( &$clause, $parent_query, $clause_key = '' ) {
534543
global $wpdb;
535544

545+
// Refuse to query by a meta key registered as non-query-cacheable.
546+
if ( $this->object_type
547+
&& isset( $clause['key'] )
548+
&& is_string( $clause['key'] )
549+
&& ! wp_meta_key_invalidates_query_cache( $this->object_type, $clause['key'] )
550+
) {
551+
_doing_it_wrong(
552+
__METHOD__,
553+
sprintf(
554+
/* translators: %s: The meta key. */
555+
__( 'The meta key "%s" is registered with invalidates_query_cache set to false and cannot be used in meta queries.' ),
556+
$clause['key']
557+
),
558+
'7.0.0'
559+
);
560+
561+
return array(
562+
'join' => array(),
563+
'where' => array(),
564+
);
565+
}
566+
536567
$sql_chunks = array(
537568
'where' => array(),
538569
'join' => array(),

src/wp-includes/default-filters.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@
122122
}
123123

124124
// Post meta.
125-
add_action( 'added_post_meta', 'wp_cache_set_posts_last_changed' );
126-
add_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed' );
127-
add_action( 'deleted_post_meta', 'wp_cache_set_posts_last_changed' );
125+
add_action( 'added_post_meta', 'wp_cache_set_posts_last_changed', 10, 3 );
126+
add_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed', 10, 3 );
127+
add_action( 'deleted_post_meta', 'wp_cache_set_posts_last_changed', 10, 3 );
128128

129129
// User meta.
130130
add_action( 'added_user_meta', 'wp_cache_set_users_last_changed' );

src/wp-includes/meta.php

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,16 +1440,17 @@ function register_meta( $object_type, $meta_key, $args, $deprecated = null ) {
14401440
}
14411441

14421442
$defaults = array(
1443-
'object_subtype' => '',
1444-
'type' => 'string',
1445-
'label' => '',
1446-
'description' => '',
1447-
'default' => '',
1448-
'single' => false,
1449-
'sanitize_callback' => null,
1450-
'auth_callback' => null,
1451-
'show_in_rest' => false,
1452-
'revisions_enabled' => false,
1443+
'object_subtype' => '',
1444+
'type' => 'string',
1445+
'label' => '',
1446+
'description' => '',
1447+
'default' => '',
1448+
'single' => false,
1449+
'sanitize_callback' => null,
1450+
'auth_callback' => null,
1451+
'show_in_rest' => false,
1452+
'revisions_enabled' => false,
1453+
'invalidates_query_cache' => true,
14531454
);
14541455

14551456
// There used to be individual args for sanitize and auth callbacks.
@@ -1644,6 +1645,43 @@ function registered_meta_key_exists( $object_type, $meta_key, $object_subtype =
16441645
return isset( $meta_keys[ $meta_key ] );
16451646
}
16461647

1648+
/**
1649+
* Checks whether a meta key invalidates query caches when updated.
1650+
*
1651+
* Meta keys registered with `'invalidates_query_cache' => false` will not
1652+
* cause query cache invalidation when added, updated, or deleted. This is
1653+
* useful for high-frequency meta that is never used in query filters, such
1654+
* as real-time collaboration sync data.
1655+
*
1656+
* Unregistered meta keys are assumed to invalidate query caches.
1657+
*
1658+
* @since 7.0.0
1659+
*
1660+
* @param string $object_type Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user',
1661+
* or any other object type with an associated meta table.
1662+
* @param string $meta_key Metadata key.
1663+
* @param string $object_subtype Optional. The subtype of the object type. Default empty string.
1664+
* @return bool True if the meta key invalidates query caches, false otherwise.
1665+
*/
1666+
function wp_meta_key_invalidates_query_cache( $object_type, $meta_key, $object_subtype = '' ) {
1667+
$meta_keys = get_registered_meta_keys( $object_type, $object_subtype );
1668+
1669+
if ( isset( $meta_keys[ $meta_key ] ) ) {
1670+
return $meta_keys[ $meta_key ]['invalidates_query_cache'];
1671+
}
1672+
1673+
// Also check keys registered without a subtype.
1674+
if ( '' !== $object_subtype ) {
1675+
$meta_keys = get_registered_meta_keys( $object_type );
1676+
if ( isset( $meta_keys[ $meta_key ] ) ) {
1677+
return $meta_keys[ $meta_key ]['invalidates_query_cache'];
1678+
}
1679+
}
1680+
1681+
// Unregistered keys default to invalidating caches.
1682+
return true;
1683+
}
1684+
16471685
/**
16481686
* Unregisters a meta key from the list of registered keys.
16491687
*

src/wp-includes/post.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,22 @@ function create_initial_post_types() {
690690
'supports' => array( 'custom-fields' ),
691691
)
692692
);
693+
694+
register_post_meta(
695+
'wp_sync_storage',
696+
'wp_sync_awareness',
697+
array(
698+
'invalidates_query_cache' => false,
699+
)
700+
);
701+
702+
register_post_meta(
703+
'wp_sync_storage',
704+
'wp_sync_update',
705+
array(
706+
'invalidates_query_cache' => false,
707+
)
708+
);
693709
}
694710

695711
register_post_status(
@@ -8439,9 +8455,23 @@ function wp_add_trashed_suffix_to_post_name_for_post( $post ) {
84398455
/**
84408456
* Sets the last changed time for the 'posts' cache group.
84418457
*
8458+
* When called from a post meta action (`added_post_meta`, `updated_post_meta`,
8459+
* or `deleted_post_meta`), the meta key is checked against the registered meta
8460+
* registry. If the meta key was registered with `'invalidates_query_cache' => false`,
8461+
* the cache is not invalidated. This allows high-frequency meta writes (e.g.
8462+
* real-time collaboration data) to avoid invalidating query caches site-wide.
8463+
*
84428464
* @since 5.0.0
8465+
* @since 7.0.0 Added the `$meta_id`, `$object_id`, and `$meta_key` parameters.
8466+
*
8467+
* @param int $meta_id Optional. Meta ID. Passed by meta action hooks.
8468+
* @param int $object_id Optional. Object ID. Passed by meta action hooks.
8469+
* @param string $meta_key Optional. Meta key. Passed by meta action hooks.
84438470
*/
8444-
function wp_cache_set_posts_last_changed() {
8471+
function wp_cache_set_posts_last_changed( $meta_id = 0, $object_id = 0, $meta_key = '' ) {
8472+
if ( $meta_key && ! wp_meta_key_invalidates_query_cache( 'post', $meta_key ) ) {
8473+
return;
8474+
}
84458475
wp_cache_set_last_changed( 'posts' );
84468476
}
84478477

0 commit comments

Comments
 (0)