summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Wendel <uw@php.net>2009-09-16 15:00:54 +0000
committerUlf Wendel <uw@php.net>2009-09-16 15:00:54 +0000
commit20005db2a0469e5ca3ca0f8ed2277a9bea058529 (patch)
tree7e51d52454a32556c8e37dd6aa7ee19499608c4c
parentfb9c4bd70950b6daad08edd973273f069f23df4c (diff)
downloadphp-git-20005db2a0469e5ca3ca0f8ed2277a9bea058529.tar.gz
Fix and tests for bug #49511 . mysqlnd and the MySQL Client Library (libmysql) use different networking APIs. mysqlnd does use PHP streams whereas libmysql uses its own wrapper of the operating level network calls. PHP sets by default a read timeout of 60s for streams - php.ini, default_socket_timeout. This default applies to all streams that set no other timeout value. mysqlnd has not set any other value and therefore it connections of long running queries can have been cut off after default_socket_timeout seconds resulting in a 2006 - MySQL Server has gone away error message. The MySQL Client Library sets a default timeout of 365 * 24 * 3600 seconds (1year) and waits for other timeouts to happen, e.g. TCP/IP timeouts. mysqlnd now uses the same very long timeout. The value is configurable through a new php.ini setting: mysqlnd.net_read_timeout. mysqlnd.net_read_timeout gets used by any extension (ext/mysql, ext/mysqli, PDO_MySQL) that uses mysqlnd. mysqlnd tells PHP Streams to use mysqlnd.net_read_timeout. Please note that there may be subtle differences between MYSQL_OPT_READ_TIMEOUT from the MySQL Client Library and PHP Streams. E.g. MYSQL_OPT_READ_TIMEOUT is documented to work only for TCP/IP connections and, prior to MySQL 5.1.2, only for Windows. PHP streams may not have this limitation. Please check the streams documentation, if in doubt.
-rw-r--r--ext/mysql/tests/mysql_mysqlnd_read_timeout_long.phpt37
-rw-r--r--ext/mysqli/tests/mysqli_mysqlnd_read_timeout.phpt36
-rw-r--r--ext/mysqli/tests/mysqli_mysqlnd_read_timeout_long.phpt37
-rw-r--r--ext/mysqli/tests/mysqli_mysqlnd_read_timeout_zero.phpt36
-rw-r--r--ext/mysqlnd/mysqlnd.c4
-rw-r--r--ext/mysqlnd/mysqlnd.h3
-rw-r--r--ext/mysqlnd/php_mysqlnd.c4
7 files changed, 156 insertions, 1 deletions
diff --git a/ext/mysql/tests/mysql_mysqlnd_read_timeout_long.phpt b/ext/mysql/tests/mysql_mysqlnd_read_timeout_long.phpt
new file mode 100644
index 0000000000..d54cb50708
--- /dev/null
+++ b/ext/mysql/tests/mysql_mysqlnd_read_timeout_long.phpt
@@ -0,0 +1,37 @@
+--TEST--
+mysqlnd.net_read_timeout > default_socket_timeout
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifconnectfailure.inc');
+require_once('connect.inc');
+?>
+--INI--
+default_socket_timeout=1
+mysqlnd.net_read_timeout=12
+max_execution_time=12
+--FILE--
+<?php
+ set_time_limit(12);
+ include ("connect.inc");
+
+ if (!$link = my_mysql_connect($host, $user, $passwd, $db, $port, $socket)) {
+ printf("[001] Connect failed, [%d] %s\n", mysql_errno(), mysqlerror());
+ }
+
+ if (!$res = mysql_query("SELECT SLEEP(6)", $link))
+ printf("[002] [%d] %s\n", mysql_errno($link), mysql_error($link));
+
+ var_dump(mysql_fetch_assoc($res));
+
+ mysql_free_result($res);
+ mysql_close($link);
+
+ print "done!";
+?>
+--EXPECTF--
+array(1) {
+ [%u|b%"SLEEP(6)"]=>
+ %unicode|string%(1) "0"
+}
+done! \ No newline at end of file
diff --git a/ext/mysqli/tests/mysqli_mysqlnd_read_timeout.phpt b/ext/mysqli/tests/mysqli_mysqlnd_read_timeout.phpt
new file mode 100644
index 0000000000..5d169b3a6b
--- /dev/null
+++ b/ext/mysqli/tests/mysqli_mysqlnd_read_timeout.phpt
@@ -0,0 +1,36 @@
+--TEST--
+mysqlnd.net_read_timeout limit check
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifconnectfailure.inc');
+require_once('connect.inc');
+if (!$IS_MYSQLND)
+ /* The libmysql read_timeout limit default is 365 * 24 * 3600 seconds. It cannot be altered through PHP API calls */
+ die("skip mysqlnd only test");
+?>
+--INI--
+default_socket_timeout=60
+max_execution_time=60
+mysqlnd.net_read_timeout=1
+--FILE--
+<?php
+ include ("connect.inc");
+
+ if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
+ printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+ }
+
+ if (!$res = mysqli_query($link, "SELECT SLEEP(5)"))
+ printf("[002] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+
+ mysqli_close($link);
+
+ print "done!";
+?>
+--EXPECTF--
+Warning: mysqli_query(): MySQL server has gone away in %s on line %d
+
+Warning: mysqli_query(): Error reading result set's header in %s on line %d
+[002] [%d] %s
+done! \ No newline at end of file
diff --git a/ext/mysqli/tests/mysqli_mysqlnd_read_timeout_long.phpt b/ext/mysqli/tests/mysqli_mysqlnd_read_timeout_long.phpt
new file mode 100644
index 0000000000..d977c78b6a
--- /dev/null
+++ b/ext/mysqli/tests/mysqli_mysqlnd_read_timeout_long.phpt
@@ -0,0 +1,37 @@
+--TEST--
+mysqlnd.net_read_timeout > default_socket_timeout
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifconnectfailure.inc');
+require_once('connect.inc');
+?>
+--INI--
+default_socket_timeout=1
+mysqlnd.net_read_timeout=12
+max_execution_time=12
+--FILE--
+<?php
+ set_time_limit(12);
+ include ("connect.inc");
+
+ if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
+ printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+ }
+
+ if (!$res = mysqli_query($link, "SELECT SLEEP(6)"))
+ printf("[002] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+
+ var_dump($res->fetch_assoc());
+
+ mysqli_free_result($res);
+ mysqli_close($link);
+
+ print "done!";
+?>
+--EXPECTF--
+array(1) {
+ [%u|b%"SLEEP(6)"]=>
+ %unicode|string%(1) "0"
+}
+done! \ No newline at end of file
diff --git a/ext/mysqli/tests/mysqli_mysqlnd_read_timeout_zero.phpt b/ext/mysqli/tests/mysqli_mysqlnd_read_timeout_zero.phpt
new file mode 100644
index 0000000000..dbfd53c75d
--- /dev/null
+++ b/ext/mysqli/tests/mysqli_mysqlnd_read_timeout_zero.phpt
@@ -0,0 +1,36 @@
+--TEST--
+mysqlnd.net_read_timeout = 0
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifconnectfailure.inc');
+require_once('connect.inc');
+?>
+--INI--
+default_socket_timeout=10
+max_execution_time=10
+mysqlnd.net_read_timeout=0
+--FILE--
+<?php
+ include ("connect.inc");
+
+ if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
+ printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+ }
+
+ if (!$res = mysqli_query($link, "SELECT SLEEP(2)"))
+ printf("[002] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+
+ var_dump($res->fetch_assoc());
+
+ mysqli_free_result($res);
+ mysqli_close($link);
+
+ print "done!";
+?>
+--EXPECTF--
+array(1) {
+ [%u|b%"SLEEP(2)"]=>
+ %unicode|string%(1) "0"
+}
+done! \ No newline at end of file
diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c
index b9d3ce0501..e5dc531e23 100644
--- a/ext/mysqlnd/mysqlnd.c
+++ b/ext/mysqlnd/mysqlnd.c
@@ -627,6 +627,10 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
mnd_efree(hashed_details);
}
+ if (!conn->options.timeout_read) {
+ /* should always happen because read_timeout cannot be set via API */
+ conn->options.timeout_read = (unsigned int) MYSQLND_G(net_read_timeout);
+ }
if (conn->options.timeout_read)
{
tv.tv_sec = conn->options.timeout_read;
diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h
index 687b989303..79ce62f3ea 100644
--- a/ext/mysqlnd/mysqlnd.h
+++ b/ext/mysqlnd/mysqlnd.h
@@ -250,7 +250,7 @@ PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char *
#define mysqlnd_select_db(conn, db, db_len) (conn)->m->select_db((conn), (db), (db_len) TSRMLS_CC)
#define mysqlnd_ping(conn) (conn)->m->ping((conn) TSRMLS_CC)
#define mysqlnd_kill(conn, pid) (conn)->m->kill_connection((conn), (pid) TSRMLS_CC)
-#define mysqlnd_refresh(conn, options) (conn)->m->refresh_server((conn), (options) TSRMLS_CC)
+#define mysqlnd_refresh(conn, options) (conn)->m->refresh_server((conn), (options) TSRMLS_CC)
#define mysqlnd_shutdown(conn, level) (conn)->m->shutdown_server((conn), (level) TSRMLS_CC)
#define mysqlnd_get_server_version(conn) (conn)->m->get_server_version((conn))
#define mysqlnd_set_character_set(conn, cs) (conn)->m->set_charset((conn), (cs) TSRMLS_CC)
@@ -374,6 +374,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqlnd)
#ifdef MYSQLND_THREADED
THREAD_T thread_id;
#endif
+ long net_read_timeout;
ZEND_END_MODULE_GLOBALS(mysqlnd)
ZEND_EXTERN_MODULE_GLOBALS(mysqlnd);
diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c
index 7ebb748024..190038ffb2 100644
--- a/ext/mysqlnd/php_mysqlnd.c
+++ b/ext/mysqlnd/php_mysqlnd.c
@@ -107,6 +107,8 @@ PHP_MINFO_FUNCTION(mysqlnd)
php_info_print_table_row(2, "Command buffer size", buf);
snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_buffer_size));
php_info_print_table_row(2, "Read buffer size", buf);
+ snprintf(buf, sizeof(buf), "%ld", MYSQLND_G(net_read_timeout));
+ php_info_print_table_row(2, "Read timeout", buf);
php_info_print_table_row(2, "Collecting statistics", MYSQLND_G(collect_statistics)? "Yes":"No");
php_info_print_table_row(2, "Collecting memory statistics", MYSQLND_G(collect_memory_statistics)? "Yes":"No");
php_info_print_table_end();
@@ -136,6 +138,7 @@ static PHP_GINIT_FUNCTION(mysqlnd)
mysqlnd_globals->dbg = NULL; /* The DBG object*/
mysqlnd_globals->net_cmd_buffer_size = 2048;
mysqlnd_globals->net_read_buffer_size = 32768;
+ mysqlnd_globals->net_read_timeout = 31536000;
mysqlnd_globals->log_mask = 0;
}
/* }}} */
@@ -149,6 +152,7 @@ PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("mysqlnd.debug", NULL, PHP_INI_SYSTEM, OnUpdateString, debug, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.net_cmd_buffer_size", "2048", PHP_INI_ALL, OnUpdateLong, net_cmd_buffer_size, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.net_read_buffer_size", "32768",PHP_INI_ALL, OnUpdateLong, net_read_buffer_size, zend_mysqlnd_globals, mysqlnd_globals)
+ STD_PHP_INI_ENTRY("mysqlnd.net_read_timeout", "31536000", PHP_INI_SYSTEM, OnUpdateLong, net_read_timeout, zend_mysqlnd_globals, mysqlnd_globals)
STD_PHP_INI_ENTRY("mysqlnd.log_mask", "0", PHP_INI_ALL, OnUpdateLong, log_mask, zend_mysqlnd_globals, mysqlnd_globals)
PHP_INI_END()
/* }}} */