Make WordPress Core

Opened 10 years ago

Closed 10 years ago

#31011 closed defect (bug) (fixed)

WP 4.1 checks for updates with every load of any admin page

Reported by: hallcp's profile hallcp Owned by: dd32's profile dd32
Milestone: 4.1.1 Priority: normal
Severity: normal Version: 4.1
Component: Administration Keywords: fixed-major
Focuses: Cc:

Description

It appears that with the introduction of 4.1 WordPress is going out and checking for updates with every load of the admin page. We can tell, because we have https: issues and we see the warnings every time it does so.

The culprit appears to be in the wp_get_update_data() function where two update calls were added in 4.1:

function wp_get_update_data() {
$counts = array( 'plugins' => 0, 'themes' => 0, 'wordpress' => 0, 'translations' => 0 );
if ( $plugins = current_user_can( 'update_plugins' ) ) {
wp_update_plugins(); \/\/ Check for Plugin updates
$update_plugins = get_site_transient( 'update_plugins' );
if ( ! empty( $update_plugins->response ) )
$countsplugins? = count( $update_plugins->response );
}
if ( $themes = current_user_can( 'update_themes' ) ) {
wp_update_themes(); \/\/ Check for Theme updates
$update_themes = get_site_transient( 'update_themes' );
if ( ! empty( $update_themes->response ) )
$countsthemes? = count( $update_themes->response );
}

This would be around lines 523-526 in update.php. This causes our site to slow to a crawl as it waits for the https: calls to time out.

Does anyone know why the update regimen was changed?

Attachments (2)

add_filters.patch (1.2 KB) - added by Grezvany13 10 years ago.
Add 3 filters (for core, plugin and theme) to enable/disable update checks
31011.override-plugin-and-theme-update-checks.php (1.8 KB) - added by SergeyBiryukov 10 years ago.
Workaround in plugin form

Download all attachments as: .zip

Change History (13)

#1 @MikeHansenMe
10 years ago

  • Keywords reporter-feedback added

Hello hallcp, thanks for reporting this. I was able to confirm that wp_update_themes and wp_update_plugins is being called on every page load (in fact multiple times) however looking at those 2 functions they should only make the request if something has changed or it has been 1 hour or 12 hours since last check depending on the current filter. Looking around line 207 and 372 is where that happens.

The functions are called 2 times each on each load with the following current_filter

Every Page function / current_filter
wp_update_plugins: false
wp_update_themes: false
wp_update_plugins: admin_menu_bar
wp_update_themes: admin_menu_bar

Themes page function / current_filter
wp_update_plugins: false
wp_update_themes: false
wp_update_themes: load-themes.php
wp_update_plugins: admin_menu_bar
wp_update_themes: admin_menu_bar

Plugins page function / current_filter
wp_update_plugins: false
wp_update_themes: false
wp_update_plugins: load-plugins.php
wp_update_plugins: admin_menu_bar
wp_update_themes: admin_menu_bar

While all these except the additional calls on themes.php and plugins.php fall under default in the switch with 12 hour as $timeout.

Can you confirm this still happens for you with the default theme and no plugins active?

#2 @hallcp
10 years ago

Yes, disabling plugins and using the default theme doesn't change anything. I was unable to follow the program logic, but I would call your attention to these two lines added in update.php with the 4.1 update (as mentioned in my original bug description). There were two direct calls added:

wp_update_plugins(); Line 521(?) of update.php
wp_update_themes(); Line 531(?) of update.php

These two lines appear to me to bypass the timed update checks. They were not present before.
I got the feeling that the legitimate calls to update came via the "_maybe_update_plugins" and "_maybe_update_themes" actions, which do appear to check the time and only "maybe" fire off an update.

Just a guess, but it smells like the problem to me! Thanks for looking into this one.

Charles.

#3 @MikeHansenMe
10 years ago

Hey Charles, the time check based on the filter is inside those two functions so even the direct call should honor the timeout. I am going to look into it a bit more.

#4 @craig0990
10 years ago

Hi folks. I'm new here as well, but I think I'm experiencing the same issue, and I think I know why. Bear with me, and if I'm wrong call me out. Impatient? Temporary fix provided at the end of this comment.

For clarity, this is a brand new install of WordPress 4.1 with no custom themes or plugins. Everything worked fine to begin with, and then suddenly starting showing these symptoms (apparently) out of nowhere.

Every time I load the WordPress 4.1 admin, I see:

Warning: An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums. (WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.) in /var/www/cgr/admin/httpdocs/wp-includes/update.php on line 295

Like hallcp, the API call to check for updates is timing out. I suspect this is why it runs on every single page load - if it never succeeds, then it never resets the timer.

I can't remember what made me think of cURL (apart from the assumption that PHP uses cURL if possible). I was also fairly sure I had update cURL fairly recently...

Sure enough, /var/log/apt/history.log shows libcurl updated to v7.35.0.

NOTE I'm not saying that libcurl caused this. I don't know enough about this sort of thing to make that kind of claim.

Putting some debugging code into wp-includes/update.php gave me a more useful error message of:

Resolving timed out after 3512 milliseconds

After Googling this, I guessed that IPv6 DNS lookup is failing, which is why it's taking such a long time to timeout/fail/whatever.

The following shell commands pretty much confirmed this:

$ curl --connect-timeout 3 -v http://api.wordpress.org
$ curl --connect-timeout 3 -v -4 http://api.wordpress.org
$ curl --connect-timeout 3 -v -6 http://api.wordpress.org

Bingo.

Now, I'm stuck a little here. I can patch my local copy for now (and have done), I can disable IPv6 at the server level, or I can disable cURL at the Apache level. None of those are particularly good choices.

I would like to propose a fix that makes this configurable in some way from wp-config.php, but I'm not sure how to go about that. My first guess would be adding a constant, say something like WP_CURL_IPV4_ONLY.

Regardless, add this:

curl_setopt( $handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );

after line 1412 in wp-includes/class-http.php to fix this issue.

P.S. The theory "I suspect this is why it runs on every single page load - if it never succeeds, then it never resets the timer" was correct.

Version 0, edited 10 years ago by craig0990 (next)

#5 @Grezvany13
10 years ago

I currently have the same problem, just under different circumstances.

For a local test I created a vagrant box and did a clean install of WP 4.1, and the first thing I see is a PHP warning (due WP_DEBUG is true) about not able to connect to wordpress.org. The reason in this case is simple; my vagrant box doesn't allow any outgoing internet connections.

Since I figured this was related to the auto update functionality of the WP core, I added the following filters in a mu-plugin:

add_filter( 'automatic_updater_disabled', '__return_true' );
add_filter( 'auto_update_core', '__return_false' );
add_filter( 'auto_update_plugin', '__return_false' );
add_filter( 'auto_update_theme', '__return_false' );
add_filter( 'auto_update_translation', '__return_false' );

Which obviously doesn't work, since the auto_update_* filters are called after checking for new versions.

Suggestions:
1) When the auto_update_* filters are set, obey them before checking for updates. The user already knows the risk of not updating automatically, so no need to check for updates either.
2) Add additional filters or definitions which check if any check should be done for possible updates. Filters like "check_update_*" would be sufficient in my eyes.
3) Instead of giving a PHP Warning (which it isn't, since it not a problem with the code), giving a admin notice in wp-admin would be desirable since right now it breaks the site with WP_DEBUG enabled.

Personally I would take option 2 and 3.

@Grezvany13
10 years ago

Add 3 filters (for core, plugin and theme) to enable/disable update checks

#6 @Grezvany13
10 years ago

  • Keywords has-patch needs-testing added

To follow up on my previous comment, I created a simple patch (add_filters.patch) which adds 3 filters and placed them in the wp_*_check methods.

  • check_update_core
  • check_update_plugin
  • check_update_theme

By default they will return true, however if you don't want to check for new updates automatically you can add the following 3 lines:

  add_filter( 'check_update_core', '__return_false' );  // don't check for core updates
  add_filter( 'check_update_plugin', '__return_false' );  // don't check for plugin updates
  add_filter( 'check_update_theme', '__return_false' );  // don't check for theme updates

It won't break any core functionality if the filters are not in place, and obviously should only be used when the user knows what he/she is doing, just like the auto_update_* filters.

I've tested this locally with WordPress 4.1 (clean install from SVN trunk), and the errors described before don't pop-up, even when the transients for the updates are reset.

Since this is my first patch (yeee!), I added 'has-patch' and 'needs-testing', just to be sure :)

#7 @SergeyBiryukov
10 years ago

  • Keywords reporter-feedback removed
  • Milestone changed from Awaiting Review to 4.1.1

Replying to craig0990:

Every time I load the WordPress 4.1 admin, I see:

Warning: An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums. (WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.) in /var/www/cgr/admin/httpdocs/wp-includes/update.php on line 295

Seeing a lot of similar reports on support forums.

Like hallcp, the API call to check for updates is timing out. I suspect this is why it runs on every single page load - if it never succeeds, then it never resets the timer.

Correct.

The culprit appears to be in the wp_get_update_data() function where two update calls were added in 4.1

Introduced in [30696].

To reproduce the issue, just set WP_HTTP_BLOCK_EXTERNAL to true and clear the 'update_plugins' and 'update_themes' transients.

On each admin page load, we check if any plugins or themes changed since the last request. If they did, or if the last request never succeeded ($current->checked is empty), the 12-hour timeout is discarded, and we're making another request, which fails as well, leading to yet another request on next page load, etc.

So, [30696] didn't introduce any new issues for sites that don't have any troubles connecting to WordPress.org, but it made the situation much worse for those that do have troubles.

I'd suggest reverting [30696] for 4.1.1 and trying to come up with a better solution in 4.2.

As a workaround, this should short-circuit the requests and prevent them from being run on each admin page load:

function override_updated_plugins_check_31011( $transient ) {
	$plugins = get_plugins();

	// Reset the timeout if previous requests never succeeded
	if ( ! isset( $transient->checked ) ) {
		$last_update->last_checked = time();
	}

	// Short-circuit the check for changed plugins
	foreach ( $plugins as $file => $plugin ) {
		if ( ! isset( $transient->checked[ $file ] ) ) {
			$transient->checked[ $file ] = $plugin['Version'];
		}
	}

	return $transient;
}
add_filter( 'site_transient_update_plugins', 'override_updated_plugins_check_31011' );

function override_updated_themes_check_31011( $transient ) {
	$themes = wp_get_themes();

	// Reset the timeout if previous requests never succeeded
	if ( ! isset( $transient->checked ) ) {
		$last_update->last_checked = time();
	}

	// Short-circuit the check for changed themes
	foreach ( $themes as $theme ) {
		$stylesheet = $theme->get_stylesheet();
		if ( ! isset( $transient->checked[ $stylesheet ] ) ) {
			$transient->checked[ $stylesheet ] = $theme->get( 'Version' );
		}
	}

	return $transient;
}
add_filter( 'site_transient_update_themes', 'override_updated_themes_check_31011' );
Last edited 10 years ago by SergeyBiryukov (previous) (diff)

#8 @dd32
10 years ago

I'd suggest reverting [30696] for 4.1.1 and trying to come up with a better solution in 4.2.

This seems like the best option, changing the update functions to properly handle failed connections seems more than 4.1.1 material. What do you think @johnbillion?

#9 @johnbillion
10 years ago

In 31390:

Revert [30696] pending further investigation.

See #31011, #13071

#10 @SergeyBiryukov
10 years ago

  • Keywords fixed-major added; has-patch needs-testing removed

Reopened #13071.

This ticket can be closed after [31390] is merged to the 4.1 branch.

#11 @dd32
10 years ago

  • Owner set to dd32
  • Resolution set to fixed
  • Status changed from new to closed

In 31394:

Revert [30696] pending further investigation.

Props johnbillion.
Merges [31390] to the 4.1 branch.
See #13071. Fixes #31011.

Note: See TracTickets for help on using tickets.