diff --git a/src/class-plugin.php b/src/class-plugin.php index 37c73fe..db60b33 100644 --- a/src/class-plugin.php +++ b/src/class-plugin.php @@ -60,8 +60,6 @@ public static function get_instance() { * @return void */ public function register_hooks() { - $this->accessed_options = \get_option( 'option_optimizer', [ 'used_options' => [] ] )['used_options']; - // Hook into all actions and filters to monitor option accesses. // @phpstan-ignore-next-line -- The 'all' hook does not need a return. \add_filter( 'all', [ $this, 'monitor_option_accesses' ] ); @@ -125,6 +123,9 @@ protected function add_option_usage( $option_name ) { /** * Update the 'option_optimizer' option with the list of used options at the end of the page load. * + * Uses transient batching to reduce database writes - only flushes to the main option + * every 5 minutes instead of on every request. + * * @return void */ public function update_tracked_options() { @@ -132,16 +133,95 @@ public function update_tracked_options() { if ( isset( $_GET['page'] ) && $_GET['page'] === 'aaa-option-optimizer' ) { return; } - // Retrieve the existing option_optimizer data. - $option_optimizer = get_option( 'option_optimizer', [ 'used_options' => [] ] ); - - $option_optimizer['used_options'] = $this->accessed_options; + // Handle reset: clear batch and main option. if ( $this->should_reset ) { + \delete_transient( 'option_optimizer_batch' ); + + $option_optimizer = \get_option( 'option_optimizer', [ 'used_options' => [] ] ); $option_optimizer['used_options'] = []; + \update_option( 'option_optimizer', $option_optimizer, false ); + return; + } + + // Get the batch data. + $batch_data = $this->get_batch_data(); + + // Add current request's options to the batch. + foreach ( $this->accessed_options as $option_name => $count ) { + if ( ! isset( $batch_data['options'][ $option_name ] ) ) { + $batch_data['options'][ $option_name ] = 0; + } + $batch_data['options'][ $option_name ] += $count; + } + + // Check if it's time to flush the batch. + $should_flush = ( \time() - $batch_data['last_flush'] ) >= $this->get_flush_interval(); + + // Flush batch to main option every 5 minutes. + if ( ! empty( $batch_data['options'] ) && $should_flush ) { + $this->flush_batch_to_option( $batch_data['options'] ); + + // Reset the batch data. + $batch_data = [ + 'options' => [], + 'last_flush' => \time(), + ]; + } + + // No expiry - batch is explicitly deleted on flush, expiry would only cause data loss. + \set_transient( 'option_optimizer_batch', $batch_data, 0 ); + } + + /** + * Get the batch data. + * + * @return array + */ + protected function get_batch_data() { + // Get existing batch (stores both data and flush timestamp in one transient). + $batch_data = \get_transient( 'option_optimizer_batch' ); + if ( ! \is_array( $batch_data ) || ! isset( $batch_data['options'], $batch_data['last_flush'] ) ) { + $batch_data = [ + 'options' => [], + 'last_flush' => \time(), + ]; + } + + return $batch_data; + } + + /** + * Get the flush interval. + * + * @return int + */ + protected function get_flush_interval() { + return (int) \apply_filters( 'aaa_option_optimizer_flush_interval', 5 * MINUTE_IN_SECONDS ); + } + + /** + * Flush the batched data to the main option_optimizer option. + * + * @param array $batch The batched option usage data. + * + * @return void + */ + protected function flush_batch_to_option( $batch ) { + + if ( empty( $batch ) ) { + return; + } + + $option_optimizer = \get_option( 'option_optimizer', [ 'used_options' => [] ] ); + + foreach ( $batch as $option_name => $count ) { + if ( ! isset( $option_optimizer['used_options'][ $option_name ] ) ) { + $option_optimizer['used_options'][ $option_name ] = 0; + } + $option_optimizer['used_options'][ $option_name ] += $count; } - // Update the 'option_optimizer' option with the new list. - update_option( 'option_optimizer', $option_optimizer, false ); + \update_option( 'option_optimizer', $option_optimizer, false ); } } diff --git a/uninstall.php b/uninstall.php index 4fa17e2..08493b6 100644 --- a/uninstall.php +++ b/uninstall.php @@ -12,5 +12,8 @@ exit; } +// Delete the batch transient. +delete_transient( 'option_optimizer_batch' ); + // Delete the plugin option. delete_option( 'option_optimizer' );