feat(2meet-data-optimizer): throttle shadow_read diffs per key and promote on clean drift checks
- The shadow diff logger records at most 20 rows per entity type + key per hour across all objects, on top of the existing per-object 5-minute window: a key stale on every object (WooCommerce total_sales, written by direct SQL) no longer logs a row per product read. - The auto-promoter's third condition is now TMDO_Drift_Check::clean_since(): every backfillable group of the entity checked within 2 days (sampled or full) with no drift; no record, no promotion. The shadow diff count no longer gates it (the filter's 4th argument is passed as 0). The Promotion Guard still refuses HivePress / WooCommerce sites. - Reason classification was dropped (decision 2026-09-27): it needs a schema change and drift-check already reports miss / diff / extra per key. - Verify: ShadowDiffThrottleTest, AutoPromoterDriftTest (RED before); dev36 simulated user shadow_read: clean_since yes, promotion still refused by the guard, mode unchanged. - PR: PR-075
This commit is contained in:
@@ -31,6 +31,14 @@ final class TMDO_Shadow_Diff_Logger {
|
||||
/** Rate limit 視窗(秒)— 同 entity+key 在此視窗內重複 diff 不重記 */
|
||||
public const RATELIMIT_WINDOW = 300;
|
||||
|
||||
/**
|
||||
* Rows per entity type + key per KEY_WINDOW, across all objects: a key
|
||||
* stale on every object (WooCommerce total_sales, written by direct SQL)
|
||||
* must not log a row for each object read.
|
||||
*/
|
||||
public const KEY_LIMIT = 20;
|
||||
public const KEY_WINDOW = 3600;
|
||||
|
||||
public static function table_name(): string {
|
||||
global $wpdb;
|
||||
return $wpdb->prefix . 'wpdo_shadow_diffs';
|
||||
@@ -69,7 +77,7 @@ final class TMDO_Shadow_Diff_Logger {
|
||||
}
|
||||
|
||||
// 不一致 → 檢查 rate limit
|
||||
if ( self::is_rate_limited( $entity_type, $entity_id, $meta_key ) ) {
|
||||
if ( self::is_rate_limited( $entity_type, $entity_id, $meta_key ) || self::key_over_limit( $entity_type, $meta_key ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,6 +191,21 @@ final class TMDO_Shadow_Diff_Logger {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count this diff against the key's KEY_LIMIT; true once the limit is
|
||||
* reached within the current KEY_WINDOW (the window starts at its first row).
|
||||
*/
|
||||
private static function key_over_limit( string $entity_type, string $meta_key ): bool {
|
||||
$key = 'wpdo_sd_kl_' . md5( "{$entity_type}:{$meta_key}" );
|
||||
$state = get_transient( $key );
|
||||
$state = is_array( $state ) ? $state : array( 0, time() );
|
||||
if ( $state[0] >= self::KEY_LIMIT ) {
|
||||
return true;
|
||||
}
|
||||
set_transient( $key, array( $state[0] + 1, $state[1] ), max( 1, $state[1] + self::KEY_WINDOW - time() ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 寫入 diff row
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user