From 43c7993c1a19c3c7c22dd1af380dc92411a11e91 Mon Sep 17 00:00:00 2001 From: Andrew Nacin Date: Sun, 2 Feb 2014 22:07:12 +0000 Subject: [PATCH] When the MySQL server has "gone away," attempt to reconnect and retry the query. props pento. see #5932. Built from https://develop.svn.wordpress.org/trunk@27075 git-svn-id: http://core.svn.wordpress.org/trunk@26948 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/wp-db.php | 126 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 15 deletions(-) diff --git a/wp-includes/wp-db.php b/wp-includes/wp-db.php index 1a7ab30266..ffc549cb70 100644 --- a/wp-includes/wp-db.php +++ b/wp-includes/wp-db.php @@ -157,6 +157,16 @@ class wpdb { */ var $queries; + /** + * The number of times to retry reconnecting before dying. + * + * @since 3.9.0 + * @access protected + * @see wpdb::check_connection() + * @var int + */ + protected $reconnect_retries = 5; + /** * WordPress table prefix * @@ -1192,11 +1202,17 @@ class wpdb { } /** - * Connect to and select database + * Connect to and select database. * * @since 3.0.0 + * + * @param bool $allow_bail Optional. Allows the function to bail, default true. If this is set + * to false, you will need to handle the lack of database connection + * manually. Available since 3.9.0. + * + * @return bool True with a successful connection, false on failure. */ - function db_connect() { + function db_connect( $allow_bail = true ) { $this->is_mysql = true; @@ -1217,7 +1233,7 @@ class wpdb { $this->dbh = @mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, $new_link, $client_flags ); } - if ( !$this->dbh ) { + if ( ! $this->dbh && $allow_bail ) { wp_load_translations_early(); // Load custom DB error template, if present. @@ -1237,16 +1253,72 @@ class wpdb {

If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.

" ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' ); - return; + return false; + } else if ( $this->dbh ) { + $this->set_charset( $this->dbh ); + $this->set_sql_mode(); + $this->ready = true; + $this->select( $this->dbname, $this->dbh ); + + return true; } - $this->set_charset( $this->dbh ); + return false; + } - $this->set_sql_mode(); + /** + * Check that the connection to the database is still up. If not, try to reconnect. + * + * If this function is unable to reconnect, it will forcibly die. + * + * @since 3.9.0 + * + * @return bool True if the connection is up. + */ + function check_connection() { + if ( @mysql_ping( $this->dbh ) ) { + return true; + } - $this->ready = true; + $error_reporting = false; - $this->select( $this->dbname, $this->dbh ); + // Disable warnings, as we don't want to see a multitude of "unable to connect" messages + if ( WP_DEBUG ) { + $error_reporting = error_reporting(); + error_reporting( $error_reporting & ~E_WARNING ); + } + + for ( $tries = 1; $tries <= $this->reconnect_retries; $tries++ ) { + // On the last try, re-enable warnings. We want to see a single instance of the + // "unable to connect" message on the bail() screen, if it appears. + if ( $this->reconnect_retries === $tries && WP_DEBUG ) { + error_reporting( $error_reporting ); + } + + if ( $this->db_connect( false ) ) { + if ( $error_reporting ) { + error_reporting( $error_reporting ); + } + + return true; + } + + sleep( 1 ); + } + + // We weren't able to reconnect, so we better bail. + $this->bail( sprintf( ( " +

Error reconnecting to the database

+

This means that we lost contact with the database server at %s. This could mean your host's database server is down.

+ +

If you're unsure what these terms mean you should probably contact your host. If you still need help you can always visit the WordPress Support Forums.

+" ), htmlspecialchars( $this->dbhost, ENT_QUOTES ) ), 'db_connect_fail' ); + + // Call dead_db() if bail didn't die, because this database is no more. It has ceased to be (at least temporarily). + dead_db(); } /** @@ -1262,6 +1334,7 @@ class wpdb { function query( $query ) { if ( ! $this->ready ) return false; + /** * Filter the database query. * @@ -1281,14 +1354,14 @@ class wpdb { // Keep track of the last query for debug.. $this->last_query = $query; - if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) - $this->timer_start(); + $this->_do_query( $query ); - $this->result = @mysql_query( $query, $this->dbh ); - $this->num_queries++; - - if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) - $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() ); + // MySQL server has gone away, try to reconnect + if ( empty( $this->dbh ) || 2006 == mysql_errno( $this->dbh ) ) { + if ( $this->check_connection() ) { + $this->_do_query( $query ); + } + } // If there is an error then take note of it.. if ( $this->last_error = mysql_error( $this->dbh ) ) { @@ -1326,6 +1399,29 @@ class wpdb { return $return_val; } + /** + * Internal function to perform the mysql_query call + * + * @since 3.9.0 + * + * @access private + * @see wpdb::query() + * + * @param string $query The query to run + */ + private function _do_query( $query ) { + if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { + $this->timer_start(); + } + + $this->result = @mysql_query( $query, $this->dbh ); + $this->num_queries++; + + if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) { + $this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() ); + } + } + /** * Insert a row into a table. *