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.)
Related
I am implementing a script in php for a Wordpress blog. The script should be executed every five minutes. The script opens a mysql connection and I want to close it when I am finished. Can you check if it works?
$db = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
mysql_select_db("XY", $db);
//set data into $data
foreach ($data => $info) {
//do stuff inserts
}
mysql_close();
Is the approach right? I am closing the connection used in this script or I am closing also other connections?
Closing the connection explicitly is not mandatory. PHP cleans up all the resources when the script finishes, this includes database connections or sockets, filehandles, any internal resources like images and last but not least, memory blocks.
You could pass your $db variable to the mysql_close() function call. Like mysql_close($db).
But be aware that the mysql_* are deprecated and will be removed in a future php version. You should look into the mysqli_* functions instead.
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...
}
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.
I am creating a simple Web Application in PHP for my college project. I am using the MySQL database.
I connect to the database in login.php. After connection I assign the connection to $_SESSION["conn"] and then redirect to main.php.
In main.php I write $conn = $_SESSION["conn"]. But the connection in $conn does not work.
I thought that as the login.php script ends, the connection gets closed. So I tried using mysql_pconnect instead of mysql_connect but that too does not work.
I know I can reconnect to the database in every PHP file. But I don't want to do this. I want to use the same connection in all PHP files.
Instead of saving the DB connection in a session you should make the connection calls in a separate file such as db.php and then require it from each of your scripts. For example, place your connection in db.php:
mysql_connect('...', '...', '...');
mysql_select_db('...');
and then bring it in in login.php:
require('db.php');
$res = mysql_query('...');
You can then do the same for each PHP file that needs access to the DB and you'll only ever have to change your DB access credentials in one file.
After connection I assign the connection to $_SESSION["conn"] and then redirect to main.php.
You'll probably want to read up on PHP sessions. You can't store resources (database connections, file handles, etc) in a session, because they can not be serialized and stored.
Keep in mind that each and every visit to a PHP script invokes a new instance of the PHP interpreter (via CGI, via FastCGI, or via a built-in module), and invokes a new instance of the script. Nothing is shared between script calls, because the entire environment goes away when the script exits.
The other answers are correct -- you'll need to connect to the database on every script call. Place the connection in a common include file for convenience.
The second request may not be served by the same web server process as the first, which means that you will have a completely separate set of database resources. You'll need to connect again in this new process in order to run queries.
What I normally have is a Connection class that pages will require in order to establish a connection. Something along the lines of:
class Connection {
public $dbConnection = null;
public $isConnectionActive = false;
private $dbServer = null;
private $dbCatalog = null;
private $dbUser = null;
private $dbPassword = null;
}
This class handles opening and closing of the connection on any pages.
It sounds that you want to make your php connections persistants , in that way and if it is so , then you have to create a PDO Object with the required parameters to create a new PHP PDO Object that will attempt to connect to mysql , and in the object instanciation , try to pass persistancy option value to true , you may have available in every php scripts the same PDO Connection Objetc but you will end up to face issues with that way ... but it is not reconmmanded as PHP Programming Best Pratices ... the best way to do so is to include a connection file for every script in your project. Read PHP Documentation for Persistant Connections in order to learn more about Issues found for these Connection Objets especially for recent php versions. PHP Announced that Persistant Connections are on the way to be dropped from futur version as it may increase server workload with persistant ressources ... Sorry if it is too late
$con = mysql_connect("localhost:".$LOCAL_DB_PORT, $LOCAL_DB_USER, $LOCAL_DB_PASS);
mysql_select_db("hr", $con);
mysql_query("set names utf8", $con);
while(true)
{
do_stuff($con);
sleep(50);
}
If connection timesout in 50 seconds,will $con still work?
If the connection times out, it won't work.
To answer the question from the comment, to cope with the problem, refer to php.net manual page for mysql_connect() which says:
If a second call is made to mysql_connect() with the same arguments, no new link will be established, but instead, the link identifier of the already opened link will be returned.
so if you want to make sure you always have an open connection, just try to open a new one with the same arguments after the code you substituted with sleep() is done executing.
Simple answer: why don't you try it and see?
I believe codeburger is correct: if the MySQL connection times out, then it's gone. You could use a persistent connection with mysql_pconnect. You will need to call that function after sleep each time but it will use an existing connection, saving overhead.