Skip to content

Commit e1e6a88

Browse files
committed
Do not use table for awareness with persistent object cache.
1 parent f71e9a9 commit e1e6a88

1 file changed

Lines changed: 66 additions & 46 deletions

File tree

src/wp-includes/collaboration/class-wp-collaboration-table-storage.php

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,38 @@ public function add_update( string $room, $update ): bool {
101101
public function get_awareness_state( string $room, int $timeout = 30 ): array {
102102
global $wpdb;
103103

104-
$cache_key = 'awareness:' . str_replace( '/', ':', $room );
105-
$cached = wp_cache_get( $cache_key, 'collaboration' );
104+
$cutoff_timestamp = time() - $timeout;
105+
$cutoff_mysql = gmdate( 'Y-m-d H:i:s', $cutoff_timestamp );
106106

107-
if ( false !== $cached ) {
108-
return $cached;
109-
}
107+
if ( wp_using_ext_object_cache() ) {
108+
$cache_key = 'awareness::' . str_replace( '/', ':', $room );
109+
$cached = wp_cache_get( $cache_key, 'collaboration' );
110+
111+
if ( false === $cached || ! is_string( $cached ) ) {
112+
// Room not set/corrupted.
113+
return array();
114+
}
115+
116+
$cached = json_decode( $cached, true );
117+
118+
// Deterministic ordering.
119+
$cached_awareness = wp_list_sort( $cached, 'client_id' );
110120

111-
$cutoff = gmdate( 'Y-m-d H:i:s', time() - $timeout );
121+
// Remove out of date entries.
122+
foreach ( $cached_awareness as $index => $client_awareness ) {
123+
if ( empty( $client_awareness['timestamp'] ) || $client_awareness['timestamp'] < $cutoff_timestamp ) {
124+
unset( $cached_awareness[ $index ] );
125+
}
126+
}
127+
128+
return array_values( $cached_awareness );
129+
}
112130

113131
$rows = $wpdb->get_results(
114132
$wpdb->prepare(
115133
"SELECT client_id, user_id, data FROM {$wpdb->collaboration} WHERE room = %s AND type = 'awareness' AND date_gmt >= %s",
116134
$room,
117-
$cutoff
135+
$cutoff_mysql
118136
)
119137
);
120138

@@ -134,8 +152,6 @@ public function get_awareness_state( string $room, int $timeout = 30 ): array {
134152
}
135153
}
136154

137-
wp_cache_set( $cache_key, $entries, 'collaboration', $timeout );
138-
139155
return $entries;
140156
}
141157

@@ -304,14 +320,50 @@ public function set_awareness_state( string $room, string $client_id, array $sta
304320
return false;
305321
}
306322

307-
$data = wp_json_encode( $state );
323+
wp_recursive_ksort( $state );
308324

309325
/*
310326
* Bucket the timestamp to 5-second intervals so most polls
311327
* short-circuit without a database write. Ceil is used instead
312328
* of floor to prevent the awareness timeout from being hit early.
313329
*/
314-
$now = gmdate( 'Y-m-d H:i:s', (int) ceil( time() / 5 ) * 5 );
330+
$now_timestamp = (int) ceil( time() / 5 ) * 5;
331+
$now_mysql = gmdate( 'Y-m-d H:i:s', $now_timestamp );
332+
333+
if ( wp_using_ext_object_cache() ) {
334+
$cache_key = 'awareness::' . str_replace( '/', ':', $room );
335+
$awareness = $this->get_awareness_state( $room );
336+
337+
foreach ( $awareness as $index => $client_awareness ) {
338+
if ( $client_awareness['client_id'] === $client_id && $client_awareness['timestamp'] === $now_timestamp ) {
339+
// Cache already has the current client state, consider update a success (avoids cache thrashing).
340+
return true;
341+
}
342+
343+
if ( $client_awareness['client_id'] === $client_id ) {
344+
// Remove stale cache entry for the current client, if it exists.
345+
unset( $awareness[ $index ] );
346+
break;
347+
}
348+
}
349+
350+
$client_awareness = array(
351+
'client_id' => $client_id,
352+
'state' => $state,
353+
'user_id' => $user_id,
354+
'timestamp' => $now_timestamp,
355+
);
356+
357+
$awareness[] = $client_awareness;
358+
359+
// Sort awareness entries by client_id.
360+
$awareness = wp_list_sort( $awareness, 'client_id' );
361+
wp_cache_set( $cache_key, wp_json_encode( $awareness ), 'collaboration', HOUR_IN_SECONDS );
362+
363+
return true;
364+
}
365+
366+
$data = wp_json_encode( $state );
315367

316368
/* Check if a row already exists. */
317369
$exists = $wpdb->get_row(
@@ -322,7 +374,7 @@ public function set_awareness_state( string $room, string $client_id, array $sta
322374
)
323375
);
324376

325-
if ( $exists && $exists->date_gmt === $now ) {
377+
if ( $exists && $exists->date_gmt === $now_mysql ) {
326378
// Row already has the current date, consider update a success.
327379
return true;
328380
}
@@ -333,7 +385,7 @@ public function set_awareness_state( string $room, string $client_id, array $sta
333385
array(
334386
'user_id' => $user_id,
335387
'data' => $data,
336-
'date_gmt' => $now,
388+
'date_gmt' => $now_mysql,
337389
),
338390
array( 'id' => $exists->id )
339391
);
@@ -346,7 +398,7 @@ public function set_awareness_state( string $room, string $client_id, array $sta
346398
'client_id' => $client_id,
347399
'user_id' => $user_id,
348400
'data' => $data,
349-
'date_gmt' => $now,
401+
'date_gmt' => $now_mysql,
350402
)
351403
);
352404
}
@@ -355,38 +407,6 @@ public function set_awareness_state( string $room, string $client_id, array $sta
355407
return false;
356408
}
357409

358-
/*
359-
* Update the cached entries in-place so the next reader in this
360-
* room gets a cache hit with fresh data. If the cache is cold,
361-
* skip — the next get_awareness_state() call will prime it.
362-
*/
363-
$cache_key = 'awareness:' . str_replace( '/', ':', $room );
364-
$cached = wp_cache_get( $cache_key, 'collaboration' );
365-
366-
if ( false !== $cached ) {
367-
$normalized_state = json_decode( $data, true );
368-
$found = false;
369-
370-
foreach ( $cached as $i => $entry ) {
371-
if ( $client_id === $entry['client_id'] ) {
372-
$cached[ $i ]['state'] = $normalized_state;
373-
$cached[ $i ]['user_id'] = $user_id;
374-
$found = true;
375-
break;
376-
}
377-
}
378-
379-
if ( ! $found ) {
380-
$cached[] = array(
381-
'client_id' => $client_id,
382-
'state' => $normalized_state,
383-
'user_id' => $user_id,
384-
);
385-
}
386-
387-
wp_cache_set( $cache_key, $cached, 'collaboration', 30 );
388-
}
389-
390410
return true;
391411
}
392412
}

0 commit comments

Comments
 (0)