Skip to content

Commit 540a003

Browse files
committed
Collaboration: Simplify benchmark output and fix stale EXPLAIN queries
- Replace per-step log lines with a WP-CLI progress bar - Drop STD/MAD columns and their helper functions; Median+P95 suffices - Remove 100k scale (unrealistic given compaction threshold of 50) - Update EXPLAIN section to reflect the merged MAX+COUNT query - Add buffer pool priming after compaction re-seeds to eliminate cold start variance in measured iterations - Shorten section headers with plain-English descriptions
1 parent 8f64268 commit 540a003

2 files changed

Lines changed: 45 additions & 101 deletions

File tree

tools/local-env/scripts/collaboration-perf/run.php

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
/**
33
* Performance benchmark for WP_Collaboration_Table_Storage at scale.
44
*
5-
* Measures idle poll, catch-up poll, and compaction at 100, 1,000, 10,000,
6-
* and 100,000 rows to verify that queries hold up under load.
5+
* Measures idle poll, catch-up poll, and compaction at 100, 1,000, and
6+
* 10,000 rows to verify that queries hold up under load.
77
*
88
* Usage:
99
* npm run test:performance:collaboration
@@ -19,7 +19,7 @@
1919
// Configuration
2020
// ============================================================
2121

22-
$scales = array( 100, 1000, 10000, 100000 );
22+
$scales = array( 100, 1000, 10000 );
2323
$rooms_per_scale = 10;
2424
$measured_iterations = 50;
2525
$warmup_iterations = 5;
@@ -52,26 +52,26 @@
5252
$results = array();
5353

5454
WP_CLI::log( '' );
55-
WP_CLI::log( WP_CLI::colorize( '%_Collaboration Storage Performance Benchmark%n' ) );
56-
WP_CLI::log( "Backend: WP_Collaboration_Table_Storage" );
57-
WP_CLI::log( "Iterations: {$measured_iterations} measured + {$warmup_iterations} warm-up" );
58-
WP_CLI::log( "Compaction: {$compaction_iterations} measured (re-seed each)" );
55+
WP_CLI::log( WP_CLI::colorize( '%_Collaboration Storage Benchmark%n' ) );
56+
WP_CLI::log( 'Measures database speed for real-time collaborative editing.' );
57+
WP_CLI::log( "Each row = one edit stored for a post being co-edited." );
58+
WP_CLI::log( "{$measured_iterations} iterations ({$warmup_iterations} warm-up), {$compaction_iterations} compaction (re-seeded)" );
5959
WP_CLI::log( '' );
6060

61+
$total_steps = count( $scales ) * 4; // seed + idle + catch-up + compaction per scale.
62+
$progress = \WP_CLI\Utils\make_progress_bar( 'Benchmarking', $total_steps );
63+
6164
foreach ( $scales as $scale ) {
6265
$per_room = (int) ceil( $scale / $rooms_per_scale );
63-
$label = number_format( $scale );
64-
WP_CLI::log( "Scale: {$label} total rows ({$per_room} per room)" );
6566

66-
WP_CLI::log( ' Seeding table...' );
6767
collaboration_perf_seed_table( $scale, $rooms_per_scale );
68+
$progress->tick();
6869

6970
$primer = new WP_Collaboration_Table_Storage();
7071
$primer->get_updates_after_cursor( $target_room, 0 );
7172
$table_idle_cursor = $primer->get_cursor( $target_room );
7273

7374
// Idle poll.
74-
WP_CLI::log( ' Idle poll...' );
7575
$results['idle_poll'][ $scale ] = collaboration_perf_stats(
7676
function () use ( $target_room, $table_idle_cursor ) {
7777
$s = new WP_Collaboration_Table_Storage();
@@ -80,9 +80,9 @@ function () use ( $target_room, $table_idle_cursor ) {
8080
$measured_iterations,
8181
$warmup_iterations
8282
);
83+
$progress->tick();
8384

8485
// Catch-up poll.
85-
WP_CLI::log( ' Catch-up poll...' );
8686
$results['catchup_poll'][ $scale ] = collaboration_perf_stats(
8787
function () use ( $target_room ) {
8888
$s = new WP_Collaboration_Table_Storage();
@@ -91,14 +91,19 @@ function () use ( $target_room ) {
9191
$measured_iterations,
9292
$warmup_iterations
9393
);
94+
$progress->tick();
9495

9596
// Compaction.
96-
WP_CLI::log( ' Compaction...' );
9797
$compaction_times = array();
9898

9999
for ( $ci = 0; $ci < $compaction_iterations; $ci++ ) {
100100
collaboration_perf_seed_table( $scale, $rooms_per_scale );
101101

102+
// Prime the buffer pool after re-seed so the DELETE measures
103+
// query speed, not cold-cache warming.
104+
$primer = new WP_Collaboration_Table_Storage();
105+
$primer->get_updates_after_cursor( $target_room, 0 );
106+
102107
$compaction_cursor_id = (int) $wpdb->get_var( $wpdb->prepare(
103108
"SELECT id FROM {$wpdb->collaboration} WHERE room = %s ORDER BY id ASC LIMIT 1 OFFSET %d",
104109
$target_room,
@@ -112,13 +117,15 @@ function () use ( $target_room ) {
112117
}
113118

114119
$results['compaction'][ $scale ] = collaboration_perf_compute_stats( $compaction_times );
120+
$progress->tick();
115121
}
116122

123+
$progress->finish();
124+
117125
// ============================================================
118126
// EXPLAIN analysis at largest scale
119127
// ============================================================
120128

121-
WP_CLI::log( 'Collecting EXPLAIN analysis...' );
122129
$explain_data = collaboration_perf_collect_explains( $target_room, end( $scales ), $rooms_per_scale );
123130

124131
// ============================================================

tools/local-env/scripts/collaboration-perf/utils.php

Lines changed: 24 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
/**
33
* Shared statistics, formatting, seeding, and cleanup utilities for collaboration storage benchmarks.
44
*
5-
* PHP equivalents of the functions in tests/performance/utils.js
6-
* (median, standardDeviation, medianAbsoluteDeviation).
7-
*
85
* @package WordPress
96
*/
107

@@ -23,34 +20,6 @@ function collaboration_perf_median( array $arr ): float {
2320
: $arr[ $mid ];
2421
}
2522

26-
/**
27-
* Computes the standard deviation of an array of numbers.
28-
*
29-
* @param float[] $arr Array of numbers.
30-
* @return float Standard deviation.
31-
*/
32-
function collaboration_perf_sd( array $arr ): float {
33-
$count = count( $arr );
34-
$mean = array_sum( $arr ) / $count;
35-
$sum_sq = 0.0;
36-
foreach ( $arr as $v ) {
37-
$sum_sq += ( $v - $mean ) ** 2;
38-
}
39-
return sqrt( $sum_sq / $count );
40-
}
41-
42-
/**
43-
* Computes the median absolute deviation of an array of numbers.
44-
*
45-
* @param float[] $arr Array of numbers.
46-
* @return float Median absolute deviation.
47-
*/
48-
function collaboration_perf_mad( array $arr ): float {
49-
$med = collaboration_perf_median( $arr );
50-
$deviations = array_map( fn( $v ) => abs( $v - $med ), $arr );
51-
return collaboration_perf_median( $deviations );
52-
}
53-
5423
/**
5524
* Computes the 95th percentile of an array of numbers.
5625
*
@@ -64,17 +33,15 @@ function collaboration_perf_p95( array $arr ): float {
6433
}
6534

6635
/**
67-
* Computes median, P95, standard deviation, and MAD.
36+
* Computes median and P95.
6837
*
6938
* @param float[] $times Array of durations in milliseconds.
70-
* @return array{ median: float, p95: float, sd: float, mad: float }
39+
* @return array{ median: float, p95: float }
7140
*/
7241
function collaboration_perf_compute_stats( array $times ): array {
7342
return array(
7443
'median' => collaboration_perf_median( $times ),
7544
'p95' => collaboration_perf_p95( $times ),
76-
'sd' => collaboration_perf_sd( $times ),
77-
'mad' => collaboration_perf_mad( $times ),
7845
);
7946
}
8047

@@ -84,7 +51,7 @@ function collaboration_perf_compute_stats( array $times ): array {
8451
* @param callable $callback Function to benchmark.
8552
* @param int $measured Number of measured iterations.
8653
* @param int $warmup Number of warm-up iterations (discarded).
87-
* @return array{ median: float, p95: float, sd: float, mad: float }
54+
* @return array{ median: float, p95: float }
8855
*/
8956
function collaboration_perf_stats( callable $callback, int $measured, int $warmup = 5 ): array {
9057
for ( $i = 0; $i < $warmup; $i++ ) {
@@ -202,22 +169,19 @@ function collaboration_perf_collect_explains( string $target_room, int $scale, i
202169
collaboration_perf_seed_table( $scale, $rooms );
203170
$wpdb->query( "ANALYZE TABLE {$wpdb->collaboration}" );
204171

205-
$table_max_id = (int) $wpdb->get_var( $wpdb->prepare(
206-
"SELECT COALESCE( MAX( id ), 0 ) FROM {$wpdb->collaboration} WHERE room = %s",
172+
$snapshot = $wpdb->get_row( $wpdb->prepare(
173+
"SELECT COALESCE( MAX( id ), 0 ) AS max_id, COUNT(*) AS total FROM {$wpdb->collaboration} WHERE room = %s",
207174
$target_room
208175
) );
209176

177+
$table_max_id = (int) $snapshot->max_id;
178+
210179
$queries = array(
211180
array(
212-
'label' => 'Idle poll (MAX cursor)',
213-
'sql' => "SELECT COALESCE(MAX(id), 0) FROM {$wpdb->collaboration} WHERE room = %s",
181+
'label' => 'Snapshot (MAX + COUNT)',
182+
'sql' => "SELECT COALESCE(MAX(id), 0) AS max_id, COUNT(*) AS total FROM {$wpdb->collaboration} WHERE room = %s",
214183
'args' => array( $target_room ),
215184
),
216-
array(
217-
'label' => 'Idle poll (COUNT)',
218-
'sql' => "SELECT COUNT(*) FROM {$wpdb->collaboration} WHERE room = %s AND id <= %d",
219-
'args' => array( $target_room, $table_max_id ),
220-
),
221185
array(
222186
'label' => 'Catch-up poll (SELECT)',
223187
'sql' => "SELECT update_value FROM {$wpdb->collaboration} WHERE room = %s AND id > %d AND id <= %d ORDER BY id ASC",
@@ -255,7 +219,7 @@ function collaboration_perf_collect_explains( string $target_room, int $scale, i
255219
* @param array $op_results Results for this operation keyed by [$scale].
256220
* @param int[] $scales Scale values.
257221
* @param int $rooms Rooms per scale.
258-
* @return array[] Rows with 'Rows per room', 'Median', 'P95', 'STD', 'MAD' keys.
222+
* @return array[] Rows with 'Rows', 'Median', 'P95' keys.
259223
*/
260224
function collaboration_perf_build_section_rows( array $op_results, array $scales, int $rooms ): array {
261225
$rows = array();
@@ -265,11 +229,9 @@ function collaboration_perf_build_section_rows( array $op_results, array $scales
265229
$stats = $op_results[ $scale ];
266230

267231
$rows[] = array(
268-
'Rows per room' => number_format( $per_room ),
269-
'Median' => collaboration_perf_format_ms( $stats['median'] ),
270-
'P95' => collaboration_perf_format_ms( $stats['p95'] ),
271-
'STD' => collaboration_perf_format_ms( $stats['sd'] ),
272-
'MAD' => collaboration_perf_format_ms( $stats['mad'] ),
232+
'Rows' => number_format( $per_room ),
233+
'Median' => collaboration_perf_format_ms( $stats['median'] ),
234+
'P95' => collaboration_perf_format_ms( $stats['p95'] ),
273235
);
274236
}
275237

@@ -287,61 +249,36 @@ function collaboration_perf_build_section_rows( array $op_results, array $scales
287249
function collaboration_perf_print_output( array $results, array $explain_data, array $config, array $scales ): void {
288250
global $wp_version, $wpdb;
289251

290-
$fields = array( 'Rows per room', 'Median', 'P95', 'STD', 'MAD' );
291-
$separator = str_repeat( '', 60 );
252+
$fields = array( 'Rows', 'Median', 'P95' );
292253

293254
WP_CLI::log( '' );
294-
WP_CLI::log( WP_CLI::colorize( '%_Collaboration Storage Performance%n' ) );
295255
WP_CLI::log( sprintf(
296-
'WordPress %s, MySQL %s, PHP %s, Docker (local dev)',
256+
'WP %s | MySQL %s | PHP %s',
297257
$wp_version,
298258
$wpdb->db_version(),
299259
phpversion()
300260
) );
301-
WP_CLI::log( sprintf(
302-
'%d measured iterations (%d warm-up discarded), fresh instance per iteration',
303-
$config['measured_iterations'],
304-
$config['warmup_iterations']
305-
) );
261+
WP_CLI::log( 'Median = typical response, P95 = slowest 5%' );
306262

307263
$sections = array(
308-
'idle_poll' => array(
309-
'title' => 'Idle Poll',
310-
'desc' => 'Checks for new updates when none exist. Called every second per open editor tab.',
311-
),
312-
'catchup_poll' => array(
313-
'title' => 'Catch-up Poll',
314-
'desc' => 'Fetches all updates from cursor 0. Called when an editor opens or reconnects.',
315-
),
316-
'compaction' => array(
317-
'title' => 'Compaction',
318-
'desc' => sprintf(
319-
'Removes old updates. Deletes ~80%% of rows (%d measured iterations, re-seeded each).',
320-
$config['compaction_iterations']
321-
),
322-
),
264+
'idle_poll' => 'Idle Poll — editor open, no new changes',
265+
'catchup_poll' => 'Catch-up Poll — opening a post or reconnecting',
266+
'compaction' => 'Compaction — pruning old edits (deletes ~80%)',
323267
);
324268

325-
foreach ( $sections as $op_key => $section ) {
326-
WP_CLI::log( '' );
327-
WP_CLI::log( $separator );
328-
WP_CLI::log( WP_CLI::colorize( "%_{$section['title']}%n" ) );
329-
WP_CLI::log( $separator );
330-
WP_CLI::log( $section['desc'] );
269+
foreach ( $sections as $op_key => $title ) {
331270
WP_CLI::log( '' );
271+
WP_CLI::log( WP_CLI::colorize( "%_{$title}%n" ) );
332272

333273
$rows = collaboration_perf_build_section_rows( $results[ $op_key ], $scales, $config['rooms'] );
334274
WP_CLI\Utils\format_items( 'table', $rows, $fields );
335275
}
336276

337277
WP_CLI::log( '' );
338-
WP_CLI::log( $separator );
339-
WP_CLI::log( WP_CLI::colorize( '%_MySQL EXPLAIN Analysis%n' ) );
340-
WP_CLI::log( $separator );
341-
WP_CLI::log( '' );
342-
278+
$explain_scale = number_format( end( $scales ) );
279+
WP_CLI::log( WP_CLI::colorize( "%_Query Plan ({$explain_scale} rows)%n" ) );
343280
WP_CLI\Utils\format_items( 'table', $explain_data, array( 'Query', 'Access' ) );
344281

345282
WP_CLI::log( '' );
346-
WP_CLI::success( 'Benchmark complete.' );
283+
WP_CLI::success( 'Done.' );
347284
}

0 commit comments

Comments
 (0)