Solving "MySQL server has gone away" errors - mysql

I have written some code in PHP that returns the html content from .edu domains. A brief introduction is given here: Errors regarding Web Crawler in PHP
The crawler works fine when the number of links to crawl are small (something around 40 URLS) but I am getting "MySQL server has gone away" error after this number.
I am storing html content as longtext in MySQL tables and I am not getting why the error arrives after at least 40-50 insertions.
Any help in this regard is highly appreciated.
Please note that I have already altered the wait_timeout and max_allowed_packet to accomodate my queries and the php code and now I don't know what to do. Please help me in this regard.

You might be inclined to handle this problem by "pinging" the mysql server before a query. This is a bad idea. For more on why, check this SO post: Should I ping mysql server before each query?
The best way to handle the issue is by wrapping queries inside try/catch blocks and catching any database exceptions so that you can handle them appropriately. This is especially important in long running and/or daemon type scripts. So, here's a very basic example using a "connection manager" to control access to DB connections:
class DbPool {
private $connections = array();
function addConnection($id, $dsn) {
$this->connections[$id] = array(
'dsn' => $dsn,
'conn' => null
);
}
function getConnection($id) {
if (!isset($this->connections[$id])) {
throw new Exception('Invalid DB connection requested');
} elseif (isset($this->connections[$id]['conn'])) {
return $this->connections[$id]['conn'];
} else {
try {
// for mysql you need to supply user/pass as well
$conn = new PDO($dsn);
// Tell PDO to throw an exception on error
// (like "MySQL server has gone away")
$conn->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
$this->connections[$id]['conn'] = $conn;
return $conn;
} catch (PDOException $e) {
return false;
}
}
}
function close($id) {
if (!isset($this->connections[$id])) {
throw new Exception('Invalid DB connection requested');
}
$this->connections[$id]['conn'] = null;
}
}
class Crawler {
private $dbPool;
function __construct(DbPool $dbPool) {
$this->dbPool = $dbPool;
}
function crawl() {
// craw and store data in $crawledData variable
$this->save($crawledData);
}
function saveData($crawledData) {
if (!$conn = $this->dbPool->getConnection('write_conn') {
// doh! couldn't retrieve DB connection ... handle it
} else {
try {
// perform query on the $conn database connection
} catch (Exception $e) {
$msg = $e->getMessage();
if (strstr($msg, 'MySQL server has gone away') {
$this->dbPool->close('write_conn');
$this->saveData($val);
} else {
// some other error occurred
}
}
}
}
}

I have another answer that deals with what I think is a similar problem, and it would require a similar answer. Basically, you can use the mysql_ping() function to test the connection before your insert. Before MySQL 5.0.14, mysql_ping() would automatically reconnect the server, but now you have to build your own reconnect logic. Something similar to this should work for you:
function check_dbconn($connection) {
if (!mysql_ping($connection)) {
mysql_close($connection);
$connection = mysql_connect('server', 'username', 'password');
mysql_select_db('db',$connection);
}
return $connection;
}
foreach($array as $value) {
$dbconn = check_dbconn($dbconn);
$sql="insert into collected values('".$value."')";
$res=mysql_query($sql, $dbconn);
//then some extra code.
}

I was facing "Mysql server has gone away" error while using Mysql connector 5.X, replacing dll to the last version solved the problem.

Are you opening a single DB connection and reusing it? Is it possible that its a simple timeout? You might be better served by opening a new DB connection for each of your read/write operations (IE contact .edu, get text, open DB, write text, close db, repeat).
Also how are you using the handle? Is it possible that it has hit an error and has 'gone away' for that reason?

Well This is what I am doing now based on rdlowrey's suggestion and I guess this is also right.
public function url_db_html($sourceLink = NULL, $source) {
$source = mysql_real_escape_string($source);
$query = "INSERT INTO html (id, sourceLink, sourceCode)
VALUES (NULL,('$sourceLink') , ('$source'))";
try {
if(mysql_query($query, $this->connection)==FALSE) {
$msg = mysql_errno($this->connection) . ": " . mysql_error($this->connection);
throw new DbException($msg);
}
} catch (DbException $e) {
echo "<br><br>Catched!!!<br><br>";
if(strstr($e->getMessage(), 'MySQL server has gone away')) {
$this->connection = mysql_connect("localhost", "root", "");
mysql_select_db("crawler1", $this->connection);
}
}
}
So once the query has failed to execute, the script will skip it but will make sure the connection is re-established.
However, my web crawler is crashing when files such as .jpg, .bmp, .pdf, etc are encountered. Is there a way to skip those urls containing these extensions. I am using preg_match and has given pdf and doc to match. Yet I want the function to skip all links containing extensions such as mp3, pdf, etc. Is this possible??

Related

Adldap2 authentication always returns true - Yii2

I'm working in Yii2 with the Adldap extension found here: https://github.com/Adldap2/Adldap2
I'm running into an issue when I try to authenticate users on my ldap server. I can successfully make a connection and and retrieve user data, but when trying to authenticate if a user's username and password are correct or not, it always returns true, even if the creds are wrong. Below is my code snippet (with the config array not showing of course):
$ad->addProvider($config);
try {
// If a successful connection is made to your server, the provider will be returned.
$provider = $ad->connect();
//User below does return the correct information from the ldap server
$user = $provider->search()->users()->find('quillin');
try{
$provider->auth()->attempt("wrongUsername","wrongPassword");
die("WIN");
}catch( Exception $e ){
die("Exception " . $e);
}
}catch (\Adldap\Auth\BindException $e) {
die( "There was an issue binding / connecting to the server. <br />" . $e);
}
No matter what I put in for the username and password fields, it always returns true and hits the die("WIN"); line. In my composer.json file, i'm using "adldap2/adldap2": "v7.0.*"
I have also tried to bind the user using the following:
try{
$provider->auth()->attempt("wrongUsername","wrongPassword", $bindAsUser = true);
die("WIN");
}catch( Exception $e ){
die("lose :(");
die("Exception " . $e);
}
And that also always returns true;
I figured this out and will explain here in anyone else has the same issue.
1) $provider->auth()->attempt() should be wrapped in an IF, and not a try/catch.
2) The first parameter, $username, is actually looking for the userprincipalname, the docs had made it sound like it was looking instead for a username.
After that, I was able to authenticate the user successfully.

Symfony3: Doctrine batch processing with exceptions handling

I need to insert multiple user from an Excel file using Symfony3 command. I have read the following article about batch processing: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/batch-processing.html
I have been wondering if there is a way to no stop the flushing process when a query fails (for a not null column for instance). I would actually like to be able to not check all my data before doing the persist and let Doctrine continue the inserts, even though a query failed in the flush of 20 queries lets say.
Thank you for your help.
Kind regards,
This template may help you go further...
$batchSize = 20;
$currentSize = 0;
$data = [ .... ];
foreach ($data as $item) {
$entity = new Entity();
$entity->setProperty($item['property']);
try {
$currentSize++;
$em->persist($entity);
if ($batchSize% $currentSize === 0) {
$em->flush();
$em->clear();
}
} catch (\Doctrine\ORM\ORMException $e) {
$currentSize--;
}
}
$em->flush();
$em->clear();

cakephp 3.1 mysql has gone away

I have a Shell script that runs continuously on a loop.
It checks the database for records and alters them if needs be.
set_time_limit(0);
while(true){
try{
$this->out(mysql_ping());
$companies = $this->findCompanies();
$companies = $this->reduceCompanies($companies, $rules);
$this->processCompanies($companies);
}catch (\Exception $e){
Log::write('debug', $e->getMessage());
$this->out($e->getMessage());
}
sleep(3);
}
Problem I'm having is this script seems to run ok, but then randomly will throw:
'2006 MySQL server has gone away'
I've tried to put some stuff in the exception catch to reconnect to the mysql server such as :
}catch (\Exception $e){
if(!mysql_ping()){//tried
$this->connection->reconnect(); //also tried
$this->Company->getDatasource()->reconnect(); neither seem to work.
}}
Any suggestions how to reconnect to the db?
I had a similar Problem where the Shell is running in an infinite loop.
Add the use for the ConnectionManager:
use Cake\Datasource\ConnectionManager;
Get the connection before the loop:
$connection = ConnectionManager::get('default');
In the loop, shortly before you first need the connection, check if you are still connected and do a connect if you are disconnected:
if(!$connection->isConnected()) {
$connection->connect();
}
If your Shell has a state where it is waiting for a longer time, you could try to manually disconnect and do a new connect when you need the connection again:
if($connection->isConnected()) {
$connection->disconnect();
}

PHP PDO - Testing connection before doing query?

public function smart_query($query, $options = null, $bindoptions = null)
{
// Code to Reconnect incase of timeout
try {
$this->db->query('SELECT * FROM templates');
}
catch (PDOException $e)
{
echo $e;
$pdooptions = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->db = new PDO("mysql:host=localhost;dbname=$this->database", "$this->username", "$this->password", $pdooptions);
}
$this->statement = $this->db->prepare($query);
if($bindoptions != null)
{
$this->bind($bindoptions);
}
$this->execute();
if($options != null)
{
// Return Single Row
if($options['rows'] == 1)
{
return $this->statement->fetch(PDO::FETCH_ASSOC);
}
// Return Multiple Rows
elseif($options['rows'] != 1)
{
return $this->statement->fetchAll(PDO::FETCH_ASSOC);
}
}
}
I've saw this code today, and got really confused.
It looks like he is trying to process a simple query, before doing the actual query.
Why is he checking if the connection is still open?
I thought that PDO only destroys it's connection upon script finishing executing automatically?
Is that correct to check if it's open or closed?
This implements a form of lazy loading.
This first time a query is executed through this class/function, the database connection may not be established yet. This is the purpose of this check, so that the consumer (you) does not have to mind about it.
The connection is then stored in the $this->db class member, for future reuse when you call this method again in the course of your script (and yes, this connection will stay open until the script ends -- unless it is closed explicitely beforehand, of course).
For information, this check is slightly inefficient. A simple $this->db->query('SELECT 1') would suffice, without the need to read a table at all.

MySQL, Asterisk Dialplans and call forwarding

How do I get Asterisk to forward incoming calls based on matching the incoming call number with a number to forward to? Both numbers are stored in a MySQL database.
Sorry for the long code sample, but more than half of it is debugging code to help you get it set up.
I'm assuming your server already has a modern version of PHP (at /usr/bin/php) with the PDO library, and that you have a database table named fwd_table with columns caller_id and destination.
In /var/lib/asterisk/agi-bin get a copy of the PHP AGI library. Then create a file named something like forward_by_callerid.agi that contains:
#!/usr/bin/php
<?php
ini_set('display_errors','false'); //Supress errors getting sent to the Asterisk process
require('phpagi.php');
$agi = new AGI();
try {
$pdo = new PDO('mysql:host='.$db_hostname.';dbname='.$db_database.';charset=UTF-8', $db_user, $db_pass);
} catch (PDOException $e) {
$agi->conlog("FAIL: Error connecting to the database! " . $e->getMessage());
die();
}
$find_fwd_by_callerid = $pdo->prepare('SELECT destination FROM fwd_table WHERE caller_id=? ');
$caller_id = $agi->request['agi_callerid'];
if($callerid=="unknown" or $callerid=="private" or $callerid==""){
$agi->conlog("Call came in without caller id, I give up");
exit;
}else{
$agi->conlog("Call came in with caller id number $caller_id.");
}
if($find_fwd_by_callerid->execute(array($caller_id)) === false){
$agi->conlog("Database problem searching for forward destination (find_fwd_by_callerid), croaking");
exit;
}
$found_fwds = $find_fwd_by_callerid->fetchAll();
if(count($found_fwds) > 0){
$destination = $found_contacts[0]['destination'];
$agi->set_variable('FWD_TO', $destination);
$agi->conlog("Caller ID matched, setting FWD_TO variable to ''");
}
?>
Then from the dial plan you can call it like this:
AGI(forward_by_callerid.agi)
And if your database has a match, it will set the variable FWD_TO with goodness. Please edit your question if you need more help getting this integrated into your dial plan.
This article should do the trick. It's about 3 lines of code and some simple queries to add and remove forwarding rules.
The solution I was looking for ended up looking like this:
[default]
exten => _X.,1,Set(ARRAY(${EXTEN}_phone)=${DTC_ICF(phone_number,${EXTEN})})
exten => _X.,n(callphone),Dial(SIP/metaswitch/${${EXTEN}_phone},26)
exten => _X.,n(end),Hangup()