hardening 2.4: 2meet-data-optimizer-hivepress-addon 0.5.0 (sort indexes, a day's transient TTL) #5

Merged
wpdev merged 3 commits from hardening/2.4 into main 2026-09-27 23:57:57 +08:00
6 changed files with 50 additions and 12 deletions
+2 -2
View File
@@ -3,7 +3,7 @@
* Plugin Name: 2meet Data Optimizer — Hivepress AddOn
* Plugin URI: https://2meet.io/2meet-data-optimizer/hivepress
* Description: HivePress + 12 擴充整合:13 adapter (core/reviews/bookings/messages/memberships/requests/favorites/statistics/tags/seo/social-links/blocks/marketplace) + 7 HPCT interceptor + 5 query interceptor + admin tab + CLI namespace
* Version: 0.4.0
* Version: 0.5.0
* Requires at least: 6.0
* Tested up to: 6.9.4
* Requires PHP: 8.1
@@ -24,7 +24,7 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
define( 'TMDO_HIVEPRESS_VERSION', '0.4.0' );
define( 'TMDO_HIVEPRESS_VERSION', '0.5.0' );
define( 'TMDO_HIVEPRESS_PATH', plugin_dir_path( __FILE__ ) );
define( 'TMDO_HIVEPRESS_URL', plugin_dir_url( __FILE__ ) );
define( 'TMDO_HIVEPRESS_FILE', __FILE__ );
+20
View File
@@ -11,6 +11,26 @@ Versioning follows [Semantic Versioning](https://semver.org/).
---
## [0.5.0] — 2026-09-28
Released with 2meet-data-optimizer 1.4.0 (hardening 2.4).
### Changed
- `hp_listing_core` declares `(hp_price, post_id)`, `(hp_rating, post_id)` and
`(hp_hourly_rate, post_id)` indexes for the listing sorts HivePress offers:
with core 1.4.0 reading the ID tie-break from the flat `post_id`, a sort walks
the index and stops at the page instead of sorting every match (dev36, 1.3M
listings: price sort 14 s native → 0.37 s routed, hourly rate 11.5 s → 0.35 s,
rating 43 s → 0.51 s). Core 1.3.0 ignores indexes ending with `post_id`, so
nothing changes there. The pinned schema hash is unchanged. (PR-119)
- Transient entries in a persistent object cache live a day (`MAX_CACHE_TTL`,
HivePress's own default) instead of a week; a timeout still outlives its value
by a minute. At a week dev36 kept 4.7M entries (1 GB) in the shared Redis and
pushed other sites' keys out. Entries already cached keep their week. (PR-120)
---
## [0.4.0] — 2026-09-27
Released with 2meet-data-optimizer 1.3.0 (hardening 2.3).
@@ -266,8 +266,13 @@ final class TMDO_HivePress_Entity_Groups {
// Only with core 1.3.0+ (ddl_allowed()): older cores run the ALTER
// inside whatever request comes first, a visitor's included.
'indexes' => method_exists( 'TMDO_Schema_Manager', 'ddl_allowed' ) ? array(
'hourly_rate' => array( 'hp_hourly_rate' ),
'rating_count' => array( 'hp_rating_count' ),
'hourly_rate' => array( 'hp_hourly_rate' ),
'rating_count' => array( 'hp_rating_count' ),
// HivePress sorts: the value then the ID tie-break, which core 1.4.0
// reads in order (flat-first) instead of sorting every match.
'price_id' => array( 'hp_price', 'post_id' ),
'rating_id' => array( 'hp_rating', 'post_id' ),
'hourly_rate_id' => array( 'hp_hourly_rate', 'post_id' ),
) : array(),
)
);
@@ -80,8 +80,8 @@ final class TMDO_Hivepress_Transient_Filter {
/** Object cache group used instead of wp_options when a persistent cache is present. */
public const CACHE_GROUP = 'tmdo_hp_transient';
/** Longest an object-cache entry lives: a week (HivePress's default is a day). */
public const MAX_CACHE_TTL = 604800;
/** Longest an object-cache entry lives: a day, HivePress's own default (2.4 PR-120). */
public const MAX_CACHE_TTL = DAY_IN_SECONDS;
/**
* Register the four metadata filters on `init`. Idempotent — safe to call
@@ -215,11 +215,14 @@ class HivepressTransientFilterTest extends TestCase {
/**
* Entries expire in the object cache too (2.3 PR-099): the value (written
* first, before its timeout is known) after a week, the timeout after the
* first, before its timeout is known) after a day, the timeout after the
* later of that and its own time, plus a minute. Orphans of deleted posts
* no longer pile up.
*/
public function test_with_an_object_cache_entries_get_a_ttl(): void {
// A day, HivePress's own default: at a week, 1.3M listings kept 4.7M
// entries (1 GB) in a shared Redis and pushed other sites' keys out (2.4 PR-120).
$this->assertSame( 86400, WPDO_Hivepress_Transient_Filter::MAX_CACHE_TTL );
$GLOBALS['_wp_using_ext_object_cache'] = true;
$key = '_transient_hp_models/cat/v1';
$translated = WPDO_Hivepress_Transient_Filter::translate_key( 100, $key );
+15 -5
View File
@@ -37,9 +37,15 @@ class HivePressEntityGroupsTest extends TestCase {
'hp_user' => 'hp_vendor_core',
);
private function post_adapter(): TMDO_Entity_Adapter_Interface {
$adapter = $this->createMock( TMDO_Entity_Adapter_Interface::class );
$adapter->method( 'get_entity_id_column' )->willReturn( 'post_id' );
return $adapter;
}
protected function setUp(): void {
TMDO_Entity_Registry::init();
TMDO_Entity_Registry::register_adapter( 'post', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
TMDO_Entity_Registry::register_adapter( 'post', $this->post_adapter() );
TMDO_Entity_Registry::register_adapter( 'user', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
TMDO_HivePress_Entity_Groups::register_entity_fields();
}
@@ -66,8 +72,12 @@ class HivePressEntityGroupsTest extends TestCase {
public function test_listing_columns_filtered_and_sorted_on_are_indexed(): void {
$this->assertSame(
array(
'hourly_rate' => array( 'hp_hourly_rate' ),
'rating_count' => array( 'hp_rating_count' ),
'hourly_rate' => array( 'hp_hourly_rate' ),
'rating_count' => array( 'hp_rating_count' ),
// Sorts: the value then the ID tie-break, read in order (2.4 PR-119).
'price_id' => array( 'hp_price', 'post_id' ),
'rating_id' => array( 'hp_rating', 'post_id' ),
'hourly_rate_id' => array( 'hp_hourly_rate', 'post_id' ),
),
TMDO_Entity_Registry::get_group_indexes( 'post', 'hp_listing_core' )
);
@@ -94,7 +104,7 @@ class HivePressEntityGroupsTest extends TestCase {
$GLOBALS['_wp_did_action']['after_setup_theme'] = 1;
try {
TMDO_Entity_Registry::init();
TMDO_Entity_Registry::register_adapter( 'post', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
TMDO_Entity_Registry::register_adapter( 'post', $this->post_adapter() );
TMDO_Entity_Registry::register_adapter( 'user', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
TMDO_HivePress_Entity_Groups::register_entity_fields();
} finally {
@@ -118,7 +128,7 @@ class HivePressEntityGroupsTest extends TestCase {
add_filter( 'tmdo_hp_listing_attribute_fields', static fn() => array( 'hp_warranty' => 'integer' ) );
try {
TMDO_Entity_Registry::init();
TMDO_Entity_Registry::register_adapter( 'post', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
TMDO_Entity_Registry::register_adapter( 'post', $this->post_adapter() );
TMDO_Entity_Registry::register_adapter( 'user', $this->createMock( TMDO_Entity_Adapter_Interface::class ) );
TMDO_HivePress_Entity_Groups::register_entity_fields();
$this->assertSame( array(), TMDO_Entity_Registry::get_group_keys( 'post', 'hp_listing_attrs' ), 'plugins_loaded run: not yet' );