Upgrade takes an hour to complete due to repeated 30-second FTP timeout
|Reported by:||mmorearty||Owned by:||dd32|
On my FreeBSD server (hosted by ISP pair.com), the built-in upgrade feature of WordPress takes at least an hour to complete. I debugged the problem, and it turns out to be due to a mismatch between the FTP responses WordPress is expecting vs. the FTP responses FreeBSD is actually sending.
(Attached is a dump of phpinfo() from the machine on which I am seeing this problem.)
Steps to reproduce:
- Clean WordPress installation on FreeBSD
- Go to the Dashboard, then click Updates
- Click "Re-install Automatically"
- Enter hostname, user name, and password; leave Connection Type as FTP; click Proceed
The update eventually completes successfully, but it takes an extraordinarily long time -- at least an hour.
Should finish much more quickly than that.
I debugged this, and here is what is happening:
- My PHP installation ends up using the "ftpsockets" filesystem to do the update. (If necessary, you can force the use of that filesystem for testing purposes by adding "define('FS_METHOD', 'ftpsockets')" to your wp-config.php.)
- Every time anyone calls WP_Filesystem_ftpsockets->exists() to see if a file exists, that function calls ftp->is_exists(), which calls ftp->file_exists() in wp-admin/includes/class-ftp.php.
- That function tests for the existence of a file by sending the FTP command "RNFR" (rename from) across the FTP connection. If the RNFR command succeeds, then the assumption is that the remote file exists; if it fails with an error message, then the assumption is that the remote file does not exist.
- Immediately after sending the RNFR command, if the RNFR succeeded, then is_exists() calls abort(), to send an ABOR -- I'm guessing this is intended to abort the rename.
- However, this is where things go bad. FreeBSD replies to the ABOR command with "426 Nothing to abort". WordPress's abort() function then attempts to read one more line, but there is nothing to read, so that read attempt times out after 30 seconds.
Here is the way the whole "exists()" conversation looks when it works correctly on my Mac:
PUT > RNFR /Users/mike/Sites/wordpress/ GET < 350 File exists, ready for destination name PUT > ABOR GET < 225 ABOR command successful. Remote file /Users/mike/Sites/wordpress/ exists
And here is the way the conversation looks when it times out on my FreeBSD machine:
PUT > RNFR /usr/www/users/morearty/blog2/ GET < 350 You may attempt to rename /usr/www/users/morearty/blog2. PUT > ABOR GET < 426 Nothing to abort. [... 30-second delay here, as abort() attempts to read one more line ... and then:] abort: Read failed
Change History (12)
- Component changed from Upgrade/Install to Filesystem
- Keywords needs-patch added
- Owner set to dd32
- Status changed from new to accepted