pg_connect is pretty slow in case of error - php

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.

Related

What would cause PHP to ignore mysqli.reconnect setting?

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...
}

PHP - Catch timeout exception from ldap_connect()

I've written a little monitoring script in PHP, which should monitor a virtual directory and it's active directories. Everything works fine but when the virtual directory service freezes is my ldap_connect() not able to connect but also doesn't get an error back. So my whole script stands still. I think that the ldap_connect function gets a timeout back (like when you try to ping an IP and it's not reachable).
That's my connect command:
$connection = ldap_connect($hostname, $port) or die("Could not connect to {$hostname});
And I haven't found something in the manual for ldap_connect() (manual) about a timelimit parameter in which you could define how long the function should try to connect until it aborts.
How ever I wasn't quite able to come up with a solution with try and catch or something like this. I also didn't wanted to use the set_time_limit() function because my script needs to be run until the end.
I appreciate every help :)
Thanks and greetings
Tim
http://www.php.net/manual/en/function.ldap-set-option.php
particular the following options :-
LDAP_OPT_NETWORK_TIMEOUT
LDAP_OPT_TIMELIMIT
http://www.php.net/manual/en/function.ldap-set-option.php
try set LDAP_OPT_REFERRALS in 0
If you don't want your PHP program to wait XXX seconds before giving up in a case when one of your corporate DC's have failed,
and since ldap_connect() does not have a mechanism to timeout on a user specified time,
this is my workaround which shows excellent practical results.
function serviceping($host, $port=389, $timeout=1)
{
$op = fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$op) return 0; //DC is N/A
else {
fclose($op); //explicitly close open socket connection
return 1; //DC is up & running, we can safely connect with ldap_connect
}
}
// ##### STATIC DC LIST, if your DNS round robin is not setup
//$dclist = array('10.111.222.111', '10.111.222.100', '10.111.222.200');
// ##### DYNAMIC DC LIST, reverse DNS lookup sorted by round-robin result
$dclist = gethostbynamel('domain.name');
foreach ($dclist as $k => $dc) if (serviceping($dc) == true) break; else $dc = 0;
//after this loop, either there will be at least one DC which is available at present, or $dc would return bool false while the next line stops program from further execution
if (!$dc) exit("NO DOMAIN CONTROLLERS AVAILABLE AT PRESENT, PLEASE TRY AGAIN LATER!"); //user being notified
//now, ldap_connect would certainly connect succesfully to DC tested previously and no timeout will occur
$ldapconn = ldap_connect($dc) or die("DC N/A, PLEASE TRY AGAIN LATER.");
Also with this approach, you get a real nice fail over functionality.
Take for an example a company with a dozen of DC-a distributed along distant places.
This way your PHP program will always have high availability if at least one DC is active at present.
You'll need to use an API that supports time-outs. Connection time-outs are not supported in a native fashion by LDAP (the protocol). The timelimit is a client-requested parameter that refers to how long the directory will spend processing a search request, and is not the same as a "connect time-out".

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 timeout on locked tables?

I have a weird problem with mysqli timeout options, here you go:
I am using mysqli_init() and real_connect() in order to set MYSQLI_OPT_CONNECT_TIMEOUT
$this->__mysqli = mysqli_init();
if(!$this->__mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT,1))
throw new Exception('Timeout settings failed')
$this->__mysqli->real_connect(host,user,pass,db);
....
Then I am initiating query on locked table (LOCKE TABLE users WRITE) and its just hanging, ignoring all my settings even:
set_time_limit(1);
ini_set('max_execution_time',1);
ini_set('default_socket_timeout',1);
ini_set('mysql.connect_timeout',1);
I understand why set_time_limit(1) and max_execution_time is ignored but why other timeouts and especially MYSQLI_OPT_CONNECT_TIMEOUT are ignored and how to solve it.
I am using PHP 5.3.1 on Windows and Linux boxes, please help.
MYSQLI_OPT_CONNECT_TIMEOUT seems to configure the timeout at connect-time :
connection timeout in seconds
(supported on Windows with TCP/IP
since PHP 5.3.1)
Here, you are trying to execute a query on a locked table... Which means you have a query that takes a lot of time (like forever) ; but you are already connected to the database.
So, what should be configured is not the connection timeout ; but some "query timeout".
Not sure how to set that "query timeout", though...
Maybe the MYSQLI_CLIENT_INTERACTIVE flag for mysql_real_connect could help, in a way or another ?
There's innodb_lock_wait_timeout. But as the name suggests it's only for InnoDB tables.
The timeout in seconds an InnoDB transaction may wait for a row lock before giving up. The default value is 50 seconds. A transaction that tries to access a row that is locked by another InnoDB transaction will hang for at most this many seconds before issuing the following error:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
In addition to Pascal MARTIN's answer:
PHP is sleeping until the query completes - so anything you've configured for PHP is ignored. If the query ever does return, then PHP will wake up and continue processing - at which point it will realise it has run out execution time and end abruptly - will it release the locks it has acquired? Maybe.
One solution would be to implement your own locking schema, e.g.
$qry="UPDATE mydb.mylocks SET user='$pid' WHERE tablename='$table_to_lock' AND user IS NULL";
$basetime=time();
$nottimedout=5;
do {
mysql_query($qry);
$locked=mysql_affected_rows();
if (!$locked && $nottimedout--) sleep(1);
} while (!$locked && $nottimedout);
if ($nottimedout) {
// do stuff here
}
mysql_qry("UPDATE mydb.mylocks SET user=NULL WHERE tablename='$table_to_lock' AND user='$pid'";
I guess a neater solution would be to implement this as a trigger on the table - but my MySQL PL/SQL is a bit ropey.
C.

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