Skip to content

Commit e77b83c

Browse files
committed
Real-time collaboration: Use prepared queries instead of *_post_meta functions.
Replaces add_post_meta/update_post_meta with wpdb->insert/wpdb->update. This prevents a real-time editing session from invalidating WP_Query and various other post caches every few seconds. RTC stores awareness and sync information in post meta with high frequency. However, every call the *_post_meta functions invalidated post caches. This commit avoids this frequent invalidation by removing the direct *_post_meta calls in favor of $wpdb calls. Props czarate, mukesh27, paulkevan. Developed in #11325. See #64696. git-svn-id: https://develop.svn.wordpress.org/trunk@62099 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 8bf48cf commit e77b83c

3 files changed

Lines changed: 793 additions & 284 deletions

File tree

src/wp-includes/collaboration/class-wp-sync-post-meta-storage.php

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ class WP_Sync_Post_Meta_Storage implements WP_Sync_Storage {
3030
* @since 7.0.0
3131
* @var string
3232
*/
33-
const AWARENESS_META_KEY = 'wp_sync_awareness';
33+
const AWARENESS_META_KEY = 'wp_sync_awareness_state';
3434

3535
/**
3636
* Meta key for sync updates.
3737
*
3838
* @since 7.0.0
3939
* @var string
4040
*/
41-
const SYNC_UPDATE_META_KEY = 'wp_sync_update';
41+
const SYNC_UPDATE_META_KEY = 'wp_sync_update_data';
4242

4343
/**
4444
* Cache of cursors by room.
@@ -69,36 +69,68 @@ class WP_Sync_Post_Meta_Storage implements WP_Sync_Storage {
6969
*
7070
* @since 7.0.0
7171
*
72+
* @global wpdb $wpdb WordPress database abstraction object.
73+
*
7274
* @param string $room Room identifier.
7375
* @param mixed $update Sync update.
7476
* @return bool True on success, false on failure.
7577
*/
7678
public function add_update( string $room, $update ): bool {
79+
global $wpdb;
80+
7781
$post_id = $this->get_storage_post_id( $room );
7882
if ( null === $post_id ) {
7983
return false;
8084
}
8185

82-
$meta_id = add_post_meta( $post_id, self::SYNC_UPDATE_META_KEY, $update, false );
83-
84-
return (bool) $meta_id;
86+
// Use direct database operation to avoid cache invalidation performed by
87+
// post meta functions (`wp_cache_set_posts_last_changed()` and direct
88+
// `wp_cache_delete()` calls).
89+
return (bool) $wpdb->insert(
90+
$wpdb->postmeta,
91+
array(
92+
'post_id' => $post_id,
93+
'meta_key' => self::SYNC_UPDATE_META_KEY,
94+
'meta_value' => wp_json_encode( $update ),
95+
),
96+
array( '%d', '%s', '%s' )
97+
);
8598
}
8699

87100
/**
88101
* Gets awareness state for a given room.
89102
*
90103
* @since 7.0.0
91104
*
105+
* @global wpdb $wpdb WordPress database abstraction object.
106+
*
92107
* @param string $room Room identifier.
93108
* @return array<int, mixed> Awareness state.
94109
*/
95110
public function get_awareness_state( string $room ): array {
111+
global $wpdb;
112+
96113
$post_id = $this->get_storage_post_id( $room );
97114
if ( null === $post_id ) {
98115
return array();
99116
}
100117

101-
$awareness = get_post_meta( $post_id, self::AWARENESS_META_KEY, true );
118+
// Use direct database operation to avoid updating the post meta cache.
119+
// ORDER BY meta_id DESC ensures the latest row wins if duplicates exist
120+
// from a past race condition in set_awareness_state().
121+
$meta_value = $wpdb->get_var(
122+
$wpdb->prepare(
123+
"SELECT meta_value FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s ORDER BY meta_id DESC LIMIT 1",
124+
$post_id,
125+
self::AWARENESS_META_KEY
126+
)
127+
);
128+
129+
if ( null === $meta_value ) {
130+
return array();
131+
}
132+
133+
$awareness = json_decode( $meta_value, true );
102134

103135
if ( ! is_array( $awareness ) ) {
104136
return array();
@@ -112,19 +144,54 @@ public function get_awareness_state( string $room ): array {
112144
*
113145
* @since 7.0.0
114146
*
147+
* @global wpdb $wpdb WordPress database abstraction object.
148+
*
115149
* @param string $room Room identifier.
116150
* @param array<int, mixed> $awareness Serializable awareness state.
117151
* @return bool True on success, false on failure.
118152
*/
119153
public function set_awareness_state( string $room, array $awareness ): bool {
154+
global $wpdb;
155+
120156
$post_id = $this->get_storage_post_id( $room );
121157
if ( null === $post_id ) {
122158
return false;
123159
}
124160

125-
// update_post_meta returns false if the value is the same as the existing value.
126-
update_post_meta( $post_id, wp_slash( self::AWARENESS_META_KEY ), wp_slash( $awareness ) );
127-
return true;
161+
// Use direct database operation to avoid cache invalidation performed by
162+
// post meta functions (`wp_cache_set_posts_last_changed()` and direct
163+
// `wp_cache_delete()` calls).
164+
//
165+
// If two concurrent requests both see no row and both INSERT, the
166+
// duplicate is harmless: get_awareness_state() reads the latest row
167+
// (ORDER BY meta_id DESC).
168+
$meta_id = $wpdb->get_var(
169+
$wpdb->prepare(
170+
"SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s ORDER BY meta_id DESC LIMIT 1",
171+
$post_id,
172+
self::AWARENESS_META_KEY
173+
)
174+
);
175+
176+
if ( $meta_id ) {
177+
return (bool) $wpdb->update(
178+
$wpdb->postmeta,
179+
array( 'meta_value' => wp_json_encode( $awareness ) ),
180+
array( 'meta_id' => $meta_id ),
181+
array( '%s' ),
182+
array( '%d' )
183+
);
184+
}
185+
186+
return (bool) $wpdb->insert(
187+
$wpdb->postmeta,
188+
array(
189+
'post_id' => $post_id,
190+
'meta_key' => self::AWARENESS_META_KEY,
191+
'meta_value' => wp_json_encode( $awareness ),
192+
),
193+
array( '%d', '%s', '%s' )
194+
);
128195
}
129196

130197
/**
@@ -168,6 +235,8 @@ private function get_storage_post_id( string $room ): ?int {
168235
'post_status' => 'publish',
169236
'name' => $room_hash,
170237
'fields' => 'ids',
238+
'orderby' => 'ID',
239+
'order' => 'ASC',
171240
)
172241
);
173242

@@ -212,6 +281,8 @@ public function get_update_count( string $room ): int {
212281
*
213282
* @since 7.0.0
214283
*
284+
* @global wpdb $wpdb WordPress database abstraction object.
285+
*
215286
* @param string $room Room identifier.
216287
* @param int $cursor Return updates after this cursor (meta_id).
217288
* @return array<int, mixed> Sync updates.
@@ -261,7 +332,10 @@ public function get_updates_after_cursor( string $room, int $cursor ): array {
261332

262333
$updates = array();
263334
foreach ( $rows as $row ) {
264-
$updates[] = maybe_unserialize( $row->meta_value );
335+
$decoded = json_decode( $row->meta_value, true );
336+
if ( null !== $decoded ) {
337+
$updates[] = $decoded;
338+
}
265339
}
266340

267341
return $updates;
@@ -272,6 +346,8 @@ public function get_updates_after_cursor( string $room, int $cursor ): array {
272346
*
273347
* @since 7.0.0
274348
*
349+
* @global wpdb $wpdb WordPress database abstraction object.
350+
*
275351
* @param string $room Room identifier.
276352
* @param int $cursor Remove updates with meta_id < this cursor.
277353
* @return bool True on success, false on failure.

0 commit comments

Comments
 (0)