What would cause PHP to ignore mysqli.reconnect setting? - php

We have several different servers running the same PHP scripts, all of which use PHP mysqli connections/functions, and we noticed that, on one new server, we started getting many MYSQL Gone Away errors.
wait_timeout in MYSQL is set to 300 seconds on all servers, and this is the length of time it takes before the connection drops on this particular server (i.e. a wait of 301 seconds between queries in the code below produces the error, but 299 seconds does not).
However, all servers also have mysqli.reconnect set to 1 (On). According to documentation, mysqli.reconnect should mean that the dropped connection is reconnected automatically, however it definitely isn't.
Here is a code snippet that I've run that demonstrates the issue. It works on all servers except this particular server:
$mysqli = new mysqli('ip', 'username', 'pass', 'db');
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$query = "SELECT * FROM table LIMIT 1";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
sleep(301);
$query = "SELECT * FROM table LIMIT 1";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
mysqli_close($mysqli);
print "Finished\n";
exit;
I have also re-written the test script to use mysqli_ping() (since the documentation states that this function should reconnect automatically if mysqli.reconnect is set to 1), however this still does not reconnect, and the second query always produces the error that MySQL has gone away.
All servers are running slightly different versions of PHP. The server that fails is running 5.3.21, other servers are running 5.3.0 and 5.3.10.

Turned out the issue was with the MYSQLND driver. Our service provider basically recompiled PHP, and this resolved the issue.

There is a php bug filed about this, resolved as wontfix: https://bugs.php.net/bug.php?id=52561

I just came across this while looking for info about the mysqli.reconnect setting. According to the manual:
https://www.php.net/manual/en/mysqli.configuration.php#ini.mysqli.reconnect
Note: This php.ini setting is ignored by the mysqlnd driver.
That didn't specify whether it meant that it always would, or always would not, auto-reconnect. "Would not" seemed more likely, which is confirmed by the comment above noting that it was not happening when the setting was 1.
My issue was that I wanted a way to know when the connection had died (for error-reporting purposes, and so I could implement selective retrying of a failed DB operation in only that case). I don't know if there's a more direct way to check whether a DB connection is alive, but I found my way to the mysqli::ping() method. Except, if it silently auto-reconnected, I wouldn't know it had happened.
So my solution is to save/disable/restore the setting along with using ping(), like this:
if (!$result) { // DB operation failed
$save = ini_get('mysqli.reconnect'); // save setting
ini_set('mysqli.reconnect', 0); // disable it
$connected = $mysqli->ping(); // check connection
ini_set('mysqli.reconnect', $save); // restore setting
}
if ($connected) {
// Failure was something else. Handle that...
} else {
// Failure was due to dropped connection.
// Explicitly reconnect.
// Ok to retry operation...
}

Related

"MySQL server has gone away" when running script as cronjob

I have a cronjob running a PHP script that fetches a few hundred RSS feeds, parses them, updates a database, and then writes new Atom and RSS feed files.
The script runs fine when I call it in the browser, but when I let crond run it, it mails me the following error:
Warning: mysqli::query(): MySQL server has gone away in /script.php on line 149
Here is the relevant section of code from the script:
// ...
// the script has now been running for some time,
// fetching feeds, parsing them, and updating the database;
// now we check if the connection to the mysql server is still there,
// and then query the database and write the feed files
if (!isset($mysqli)) {
$mysqli = new mysqli($db_host,
$db_username,
$db_password,
$db_name);
$mysqli->set_charset('utf8');
$mysqli->query("SET lc_time_names = 'de_DE'");
}
$query = "..."; // some SELECT query
if (!($result = $mysqli->query($query))) { // this is line 149
// error message
}
// write the feed files
// ...
As you can see, I check, if the connection stands, before I send the query. Apparently, the database server drops the connection between checking it and performing the query. At least that is what I guess.
So, why is this happening, and what can I do to keep the connection alive (if that is actually the problem)?
My PHP scripts run on a shared server (Linux with Apache), and I don't have root access to the MySQL server – so none of the related questions answer my problem, since they all recommend to change settings in the database server config files.
Also, if called from the browser, the script is run under a different PHP version (5.6) than when I have crond execute it (5.5). See this related question.
I have tried closing the existing connection and reestablishing it, as recommended in this answer:
$mysqli->close();
if (!isset($mysqli)) {
$mysqli = new mysqli($db_host,
$db_benutzername,
$db_passwort,
$db_name);
$mysqli->set_charset('utf8');
$mysqli->query("SET lc_time_names = 'de_DE'");
}
and I have tried to set the execution time limit, as recommended in this answer:
set_time_limit(180);
or in Prince Rajput's answer below:
ignore_user_abort(true);
set_time_limit(0);
but none of that helped.
Try this on the header of cron job file:
<?php
ignore_user_abort(true);
set_time_limit(0);
?>
The problem with the first attempt at a solution (given in my question) is that closing a connection does not unset the variable, so that in
$mysqli->close();
if (!isset($mysqli)) {
the if-condition is always false.
One solution would be to unset the variable after closing the connection:
$mysqli->close();
unset($mysqli);
if (!isset($mysqli)) {
but a simpler solution is to check the connection instead of the variable:
if (!mysql_ping($mysqli)) {
(Note that of course we do not close the connection to test if it is open.)

pg_connect is pretty slow in case of error

I have a php script that is used to store some info in a postgresql db. In this script I am using a function to store some logs about certain db operations. During tests I found out that my script will log my errors related to pg_connect function, only after 4-5 seconds - and this represents a big issue for me.
Below you can find my tests and the results:
Case 1 - no error in pg_connect function:
Log("START DBCONN - without errors");
$dbconn = pg_connect("host=$host dbname=$dbname user=$dbuser password=$dbpassword");
if(!$dbconn){
Log("ERROR: Could not connect to database");
}
Log("END");
Case 2 - error in pg_connect:
Log("START DBCONN - with errors");
$dbconn = pg_connect("host=$host12 dbname=$dbname user=$dbuser password=$dbpassword");
if(!$dbconn){
Log("ERROR: Could not connect to database");
}
Log("END");
Results
case 1:
[12-Jan-2016 09:31:21] START DBCONN - without errors
[12-Jan-2016 09:31:21] END
case 2:
[12-Jan-2016 09:31:59] START DBCONN - with errors
[12-Jan-2016 09:32:03] ERROR: Could not connect to database
[12-Jan-2016 09:32:03] END
Do you know a solution for this ? because, I couldn't find one, yet :(
Thanks for your help !
Depending on the nature of the errors, there may be different reasons for it:
If the username or password is wrong, the server may delay the negative response to make brute-forcing a valid combination more cumbersome.
If the host is not reachable, the tcp connect timeout may be several seconds.
Both issues are hard to fix in a clean way.
There are also error conditions which should not result in a delay, for example:
if the host is reachable, but the database server is not running (i.e. "Connection refused").
The time to fail in case of a non-responding remote host can be adjusted with the connect_timeout option in the pg_connect() call.
PHP's pg_connect() documentation does not give any detail on connect_timeout but it just passes it to libpq, the C client library, where it's documented as:
http://www.postgresql.org/docs/current/static/libpq-connect.html
connect_timeout
Maximum wait for connection, in seconds (write as a decimal integer string). Zero or not specified means wait indefinitely. It is
not recommended to use a timeout of less than 2 seconds.
However, the 4 seconds before failure shown in the question seem reasonable for a TCP problem. If you expect recurring network errors with your database host, maybe a local connection pooler like pgBouncer should be considered, to insulate the web environnement from these high-latency errors.

mysql Link to server lost, unable to reconnect

I get the following error:
Link to server lost, unable to reconnect
I have a mysql daemon coded, and I use mysql_pconnect to connect to mysql but after a while, I get the following error and the daemon stops functioning properly:
Link to server lost, unable to reconnect
What I do is the following:
while(true)
{
$connect = mysql_pconnect(...);
$db = mysql_select_db(...);
}
What can I do to prevent this? I need mysql connection to stay steady for the whole duration of the daemon - which may be forever.
You have a few choices.
But just as a precursor, you should try and move away from relying on mysql_* and start using the PDO instead.
Anyway...
When you open a mysql connection, it will stay "available" until the wait timeout has expired. What you are doing is constantly creating a new connection, (also without closing the other connection). This means you will either hit the server mysql limit, or your unix box socket limit very quickly.
Wait Timeout
You should first check with your server to see what this timeout is set to.
You might want to consider increasing it in your my.cnf if it is terribly low. The default period is 28800 seconds (if I recall correctly).
You can check by issuing this query:
SHOW variables like "%wait_timeout%"
You can then change the value in your my.cnf to increase it or you can also set it using
SET ##GLOBAL.wait_timeout=288000
SELECT 1
Okay, now, with a reasonable set timeout set, the "typical" way that you can make sure that you don't get the mysql has gone away message, is to just do a SELECT 1. This will do another query and make sure that the connection is held open.
And, the benefit of using the PDO is that you can then catch PDOExceptions which might be thrown when a SELECT 1 fails.
This means, that you can then try and connect again, if an exception is thrown, and then check again. If you can't connect after that then you should probably kill your daemon.
Here is some code.... you would clearly have to make your PDO object using a valid connection string.
// $pdo holds the connection and a PDO object.
try {
$pdo->execute("SELECT 1");
} catch (PDOException $e) {
// Mysql has gone away.
$pdo = null
$pdo = new PDO( $connectionString );
echo "Mysql has gone away - But we attempted to reconnect\n";
try {
$pdo->execute("SELECT 1");
} catch (PDOException $e) {
echo "Mysql has failed twice - Kill Daemon\n";
throw($e);
}
}
This is the solution that I would probably take.
You can easily substitute the SELECT 1 with your own query that you actually want to use. This is just an example.

PHP mysqli reconnect problem

I am having trouble using the mysqli class in PHP and I haven't been able to find the answer anywhere.
In my script a class creates a mysqli connection that it uses throughout it's functions. Afterward, this script forks. The connection is used by the children as well, but I'm running into the problem of the connection being closed (MYSQL Server Has Gone Away) in the parent when the children die.
Before I switched to mysqli (was just using mysql) I simply called mysql_ping to assure that the db connection was there before performing the query in the parent process. Mysqli has a similar ping function BUT it doesn't actually reconnect if the connection is gone. I tried using the mysqli.reconnect=ON global setting with no luck (using php.ini and ini_set).
The php mysql_connect function allows you to grab an already-existing connection, so if I was using mysql instead of mysqli, I could simply reuse the connection in the child right after the process forked. BUT mysqli doesn't seem to have any such functionality...
The only thing I was able to do was call mysqli->ping() and if that returned false then reconnect to the database in the parent. This is terribly inefficient, and I would much rather figure out how to do it correctly with mysqli (and no need for manual reconnects) that having to change back to mysql..
Any suggestions?
The doc for mysqli_ping() says that if you set the global option mysqli.reconnect to 1 (in your php.ini) then mysqli_ping() will reconnect when it detects the connection has gone away.
Update: Since I answered this question in 2009, PHP has mostly moved on to use the mysqlnd driver by default instead of libmysql. As mentioned in the comment below, mysqlnd does not support the mysqli_ping() function, and the PHP developers did this intentionally, according to their "Won't Fix" answer in https://bugs.php.net/bug.php?id=52561
So there appears to be no solution for automatic reconnect in PHP anymore.
u can try something a bit different .. instead of ping .. try to send a really simple low intensity query like "select now()" and see if you get any better results.
Can't you use persistent connections? Also, is that really necessary to fork() ?
function IsConnected() {
if (empty($this->_connectionID) === FALSE && mysqli_ping($this->_connectionID) === TRUE) {
return true; // still have connect
}
$this->_connectionID = false;
return false; // lose connection
}
Use http://www.php.net/manual/en/mysqli.real-connect.php ... reinstall php and check your php.ini settings
I think you need to set mysqli.reconnect in my.cng, which is the mysql config, rather than php.ini, I couldn't manage to change reconnect via ini_set, but my sys admin did it in my.cnf.
So, tad confused that others have done it within php.ini....

pg_send_query(): Cannot set connection to blocking mode?

I have a long-running script that seems to occasionally report the following NOTICE-level error:
pg_send_query(): Cannot set connection to blocking mode
It seems to continue to send queries afterward, but it's unclear if it successfully sends the query that generates the error.
What is this a symptom of?
Edit: There are no entries in the postgres log at the time the error occurred, suggesting this is solely a connection error, not something going wrong on postgres' side (e.g. probably not the result of postgres crashing and restarting or something)
Edit: As far as I can tell, my INSERT statements are succeeding, one way or another, when this error is triggered.
Edit: Looks like this may have been fixed in June 2013: https://bugs.php.net/bug.php?id=65015
It is a symptom of pg_send_query() not being able to successfully switch the connection back to blocking mode. Looking at the source code in PHPs pgsql.c, you can find:
/* {{{ proto bool pg_send_query(resource connection, string query)
Send asynchronous query */
PHP_FUNCTION(pg_send_query)
{
<... snipped function setup stuff ...>
if (PQ_SETNONBLOCKING(pgsql, 1)) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to nonblocking mode");
RETURN_FALSE;
}
<... snipped main function execution stuff ...>
if (PQ_SETNONBLOCKING(pgsql, 0)) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot set connection to blocking mode");
}
RETURN_TRUE;
}
So the error gets raised at the end of the function, after the main work is done. This fits with your observation that your INSERT statements get executed.
The whole purpose of the two PQ_SETNONBLOCKING calls is to put the connection in non blocking mode to allow asynchronous execution and putting it back to the default blocking behaviour afterwards. From the documentation of PQsetnonblocking: (PQ_SETNONBLOCKING is just an alias defined for that function):
Sets the nonblocking status of the
connection.
int PQsetnonblocking(PGconn *conn, int arg);
Sets the state of the connection to nonblocking if arg is 1,
or blocking if arg is 0. Returns 0 if
OK, -1 if error.
In the nonblocking state, calls to
PQsendQuery, PQputline, PQputnbytes,
and PQendcopy will not block but
instead return an error if they need
to be called again.
Note that PQexec does not honor
nonblocking mode; if it is called, it
will act in blocking fashion anyway.
Looking further at the source of PQsetnonblocking (in PostgeSQLs fe-exec.c), there are two possible reasons why the call could fail:
/* PQsetnonblocking:
* sets the PGconn's database connection non-blocking if the arg is TRUE
* or makes it non-blocking if the arg is FALSE, this will not protect
* you from PQexec(), you'll only be safe when using the non-blocking API.
* Needs to be called only on a connected database connection.
*/
int
PQsetnonblocking(PGconn *conn, int arg)
{
bool barg;
if (!conn || conn->status == CONNECTION_BAD)
return -1;
barg = (arg ? TRUE : FALSE);
/* early out if the socket is already in the state requested */
if (barg == conn->nonblocking)
return 0;
/*
* to guarantee constancy for flushing/query/result-polling behavior we
* need to flush the send queue at this point in order to guarantee proper
* behavior. this is ok because either they are making a transition _from_
* or _to_ blocking mode, either way we can block them.
*/
/* if we are going from blocking to non-blocking flush here */
if (pqFlush(conn))
return -1;
conn->nonblocking = barg;
return 0;
}
So either the connection got lost somehow, or pqFlush did not finish successfully, indicating leftover stuff in the connection output buffer.
The first case would be harmless, as your script would certainly notice the lost connection for later calls and react to that (or fail more noticeable).
This leaves the second case, which would mean you have a connection in the non default, non blocking state. I do not know if this could affect later calls that would reuse this connection. If you want to play it safe, you'd close the connection in this case and use a new/other one.
It sounds like you're trying to use the pg_send_query() function for sending asynchronous queries to PostgreSQL. The purpose of this function is to allow your PHP script to continue executing other code while waiting for PostgreSQL to execute your query and make a result ready.
The example given in the docs for pg_send_query() suggest that you shouldn't send a query if PostgreSQL is already chewing on another query:
if (!pg_connection_busy($dbconn)) {
pg_send_query($dbconn, "select * from authors; select count(*) from authors;");
}
Is there a reason you're using pg_send_query() instead of pg_query()? If you can allow your script to block waiting for query execution, I'm guessing (admittedly without having tried it) that you won't see these errors.
I've recently had the same problem, and with the help from Henrik Opels answer realized that PHP does not actually wait for the buffer to flush before setting the connection back to blocking mode.
The 'cannot set connection to blocking mode' is trivially repeatable with large enough queries to fill the send buffer (padding with spaces at the end is enough). With smaller queries I imagine it is dependent on load, and rather intermittent.
if you do actually need asynchronous mode then try the patch at https://bugs.php.net/bug.php?id=65015
This could occur if you are using threads and the connection is being reused.
If is this the case you could use the PGSQL_CONNECT_FORCE_NEW like this:
pg_connect("...", PGSQL_CONNECT_FORCE_NEW)
This will force a new database connection resource but be advised: you could run out of connections clients, so be carefully using this inside threads so don't forget to use pg_close().
I encountered same error message with PHP 5.6.9
It occurs when persistent connection made by pg_pconnect() is lost and
pgsql.auto_reset_persistent is set to Off.
Connection might get lost when:
PHP Session expires
Connection to DB timeouts
Webserver / DB server is restarted
You can check PHP.ini for pgsql.auto_reset_persistent and set it to On.
With pgsql.auto_reset_persistent enabled, each time pg_pconnect() is being called, connection link is checked, if it is still valid. This requires a little overhead, but fixies error message when conncetion is lost.
I got that error too. I solve my problem by restarting the web server (Apache).

Categories