WordPress.org

Make WordPress Core

Changeset 18659


Ignore:
Timestamp:
09/09/2011 07:59:44 PM (7 years ago)
Author:
ryan
Message:

Improve cron locking. Avoid multiple cron processes looping over the same events. fixes #17462

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-cron.php

    r13725 r18659  
    2727}
    2828
     29// Uncached doing_cron transient fetch
     30function _get_cron_lock() {
     31    global $_wp_using_ext_object_cache, $wpdb;
     32
     33    $value = 0;
     34    if ( $_wp_using_ext_object_cache ) {
     35        // Skip local cache and force refetch of doing_cron transient in case
     36        // another processs updated the cache
     37        $value = wp_cache_get( 'doing_cron', 'transient', true );
     38    } else {
     39        $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) );
     40        if ( is_object( $row ) )
     41            $value = $row->option_value;
     42    }
     43
     44    return $value;
     45}
     46
    2947if ( false === $crons = _get_cron_array() )
    3048    die();
     
    3654    die();
    3755
    38 foreach ($crons as $timestamp => $cronhooks) {
     56$doing_cron_transient = get_transient( 'doing_cron');
     57
     58// Use global $doing_wp_cron lock otherwise use the GET lock. If no lock, trying grabbing a new lock.
     59if ( empty( $doing_wp_cron ) ) {
     60    if ( empty( $_GET[ 'doing_wp_cron' ] ) ) {
     61        // Called from external script/job. Try setting a lock.
     62        if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $local_time ) )
     63            return;
     64        $doing_cron_transient = $doing_wp_cron = time();
     65        set_transient( 'doing_cron', $doing_wp_cron );
     66    } else {
     67        $doing_wp_cron = $_GET[ 'doing_wp_cron' ];
     68    }
     69}
     70
     71// Check lock
     72if ( $doing_cron_transient != $doing_wp_cron )
     73    return;
     74
     75foreach ( $crons as $timestamp => $cronhooks ) {
    3976    if ( $timestamp > $local_time )
    4077        break;
    4178
    42     foreach ($cronhooks as $hook => $keys) {
     79    foreach ( $cronhooks as $hook => $keys ) {
    4380
    44         foreach ($keys as $k => $v) {
     81        foreach ( $keys as $k => $v ) {
    4582
    4683            $schedule = $v['schedule'];
    4784
    48             if ($schedule != false) {
     85            if ( $schedule != false ) {
    4986                $new_args = array($timestamp, $schedule, $hook, $v['args']);
    5087                call_user_func_array('wp_reschedule_event', $new_args);
    5188            }
    5289
    53             wp_unschedule_event($timestamp, $hook, $v['args']);
     90            wp_unschedule_event( $timestamp, $hook, $v['args'] );
    5491
    55             do_action_ref_array($hook, $v['args']);
     92            do_action_ref_array( $hook, $v['args'] );
     93
     94            // If the hook ran too long and another cron process stole the lock, quit.
     95            if ( _get_cron_lock() != $doing_wp_cron )
     96                return;
    5697        }
    5798    }
    5899}
    59100
     101if ( _get_cron_lock() == $doing_wp_cron )
     102    delete_transient( 'doing_cron' );
     103
    60104die();
  • trunk/wp-includes/cache.php

    r18653 r18659  
    103103 * @param int|string $key What the contents in the cache are called
    104104 * @param string $group Where the cache contents are grouped
     105 * @param bool $force Whether to force an update of the local cache from the persistent cache (default is false)
    105106 * @return bool|mixed False on failure to retrieve contents or the cache
    106107 *      contents on success
    107108 */
    108 function wp_cache_get($key, $group = '') {
    109     global $wp_object_cache;
    110 
    111     return $wp_object_cache->get($key, $group);
     109function wp_cache_get( $key, $group = '', $force = false ) {
     110    global $wp_object_cache;
     111
     112    return $wp_object_cache->get( $key, $group, $force );
    112113}
    113114
     
    404405     * @param int|string $key What the contents in the cache are called
    405406     * @param string $group Where the cache contents are grouped
     407     * @param string $force Whether to force a refetch rather than relying on the local cache (default is false)
    406408     * @return bool|mixed False on failure to retrieve contents or the cache
    407409     *      contents on success
    408410     */
    409     function get($key, $group = 'default') {
     411    function get( $key, $group = 'default', $force = false) {
    410412        if ( empty ($group) )
    411413            $group = 'default';
  • trunk/wp-includes/cron.php

    r16938 r18659  
    205205    * try to make this as atomic as possible by setting doing_cron switch
    206206    */
    207     $flag = get_transient('doing_cron');
    208 
    209     if ( $flag > $local_time + 10*60 )
    210         $flag = 0;
     207    $lock = get_transient('doing_cron');
     208
     209    if ( $lock > $local_time + 10*60 )
     210        $lock = 0;
    211211
    212212    // don't run if another process is currently running it or more than once every 60 sec.
    213     if ( $flag + 60 > $local_time )
     213    if ( $lock + WP_CRON_LOCK_TIMEOUT > $local_time )
    214214        return;
    215215
     
    227227            return;
    228228
    229         set_transient( 'doing_cron', $local_time );
     229        $doing_wp_cron = $local_time;
     230        set_transient( 'doing_cron', $doing_wp_cron );
    230231
    231232        ob_start();
    232         wp_redirect( add_query_arg('doing_wp_cron', '', stripslashes($_SERVER['REQUEST_URI'])) );
     233        wp_redirect( add_query_arg('doing_wp_cron', $doing_wp_cron, stripslashes($_SERVER['REQUEST_URI'])) );
    233234        echo ' ';
    234235
     
    241242    }
    242243
    243     set_transient( 'doing_cron', $local_time );
    244 
    245     $cron_url = get_option( 'siteurl' ) . '/wp-cron.php?doing_wp_cron';
     244    $doing_wp_cron = $local_time;
     245    set_transient( 'doing_cron', $doing_wp_cron );
     246
     247    $cron_url = get_option( 'siteurl' ) . '/wp-cron.php?doing_wp_cron=' . $doing_wp_cron;
    246248    wp_remote_post( $cron_url, array('timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters('https_local_ssl_verify', true)) );
    247249}
  • trunk/wp-includes/default-constants.php

    r18545 r18659  
    269269    if ( !defined('WP_POST_REVISIONS') )
    270270        define('WP_POST_REVISIONS', true);
     271
     272    /**
     273     * @since 3.3.0
     274     */
     275    if ( !defined( 'WP_CRON_LOCK_TIMEOUT' ) )
     276        define('WP_CRON_LOCK_TIMEOUT', 60);  // In seconds
    271277}
    272278
Note: See TracChangeset for help on using the changeset viewer.