Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/wp-admin/includes/class-wp-upgrader.php
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,22 @@ public static function create_lock( $lock_name, $release_timeout = null ) {
// Try to lock.
$lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'off') /* LOCK */", $lock_option, time() ) );

/*
* Invalidate the notoptions cache for the lock option.
*
* The lock is created with a direct INSERT IGNORE query, bypassing the
* Options API. This means the notoptions cache may still contain a stale
* entry for this option (added when the option previously did not exist),
* causing get_option() to return false without querying the database.
*
* @see https://core.trac.wordpress.org/ticket/64080
*/
$notoptions = wp_cache_get( 'notoptions', 'options' );
if ( is_array( $notoptions ) && isset( $notoptions[ $lock_option ] ) ) {
unset( $notoptions[ $lock_option ] );
wp_cache_set( 'notoptions', $notoptions, 'options' );
}

if ( ! $lock_result ) {
$lock_result = get_option( $lock_option );

Expand Down
21 changes: 21 additions & 0 deletions tests/phpunit/tests/admin/wpUpgrader.php
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,27 @@ public function test_release_lock_should_remove_lock_option() {
$this->assertNotSame( 'content', get_option( 'lock.lock' ) );
}

/**
* Tests that `WP_Upgrader::create_lock()` invalidates stale notoptions cache
* so that expired locks can be detected and re-created.
*
* @ticket 64080
*
* @covers WP_Upgrader::create_lock
*/
public function test_create_lock_should_invalidate_stale_notoptions_cache() {
// Prime notoptions cache by requesting the non-existent lock option.
get_option( 'test.lock' );

$this->assertTrue( WP_Upgrader::create_lock( 'test' ), 'create_lock() should succeed despite stale notoptions cache.' );
$this->assertNotFalse( get_option( 'test.lock' ), 'get_option() should return the lock timestamp after create_lock().' );

update_option( 'test.lock', time() - 10 );
$this->assertTrue( WP_Upgrader::create_lock( 'test', 5 ), 'Expired lock should be released and re-created.' );

WP_Upgrader::release_lock( 'test' );
}

/**
* Tests that `WP_Upgrader::download_package()` returns early when
* the 'upgrader_pre_download' filter returns a non-false value.
Expand Down
Loading