Files
2meet-data-optimizer/admin/templates/post-stress-test.php
T
wpdev ad7acc40b7 chore(2meet-data-optimizer): address the 2.1 audit notes
- The four public REST routes say why they are public (viewable posts only, show_in_rest fields only, per_page cap, one view per IP per post per hour).
- Admin ternary echoes of literal strings are escaped for their context: esc_attr() in attributes, esc_html() for text; the object-cache badge is an if/else and aria-current is always printed ("false" off the current tab).
- Exceptions in the migration engine and the health cron go to TMDO_Logger instead of error_log().
- Verify: audit greps (ternary echoes 23 → 0, error_log outside the logger 0); dev36 renders 8 admin tabs with the change, no PHP notice, one aria-current="page" each.
- PR: PR-078
2026-09-27 01:19:22 +08:00

392 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Post Stress Test template (v2.11.4).
*
* Full async + polling UI mirroring User Stress Test:
* 1. 設定並啟動測試(form: post_type / target / mode / batch_size + Start/Cancel)
* 2. 即時進度(progress bar + processed/target/rate/ETA/peak memory)
* 3. Benchmark 報告(write metrics + DB sizes + query performance)
*
* The UI uses REST/AJAX exclusively; legacy admin action handlers now require POST.
*
* Variables in scope from render_post_stress_test():
* $test_post_count — int, posts matching TMDO_STRESS_TEST_ prefix
* $diagnose — TMDO_Post_Migration::diagnose() output
*
* @package WP_Data_Optimizer
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$page_url = admin_url( 'tools.php?page=wp-data-optimizer&tab=post-stress-test' );
// Read-only display banner — server-set redirect message, no form processing.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$msg_raw = isset( $_GET['wpdo_msg'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['wpdo_msg'] ) ) : '';
$msg_text = '';
$msg_class = '';
if ( '' !== $msg_raw ) {
if ( str_starts_with( $msg_raw, 'stress_cleanup_' ) ) {
$n = (int) substr( $msg_raw, strlen( 'stress_cleanup_' ) );
$msg_text = sprintf(
/* translators: %s: count */
__( '已清除 %s 個 stress test posts(連同 wp_postmeta + 7 張 flat 表的對應 row)。', '2meet-data-optimizer' ),
number_format_i18n( $n )
);
$msg_class = 'notice-success';
} elseif ( str_starts_with( $msg_raw, 'err_' ) ) {
$msg_text = __( '錯誤:請查看 wpdo_errors 日誌。', '2meet-data-optimizer' );
$msg_class = 'notice-error';
}
}
// Live state from the new state machine — drives initial render of cards.
$state = class_exists( 'TMDO_Post_Stress_Tester' ) ? TMDO_Post_Stress_Tester::get_progress( false ) : array();
$wpdo_pst_stat = (string) ( $state['status'] ?? 'idle' );
$is_running = ( 'running' === $wpdo_pst_stat || 'benchmarking' === $wpdo_pst_stat );
// Map of post_type → seed key count, surfaced inline next to the dropdown.
$post_type_options = array(
'product' => array( 'product (WC)', 5 ),
'hp_listing' => array( 'hp_listing (HivePress)', 7 ),
'hp_request' => array( 'hp_request', 5 ),
'hp_vendor' => array( 'hp_vendor', 5 ),
'attachment' => array( 'attachment', 2 ),
'nav_menu_item' => array( 'nav_menu_item', 5 ),
'post' => array( 'post', 2 ),
);
?>
<div class="wpdo-post-stress-test-tab">
<h2><?php esc_html_e( 'Post Entity 壓力測試 & Benchmark', '2meet-data-optimizer' ); ?></h2>
<div class="wpdo-notice-error">
⚠️ <strong><?php esc_html_e( '此工具僅供開發 / 測試環境使用。', '2meet-data-optimizer' ); ?></strong>
<?php esc_html_e( '會建立大量測試 post 並填滿對應 wp_postmeta + flat 表。請勿在生產環境執行。', '2meet-data-optimizer' ); ?>
</div>
<p class="description">
<?php esc_html_e( '透過自動產生大量測試 post,評估 post entity 反 EAV 系統在不同規模下的寫入吞吐與查詢效能。所有測試 post 均以 TMDO_STRESS_TEST_ 為 post_title 前綴,可一鍵清除。', '2meet-data-optimizer' ); ?>
</p>
<?php if ( '' !== $msg_text ) : ?>
<div class="notice <?php echo esc_attr( $msg_class ); ?> is-dismissible">
<p><?php echo esc_html( $msg_text ); ?></p>
</div>
<?php endif; ?>
<!-- Diagnose snapshot -->
<?php
$current_post_mode = (string) ( $diagnose['mode'] ?? 'disabled' );
$mode_color = 'aeav_only' === $current_post_mode ? '#28a745' : '#dc3545';
$mode_optimal = 'aeav_only' === $current_post_mode;
$mode_text_cls = $mode_optimal ? 'wpdo-text-success' : 'wpdo-text-danger';
$mode_card_cls = $mode_optimal ? 'ok' : 'warn';
$settings_tab_url = admin_url( 'tools.php?page=wp-data-optimizer&tab=settings' );
?>
<div class="wpdo-card wpdo-mt-3">
<h3 class="wpdo-mt-0"><?php esc_html_e( '當前 Post Entity 狀態', '2meet-data-optimizer' ); ?></h3>
<table class="widefat striped">
<tr>
<td><?php esc_html_e( 'Posts', '2meet-data-optimizer' ); ?></td>
<td><strong><?php echo esc_html( number_format_i18n( $diagnose['posts'] ) ); ?></strong></td>
<td><?php esc_html_e( 'Postmeta', '2meet-data-optimizer' ); ?></td>
<td><strong><?php echo esc_html( number_format_i18n( $diagnose['postmeta'] ) ); ?></strong></td>
</tr>
<tr>
<td><?php esc_html_e( 'Ratio', '2meet-data-optimizer' ); ?></td>
<td><strong>1:<?php echo esc_html( (string) $diagnose['ratio'] ); ?></strong></td>
<td><?php esc_html_e( 'Mode', '2meet-data-optimizer' ); ?></td>
<td><strong class="<?php echo esc_attr( $mode_text_cls ); ?>"><?php echo esc_html( $current_post_mode ); ?></strong></td>
</tr>
<tr>
<td colspan="2"><?php esc_html_e( 'Stress 測試 post 數', '2meet-data-optimizer' ); ?></td>
<td colspan="2"><strong id="wpdo-pst-count" class="wpdo-count-danger"><?php echo esc_html( number_format_i18n( $test_post_count ) ); ?></strong></td>
</tr>
</table>
</div>
<!-- 驗證反 EAV 優化指南 -->
<div class="wpdo-card wpdo-mode-card wpdo-mode-card--<?php echo esc_attr( $mode_card_cls ); ?>">
<h3 class="wpdo-mt-0 wpdo-h3-sm">
<?php if ( $mode_optimal ) : ?>
✅ <?php esc_html_e( 'Post Mode = aeav_only — 已具備驗證優化的條件', '2meet-data-optimizer' ); ?>
<?php else : ?>
⚠️
<?php
printf(
/* translators: %s: current mode */
esc_html__( 'Post Mode = %s — 此模式下壓力測試結果不會展示反 EAV 優化效果', '2meet-data-optimizer' ),
'<code class="wpdo-code-white">' . esc_html( $current_post_mode ) . '</code>'
);
?>
<?php endif; ?>
</h3>
<p class="wpdo-mt-1">
<strong><?php esc_html_e( '想看 wp_postmeta 真實減量?必須同時滿足兩個條件:', '2meet-data-optimizer' ); ?></strong>
</p>
<ol class="wpdo-list-ol-tight">
<li>
<?php
printf(
/* translators: 1: settings tab anchor open, 2: settings tab anchor close */
esc_html__( 'Post mode 設為 %1$saeav_only%2$s(前往設定 tab → Entity Bridge → Post entity)', '2meet-data-optimizer' ),
'<code class="wpdo-code-white">',
'</code>'
);
?>
<?php if ( ! $mode_optimal ) : ?>
<a href="<?php echo esc_url( $settings_tab_url ); ?>" class="button button-small wpdo-ml-2">→ <?php esc_html_e( '前往設定', '2meet-data-optimizer' ); ?></a>
<?php endif; ?>
</li>
<li>
<?php esc_html_e( '寫入模式選 🐢 Realistic(走 wp_insert_post + Hook Bus,會被攔截短路 wp_postmeta)', '2meet-data-optimizer' ); ?>
</li>
</ol>
<details class="wpdo-mt-1">
<summary class="wpdo-summary-toggle"><?php esc_html_e( '📐 模式 × 寫入路徑 → 預期結果矩陣', '2meet-data-optimizer' ); ?></summary>
<table class="widefat wpdo-mt-1 wpdo-table-inset">
<thead>
<tr class="wpdo-tr-header">
<th><?php esc_html_e( 'Post Mode', '2meet-data-optimizer' ); ?></th>
<th>⚡ Fast</th>
<th>🐢 Realistic</th>
</tr>
</thead>
<tbody>
<tr class="<?php echo esc_attr( 'disabled' === $current_post_mode ? 'wpdo-tr-highlight' : '' ); ?>">
<td><code>disabled</code></td>
<td>wp_postmeta 5 rows / flat 0 → <strong>1:5(無優化)</strong></td>
<td>wp_postmeta 5 / flat 0 → 1:5(無優化)</td>
</tr>
<tr class="<?php echo esc_attr( 'dual_write' === $current_post_mode ? 'wpdo-tr-highlight' : '' ); ?>">
<td><code>dual_write</code></td>
<td>wp_postmeta 5 / flat 0 → 1:5</td>
<td>wp_postmeta 5 + flat 1 → 1:5(有 flat 但 wp_postmeta 不減)</td>
</tr>
<tr class="<?php echo esc_attr( 'shadow_read' === $current_post_mode ? 'wpdo-tr-highlight' : '' ); ?>">
<td><code>shadow_read</code></td>
<td>wp_postmeta 5 / flat 0 → 1:5</td>
<td>wp_postmeta 5 + flat 1 → 1:5(讀走 flat,寫仍雙寫)</td>
</tr>
<tr class="<?php echo esc_attr( 'aeav_only' === $current_post_mode ? 'wpdo-tr-success' : '' ); ?>">
<td><code>aeav_only</code></td>
<td>wp_postmeta 5(直 SQL 繞過 Hook Bus)/ flat 0 → 1:5 ⚠️</td>
<td>wp_postmeta <strong>0</strong> / flat 1 → <strong>0:1(完全優化)</strong> ✅</td>
</tr>
</tbody>
</table>
<p class="description wpdo-mt-1">
<?php esc_html_e( '範例以 nav_menu_item(5 keys/post)為基準。Fast 模式直接 $wpdb->insert,故意繞過 Hook Bus → 即使 mode=aeav_only 也會寫滿 wp_postmeta(用途:快速灌 fixture 給 Query Router benchmark)。驗證反 EAV 優化效果一律用 Realistic。', '2meet-data-optimizer' ); ?>
</p>
</details>
</div>
<div class="wpdo-grid-2col">
<!-- Left: Configure & Start -->
<div class="wpdo-card">
<h3 class="wpdo-mt-0"><?php esc_html_e( '1. 設定並啟動測試', '2meet-data-optimizer' ); ?></h3>
<table class="form-table wpdo-mt-0">
<tr>
<th class="wpdo-th-main"><label for="wpdo-pst-post-type"><?php esc_html_e( 'Post Type', '2meet-data-optimizer' ); ?></label></th>
<td>
<select id="wpdo-pst-post-type" class="regular-text">
<?php foreach ( $post_type_options as $pt => $info ) : ?>
<?php
list( $label, $key_count ) = $info;
?>
<option value="<?php echo esc_attr( $pt ); ?>" data-keys="<?php echo esc_attr( (string) $key_count ); ?>">
<?php
printf(
/* translators: 1: post_type slug, 2: human label, 3: number of seeded keys */
esc_html__( '%1$s — %2$s · %3$d keys/post', '2meet-data-optimizer' ),
esc_html( $pt ),
esc_html( $label ),
(int) $key_count
);
?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( '每筆 post 會 seed 該 post_type 對應 entity group 的標準 meta keys(v2.9.1 entity registry)。', '2meet-data-optimizer' ); ?></p>
</td>
</tr>
<tr>
<th><label for="wpdo-pst-target"><?php esc_html_e( '要建立的 post 數', '2meet-data-optimizer' ); ?></label></th>
<td>
<input type="number" id="wpdo-pst-target" min="1" max="100000" value="500" class="regular-text" />
<p class="description"><?php esc_html_e( '常用:100 / 500 / 1,000 / 10,000(上限 100,000)', '2meet-data-optimizer' ); ?></p>
</td>
</tr>
<tr>
<th><label><?php esc_html_e( '寫入模式', '2meet-data-optimizer' ); ?></label></th>
<td>
<label class="wpdo-label-block">
<input type="radio" name="wpdo-pst-mode" value="fast" checked />
<strong>Fast</strong> — <?php esc_html_e( '直接 $wpdb->insert,跳過 WP filter chain(最快,但不測 Hook Bus)', '2meet-data-optimizer' ); ?>
</label>
<label class="wpdo-label-block">
<input type="radio" name="wpdo-pst-mode" value="realistic" />
<strong>Realistic</strong> — <?php esc_html_e( '走 wp_insert_post + update_post_meta(較慢,模擬生產路徑 + 觸發 Hook Bus → mode=dual_write+ 時 flat 表自動填入)', '2meet-data-optimizer' ); ?>
</label>
</td>
</tr>
<tr>
<th><label for="wpdo-pst-batch"><?php esc_html_e( '批次大小', '2meet-data-optimizer' ); ?></label></th>
<td>
<input type="number" id="wpdo-pst-batch" min="1" max="1000" value="200" class="small-text" />
<p class="description"><?php esc_html_e( '每批執行有 8 秒 wall-clock 上限(避免 nginx 504)。Fast 建議 200-1000;Realistic 建議 5-20(每 post ~100-300ms)。', '2meet-data-optimizer' ); ?></p>
</td>
</tr>
</table>
<p>
<button type="button" class="button button-primary button-large" id="wpdo-pst-start" <?php disabled( $is_running ); ?>>
<?php esc_html_e( '🚀 啟動壓力測試', '2meet-data-optimizer' ); ?>
</button>
<button type="button" class="button" id="wpdo-pst-cancel" <?php disabled( ! $is_running ); ?>>
<?php esc_html_e( '⏹ 取消', '2meet-data-optimizer' ); ?>
</button>
</p>
</div>
<!-- Right: Cleanup + Re-run benchmark -->
<div class="wpdo-card">
<h3 class="wpdo-mt-0"><?php esc_html_e( '清除測試資料', '2meet-data-optimizer' ); ?></h3>
<p>
<?php esc_html_e( '目前 stress test post 數:', '2meet-data-optimizer' ); ?>
<strong id="wpdo-pst-count-mirror" class="wpdo-count-danger">
<?php echo esc_html( number_format_i18n( $test_post_count ) ); ?>
</strong>
</p>
<p class="description">
<?php esc_html_e( '一鍵清除所有 post_title 前綴 TMDO_STRESS_TEST_ 的 post,連同 wp_postmeta + 7 張 flat 表的對應 row。', '2meet-data-optimizer' ); ?>
</p>
<p>
<button type="button" class="button button-secondary" id="wpdo-pst-cleanup" <?php disabled( $is_running || 0 === $test_post_count ); ?>>
<?php esc_html_e( '🗑 清除全部測試 post', '2meet-data-optimizer' ); ?>
</button>
</p>
<hr class="wpdo-hr-section" />
<p>
<button type="button" class="button" id="wpdo-pst-rerun-bench" <?php disabled( $is_running || 0 === $test_post_count ); ?>>
<?php esc_html_e( '📊 重跑 Benchmark(不新增資料)', '2meet-data-optimizer' ); ?>
</button>
</p>
<p class="description">
<?php esc_html_e( '針對最後一次啟動所選的 post_type 重新量測寫入指標(保留)+ DB 容量 + 查詢效能。', '2meet-data-optimizer' ); ?>
</p>
</div>
</div>
<!-- Progress section (live) -->
<div id="wpdo-pst-progress-card" class="wpdo-card wpdo-mt-3<?php echo esc_attr( $is_running ? '' : ' wpdo-hidden' ); ?>">
<h3 class="wpdo-mt-0"><?php esc_html_e( '2. 即時進度', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-bar-section">
<div class="wpdo-bar-header">
<span>
<strong id="wpdo-pst-pg-status"><?php echo esc_html( $wpdo_pst_stat ); ?></strong>
· <code id="wpdo-pst-pg-post-type"><?php echo esc_html( (string) ( $state['post_type'] ?? '' ) ); ?></code>
· <span id="wpdo-pst-pg-mode"><?php echo esc_html( (string) ( $state['mode'] ?? '' ) ); ?></span> mode
</span>
<span id="wpdo-pst-pg-pct" class="wpdo-bar-pct"><?php echo esc_html( (string) ( $state['pct'] ?? 0 ) ); ?>%</span>
</div>
<div class="wpdo-bar-track">
<div id="wpdo-pst-pg-bar" class="wpdo-bar-fill" style="--wpdo-bar-width:<?php echo esc_attr( (string) ( $state['pct'] ?? 0 ) ); ?>%"></div>
</div>
</div>
<table class="wpdo-stats-table">
<tr>
<td class="wpdo-td-muted"><?php esc_html_e( '已建立', '2meet-data-optimizer' ); ?></td>
<td class="wpdo-td-val"><span id="wpdo-pst-pg-processed"><?php echo esc_html( (string) ( $state['processed'] ?? 0 ) ); ?></span> / <span id="wpdo-pst-pg-target"><?php echo esc_html( (string) ( $state['target'] ?? 0 ) ); ?></span></td>
<td class="wpdo-td-muted"><?php esc_html_e( '速率', '2meet-data-optimizer' ); ?></td>
<td class="wpdo-td-val"><span id="wpdo-pst-pg-rate"><?php echo esc_html( (string) ( $state['rate_per_sec'] ?? 0 ) ); ?></span> posts/sec</td>
</tr>
<tr>
<td class="wpdo-td-muted"><?php esc_html_e( '已耗時', '2meet-data-optimizer' ); ?></td>
<td class="wpdo-td"><span id="wpdo-pst-pg-elapsed"><?php echo esc_html( (string) ( $state['elapsed_sec'] ?? 0 ) ); ?></span> 秒</td>
<td class="wpdo-td-muted"><?php esc_html_e( '預估剩餘', '2meet-data-optimizer' ); ?></td>
<td class="wpdo-td"><span id="wpdo-pst-pg-eta"><?php echo esc_html( (string) ( $state['eta_sec'] ?? 0 ) ); ?></span> 秒</td>
</tr>
<tr>
<td class="wpdo-td-muted"><?php esc_html_e( '完成批次', '2meet-data-optimizer' ); ?></td>
<td class="wpdo-td"><span id="wpdo-pst-pg-batches"><?php echo esc_html( (string) ( $state['batches_done'] ?? 0 ) ); ?></span></td>
<td class="wpdo-td-muted"><?php esc_html_e( 'PHP Peak Mem', '2meet-data-optimizer' ); ?></td>
<td class="wpdo-td"><span id="wpdo-pst-pg-mem"><?php echo esc_html( (string) round( ( (int) ( $state['peak_memory'] ?? 0 ) ) / 1048576, 1 ) ); ?></span> MB</td>
</tr>
</table>
</div>
<!-- Benchmark report -->
<div id="wpdo-pst-bench-card" class="wpdo-card wpdo-mt-3<?php echo esc_attr( ! empty( $state['benchmark'] ) ? '' : ' wpdo-hidden' ); ?>">
<h3 class="wpdo-mt-0"><?php esc_html_e( '3. Benchmark 報告', '2meet-data-optimizer' ); ?></h3>
<div id="wpdo-pst-bench-content"></div>
</div>
<!-- 7-group seed map info -->
<div class="wpdo-card wpdo-mt-3">
<h3 class="wpdo-mt-0"><?php esc_html_e( '4. TMDO_Post_Stress_Tester seed map(7 個 post_type)', '2meet-data-optimizer' ); ?></h3>
<p class="description">
<?php esc_html_e( '每個 stress post 自動 seed 對應 group 的 canonical meta keys(v2.9.1 entity registry 定義)。', '2meet-data-optimizer' ); ?>
</p>
<table class="widefat striped">
<thead>
<tr>
<th><?php esc_html_e( 'post_type', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Seeded keys', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Flat 表', '2meet-data-optimizer' ); ?></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>product</code></td>
<td>_price, _regular_price, _stock, _stock_status, _sku</td>
<td><code>wp_wpdo_post_wc_product</code></td>
</tr>
<tr>
<td><code>hp_listing</code></td>
<td>hp_price, hp_status, hp_featured, hp_verified, hp_vendor, hp_view_count, hp_expired_time</td>
<td><code>wp_wpdo_post_hp_listing_core</code></td>
</tr>
<tr>
<td><code>hp_request</code></td>
<td>hp_status, hp_user, hp_budget, hp_view_count, hp_expired_time</td>
<td><code>wp_wpdo_post_hp_request_core</code></td>
</tr>
<tr>
<td><code>hp_vendor</code></td>
<td>hp_user, hp_verified, hp_hourly_rate, hp_rating_count, hp_rating</td>
<td><code>wp_wpdo_post_hp_vendor_core</code></td>
</tr>
<tr>
<td><code>attachment</code></td>
<td>_wp_attached_file, _wp_attachment_image_alt</td>
<td><code>wp_wpdo_post_attachment</code></td>
</tr>
<tr>
<td><code>nav_menu_item</code></td>
<td>_menu_item_type, _menu_item_object_id, _menu_item_object, _menu_item_target, _menu_item_url</td>
<td><code>wp_wpdo_post_nav_menu_item</code></td>
</tr>
<tr>
<td><code>post</code></td>
<td>_thumbnail_id, _edit_last</td>
<td><code>wp_wpdo_post_wp_core</code></td>
</tr>
</tbody>
</table>
</div>
</div>