php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #49965 Setting MYSQLI_OPT_CONNECT_TIMEOUT does not seem to have an effect on timeout
Submitted: 2009-10-23 08:10 UTC Modified: 2009-11-03 14:58 UTC
From: ed at bellcpa dot com Assigned: mysql (profile)
Status: Closed Package: MySQLi related
PHP Version: 5.*, 6.* OS: *
Private report: No CVE-ID: None
 [2009-10-23 08:10 UTC] ed at bellcpa dot com
Description:
------------
I have a script that attempts to contact/query an MySQL server on a LAN. If the server is available, the script runs successfully. If the MySQL server is offline, however, the script hangs along with the browser session indefinately. This is despite MYSQLI_OPT_CONNECT_TIMEOUT being set to 5 seconds.

Specifically, the script below hangs when "$mysqli->real_connect" is executed

Looking for a way to timeout connection attempt.

Running VC9 x86 Thread Safe 5.3. Default mysqli/mysqlnd php.ini settings.

The code below worked fine in the past in the same scenario on version 5.2.8.

Reproduce code:
---------------
$mysqli = mysqli_init();
if (!$mysqli) exit(1);

if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 5)) {
    exit(1);
}

if (!$mysqli->real_connect('localhost', 'my_user', 'my_password', 'my_db')) {
    exit(1);
}


Expected result:
----------------
Quit in around 5 sec with an code 1 if MySQL is offline.

Actual result:
--------------
Script hangs indefinately and so does a browser session through which the script is invoked. After some time (e.g. 2-5 min) the following is logged in php_errors.log:

[23-Oct-2009 00:48:26] PHP Warning:  mysqli::real_connect(): [2002] A connection attempt failed because the connected party did not  (trying to connect via tcp://192.168.201.222:3306) in C:\app\php\script.php on line 30

[23-Oct-2009 00:48:26] PHP Warning:  mysqli::real_connect(): (HY000/2002): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-10-23 17:13 UTC] ed at bellcpa dot com
Oh but I did search and have seen #49511 propr to submitting....


#49511 mainly focuses on a query timeout and not a connection timeout, which is my issue. MYSQL_OPT_CONNECT_TIMEOUT mentioned is unaddressed.

Also tried ini_set('default_socket_timeout', 5) as suggested in that submission with no effect.
 [2009-10-23 18:37 UTC] ed at bellcpa dot com
Worked around the problem for now by testing the connection with fsockopen prior to real_connect.
 [2009-10-23 19:22 UTC] uw@php.net
Can you give more details? I'm unable to reproduce the issue with 5.3.2-dev (= should become 5.3.1). Is it possible for you to try a snapshot? Can you provide me with a Wireshark (network sniffer) protocol?

The connect timeout itself should work. If not, it should be a streams or Windows matter. First question is what exactly "connect timeout" means. A wireshark protocol should be able to tell. In 5.3.0 you may get a successful connect within the time limit you set but then, when reading data, experience default_socket_timeout.

Thanks!
 [2009-10-23 19:24 UTC] uw@php.net
PS: don't rely on connect timeout of libmysql - it does not work in every case. Check bugs.mysql.com.
 [2009-10-26 21:10 UTC] ed at bellcpa dot com
After further testing:

The php_errors.log messages previously mentioned are logged exactly 60 sec after a connection attempt is made so some timeout is likely in effect. Also, the script remains running in a "hung" state after the errors are logged.

Post connection timeout is not a concern here, instead the issue is with the length of the timeout when MySQL server is purposely not online.

I have Wireshark captures for when the MySQL server is offline and connection times out as well as when MySQL is online and connection succeeds right away. Can I email these to you?

I am using mysqli on 5.3.0 with default settings, shouldn't mysqlnd be used by default?

I will try a snapshot soon once I find the time to test.

Thanks!
 [2009-10-27 10:04 UTC] uw@php.net
60s - we are back to some config problem. Can you try change the default_socket_timeout? Can you try a 5.3.1 snapshot?

Thanks!
Ulf


 [2009-10-27 10:06 UTC] uw@php.net
Regarding wireshark protocol: yes, please do mail them to me or offer them for download anywhere (make sure they don't show any confidential data, e.g. server IP, username, password!) 

Thanks!
Ulf
 [2009-10-27 21:19 UTC] ed at bellcpa dot com
Hi Ulf,

I tried a 5.3.2-dev VC9 x86 thread-safe snapshot (not sure how to obtain 5.3.1) with same results. Also tried a 5 second default_socket_timeout with no effect.

Will send the captures to you by email.

Thanks
 [2009-10-28 16:54 UTC] uw@php.net
Hi Ed,

as you may know from the Wireshark protocol you provided (mysql-noserver.pcap - MySQL is offline) there is a TCP SYN->RST,ACK  ping-pong between the client machine and the server. 

PHP tries to establish a connection and sends SYN. The server's response is RST (, ACK) indicating that no process on the server is wating for requests on the requested port (MySQL is offline).

At this point PHP should get ECONNREFUSED from connect() and bubble up the error. But it seems as if this is not the case. According to the Wireshark protocol the first SYN->RST,ACK is followed by another connection attempt after about 0.5s which fails. PHP continues to make another connection attempt after 1s which fails again.

The ping-pong should start because PHP recognizes after 0.5s respectively 1s that there is time left for more connection attempts (you set the connect timeout to 5s). 

I don't know why the protocol shows no more records after 1s.

However, can you try using fsockopen() to connect to the MySQL host if MySQL is offline? 

Something like this:

sapi/cli/php -r '$start = microtime(true); $errno = 0; $error = ""; $fp = fsockopen("tcp://192.168.201.222", 3306, $errno, $error, 5); var_dump($fp); var_dump($errno); var_dump($error); var_dump(microtime(true) - $start); '

Again, I'd be interested in the Wireshark protocol (unless it is blocking endless). 

Thanks!
Ulf
 [2009-10-28 17:07 UTC] uw@php.net
Ah, looks I'm wrong on the loop. PHP should iterate over all possible hosts and only decrement remaining time if there are multiple hosts...  

Anyway, fsockopen() would be interesting.
 [2009-10-28 17:40 UTC] ed at bellcpa dot com
Ulf,

Here is the fsockopen() result, looks like timeout works as expected:

C:\temp2>php testme.php
PHP Warning:  fsockopen(): unable to connect to tcp://192.168.201.222:3306 (A co
nnection attempt failed because the connected party did not properly respond aft
er a period of time, or established connection failed because connected host has
 failed to respond.
) in C:\temp2\testme.php on line 6
PHP Stack trace:
PHP   1. {main}() C:\temp2\testme.php:0
PHP   2. fsockopen() C:\temp2\testme.php:6

Warning: fsockopen(): unable to connect to tcp://192.168.201.222:3306 (A connect
ion attempt failed because the connected party did not properly respond after a
period of time, or established connection failed because connected host has fail
ed to respond.
) in C:\temp2\testme.php on line 6

Call Stack:
    0.0312     324832   1. {main}() C:\temp2\testme.php:0
    0.0491     325280   2. fsockopen() C:\temp2\testme.php:6

bool(false)
int(10060)
string(185) "A connection attempt failed because the connected party did not pro
perly respond after a period of time, or established connection failed because c
onnected host has failed to respond.
"
float(5.1100761890411)
 [2009-10-28 17:52 UTC] uw@php.net
Interesting. I assume it does not matter if you use 5.3.0 or 5.3.2-dev?

Thanks so far!
Ulf
 [2009-10-28 23:48 UTC] uw@php.net
Hi Ed,

thanks for the additional information given in a private mail.

Yes, it matters what type the option value has that you pass to mysqli_options(). It matters ever since 5.0. It is irrelevant if you use mysqlnd or libmysql.

mysqli_options(mysqli $link, int $option, mixed $value) inspects the type of the $value parameter. If you pass a string value, as stated in your private mail, ext/mysqli forwards a string to mysqlnd resp. libmysql. Both mysqlnd and libmysql expect that the data they get is of the appropriate type for $option allowing them to use a simple cast to interpret the data. 

String is not the appropriate type for MYSQLI_OPT_CONNECT_TIMEOUT - the cast inside mysqlnd/libmysql causes a bogus value to be used...

This will not work:

 $timeout = "5"
 mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, $timeout);
 mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, "5");

You must use the following:

 $timeout = 5;
 mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, $timeout);
 mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, 5);


 [2009-10-28 23:58 UTC] ed at bellcpa dot com
Ah, makes perfect sense. Glad it wasn't something more serious and sorry for the oversight. Enclosing my option value in intval() solved it as expected.

Thanks!
 [2009-10-29 18:19 UTC] uw@php.net
I discussed this with Andrey. We propose to change ext/mysqli to cast $value passed to mysqli_options to an appropriate type internally. Old code should not notice the change because it should be using an appropriate type for $value in mysqli_options($link, $option, $value) already.

I'll soon patch ext/mysqli.


 [2009-11-03 14:56 UTC] svn@php.net
Automatic comment from SVN on behalf of uw
Revision: http://svn.php.net/viewvc/?view=revision&revision=290170
Log: Fix for bug #49965 . Let mysqli_options() try to cast option values to the appropriate type.
 [2009-11-03 14:58 UTC] uw@php.net
This bug has been fixed in SVN.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Apr 23 07:01:29 2024 UTC