Changeset 3423968
- Timestamp:
- 12/19/2025 09:02:46 PM (3 months ago)
- Location:
- weather-write/trunk
- Files:
-
- 3 edited
-
includes/class-scheduler.php (modified) (5 diffs)
-
readme.txt (modified) (2 diffs)
-
weather-write.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
weather-write/trunk/includes/class-scheduler.php
r3423169 r3423968 151 151 // local wp-cron events when an external token is present. 152 152 if ( $has_external ) { 153 // Clear any existing WP-Cron events that may have been scheduled before external mode was enabled. 154 // This prevents old internal cron events from firing alongside external cron calls. 153 // Clear any existing WP-Cron post generation events 155 154 wp_clear_scheduled_hook( self::CRON_HOOK ); 156 wp_clear_scheduled_hook( self::WATCHDOG_HOOK ); 155 156 // IMPORTANT: Still schedule watchdog for monitoring in external cron mode 157 // The watchdog monitors for failures but doesn't trigger posts itself 158 // It only sends alerts when posts fail or are missed 159 if ( ! wp_next_scheduled( self::WATCHDOG_HOOK ) ) { 160 $next = self::compute_next_run( $options ); 161 if ( $next['ts'] ) { 162 // Schedule watchdog 2 minutes after next expected post time 163 $watchdog_ts = $next['ts'] + 120; 164 wp_schedule_single_event( $watchdog_ts, self::WATCHDOG_HOOK, [ $next['hm'] ] ); 165 } 166 } 157 167 return; 158 168 } … … 241 251 $today_local = function_exists( 'wp_date' ) ? wp_date( 'Y-m-d', null, $tz ) : date_i18n( 'Y-m-d' ); 242 252 $dedupe_key = 'wwrt_run_' . md5( $today_local . '|' . $hm_key . '|' . $location_key ); 253 254 // ATOMIC LOCK: Use add_option for atomic check-and-set to prevent race conditions 255 // when cron-job.org retries due to timeout (30 seconds) 256 // This prevents the microsecond race window between get_transient() and set_transient() 257 $lock_key = $dedupe_key . '_lock'; 258 $lock_acquired = add_option( $lock_key, time(), '', 'no' ); // add_option is atomic 259 260 if ( ! $lock_acquired ) { 261 // Lock already exists - another request is processing this slot 262 wwrt_log_and_notify( 'duplicate_run', 'Duplicate run prevented (atomic lock)', [ 263 'slot_time' => $slot_time, 264 'location_key' => $location_key, 265 'run_id' => $run_id, 266 ], false ); 267 return new WP_Error( 'wwrt_duplicate_run', 'A run for this time slot is already in progress.' ); 268 } 269 270 // Check transient as secondary guard (in case lock was from old run) 243 271 if ( get_transient( $dedupe_key ) ) { 244 // A run for this slot has already been executed recently; skip to avoid duplicate posts and API usage.245 wwrt_log_and_notify( 'duplicate_run', 'Duplicate run prevented for this time slot', [272 delete_option( $lock_key ); // Clean up our lock 273 wwrt_log_and_notify( 'duplicate_run', 'Duplicate run prevented (transient check)', [ 246 274 'slot_time' => $slot_time, 247 275 'location_key' => $location_key, … … 250 278 return new WP_Error( 'wwrt_duplicate_run', 'A run for this time slot has already been executed.' ); 251 279 } 252 // Mark this slot as in-progress/done for a short window (15 minutes is plenty for a single run). 253 set_transient( $dedupe_key, 'running', 15 * MINUTE_IN_SECONDS ); 280 281 // Mark this slot as in-progress/done for 60 minutes to prevent any delayed retries or duplicate triggers 282 set_transient( $dedupe_key, 'running', 60 * MINUTE_IN_SECONDS ); 283 284 // Clean up lock after transient is set (lock served its purpose) 285 delete_option( $lock_key ); 254 286 } 255 287 … … 1672 1704 } 1673 1705 1674 $window_start = $slot_ts - 120; // 2 minutes before scheduled time 1675 $window_end = $slot_ts + 120; // 2 minutes after scheduled time (when watchdog runs) 1706 // WIDENED WINDOW: 5 minutes before and 5 minutes after to account for: 1707 // - Slow post generation (API delays, large content) 1708 // - Clock drift between WordPress server and cron-job.org 1709 // - Database commit delays 1710 $window_start = $slot_ts - 300; // 5 minutes before scheduled time 1711 $window_end = $slot_ts + 300; // 5 minutes after scheduled time 1676 1712 $has_success = false; $has_failed = false; $already_missed = false; 1677 1713 … … 1706 1742 if ( $already_missed || $has_success ) { 1707 1743 return; // No notification needed 1744 } 1745 1746 // CRITICAL: Check if actual WordPress posts exist for this time slot 1747 // This catches cases where event logging failed but post was created successfully 1748 $args = [ 1749 'post_type' => 'post', 1750 'post_status' => ['publish', 'draft', 'pending'], 1751 'meta_query' => [ 1752 [ 1753 'key' => '_wwrt_schedule_time', 1754 'value' => $hm, 1755 'compare' => '=' 1756 ] 1757 ], 1758 'date_query' => [ 1759 [ 1760 'after' => gmdate( 'Y-m-d H:i:s', $window_start ), 1761 'before' => gmdate( 'Y-m-d H:i:s', $window_end ), 1762 'inclusive' => true 1763 ] 1764 ], 1765 'posts_per_page' => 1, 1766 'fields' => 'ids' 1767 ]; 1768 $existing_posts = get_posts( $args ); 1769 if ( ! empty( $existing_posts ) ) { 1770 // Post exists! Event logging may have failed, but post was created successfully 1771 // No notification needed 1772 return; 1708 1773 } 1709 1774 -
weather-write/trunk/readme.txt
r3423169 r3423968 4 4 Requires at least: 6.5 5 5 Tested up to: 6.8 6 Stable tag: 1.3. 86 Stable tag: 1.3.9 7 7 License: GPLv2 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 87 87 88 88 == Changelog == 89 90 = 1.3.9 = 91 - CRITICAL FIX: Resolved duplicate posts issue caused by narrow watchdog detection window 92 - Extended deduplication transient from 15 to 60 minutes to prevent delayed retries 93 - Widened watchdog window from 4 minutes to 10 minutes (accounts for slow generation and clock drift) 94 - Added actual WordPress post existence check to prevent false positive watchdog alerts 95 - Improved reliability when API calls are slow or servers have clock differences 89 96 90 97 = 1.3.8 = -
weather-write/trunk/weather-write.php
r3423169 r3423968 3 3 * Plugin Name: Weather Write 4 4 * Description: Generate and publish weather-aware posts with summaries, charts, images, alerts, SEO, and more — fully automated or on-demand. 5 * Version: 1.3. 85 * Version: 1.3.9 6 6 * Author: Mike Freeman - WeatherWrite 7 7 * Plugin URI: https://www.weatherwrite.com/
Note: See TracChangeset
for help on using the changeset viewer.