WordPress.org

Make WordPress Core

Changeset 36349


Ignore:
Timestamp:
01/19/2016 05:06:46 AM (4 years ago)
Author:
dd32
Message:

Core Upgrader: Add a locking mechanism to avoid two concurrent updates of WordPress occuring.

Fixes #34878

Location:
trunk/src/wp-admin
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-upgrader.php

    r35642 r36349  
    752752    }
    753753
     754    /**
     755     * Create a Lock using WordPress options.
     756     *
     757     * @since 4.5.0
     758     * @access public
     759     *
     760     * @param string $lock_name       The name of this unique lock.
     761     * @param int    $release_timeout The duration in seconds to respect an existing lock. Default: 1 hour.
     762     * @return bool
     763     */
     764    public function create_lock( $lock_name, $release_timeout = null ) {
     765        global $wpdb;
     766        if ( ! $release_timeout ) {
     767            $release_timeout = HOUR_IN_SECONDS;
     768        }
     769        $lock_option = $lock_name . '.lock';
     770
     771        // Try to lock
     772        $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_option, time() ) );
     773
     774        if ( ! $lock_result ) {
     775            $lock_result = get_option( $lock_option );
     776
     777            // If we couldn't create a lock, and there isn't a lock, bail
     778            if ( ! $lock_result ) {
     779                return false;
     780            }
     781
     782            // Check to see if the lock is still valid
     783            if ( $lock_result > ( time() - $release_timeout ) ) {
     784                return false;
     785            }
     786
     787            // There must exist an expired lock, clear it and re-gain it.
     788            $this->release_lock( $lock_name );
     789
     790            return $this->create_lock( $lock_name, $release_timeout );
     791        }
     792
     793        // Update the lock, as by this point we've definitely got a lock, just need to fire the actions
     794        update_option( $lock_option, time() );
     795
     796        return true;
     797    }
     798
     799    /**
     800     * Release a lock created by `WP_Upgrader::create_lock()`.
     801     *
     802     * @since 4.5.0
     803     * @access public
     804     *
     805     * @param string $lock_name The name of this unique lock.
     806     * @return bool
     807     */
     808    public function release_lock( $lock_name ) {
     809        return delete_option( $lock_name . '.lock' );
     810    }
     811
    754812}
    755813
     
    21692227    public function upgrade_strings() {
    21702228        $this->strings['up_to_date'] = __('WordPress is at the latest version.');
     2229        $this->strings['locked'] = __('Another update is currently in progress.');
    21712230        $this->strings['no_package'] = __('Update package not available.');
    21722231        $this->strings['downloading_package'] = __('Downloading update from <span class="code">%s</span>&#8230;');
     
    22532312            $to_download = 'full';
    22542313
     2314        // Lock to prevent multiple Core Updates occuring
     2315        $lock = $this->create_lock( 'core_updater', 15 * MINUTE_IN_SECONDS );
     2316        if ( ! $lock ) {
     2317            return new WP_Error( 'locked', $this->strings['locked'] );
     2318        }
     2319
    22552320        $download = $this->download_package( $current->packages->$to_download );
    2256         if ( is_wp_error($download) )
     2321        if ( is_wp_error( $download ) ) {
     2322            $this->release_lock( 'core_updater' );
    22572323            return $download;
     2324        }
    22582325
    22592326        $working_dir = $this->unpack_package( $download );
    2260         if ( is_wp_error($working_dir) )
     2327        if ( is_wp_error( $working_dir ) ) {
     2328            $this->release_lock( 'core_updater' );
    22612329            return $working_dir;
     2330        }
    22622331
    22632332        // Copy update-core.php from the new version into place.
    22642333        if ( !$wp_filesystem->copy($working_dir . '/wordpress/wp-admin/includes/update-core.php', $wp_dir . 'wp-admin/includes/update-core.php', true) ) {
    22652334            $wp_filesystem->delete($working_dir, true);
     2335            $this->release_lock( 'core_updater' );
    22662336            return new WP_Error( 'copy_failed_for_update_core_file', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), 'wp-admin/includes/update-core.php' );
    22672337        }
     
    22702340        require_once( ABSPATH . 'wp-admin/includes/update-core.php' );
    22712341
    2272         if ( ! function_exists( 'update_core' ) )
     2342        if ( ! function_exists( 'update_core' ) ) {
     2343            $this->release_lock( 'core_updater' );
    22732344            return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] );
     2345        }
    22742346
    22752347        $result = update_core( $working_dir, $wp_dir );
     
    23452417            wp_version_check( $stats );
    23462418        }
     2419
     2420        $this->release_lock( 'core_updater' );
    23472421
    23482422        return $result;
     
    29393013        }
    29403014
    2941         // Core doesn't output this, so let's append it so we don't get confused.
    29423015        if ( 'core' == $type ) {
     3016            if ( is_wp_error( $upgrade_result ) && ( 'up_to_date' == $upgrade_result->get_error_code() || 'locked' == $upgrade_result->get_error_code() ) ) {
     3017                // These aren't actual errors, treat it as a skipped-update instead to avoid triggering the post-core update failure routines.
     3018                return false;
     3019            }
     3020
     3021            // Core doesn't output this, so let's append it so we don't get confused.
    29433022            if ( is_wp_error( $upgrade_result ) ) {
    29443023                $skin->error( __( 'Installation Failed' ), $upgrade_result );
     
    29763055            return;
    29773056
    2978         $lock_name = 'auto_updater.lock';
    2979 
    2980         // Try to lock
    2981         $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) );
    2982 
    2983         if ( ! $lock_result ) {
    2984             $lock_result = get_option( $lock_name );
    2985 
    2986             // If we couldn't create a lock, and there isn't a lock, bail
    2987             if ( ! $lock_result )
    2988                 return;
    2989 
    2990             // Check to see if the lock is still valid
    2991             if ( $lock_result > ( time() - HOUR_IN_SECONDS ) )
    2992                 return;
    2993         }
    2994 
    2995         // Update the lock, as by this point we've definitely got a lock, just need to fire the actions
    2996         update_option( $lock_name, time() );
     3057        if ( ! $this->create_lock( 'auto_updater' ) )
     3058            return;
    29973059
    29983060        // Don't automatically run these thins, as we'll handle it ourselves
     
    30933155        }
    30943156
    3095         // Clear the lock
    3096         delete_option( $lock_name );
     3157        $this->release_lock( 'auto_updater' );
    30973158    }
    30983159
     
    31643225         */
    31653226        $send = true;
    3166         $transient_failures = array( 'incompatible_archive', 'download_failed', 'insane_distro' );
     3227        $transient_failures = array( 'incompatible_archive', 'download_failed', 'insane_distro', 'locked' );
    31673228        if ( in_array( $error_code, $transient_failures ) && ! get_site_option( 'auto_core_update_failed' ) ) {
    31683229            wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_maybe_auto_update' );
  • trunk/src/wp-admin/update-core.php

    r36182 r36349  
    485485    if ( is_wp_error($result) ) {
    486486        show_message($result);
    487         if ('up_to_date' != $result->get_error_code() )
     487        if ( 'up_to_date' != $result->get_error_code() && 'locked' != $result->get_error_code() )
    488488            show_message( __('Installation Failed') );
    489489        echo '</div>';
Note: See TracChangeset for help on using the changeset viewer.