Make WordPress Core


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

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

Fixes #34878

File:
1 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' );
Note: See TracChangeset for help on using the changeset viewer.