chore: initial snapshot of 2meet-data-optimizer v0.1.0
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
/**
|
||||
* TMDO_Monthly_Summary — 30-day rollup report (v2.4.0 M11).
|
||||
*
|
||||
* Generates a monthly executive summary covering:
|
||||
* - Health success / fail / rate-limit ratios over last 30 days
|
||||
* - Zone size growth (deltas from 30 days ago snapshot if available)
|
||||
* - Per-module FSM trajectory (who promoted, who rolled back)
|
||||
* - Snapshot retention overview (count, oldest, newest)
|
||||
*
|
||||
* Hooks the existing `wpdo_health_snapshot_monthly` action (Core line ~489)
|
||||
* so it runs once per month at 03:00 UTC on the 1st. Output:
|
||||
* 1. Persisted to wp_wpdo_audit (op='monthly_summary').
|
||||
* 2. Stored as wp_options.wpdo_monthly_summary_latest (autoload=no).
|
||||
* 3. Rendered on the admin Doctor tab "📅 Monthly Summary" sub-section.
|
||||
*
|
||||
* @package WP_Data_Optimizer
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Monthly summary aggregator.
|
||||
*/
|
||||
class TMDO_Monthly_Summary {
|
||||
|
||||
public const OPTION_LATEST = 'wpdo_monthly_summary_latest';
|
||||
|
||||
/**
|
||||
* Hook into Core's existing monthly cron.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register(): void {
|
||||
add_action( 'wpdo_health_snapshot_monthly', array( __CLASS__, 'generate' ), 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build + persist the monthly summary.
|
||||
*
|
||||
* @return array Summary structure.
|
||||
*/
|
||||
public static function generate(): array {
|
||||
$started_at = microtime( true );
|
||||
$summary = array(
|
||||
'period_start' => gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) ),
|
||||
'period_end' => gmdate( 'Y-m-d H:i:s' ),
|
||||
'health' => self::aggregate_health(),
|
||||
'fsm_trajectory' => self::aggregate_fsm(),
|
||||
'snapshots' => self::aggregate_snapshots(),
|
||||
'zone_growth' => self::aggregate_zone_growth(),
|
||||
'autoload_size' => self::measure_autoload(),
|
||||
'duration_ms' => 0,
|
||||
'generated_at' => gmdate( 'Y-m-d H:i:s' ),
|
||||
);
|
||||
$summary['duration_ms'] = (int) round( ( microtime( true ) - $started_at ) * 1000 );
|
||||
|
||||
update_option( self::OPTION_LATEST, $summary, false );
|
||||
|
||||
// v2.5.0 M14: archive into history (max 12 entries).
|
||||
$history = (array) get_option( 'wpdo_monthly_summary_history', array() );
|
||||
array_unshift( $history, $summary );
|
||||
$history = array_slice( $history, 0, 12 );
|
||||
update_option( 'wpdo_monthly_summary_history', $history, false );
|
||||
|
||||
if ( class_exists( 'TMDO_Logger' ) ) {
|
||||
TMDO_Logger::info(
|
||||
'monthly_summary',
|
||||
array(
|
||||
'health_success' => (int) ( $summary['health']['success'] ?? 0 ),
|
||||
'health_critical' => (int) ( $summary['health']['critical'] ?? 0 ),
|
||||
'fsm_promotions' => count( $summary['fsm_trajectory']['promotions'] ?? array() ),
|
||||
'fsm_rollbacks' => count( $summary['fsm_trajectory']['rollbacks'] ?? array() ),
|
||||
'snapshots_total' => (int) ( $summary['snapshots']['total'] ?? 0 ),
|
||||
'autoload_kb' => (int) round( ( $summary['autoload_size'] ?? 0 ) / 1024 ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
do_action( 'wpdo/monthly_summary_generated', $summary );
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the latest summary (from wp_options).
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public static function get_latest(): ?array {
|
||||
$v = get_option( self::OPTION_LATEST );
|
||||
return is_array( $v ) ? $v : null;
|
||||
}
|
||||
|
||||
// ─── private ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Aggregate health-check audit log entries from last 30 days.
|
||||
*
|
||||
* @return array {success:int, recommended:int, critical:int, total:int}
|
||||
*/
|
||||
private static function aggregate_health(): array {
|
||||
global $wpdb;
|
||||
$out = array(
|
||||
'success' => 0,
|
||||
'recommended' => 0,
|
||||
'critical' => 0,
|
||||
'total' => 0,
|
||||
);
|
||||
|
||||
// wp_wpdo_audit may not exist on early v1.x installs; check first.
|
||||
$audit = $wpdb->prefix . 'wpdo_audit';
|
||||
$exists = (int) $wpdb->get_var(
|
||||
$wpdb->prepare( // phpcs:ignore WordPress.DB
|
||||
'SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s',
|
||||
$audit
|
||||
)
|
||||
);
|
||||
if ( 0 === $exists ) {
|
||||
return $out;
|
||||
}
|
||||
// Health check audit rows have op='health_check_daily'.
|
||||
$rows = (array) $wpdb->get_results( "SELECT * FROM `{$audit}` WHERE op = 'health_check_daily' AND ts >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 DAY)", ARRAY_A ); // phpcs:ignore WordPress.DB
|
||||
foreach ( $rows as $r ) {
|
||||
$ctx = isset( $r['value_after'] ) ? json_decode( (string) $r['value_after'], true ) : null;
|
||||
if ( ! is_array( $ctx ) ) {
|
||||
continue;
|
||||
}
|
||||
$crit = (int) ( $ctx['critical'] ?? 0 );
|
||||
$rec = (int) ( $ctx['recommended'] ?? 0 );
|
||||
++$out['total'];
|
||||
if ( $crit > 0 ) {
|
||||
++$out['critical'];
|
||||
} elseif ( $rec > 0 ) {
|
||||
++$out['recommended'];
|
||||
} else {
|
||||
++$out['success'];
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort FSM trajectory: which modules changed state in the last 30
|
||||
* days. Reads `wpdo_fsm_state_entered` + current state.
|
||||
*
|
||||
* @return array {promotions:array, rollbacks:array, stationary:array}
|
||||
*/
|
||||
private static function aggregate_fsm(): array {
|
||||
$out = array(
|
||||
'promotions' => array(),
|
||||
'rollbacks' => array(),
|
||||
'stationary' => array(),
|
||||
);
|
||||
if ( ! class_exists( 'TMDO_Feature_Flags' ) ) {
|
||||
return $out;
|
||||
}
|
||||
$entered = (array) get_option( 'wpdo_fsm_state_entered', array() );
|
||||
$now = time();
|
||||
foreach ( TMDO_Feature_Flags::all() as $module => $state ) {
|
||||
$ts = isset( $entered[ $module ]['entered_at'] ) ? strtotime( (string) $entered[ $module ]['entered_at'] . ' UTC' ) : 0;
|
||||
$age_days = $ts > 0 ? (int) floor( ( $now - $ts ) / DAY_IN_SECONDS ) : null;
|
||||
|
||||
if ( $ts > 0 && ( $now - $ts ) <= ( 30 * DAY_IN_SECONDS ) ) {
|
||||
// Recently changed — bucket by direction.
|
||||
if ( 'idle' === $state ) {
|
||||
$out['rollbacks'][ $module ] = array(
|
||||
'state' => $state,
|
||||
'days_in_state' => $age_days,
|
||||
);
|
||||
} else {
|
||||
$out['promotions'][ $module ] = array(
|
||||
'state' => $state,
|
||||
'days_in_state' => $age_days,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$out['stationary'][ $module ] = array(
|
||||
'state' => $state,
|
||||
'days_in_state' => $age_days,
|
||||
);
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate snapshot retention stats from the snapshots table.
|
||||
*
|
||||
* @return array {total:int, oldest:string|null, newest:string|null, total_bytes:int}
|
||||
*/
|
||||
private static function aggregate_snapshots(): array {
|
||||
global $wpdb;
|
||||
$table = $wpdb->prefix . 'wpdo_snapshots';
|
||||
$exists = (int) $wpdb->get_var(
|
||||
$wpdb->prepare( // phpcs:ignore WordPress.DB
|
||||
'SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s',
|
||||
$table
|
||||
)
|
||||
);
|
||||
if ( 0 === $exists ) {
|
||||
return array(
|
||||
'total' => 0,
|
||||
'oldest' => null,
|
||||
'newest' => null,
|
||||
'total_bytes' => 0,
|
||||
);
|
||||
}
|
||||
$row = $wpdb->get_row( "SELECT COUNT(*) AS c, MIN(created_at) AS oldest, MAX(created_at) AS newest, COALESCE(SUM(size_bytes),0) AS bytes FROM `{$table}`", ARRAY_A ); // phpcs:ignore WordPress.DB
|
||||
return array(
|
||||
'total' => (int) ( $row['c'] ?? 0 ),
|
||||
'oldest' => isset( $row['oldest'] ) ? (string) $row['oldest'] : null,
|
||||
'newest' => isset( $row['newest'] ) ? (string) $row['newest'] : null,
|
||||
'total_bytes' => (int) ( $row['bytes'] ?? 0 ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-zone row count snapshot. Compares to previous month's value when
|
||||
* available (delta_rows positive = growth).
|
||||
*
|
||||
* @return array<string,array>
|
||||
*/
|
||||
private static function aggregate_zone_growth(): array {
|
||||
// Use historical site_metrics when available — avoids live COUNT(*) on large tables
|
||||
// and provides a 30-day delta (current − oldest snapshot).
|
||||
if ( class_exists( 'TMDO_Site_Metrics_Collector' ) ) {
|
||||
$latest = TMDO_Site_Metrics_Collector::get_latest_snapshot();
|
||||
if ( ! empty( $latest ) ) {
|
||||
// Get oldest snapshot within 30 days for delta calculation.
|
||||
$keys_of_interest = array( 'eav.postmeta_rows', 'flat.hot_rows', 'flat.cold_rows', 'flat.warm_rows', 'custom_tables.total_rows' );
|
||||
$out = array();
|
||||
foreach ( $keys_of_interest as $mk ) {
|
||||
if ( ! isset( $latest[ $mk ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$history = TMDO_Site_Metrics_Collector::get_history( $mk, 30 );
|
||||
$oldest = ! empty( $history ) ? (int) $history[0]['value'] : null;
|
||||
$current = (int) $latest[ $mk ];
|
||||
$out[ $mk ] = array(
|
||||
'rows' => $current,
|
||||
'delta_rows' => null !== $oldest ? $current - $oldest : null,
|
||||
);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: live COUNT(*) for warm + archive tables (pre-v2.6.2 installs).
|
||||
global $wpdb;
|
||||
$out = array();
|
||||
$keys = array( 'wpdo_warm', 'wpdo_archive' );
|
||||
foreach ( $keys as $slug ) {
|
||||
$table = $wpdb->prefix . $slug;
|
||||
$exists = (int) $wpdb->get_var(
|
||||
$wpdb->prepare( // phpcs:ignore WordPress.DB
|
||||
'SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s',
|
||||
$table
|
||||
)
|
||||
);
|
||||
if ( 0 === $exists ) {
|
||||
continue;
|
||||
}
|
||||
$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}`" ); // phpcs:ignore WordPress.DB
|
||||
$out[ $slug ] = array(
|
||||
'rows' => $count,
|
||||
'delta_rows' => null,
|
||||
);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total byte size of autoloaded options.
|
||||
*
|
||||
* @return int Bytes in autoloaded options.
|
||||
*/
|
||||
private static function measure_autoload(): int {
|
||||
global $wpdb;
|
||||
return (int) $wpdb->get_var( "SELECT COALESCE(SUM(LENGTH(option_value)),0) FROM `{$wpdb->options}` WHERE autoload = 'yes'" ); // phpcs:ignore WordPress.DB
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user