900, // 15 minutes in seconds (literal for phpcs/scanners). 'display' => sprintf( /* translators: %d: number of minutes. */ __( 'Every %d Minutes (AI Content by Parallax)', 'ai-content-by-parallax' ), self::INTERVAL_MIN ), ); } return $schedules; } /** * Schedule the event if missing. */ public static function maybe_schedule(): void { if ( ! wp_next_scheduled( self::EVENT_HOOK ) ) { wp_schedule_event( time() + self::SCHEDULE_OFFSET_SECONDS, self::INTERVAL_KEY, self::EVENT_HOOK ); } } /** * Activation hook to ensure cron is scheduled. */ public static function activate(): void { self::maybe_schedule(); } /** * Deactivation hook to clear scheduled events and locks. */ public static function deactivate(): void { // Clear all future instances of this hook. $timestamp = wp_next_scheduled( self::EVENT_HOOK ); while ( $timestamp ) { wp_unschedule_event( $timestamp, self::EVENT_HOOK ); $timestamp = wp_next_scheduled( self::EVENT_HOOK ); } // Drop lock if set. delete_transient( self::lock_key() ); } /** * Cron callback that pulls pending batch requests while avoiding overlaps. */ public static function run(): void { $lock_key = self::lock_key(); // Prevent overlapping runs. if ( get_transient( $lock_key ) ) { Logger::write_log( 'info', '[CRON] pull_all skipped: lock present.' ); return; } /* * Lock TTL slightly under interval. * Clamp to a safe range to avoid overly long locks. */ $ttl_seconds = (int) max( 60, min( self::INTERVAL_SECONDS - 30, 3600 ) ); // Acquire lock ASAP to avoid race conditions. set_transient( $lock_key, 1, $ttl_seconds ); try { if ( ! BatchRequest::has_incomplete_batch_requests() ) { Logger::write_log( 'info', '[CRON] No incomplete batch requests. Skipping pull_all.' ); return; } Logger::write_log( 'info', '[CRON] Incomplete batch requests found. Running pull_all().' ); BatchRequest::pull_all(); } catch ( \Throwable $e ) { Logger::write_log( 'info', '[CRON] pull_all error: ' . $e->getMessage() ); } finally { delete_transient( $lock_key ); } } /** * Get the lock transient key. * * @return string */ protected static function lock_key(): string { return 'ai_content_by_parallax_pull_all_lock'; } }