d36bb954d1
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
298 lines
10 KiB
PHP
298 lines
10 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Performance benchmark integration tests.
|
|
*
|
|
* Exercises Zone A (Hot), Zone B (Warm), and Zone C (Cold) with N=200
|
|
* write + read operations and verifies data integrity. Timing is captured
|
|
* and printed to STDOUT so it appears in --testdox output. No hard timing
|
|
* assertions are made — correctness assertions guard against regressions.
|
|
*
|
|
* Run alone: ./vendor/bin/phpunit --configuration phpunit-integration.xml
|
|
* --filter BenchmarkIntegrationTest --testdox
|
|
*/
|
|
class BenchmarkIntegrationTest extends TestCase {
|
|
|
|
private const N = 200; // Rows per benchmark zone.
|
|
private const POST_TYPE = 'bench';
|
|
|
|
/** Zone A table: wp_itest_wpdo_hot_bench */
|
|
private static string $hot_table;
|
|
|
|
/** Zone B table: wp_itest_wpdo_warm_bench (isolated from other warm tests) */
|
|
private static string $warm_table;
|
|
|
|
/** Zone C table: wp_itest_wpdo_cold_bench */
|
|
private static string $cold_table;
|
|
|
|
/** Collected timing results printed in tearDownAfterClass(). */
|
|
private static array $report = [];
|
|
|
|
// ── Fixture lifecycle ─────────────────────────────────────────────────────
|
|
|
|
public static function setUpBeforeClass(): void {
|
|
global $wpdb;
|
|
|
|
self::$hot_table = $wpdb->prefix . 'wpdo_hot_bench';
|
|
self::$warm_table = $wpdb->prefix . 'wpdo_warm_bench';
|
|
self::$cold_table = $wpdb->prefix . 'wpdo_cold_bench';
|
|
|
|
// Zone A table.
|
|
$wpdb->query(
|
|
"CREATE TABLE IF NOT EXISTS `" . self::$hot_table . "` (
|
|
post_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
|
|
bench_val DECIMAL(10,2) DEFAULT NULL,
|
|
updated_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
|
|
PRIMARY KEY (post_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
|
);
|
|
|
|
// Zone B table (KV schema).
|
|
$wpdb->query(
|
|
"CREATE TABLE IF NOT EXISTS `" . self::$warm_table . "` (
|
|
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
post_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
|
|
meta_key VARCHAR(255) NOT NULL DEFAULT '',
|
|
meta_value LONGTEXT DEFAULT NULL,
|
|
expires_at DATETIME DEFAULT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY post_meta (post_id, meta_key)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
|
);
|
|
|
|
// Zone C table.
|
|
$wpdb->query(
|
|
"CREATE TABLE IF NOT EXISTS `" . self::$cold_table . "` (
|
|
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
post_id BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
|
|
data LONGTEXT NOT NULL,
|
|
updated_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY ui_post_id (post_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
|
);
|
|
}
|
|
|
|
public static function tearDownAfterClass(): void {
|
|
global $wpdb;
|
|
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$hot_table . "`" );
|
|
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$warm_table . "`" );
|
|
$wpdb->query( "DROP TABLE IF EXISTS `" . self::$cold_table . "`" );
|
|
|
|
// Print timing summary.
|
|
fwrite( STDOUT, "\n\n ── Benchmark Results (N=" . self::N . " per zone) ──────────────────────\n" );
|
|
foreach ( self::$report as $label => $ms ) {
|
|
fwrite( STDOUT, sprintf( " %-40s %7.1f ms\n", $label, $ms ) );
|
|
}
|
|
fwrite( STDOUT, " ──────────────────────────────────────────────────────\n\n" );
|
|
}
|
|
|
|
protected function setUp(): void {
|
|
global $wpdb;
|
|
$wpdb->query( "TRUNCATE TABLE `" . self::$hot_table . "`" );
|
|
$wpdb->query( "TRUNCATE TABLE `" . self::$warm_table . "`" );
|
|
$wpdb->query( "TRUNCATE TABLE `" . self::$cold_table . "`" );
|
|
$GLOBALS['_wp_cache'] = [];
|
|
}
|
|
|
|
// ── Zone A (Hot) ─────────────────────────────────────────────────────────
|
|
|
|
public function test_zone_a_bulk_write_performance(): void {
|
|
global $wpdb;
|
|
$now = gmdate( 'Y-m-d H:i:s' );
|
|
$start = microtime( true );
|
|
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$wpdb->query(
|
|
$wpdb->prepare(
|
|
"INSERT INTO `" . self::$hot_table . "` (post_id, bench_val, updated_at)
|
|
VALUES (%d, %f, %s)
|
|
ON DUPLICATE KEY UPDATE bench_val = VALUES(bench_val), updated_at = VALUES(updated_at)",
|
|
$i,
|
|
$i * 10.0,
|
|
$now
|
|
)
|
|
);
|
|
}
|
|
|
|
$ms = ( microtime( true ) - $start ) * 1000;
|
|
self::$report['Zone A: ' . self::N . ' UPSERT writes'] = $ms;
|
|
|
|
$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::$hot_table . "`" );
|
|
$this->assertSame( self::N, $count, 'Zone A: all rows written' );
|
|
}
|
|
|
|
public function test_zone_a_bulk_read_performance(): void {
|
|
global $wpdb;
|
|
|
|
// Seed data.
|
|
$now = gmdate( 'Y-m-d H:i:s' );
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$wpdb->query( $wpdb->prepare(
|
|
"INSERT INTO `" . self::$hot_table . "` (post_id, bench_val, updated_at) VALUES (%d, %f, %s)",
|
|
$i, $i * 10.0, $now
|
|
) );
|
|
}
|
|
|
|
// Benchmark individual point-reads.
|
|
$start = microtime( true );
|
|
$values = [];
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$values[] = $wpdb->get_var(
|
|
$wpdb->prepare( "SELECT bench_val FROM `" . self::$hot_table . "` WHERE post_id = %d", $i )
|
|
);
|
|
}
|
|
$ms = ( microtime( true ) - $start ) * 1000;
|
|
self::$report['Zone A: ' . self::N . ' point reads'] = $ms;
|
|
|
|
$this->assertCount( self::N, $values, 'Zone A: all rows readable' );
|
|
$this->assertSame( '10.00', $values[0] ); // post_id=1 → 1*10=10
|
|
}
|
|
|
|
public function test_zone_a_filtered_query_performance(): void {
|
|
global $wpdb;
|
|
|
|
$now = gmdate( 'Y-m-d H:i:s' );
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$wpdb->query( $wpdb->prepare(
|
|
"INSERT INTO `" . self::$hot_table . "` (post_id, bench_val, updated_at) VALUES (%d, %f, %s)",
|
|
$i, $i * 10.0, $now
|
|
) );
|
|
}
|
|
|
|
// Filtered query: bench_val >= 1000 (100 rows).
|
|
$start = microtime( true );
|
|
$rows = $wpdb->get_results(
|
|
$wpdb->prepare(
|
|
"SELECT post_id, bench_val FROM `" . self::$hot_table . "` WHERE bench_val >= %f ORDER BY bench_val ASC",
|
|
1000.0
|
|
),
|
|
ARRAY_A
|
|
);
|
|
$ms = ( microtime( true ) - $start ) * 1000;
|
|
self::$report['Zone A: filtered query (half dataset)'] = $ms;
|
|
|
|
$this->assertCount( 101, $rows, 'Zone A: filter returns correct row count' );
|
|
$this->assertSame( '1000.00', $rows[0]['bench_val'] );
|
|
}
|
|
|
|
// ── Zone B (Warm) ─────────────────────────────────────────────────────────
|
|
|
|
public function test_zone_b_bulk_write_performance(): void {
|
|
global $wpdb;
|
|
$now = gmdate( 'Y-m-d H:i:s' );
|
|
$start = microtime( true );
|
|
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$wpdb->query(
|
|
$wpdb->prepare(
|
|
"INSERT INTO `" . self::$warm_table . "`
|
|
(post_id, meta_key, meta_value, created_at)
|
|
VALUES (%d, %s, %s, %s)
|
|
ON DUPLICATE KEY UPDATE meta_value = VALUES(meta_value)",
|
|
$i, 'bench_views', (string) $i, $now
|
|
)
|
|
);
|
|
}
|
|
|
|
$ms = ( microtime( true ) - $start ) * 1000;
|
|
self::$report['Zone B: ' . self::N . ' KV writes'] = $ms;
|
|
|
|
$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::$warm_table . "`" );
|
|
$this->assertSame( self::N, $count, 'Zone B: all KV rows written' );
|
|
}
|
|
|
|
public function test_zone_b_bulk_read_performance(): void {
|
|
global $wpdb;
|
|
|
|
$now = gmdate( 'Y-m-d H:i:s' );
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$wpdb->query( $wpdb->prepare(
|
|
"INSERT INTO `" . self::$warm_table . "` (post_id, meta_key, meta_value, created_at) VALUES (%d, %s, %s, %s)",
|
|
$i, 'bench_views', (string) $i, $now
|
|
) );
|
|
}
|
|
|
|
$start = microtime( true );
|
|
$values = [];
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$values[] = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT meta_value FROM `" . self::$warm_table . "` WHERE post_id = %d AND meta_key = %s",
|
|
$i, 'bench_views'
|
|
) );
|
|
}
|
|
$ms = ( microtime( true ) - $start ) * 1000;
|
|
self::$report['Zone B: ' . self::N . ' KV reads'] = $ms;
|
|
|
|
$this->assertCount( self::N, $values, 'Zone B: all KV rows readable' );
|
|
$this->assertSame( '1', $values[0] ); // post_id=1 → value=1
|
|
}
|
|
|
|
// ── Zone C (Cold) ─────────────────────────────────────────────────────────
|
|
|
|
public function test_zone_c_bulk_write_performance(): void {
|
|
global $wpdb;
|
|
$now = gmdate( 'Y-m-d H:i:s' );
|
|
$start = microtime( true );
|
|
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$json = wp_json_encode( [
|
|
'hp_description' => 'Benchmark listing description number ' . $i,
|
|
'hp_website' => 'https://listing' . $i . '.example.com',
|
|
'hp_facebook' => 'https://facebook.com/listing' . $i,
|
|
] );
|
|
$wpdb->query(
|
|
$wpdb->prepare(
|
|
"INSERT INTO `" . self::$cold_table . "` (post_id, data, updated_at)
|
|
VALUES (%d, %s, %s)
|
|
ON DUPLICATE KEY UPDATE data = VALUES(data), updated_at = VALUES(updated_at)",
|
|
$i, $json, $now
|
|
)
|
|
);
|
|
}
|
|
|
|
$ms = ( microtime( true ) - $start ) * 1000;
|
|
self::$report['Zone C: ' . self::N . ' JSON blob writes'] = $ms;
|
|
|
|
$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `" . self::$cold_table . "`" );
|
|
$this->assertSame( self::N, $count, 'Zone C: all JSON rows written' );
|
|
}
|
|
|
|
public function test_zone_c_bulk_read_performance(): void {
|
|
global $wpdb;
|
|
|
|
$now = gmdate( 'Y-m-d H:i:s' );
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$json = wp_json_encode( [
|
|
'hp_description' => 'Description ' . $i,
|
|
'hp_website' => 'https://listing' . $i . '.example.com',
|
|
] );
|
|
$wpdb->query( $wpdb->prepare(
|
|
"INSERT INTO `" . self::$cold_table . "` (post_id, data, updated_at) VALUES (%d, %s, %s)",
|
|
$i, $json, $now
|
|
) );
|
|
}
|
|
|
|
$start = microtime( true );
|
|
$decoded = 0;
|
|
for ( $i = 1; $i <= self::N; $i++ ) {
|
|
$json = $wpdb->get_var( $wpdb->prepare(
|
|
"SELECT data FROM `" . self::$cold_table . "` WHERE post_id = %d",
|
|
$i
|
|
) );
|
|
$data = json_decode( (string) $json, true );
|
|
if ( is_array( $data ) && isset( $data['hp_description'] ) ) {
|
|
$decoded++;
|
|
}
|
|
}
|
|
$ms = ( microtime( true ) - $start ) * 1000;
|
|
self::$report['Zone C: ' . self::N . ' JSON blob reads'] = $ms;
|
|
|
|
$this->assertSame( self::N, $decoded, 'Zone C: all JSON blobs readable' );
|
|
}
|
|
}
|