RTC: Transients for awareness and JIT meta cache flushing#11348
RTC: Transients for awareness and JIT meta cache flushing#11348peterwilsoncc wants to merge 26 commits intoWordPress:trunkfrom
Conversation
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
2977e90 to
f3467c2
Compare
src/wp-includes/collaboration/class-wp-sync-post-meta-storage.php
Outdated
Show resolved
Hide resolved
0ddf83c to
bfb758e
Compare
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
src/wp-includes/collaboration/class-wp-http-polling-sync-server.php
Outdated
Show resolved
Hide resolved
src/wp-includes/collaboration/class-wp-http-polling-sync-server.php
Outdated
Show resolved
Hide resolved
src/wp-includes/collaboration/class-wp-sync-post-meta-storage.php
Outdated
Show resolved
Hide resolved
660c289 to
be9e5e4
Compare
|
I've pushed a few changes following reviews: Following @aaronjorbin's review and suggestions in Slack
Following @chriszarate's review:
Additionally:
|
be9e5e4 to
74dc594
Compare
desrosj
left a comment
There was a problem hiding this comment.
Some small improvements for the inline docs, but this is looking promising so far.
src/wp-includes/collaboration/class-wp-http-polling-sync-server.php
Outdated
Show resolved
Hide resolved
src/wp-includes/collaboration/class-wp-http-polling-sync-server.php
Outdated
Show resolved
Hide resolved
src/wp-includes/collaboration/class-wp-http-polling-sync-server.php
Outdated
Show resolved
Hide resolved
src/wp-includes/collaboration/class-wp-sync-post-meta-storage.php
Outdated
Show resolved
Hide resolved
| * @param int $object_id Object ID for which the meta was updated. | ||
| * @param string $meta_key Meta key that was updated. | ||
| */ | ||
| function wp_cache_set_needs_meta_query_flush( $meta_ids, $object_id, $meta_key ) { |
There was a problem hiding this comment.
A bit of help with naming things here would be great, same for the name of the cache key.
In an ideal world the cache key would be of positive intent, wp_query_meta_query_needs_flush or similar, but that would be problematic if an object cache's eviction policy drops the value.
Caching false can be problematic with some implementations so whatever the cache key is named, it needs to make sense that true is used to indicate that the cache does not need a JIT flush. Sigh.
aaronjorbin
left a comment
There was a problem hiding this comment.
Thank you @peterwilsoncc ! Overall, I think this is moving the code in a very positive direction.
| ) | ||
| ); | ||
|
|
||
| // @todo This should probably go elsewhere. |
There was a problem hiding this comment.
wp_create_initial_post_meta makes sense to me and has a wp_is_collaboration_enabled() gate if we want WP_Sync_Post_Meta_Storage::SYNC_UPDATE_META_KEY to be conditionally registered.
| * array with 'schema' or 'prepare_callback' keys instead of a boolean. | ||
| * @type bool $revisions_enabled Whether to enable revisions support for this meta_key. Can only be used when the | ||
| * object type is 'post'. | ||
| * @type bool $jit_cache_invalidation Whether to enable just-in-time cache invalidation for this meta key. When enabled, the cache |
There was a problem hiding this comment.
@kittenkamala: This addition to post_meta should likely get a mention in the misc changes devnote.
| ); | ||
|
|
||
| $original_wpdb = $wpdb; | ||
| $proxy_wpdb = new class( $original_wpdb, $storage_post_id, $injected_update ) { |
There was a problem hiding this comment.
Not blocking and I know this isn't as new as the green makes it look, but I don't love this living inside the test function. It's also copied in multiple places.
| // @todo This should probably go elsewhere. | ||
| register_post_meta( | ||
| '', | ||
| '_edit_lock', |
There was a problem hiding this comment.
As part of this change, I added _edit_lock for JIT invalidation. Discuss.
I think this makes sense. "Is this only used for writing purposes and not for display purposes" feels like it could be a good rule of thumb.
There was a problem hiding this comment.
If this is storing things in both post-meta and transients, should it (and the class) have it's name changed? It also means that if a new DB table is added later, this class can be continue to be used as the interface for extenders.
It could also be made it clear in a devnote (or inline comment?) that interacting with sync related data should be done through this class, not through any hooks called as a result of how it implements the storage. i.e. don't count on update_postmeta firing forever.
This feels inline with the comment on the class being "Core class that provides an interface for storing and retrieving sync updates and awareness data during a collaborative session."
| * transient longer than the awareness can avoid adding new entries in the options | ||
| * table unnecessarily. | ||
| */ | ||
| set_transient( self::AWARENESS_TRANSIENT_PREFIX . ":{$room_hash}", $awareness, DAY_IN_SECONDS ); |
There was a problem hiding this comment.
set_transient has a race condition where we might end up calling add_option multiple times. There's probably a million different possible outcomes depending on whether we have object cache, is it persistent, etc. and it's important we don't end up inserting states A and B and then end up reading A but updating B, or reading A on some requests but B on other requests.
There was a problem hiding this comment.
Is the race description you are talking about https://core.trac.wordpress.org/ticket/63450 or something different?
| if ( null !== $decoded ) { | ||
| $updates[] = $decoded; | ||
| } | ||
| $updates[] = maybe_unserialize( $row->meta_value ); |
There was a problem hiding this comment.
Is there any way we can avoid relying on serialize()? I really liked how we dealt with JSON before, it removes an entire class of serialize()-related security vulnerabilities.
| } | ||
|
|
||
| $awareness = json_decode( $meta_value, true ); | ||
| $room_hash = md5( $room ); // Not used for cryptographic purposes. |
There was a problem hiding this comment.
any way we can avoid all the hashing? It could add up to substantial load when there's enough users of this feature
There was a problem hiding this comment.
any way we can avoid all the hashing? It could add up to substantial load when there's enough users of this feature
@adamziel There isn't as we can't use the room ids in slugs due to WP sanitization and transients have a maximum lenght we need to avoid. We could aim for a less intensive hashing mechanism if you have a recommendation.
There was a problem hiding this comment.
Coming from a place of curiosity: Is md5 really that expensive? It seems to be a bit slower than serializing a simple array:
> php -v
PHP 8.5.4 (cli) (built: Mar 10 2026 23:15:23) (NTS)
Copyright (c) The PHP Group
Built by Homebrew
Zend Engine v4.5.4, Copyright (c) Zend Technologies
with Zend OPcache v8.5.4, Copyright (c), by Zend Technologies
> php -r '$start = microtime(true); for ($i = 0; $i < 1000000; $i++) { md5("test" . $i); } $end = microtime(true); echo "Time: " . ($end - $start) . " seconds\n";'
Time: 0.12329506874084 seconds
> php -r '$start = microtime(true); for ($i = 0; $i < 1000000; $i++) { serialize([["foo" => "test" . $i]]); } $end = microtime(true); echo "Time: " . ($end - $start) . " seconds\n";'
Time: 0.099403858184814 secondsThere was a problem hiding this comment.
Also, we only call md5 once per room per request.
| $join .= $clauses['join']; | ||
| $where .= $clauses['where']; | ||
|
|
||
| if ( ! wp_cache_get( 'wp_query_meta_query_updated', 'post_meta' ) ) { |
There was a problem hiding this comment.
Can we add a thorough writeup in an inline docblock that goes through the entire data flow and the reasoning behind this? Every time I see it it takes a lot of effort to unpack
Co-Authored-By: aaronjorbin <jorbin@git.wordpress.org>
Co-Authored-By: chriszarate <czarate@git.wordpress.org>
Co-Authored-By: chriszarate <czarate@git.wordpress.org>
Co-authored-by: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com>
b57a482 to
59995f5
Compare
This does a few things:
wp_sync_awareness_timestamp_granularityto allow granularity to be modified by third party devsIntroduces just in time cache invalidation for meta queries inWP_Query-- modifying the meta data will set a flag that theWP_Querypost group will need to be invalidated next time a site makes a meta queryWP_Query.Trac ticket: https://core.trac.wordpress.org/ticket/64696
Use of AI Tools
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.