perf(2meet-data-optimizer-hivepress-addon): expire object-cache transients

- with an external object cache, a HivePress timeout entry expires when it
  records; the value (written before its timeout) after at most a week — a
  miss only makes HivePress rebuild it. Entries of deleted posts no longer
  accumulate without end
- Verify: HivepressTransientFilterTest TTL test, integration 53, unit 145;
  dev36 Redis TTL on tmdo_hp_transient keys
- PR: PR-099
This commit is contained in:
2026-09-27 16:42:52 +08:00
parent f06a80eae5
commit d129139c7d
3 changed files with 37 additions and 3 deletions
@@ -80,6 +80,9 @@ 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;
/**
* Register the four metadata filters on `init`. Idempotent — safe to call
* multiple times.
@@ -267,8 +270,10 @@ final class TMDO_Hivepress_Transient_Filter {
* In wp_options both halves of the transient pair are written with
* `autoload=no` (they are read on-demand only — never via
* wp_load_alloptions() — so eagerly loading them on every page would defeat
* the point of the move). HivePress checks the timeout itself, so the
* cache entries get no TTL of their own.
* the point of the move). In the object cache a timeout entry expires when
* it says; the value, written before its timeout, after MAX_CACHE_TTL — a
* miss only makes HivePress rebuild it — so entries of deleted posts do
* not pile up (2.3 PR-099).
*
* @param int $post_id Post ID.
* @param string $meta_key Original `_transient_*` meta_key.
@@ -281,7 +286,8 @@ final class TMDO_Hivepress_Transient_Filter {
$option_name = ( $is_timeout ? '_transient_timeout_' : '_transient_' ) . $translated;
if ( wp_using_ext_object_cache() ) {
wp_cache_set( $option_name, $meta_value, self::CACHE_GROUP );
$ttl = $is_timeout ? min( self::MAX_CACHE_TTL, max( 1, (int) $meta_value - time() ) ) : self::MAX_CACHE_TTL;
wp_cache_set( $option_name, $meta_value, self::CACHE_GROUP, $ttl );
return;
}
update_option( $option_name, $meta_value, false );
@@ -213,6 +213,26 @@ class HivepressTransientFilterTest extends TestCase {
$this->assertSame( array( '99999999' ), WPDO_Hivepress_Transient_Filter::on_read( null, 100, '_transient_timeout_hp_models/cat/v1', true ) );
}
/**
* Entries expire in the object cache too (2.3 PR-099): the timeout at the
* time it records, the value (written first, before its timeout is known)
* after at most a week. Orphans of deleted posts no longer pile up.
*/
public function test_with_an_object_cache_entries_get_a_ttl(): void {
$GLOBALS['_wp_using_ext_object_cache'] = true;
$key = '_transient_hp_models/cat/v1';
$translated = WPDO_Hivepress_Transient_Filter::translate_key( 100, $key );
WPDO_Hivepress_Transient_Filter::on_update( null, 100, $key, 'val', '' );
WPDO_Hivepress_Transient_Filter::on_update( null, 100, '_transient_timeout_hp_models/cat/v1', (string) ( time() + 3600 ), '' );
WPDO_Hivepress_Transient_Filter::on_update( null, 101, '_transient_timeout_hp_models/cat/v1', (string) ( time() - 5 ), '' );
$ttl = $GLOBALS['_wp_cache_ttl'][ WPDO_Hivepress_Transient_Filter::CACHE_GROUP ];
$this->assertSame( WPDO_Hivepress_Transient_Filter::MAX_CACHE_TTL, $ttl[ '_transient_' . $translated ] );
$this->assertEqualsWithDelta( 3600, $ttl[ '_transient_timeout_' . $translated ], 2 );
$this->assertSame( 1, $ttl[ '_transient_timeout_' . WPDO_Hivepress_Transient_Filter::translate_key( 101, $key ) ], 'already expired' );
}
public function test_with_an_object_cache_a_value_left_in_wp_options_is_not_read(): void {
// A miss must not fall back to get_option(): the option is absent after
// cleanup and WordPress would add its name to `notoptions` for good.
+8
View File
@@ -21,6 +21,14 @@ if ( ! file_exists( $tmdo_core_bootstrap ) ) {
. dirname( __DIR__, 3 ) . '/2meet-data-optimizer'
);
}
// Defined before the core bootstrap (which only adds it when missing) so tests can read the TTL.
if ( ! function_exists( 'wp_cache_set' ) ) {
function wp_cache_set( $key, $value, $group = '', $ttl = 0 ): bool {
$GLOBALS['_wp_cache'][ $group ][ $key ] = $value;
$GLOBALS['_wp_cache_ttl'][ $group ][ $key ] = (int) $ttl;
return true;
}
}
require_once $tmdo_core_bootstrap;
// External object cache switch for the transient filter tests (Redis on real sites).