fix(2meet-data-optimizer): 1.4.2 acceptance fixes for archive sweep and uninstall
Anti-EAV Lint + Quality Gate / anti-eav-lint (pull_request) Successful in 21s
Tests / PHP Lint (pull_request) Successful in 18s
Tests / Unit Tests (pull_request) Successful in 41s
Tests / Integration Tests (pull_request) Successful in 1m25s
Tests / PHPCS (pull_request) Successful in 44s
Tests / PHPStan (pull_request) Successful in 47s
Anti-EAV Lint + Quality Gate / anti-eav-lint (pull_request) Successful in 21s
Tests / PHP Lint (pull_request) Successful in 18s
Tests / Unit Tests (pull_request) Successful in 41s
Tests / Integration Tests (pull_request) Successful in 1m25s
Tests / PHPCS (pull_request) Successful in 44s
Tests / PHPStan (pull_request) Successful in 47s
- Archive sweep waits until idx_original_meta exists (a plugin update does not run dbDelta; without the index NOT EXISTS scans the archive per row) - Duplicate archive cleanup runs from WP-CLI only, never in a web activation - Uninstall keeps the LatePoint add-on's tmdo_latepoint_db_version - AJAX save errors stay on screen instead of fading after 2.5 s - Verify: 2 new integration tests; unit 711, integration 612, phpcs 0, stan OK - PR: PR-141 (2.4.2 correctness review)
This commit is contained in:
+10
-2
@@ -32,8 +32,16 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|||||||
### 升級後
|
### 升級後
|
||||||
|
|
||||||
- 每個站台執行一次 `wp tmdo install`:為 archive 表加上 `idx_original_meta` 並刪除重複的 archive
|
- 每個站台執行一次 `wp tmdo install`:為 archive 表加上 `idx_original_meta` 並刪除重複的 archive
|
||||||
列。表很大時請在離峰時段執行(CLI,不在 web 請求中做 DDL)。未執行也不影響功能,只是 sweep
|
列(刪除重複只在 CLI 執行,web 啟用外掛時不做)。表很大時請在離峰時段執行。
|
||||||
的 NOT EXISTS 檢查少了索引。
|
- **未執行前 archive sweep 會暫停**(沒有索引時 NOT EXISTS 會對每筆候選掃整張 archive 表)。
|
||||||
|
sweep 只複製、不刪 postmeta,暫停不會遺失資料。
|
||||||
|
- 解除安裝的 option 清理保留 LatePoint AddOn 的 `tmdo_latepoint_db_version`;設定頁的 AJAX 錯誤訊息
|
||||||
|
不再 2.5 秒後自動消失。(PR-141,驗收修正)
|
||||||
|
|
||||||
|
### 已知、未修改
|
||||||
|
|
||||||
|
- 已歸檔的 meta 以 `meta_id` 判斷:文章還原、修改後再次丟到回收桶,archive 保留的是第一次的值。
|
||||||
|
- post 壓測工具預設仍是 `product`,但 product 已沒有 flat 表,量測會回報 `flat_table_missing`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a transient status message, then fade it out after 2.5s.
|
* Show a status message; success fades out after 2.5s, errors stay.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} statusEl
|
* @param {HTMLElement} statusEl
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
@@ -56,6 +56,9 @@
|
|||||||
statusEl.style.color = isError ? 'var(--color-error, #b03d3d)' : 'var(--color-success, #5a8c5a)';
|
statusEl.style.color = isError ? 'var(--color-error, #b03d3d)' : 'var(--color-success, #5a8c5a)';
|
||||||
statusEl.classList.add( 'visible' );
|
statusEl.classList.add( 'visible' );
|
||||||
clearTimeout( statusEl._wpdo_timer );
|
clearTimeout( statusEl._wpdo_timer );
|
||||||
|
if ( isError ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
statusEl._wpdo_timer = setTimeout( () => {
|
statusEl._wpdo_timer = setTimeout( () => {
|
||||||
statusEl.classList.remove( 'visible' );
|
statusEl.classList.remove( 'visible' );
|
||||||
}, 2500 );
|
}, 2500 );
|
||||||
|
|||||||
@@ -218,7 +218,8 @@ class TMDO_Installer {
|
|||||||
self::install_v2_tables();
|
self::install_v2_tables();
|
||||||
|
|
||||||
// 1.4.2: archive sweeps before this release copied trashed meta daily.
|
// 1.4.2: archive sweeps before this release copied trashed meta daily.
|
||||||
if ( class_exists( 'TMDO_Zone_Archive' ) ) {
|
// One unbounded DELETE: CLI only, never in a web activation.
|
||||||
|
if ( defined( 'WP_CLI' ) && WP_CLI && class_exists( 'TMDO_Zone_Archive' ) ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
$archive = TMDO_Zone_Archive::table();
|
$archive = TMDO_Zone_Archive::table();
|
||||||
if ( $archive === $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $archive ) ) ) ) {
|
if ( $archive === $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $archive ) ) ) ) {
|
||||||
@@ -993,6 +994,7 @@ class TMDO_Installer {
|
|||||||
// accidental option removal.
|
// accidental option removal.
|
||||||
// 1.4.2: tmdo_* options and their transients too (tmdo_schema_deferred,
|
// 1.4.2: tmdo_* options and their transients too (tmdo_schema_deferred,
|
||||||
// tmdo_entity_indexes, tmdo_cov_* …) — a reinstall saw stale ones.
|
// tmdo_entity_indexes, tmdo_cov_* …) — a reinstall saw stale ones.
|
||||||
|
// tmdo_latepoint_* belongs to the LatePoint add-on.
|
||||||
foreach ( array( 'wpdo_', 'tmdo_', '_transient_tmdo_', '_transient_timeout_tmdo_' ) as $prefix ) {
|
foreach ( array( 'wpdo_', 'tmdo_', '_transient_tmdo_', '_transient_timeout_tmdo_' ) as $prefix ) {
|
||||||
$residual = $wpdb->get_col(
|
$residual = $wpdb->get_col(
|
||||||
$wpdb->prepare(
|
$wpdb->prepare(
|
||||||
@@ -1001,7 +1003,7 @@ class TMDO_Installer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
foreach ( (array) $residual as $opt ) {
|
foreach ( (array) $residual as $opt ) {
|
||||||
if ( preg_match( '/^(_transient_(timeout_)?)?(wpdo|tmdo)_[a-zA-Z0-9_]+$/', $opt ) && delete_option( $opt ) ) {
|
if ( preg_match( '/^(_transient_(timeout_)?)?(wpdo|tmdo)_(?!latepoint_)[a-zA-Z0-9_]+$/', $opt ) && delete_option( $opt ) ) {
|
||||||
++$options_deleted;
|
++$options_deleted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,10 @@ class TMDO_Zone_Archive {
|
|||||||
// Rows already archived are skipped: the sweep used to copy the same
|
// Rows already archived are skipped: the sweep used to copy the same
|
||||||
// trashed meta again every day (2.4.2 PR-136).
|
// trashed meta again every day (2.4.2 PR-136).
|
||||||
$table = self::table();
|
$table = self::table();
|
||||||
|
// Without the index each candidate scans the archive: wait for `wp tmdo install`.
|
||||||
|
if ( ! TMDO_IS_SQLITE && ! $wpdb->get_var( "SHOW INDEX FROM `{$table}` WHERE Key_name = 'idx_original_meta'" ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- internal table name
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from internal self::table()
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name from internal self::table()
|
||||||
$rows = $wpdb->get_results(
|
$rows = $wpdb->get_results(
|
||||||
|
|||||||
+1
-1
@@ -109,7 +109,7 @@ The Classifier tab analyzes your existing `wp_postmeta` data (access frequency,
|
|||||||
|
|
||||||
= 1.4.2 =
|
= 1.4.2 =
|
||||||
* The settings page shows why a bridge-mode change was refused (unsafe jump, Promotion Guard) instead of reporting it saved
|
* The settings page shows why a bridge-mode change was refused (unsafe jump, Promotion Guard) instead of reporting it saved
|
||||||
* Archive sweep skips meta already archived; `wp tmdo install` adds an index on original_meta_id and removes duplicate archive rows
|
* Archive sweep skips meta already archived; `wp tmdo install` adds an index on original_meta_id and removes duplicate archive rows (the sweep waits until the index exists)
|
||||||
* Entity health sees a running backfill (batches are scheduled with entity and group args)
|
* Entity health sees a running backfill (batches are scheduled with entity and group args)
|
||||||
* Deactivate and uninstall clear every scheduled event, including ones scheduled with args; uninstall also removes tmdo_* options and transients
|
* Deactivate and uninstall clear every scheduled event, including ones scheduled with args; uninstall also removes tmdo_* options and transients
|
||||||
* The refused-uninstall notice points to `wp tmdo bridge-restore-native`; the post stress tester no longer uses the retired wc_product table
|
* The refused-uninstall notice points to `wp tmdo bridge-restore-native`; the post stress tester no longer uses the retired wc_product table
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class InstallerCleanupTest extends TestCase {
|
|||||||
/** tmdo_* options and transients were left behind; a reinstall then saw stale deferrals and indexes (F-5). */
|
/** tmdo_* options and transients were left behind; a reinstall then saw stale deferrals and indexes (F-5). */
|
||||||
public function test_uninstall_removes_tmdo_options_and_transients(): void {
|
public function test_uninstall_removes_tmdo_options_and_transients(): void {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
foreach ( array( 'tmdo_schema_deferred', 'tmdo_entity_indexes', '_transient_tmdo_cov_x', '_transient_timeout_tmdo_cov_x', 'hivepress_settings' ) as $name ) {
|
foreach ( array( 'tmdo_schema_deferred', 'tmdo_entity_indexes', '_transient_tmdo_cov_x', '_transient_timeout_tmdo_cov_x', 'hivepress_settings', 'tmdo_latepoint_db_version' ) as $name ) {
|
||||||
$wpdb->insert( $wpdb->options, array( 'option_name' => $name, 'option_value' => '1' ) );
|
$wpdb->insert( $wpdb->options, array( 'option_name' => $name, 'option_value' => '1' ) );
|
||||||
$GLOBALS['_wp_options'][ $name ] = '1';
|
$GLOBALS['_wp_options'][ $name ] = '1';
|
||||||
}
|
}
|
||||||
@@ -109,6 +109,7 @@ class InstallerCleanupTest extends TestCase {
|
|||||||
$this->assertArrayNotHasKey( $name, $GLOBALS['_wp_options'], $name );
|
$this->assertArrayNotHasKey( $name, $GLOBALS['_wp_options'], $name );
|
||||||
}
|
}
|
||||||
$this->assertArrayHasKey( 'hivepress_settings', $GLOBALS['_wp_options'], 'other plugins untouched' );
|
$this->assertArrayHasKey( 'hivepress_settings', $GLOBALS['_wp_options'], 'other plugins untouched' );
|
||||||
|
$this->assertArrayHasKey( 'tmdo_latepoint_db_version', $GLOBALS['_wp_options'], 'the LatePoint add-on owns it' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_drops_static_tables(): void {
|
public function test_drops_static_tables(): void {
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ class ZoneArchiveIntegrationTest extends TestCase {
|
|||||||
`original_meta_id` bigint(20) unsigned NOT NULL DEFAULT 0,
|
`original_meta_id` bigint(20) unsigned NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `post_id` (`post_id`),
|
KEY `post_id` (`post_id`),
|
||||||
KEY `archived_at` (`archived_at`)
|
KEY `archived_at` (`archived_at`),
|
||||||
|
KEY `idx_original_meta` (`original_meta_id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -81,6 +82,20 @@ class ZoneArchiveIntegrationTest extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Until `wp tmdo install` adds the index, NOT EXISTS would scan the archive per candidate. */
|
||||||
|
public function test_sweep_waits_for_the_original_meta_index(): void {
|
||||||
|
global $wpdb;
|
||||||
|
$this->seed_trashed_post();
|
||||||
|
$wpdb->query( 'ALTER TABLE `' . self::TABLE . '` DROP INDEX `idx_original_meta`' );
|
||||||
|
try {
|
||||||
|
$this->assertSame( 0, WPDO_Zone_Archive::sweep( 90, false ) );
|
||||||
|
$this->assertSame( 0, $this->archive_rows() );
|
||||||
|
} finally {
|
||||||
|
$wpdb->query( 'ALTER TABLE `' . self::TABLE . '` ADD KEY `idx_original_meta` (`original_meta_id`)' );
|
||||||
|
$this->drop_posts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function test_remove_duplicates_keeps_the_first_copy_of_each_meta_row(): void {
|
public function test_remove_duplicates_keeps_the_first_copy_of_each_meta_row(): void {
|
||||||
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_price', '5', 101 );
|
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_price', '5', 101 );
|
||||||
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_price', '5', 101 );
|
WPDO_Zone_Archive::archive( 10, 'hp_listing', 'hp_price', '5', 101 );
|
||||||
|
|||||||
Reference in New Issue
Block a user