PHP MySQLi timeout on locked tables? - php

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.

Related

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.

PHP/MongoDB: find() is ignoring timeout setting

Trying mongodb global timeout etc. is still ignored by find() queries in my PHP script.
I'd like a findOne({...}) or find({...}) lookup and wait max 20ms for the DB server before timeout.
How to make sure that PHP does not utilize this setting as soft limit? It's still ignored and processing answers even 5sec later.
Is this a PHP mongo driver bug?
Example:
MongoCursor::$timeout=20;
$nosql_server=new Mongo('mongodb://user:pw#'.implode(",",$arr_replicas).'',array("replicaSet" => "gmt","timeout"=>10)) OR troubles("too slow to connect");
$nosql_db=$nosql_server->selectDB('aDB');
$nosql_collection_mcol=$nosql_db->mcol;
$testFind=$nosql_collection_mcol->find(array('crit'=>123));
//If PHP considered the MongoCursor::$timeout, I'd expect the prev. line to be skipped or throwing a mongo/timeout exception if DB does not return the find result cursor ready within 20ms.
//However, I arrive with this line after seconds, without exception whenever the DB has some lock or delay, without skipping previous line.
In the PHP documentation for $timeout the following is the explanation for the cursor timeout:
Causes methods that fetch results to throw a
MongoCursorTimeoutException if the query takes longer than the
specified number of milliseconds.
I believe that the timeout is referring to the operations performed on the cursor (e.g. getNext()).
Do not do this:
MongoCursor::$timeout=20;
That is calling a static method and won't do you any good AFAIK.
What you need to realize is that in your code example, $testFind is the MongoCursor object. Therefore in the code snippet you gave, what you should do is add this after everything else in order to set the timeout of the $testFind MongoCursor:
$testFind->timeout(100);
NOTE: If you want to deal with $testFind as an an array you need to do:
$testFindArray = iterator_to_array($testFind);
That one threw me for a loop for awhile. Hope this helps someone.
Pay attention on the readPreference attribute. The possible values are:
MongoClient::RP_PRIMARY
MongoClient::RP_PRIMARY_PREFERRED
MongoClient::RP_SECONDARY
MongoClient::RP_SECONDARY_PREFERRED
MongoClient::RP_NEAREST

Mongo fatal error

I am using php and mongo db... I want to get user details using userkey which is a unique
key.. My mongo query is :
$obj= $mongo->user;
$filter = array(
'userkey'=>$value
);
$exist = $obj->findone($filter);
When i execute this query, am getting error as ..
Fatal error: Maximum execution time of 30 seconds exceeded in line 5
ie $exist = $obj->findone($filter); shows error
How to solve this problem
Can someone help me plz...
Can you get any mongo queries to work, or is it just this one that fails? It could be something with your connection to the database. I believe that "findone()" will wait forever on a stalled connection. I would recommend using "find()" instead, and giving it a low timeout, like 100ms. This may help.
$cursor = $collection->find($query,$fields)->timeout(100);
$record = $cursor->getNext();
Obviously, you will want to wrap this in a try/catch block to capture the timeout exception. Be sure to use the latest mongo drivers (1.2.10 or better), because earlier versions of the php mongo drivers would segfault on a connection timeout.

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.

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