Ticket #17462: 17462.6.diff

File 17462.6.diff, 5.9 KB (added by ryan, 21 months ago)

Use doing_wp_cron instead of lock

Line 
1Index: wp-includes/default-constants.php
2===================================================================
3--- wp-includes/default-constants.php   (revision 18653)
4+++ wp-includes/default-constants.php   (working copy)
5@@ -268,6 +268,12 @@
6 
7        if ( !defined('WP_POST_REVISIONS') )
8                define('WP_POST_REVISIONS', true);
9+
10+       /**
11+        * @since 3.3.0
12+        */
13+       if ( !defined( 'WP_CRON_LOCK_TIMEOUT' ) )
14+               define('WP_CRON_LOCK_TIMEOUT', 60);  // In seconds
15 }
16 
17 /**
18Index: wp-includes/cache.php
19===================================================================
20--- wp-includes/cache.php       (revision 18653)
21+++ wp-includes/cache.php       (working copy)
22@@ -102,13 +102,14 @@
23  *
24  * @param int|string $key What the contents in the cache are called
25  * @param string $group Where the cache contents are grouped
26+ * @param bool $force Whether to force an update of the local cache from the persistent cache (default is false)
27  * @return bool|mixed False on failure to retrieve contents or the cache
28  *             contents on success
29  */
30-function wp_cache_get($key, $group = '') {
31+function wp_cache_get( $key, $group = '', $force = false ) {
32        global $wp_object_cache;
33 
34-       return $wp_object_cache->get($key, $group);
35+       return $wp_object_cache->get( $key, $group, $force );
36 }
37 
38 /**
39@@ -403,10 +404,11 @@
40         *
41         * @param int|string $key What the contents in the cache are called
42         * @param string $group Where the cache contents are grouped
43+        * @param string $force Whether to force a refetch rather than relying on the local cache (default is false)
44         * @return bool|mixed False on failure to retrieve contents or the cache
45         *              contents on success
46         */
47-       function get($key, $group = 'default') {
48+       function get( $key, $group = 'default', $force = false) {
49                if ( empty ($group) )
50                        $group = 'default';
51 
52Index: wp-includes/cron.php
53===================================================================
54--- wp-includes/cron.php        (revision 18653)
55+++ wp-includes/cron.php        (working copy)
56@@ -204,13 +204,13 @@
57        * multiple processes on multiple web servers can run this code concurrently
58        * try to make this as atomic as possible by setting doing_cron switch
59        */
60-       $flag = get_transient('doing_cron');
61+       $lock = get_transient('doing_cron');
62 
63-       if ( $flag > $local_time + 10*60 )
64-               $flag = 0;
65+       if ( $lock > $local_time + 10*60 )
66+               $lock = 0;
67 
68        // don't run if another process is currently running it or more than once every 60 sec.
69-       if ( $flag + 60 > $local_time )
70+       if ( $lock + WP_CRON_LOCK_TIMEOUT > $local_time )
71                return;
72 
73        //sanity check
74@@ -226,10 +226,11 @@
75                if ( !empty($_POST) || defined('DOING_AJAX') )
76                        return;
77 
78-               set_transient( 'doing_cron', $local_time );
79+               $doing_wp_cron = $local_time;
80+               set_transient( 'doing_cron', $doing_wp_cron );
81 
82                ob_start();
83-               wp_redirect( add_query_arg('doing_wp_cron', '', stripslashes($_SERVER['REQUEST_URI'])) );
84+               wp_redirect( add_query_arg('doing_wp_cron', $doing_wp_cron, stripslashes($_SERVER['REQUEST_URI'])) );
85                echo ' ';
86 
87                // flush any buffers and send the headers
88@@ -240,9 +241,10 @@
89                return;
90        }
91 
92-       set_transient( 'doing_cron', $local_time );
93+       $doing_wp_cron = $local_time;
94+       set_transient( 'doing_cron', $doing_wp_cron );
95 
96-       $cron_url = get_option( 'siteurl' ) . '/wp-cron.php?doing_wp_cron';
97+       $cron_url = get_option( 'siteurl' ) . '/wp-cron.php?doing_wp_cron=' . $doing_wp_cron;
98        wp_remote_post( $cron_url, array('timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters('https_local_ssl_verify', true)) );
99 }
100 
101Index: wp-cron.php
102===================================================================
103--- wp-cron.php (revision 18653)
104+++ wp-cron.php (working copy)
105@@ -26,6 +26,24 @@
106        require_once('./wp-load.php');
107 }
108 
109+// Uncached doing_cron transient fetch
110+function _get_cron_lock() {
111+       global $_wp_using_ext_object_cache, $wpdb;
112+
113+       $value = 0;
114+       if ( $_wp_using_ext_object_cache ) {
115+               // Skip local cache and force refetch of doing_cron transient in case
116+               // another processs updated the cache
117+               $value = wp_cache_get( 'doing_cron', 'transient', true );
118+       } else {
119+               $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) );
120+               if ( is_object( $row ) )
121+                       $value = $row->option_value;
122+       }
123+
124+       return $value;
125+}
126+
127 if ( false === $crons = _get_cron_array() )
128        die();
129 
130@@ -35,26 +53,52 @@
131 if ( isset($keys[0]) && $keys[0] > $local_time )
132        die();
133 
134-foreach ($crons as $timestamp => $cronhooks) {
135+$doing_cron_transient = get_transient( 'doing_cron');
136+
137+// Use global $doing_wp_cron lock otherwise use the GET lock. If no lock, trying grabbing a new lock.
138+if ( empty( $doing_wp_cron ) ) {
139+       if ( empty( $_GET[ 'doing_wp_cron' ] ) ) {
140+               // Called from external script/job. Try setting a lock.
141+               if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $local_time ) )
142+                       return;
143+               $doing_cron_transient = $doing_wp_cron = time();
144+               set_transient( 'doing_cron', $doing_wp_cron );
145+       } else {
146+               $doing_wp_cron = $_GET[ 'doing_wp_cron' ];
147+       }
148+}
149+
150+// Check lock
151+if ( $doing_cron_transient != $doing_wp_cron )
152+       return;
153+
154+foreach ( $crons as $timestamp => $cronhooks ) {
155        if ( $timestamp > $local_time )
156                break;
157 
158-       foreach ($cronhooks as $hook => $keys) {
159+       foreach ( $cronhooks as $hook => $keys ) {
160 
161-               foreach ($keys as $k => $v) {
162+               foreach ( $keys as $k => $v ) {
163 
164                        $schedule = $v['schedule'];
165 
166-                       if ($schedule != false) {
167+                       if ( $schedule != false ) {
168                                $new_args = array($timestamp, $schedule, $hook, $v['args']);
169                                call_user_func_array('wp_reschedule_event', $new_args);
170                        }
171 
172-                       wp_unschedule_event($timestamp, $hook, $v['args']);
173+                       wp_unschedule_event( $timestamp, $hook, $v['args'] );
174 
175-                       do_action_ref_array($hook, $v['args']);
176+                       do_action_ref_array( $hook, $v['args'] );
177+
178+                       // If the hook ran too long and another cron process stole the lock, quit.
179+                       if ( _get_cron_lock() != $doing_wp_cron )
180+                               return;
181                }
182        }
183 }
184 
185+if ( _get_cron_lock() == $doing_wp_cron )
186+       delete_transient( 'doing_cron' );
187+
188 die();