Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/wp-includes/class-wp-query.php
Original file line number Diff line number Diff line change
Expand Up @@ -2473,6 +2473,10 @@ public function get_posts() {
$clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
$join .= $clauses['join'];
$where .= $clauses['where'];

if ( ! wp_cache_get( 'wp_query_meta_query_updated', 'post_meta' ) ) {
wp_cache_set_posts_last_changed();
}
}

$rand = ( isset( $query_vars['orderby'] ) && 'rand' === $query_vars['orderby'] );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,25 @@ private function process_awareness_update( string $room, int $client_id, ?array
$updated_awareness[] = $entry;
}

/**
* Filters granularity used for rounding up a client's awareness timestamp.
*
* Use to modify the granularity used when recording the latest time a client update their
* awareness state. This allows implementations to increase or reduce the granularity
* of awareness updates which can help reduce server load.
*
* @since 7.0.0
*
* @param int $granularity Granularity in seconds. Default 10.
*/
$granularity = (int) apply_filters( 'wp_sync_awareness_timestamp_granularity', 10 );

// Add this client's awareness state.
if ( null !== $awareness_update ) {
$updated_awareness[] = array(
'client_id' => $client_id,
'state' => $awareness_update,
'updated_at' => $current_time,
'updated_at' => ceil( time() / $granularity ) * $granularity, // Round up to nearest granularity to reduce database churn.
'wp_user_id' => get_current_user_id(),
);
}
Expand Down
119 changes: 23 additions & 96 deletions src/wp-includes/collaboration/class-wp-sync-post-meta-storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ class WP_Sync_Post_Meta_Storage implements WP_Sync_Storage {
* @since 7.0.0
* @var string
*/
const AWARENESS_META_KEY = 'wp_sync_awareness_state';
const AWARENESS_TRANSIENT_PREFIX = 'wp_sync_awareness';

/**
* Meta key for sync updates.
*
* @since 7.0.0
* @var string
*/
const SYNC_UPDATE_META_KEY = 'wp_sync_update_data';
const SYNC_UPDATE_META_KEY = 'wp_sync_update';

/**
* Cache of cursors by room.
Expand Down Expand Up @@ -69,73 +69,39 @@ class WP_Sync_Post_Meta_Storage implements WP_Sync_Storage {
*
* @since 7.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string $room Room identifier.
* @param mixed $update Sync update.
* @return bool True on success, false on failure.
*/
public function add_update( string $room, $update ): bool {
global $wpdb;

$post_id = $this->get_storage_post_id( $room );
if ( null === $post_id ) {
return false;
}

// Use direct database operation to avoid cache invalidation performed by
// post meta functions (`wp_cache_set_posts_last_changed()` and direct
// `wp_cache_delete()` calls).
return (bool) $wpdb->insert(
$wpdb->postmeta,
array(
'post_id' => $post_id,
'meta_key' => self::SYNC_UPDATE_META_KEY,
'meta_value' => wp_json_encode( $update ),
),
array( '%d', '%s', '%s' )
);
$meta_id = add_post_meta( $post_id, self::SYNC_UPDATE_META_KEY, $update, false );

return (bool) $meta_id;
}

/**
* Gets awareness state for a given room.
*
* @since 7.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string $room Room identifier.
* @return array<int, mixed> Awareness state.
*/
public function get_awareness_state( string $room ): array {
global $wpdb;

$post_id = $this->get_storage_post_id( $room );
if ( null === $post_id ) {
return array();
}

// Use direct database operation to avoid updating the post meta cache.
// ORDER BY meta_id DESC ensures the latest row wins if duplicates exist
// from a past race condition in set_awareness_state().
$meta_value = $wpdb->get_var(
$wpdb->prepare(
"SELECT meta_value FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s ORDER BY meta_id DESC LIMIT 1",
$post_id,
self::AWARENESS_META_KEY
)
);

if ( null === $meta_value ) {
return array();
}

$awareness = json_decode( $meta_value, true );
$room_hash = md5( $room ); // Not used for cryptographic purposes.
$awareness = get_transient( self::AWARENESS_TRANSIENT_PREFIX . ":{$room_hash}" );

if ( ! is_array( $awareness ) ) {
return array();
}

// Deterministic ordering of transient data.
$awareness = wp_list_sort( $awareness, 'client_id' );
return array_values( $awareness );
}

Expand All @@ -144,54 +110,24 @@ public function get_awareness_state( string $room ): array {
*
* @since 7.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string $room Room identifier.
* @param array<int, mixed> $awareness Serializable awareness state.
* @return bool True on success, false on failure.
*/
public function set_awareness_state( string $room, array $awareness ): bool {
global $wpdb;

$post_id = $this->get_storage_post_id( $room );
if ( null === $post_id ) {
return false;
}

// Use direct database operation to avoid cache invalidation performed by
// post meta functions (`wp_cache_set_posts_last_changed()` and direct
// `wp_cache_delete()` calls).
//
// If two concurrent requests both see no row and both INSERT, the
// duplicate is harmless: get_awareness_state() reads the latest row
// (ORDER BY meta_id DESC).
$meta_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s ORDER BY meta_id DESC LIMIT 1",
$post_id,
self::AWARENESS_META_KEY
)
);

if ( $meta_id ) {
return (bool) $wpdb->update(
$wpdb->postmeta,
array( 'meta_value' => wp_json_encode( $awareness ) ),
array( 'meta_id' => $meta_id ),
array( '%s' ),
array( '%d' )
);
}

return (bool) $wpdb->insert(
$wpdb->postmeta,
array(
'post_id' => $post_id,
'meta_key' => self::AWARENESS_META_KEY,
'meta_value' => wp_json_encode( $awareness ),
),
array( '%d', '%s', '%s' )
);
$room_hash = md5( $room ); // Not used for cryptographic purposes.
// Deterministic ordering of transient data.
$awareness = wp_list_sort( $awareness, 'client_id' );

/*
* Maintain transient for longer than awareness.
*
* The more recently a room is used, the more likely it is to be used again
* soon, so the transient can be maintained for longer to avoid additional
* entries in the options table.
*/
set_transient( self::AWARENESS_TRANSIENT_PREFIX . ":{$room_hash}", $awareness, HOUR_IN_SECONDS );
return true;
}

/**
Expand Down Expand Up @@ -235,8 +171,6 @@ private function get_storage_post_id( string $room ): ?int {
'post_status' => 'publish',
'name' => $room_hash,
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
)
);

Expand Down Expand Up @@ -281,8 +215,6 @@ public function get_update_count( string $room ): int {
*
* @since 7.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string $room Room identifier.
* @param int $cursor Return updates after this cursor (meta_id).
* @return array<int, mixed> Sync updates.
Expand Down Expand Up @@ -332,10 +264,7 @@ public function get_updates_after_cursor( string $room, int $cursor ): array {

$updates = array();
foreach ( $rows as $row ) {
$decoded = json_decode( $row->meta_value, true );
if ( null !== $decoded ) {
$updates[] = $decoded;
}
$updates[] = maybe_unserialize( $row->meta_value );
}

return $updates;
Expand All @@ -346,8 +275,6 @@ public function get_updates_after_cursor( string $room, int $cursor ): array {
*
* @since 7.0.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param string $room Room identifier.
* @param int $cursor Remove updates with meta_id < this cursor.
* @return bool True on success, false on failure.
Expand Down
6 changes: 3 additions & 3 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@
}

// Post meta.
add_action( 'added_post_meta', 'wp_cache_set_posts_last_changed' );
add_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed' );
add_action( 'deleted_post_meta', 'wp_cache_set_posts_last_changed' );
add_action( 'added_post_meta', 'wp_cache_set_needs_meta_query_flush' );
add_action( 'updated_post_meta', 'wp_cache_set_needs_meta_query_flush' );
add_action( 'deleted_post_meta', 'wp_cache_set_needs_meta_query_flush' );

// User meta.
add_action( 'added_user_meta', 'wp_cache_set_users_last_changed' );
Expand Down
11 changes: 11 additions & 0 deletions src/wp-includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -8444,6 +8444,17 @@ function wp_add_trashed_suffix_to_post_name_for_post( $post ) {
*/
function wp_cache_set_posts_last_changed() {
wp_cache_set_last_changed( 'posts' );
// Indicate meta query cache is updated for use in WP_Query
wp_cache_set( 'wp_query_meta_query_updated', true, 'post_meta' );
}

/**
* Marks the WP_Query cache as needing to be flushed after a meta query change.
*
* @since 7.0.0
*/
function wp_cache_set_needs_meta_query_flush() {
wp_cache_delete( 'wp_query_meta_query_updated', 'post_meta' );
}

/**
Expand Down
Loading
Loading