From cc6979b0857d2ace2946c3f6635395dd748d9921 Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Sun, 19 Apr 2026 03:40:05 +0700 Subject: [PATCH] Fix false stall detection for multi-article chained generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The frontend status poller measured elapsed time from `started` (set once at the beginning of the run) and declared a stall at 300s. Three articles take ~6 minutes total, so the poller always false-alarmed on article 3. Now every stage broadcast writes `last_updated` into the transient, and the stall check compares idle time since the last update — not since the run started. This lets N-article runs complete without false timeout as long as each individual article finishes within 5 minutes. Co-Authored-By: Claude Opus 4.6 --- includes/class-executor.php | 22 +++++++++++----------- includes/core/class-article-worker.php | 3 ++- includes/core/class-pipeline-runner.php | 10 ++++++---- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/includes/class-executor.php b/includes/class-executor.php index ff18a06..01bfc16 100644 --- a/includes/class-executor.php +++ b/includes/class-executor.php @@ -105,9 +105,10 @@ class PRAutoBlogger_Executor { // Write initial "running" status for the frontend to poll. set_transient( self::STATUS_TRANSIENT, [ - 'status' => 'running', - 'stage' => __( 'Starting generation...', 'prautoblogger' ), - 'started' => time(), + 'status' => 'running', + 'stage' => __( 'Starting generation...', 'prautoblogger' ), + 'started' => time(), + 'last_updated' => time(), ], self::STATUS_TTL ); // Schedule immediate one-shot cron event. WordPress will fire this @@ -190,11 +191,7 @@ class PRAutoBlogger_Executor { } } - /** - * Cron handler: process the next queued article in its own PHP process. - * - * Chained by Pipeline_Runner — each article gets its own execution time. - */ + /** Cron handler: process next queued article. Chained by Pipeline_Runner. */ public function on_process_article_queue(): void { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged @ignore_user_abort( true ); @@ -249,8 +246,10 @@ class PRAutoBlogger_Executor { } } - // After 5 min with no progress, declare failure. - if ( $elapsed > 300 ) { + // Stall = 5 min since last stage update (not since start). + $last_activity = $status['last_updated'] ?? $status['started'] ?? 0; + $idle_seconds = time() - $last_activity; + if ( $idle_seconds > 300 ) { $this->abort_orphaned_run( __( 'Generation stalled. Check Activity Log.', 'prautoblogger' ) ); return; } @@ -275,7 +274,8 @@ class PRAutoBlogger_Executor { private function update_generation_stage( string $stage ): void { $current = get_transient( self::STATUS_TRANSIENT ); if ( is_array( $current ) ) { - $current['stage'] = $stage; + $current['stage'] = $stage; + $current['last_updated'] = time(); set_transient( self::STATUS_TRANSIENT, $current, self::STATUS_TTL ); } } diff --git a/includes/core/class-article-worker.php b/includes/core/class-article-worker.php index a2bd6b1..40d6ca2 100644 --- a/includes/core/class-article-worker.php +++ b/includes/core/class-article-worker.php @@ -137,7 +137,8 @@ class PRAutoBlogger_Article_Worker { $transient_key = 'prautoblogger_generation_status'; $current = get_transient( $transient_key ); if ( is_array( $current ) && 'running' === ( $current['status'] ?? '' ) ) { - $current['stage'] = $stage; + $current['stage'] = $stage; + $current['last_updated'] = time(); set_transient( $transient_key, $current, 600 ); } } diff --git a/includes/core/class-pipeline-runner.php b/includes/core/class-pipeline-runner.php index c7ba904..6d19bd6 100755 --- a/includes/core/class-pipeline-runner.php +++ b/includes/core/class-pipeline-runner.php @@ -213,9 +213,10 @@ class PRAutoBlogger_Pipeline_Runner { $current = get_transient( 'prautoblogger_generation_status' ); set_transient( 'prautoblogger_generation_status', [ - 'status' => 'running', - 'stage' => sprintf( __( 'Generating article %1$d of %2$d…', 'prautoblogger' ), $done + 1, $total ), - 'started' => is_array( $current ) ? ( $current['started'] ?? time() ) : time(), + 'status' => 'running', + 'stage' => sprintf( __( 'Generating article %1$d of %2$d…', 'prautoblogger' ), $done + 1, $total ), + 'started' => is_array( $current ) ? ( $current['started'] ?? time() ) : time(), + 'last_updated' => time(), ], 600 ); } @@ -256,7 +257,8 @@ class PRAutoBlogger_Pipeline_Runner { $key = 'prautoblogger_generation_status'; $current = get_transient( $key ); if ( is_array( $current ) && 'running' === ( $current['status'] ?? '' ) ) { - $current['stage'] = $stage; + $current['stage'] = $stage; + $current['last_updated'] = time(); set_transient( $key, $current, 600 ); } }