Error: "Mysql Server has gone away"
Ik ben bezig met data die ik via een API (JSON) binnen krijg in mijn eigen DB (MySQL) te plaatsen. Dit werkt perfect. Wanneer ik bijvoorbeeld 200x een request stuur krijg ik alles netjes terug en kan ik het gewoon in mijn DB stoppen. De JSON die ik terug krijg is altijd het zelfde alleen met andere informatie uiteraard.
Nu mijn probleem, wanneer ik mijn script 1000x een request laat sturen, krijg ik op een gegeven moment de error “Mysql Server has gone away”. Uiteraard heb ik gekeken of ik dit kan oplossen door 'max_allowed_packet' te vergroten en dit is inderdaad een oplossing.
Maar ik begrijp niet waarom het mis gaat. Wanneer ik de request waar het misgaat individueel ophaal dan is er geen probleem.
Mijn vraag is dan ook, Hoe kan het dat wanneer ik over een groot aantal request loop ik deze error krijg maar als ik die specifieke request alleen doe dat er geen probleem is?
Ik kan mij zo voorstellen dat als je een heleboel requests parallel door de pijplijn probeert te persen dat er op enig moment een connectie-limiet wordt bereikt. Misschien kan het maken van een persistente connectie uitkomst bieden (dit is mogelijk ook vele malen efficienter in dit geval?)?
Anyhoo, zonder script (en de manier van aanroepen) blijft dit koffiedik kijken.
Gewijzigd op 09/10/2015 14:49:08 door Thomas van den Heuvel
Of de queries aanpassen zodat je meerdere data kan inserten met een enkele INSERT.
1. Haal ids uit DB, stop ze in een array als urls.
2. pak een chunk van deze urls
3. opzetten en uitvoeren multi cURL
4. check op errors
5. informatie manipuleren
6. informatie invoeren in mijn DB
PS: opmerkingen over mijn stappen of andere opmerkingen over mijn code zijn altijd welkom.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<?php
include ('connect.php');
set_time_limit(0);
$api_key = '***';
$ids = mysqli_query($connect, "SELECT Id FROM `table` LIMIT 2000");
$urls = array();
while($result = mysqli_fetch_array($ids))
{
$urls[] = 'https://website.com/api/json/' . $ids['id'] . '?api_key=' . $api_key;
}
//number of urls called simultaneously
$threads = 10;
//create the multiple cURL handle
$mh = curl_multi_init();
while($urls){
//take the first chunk ($threads) of all urls
$chunk = array_splice($urls, 0, $threads);
//create a cURL resource for all urls in this chunk
foreach($chunk AS $k => $url){
//create cURL resources
$ch[$k] = curl_init();
//set URL and other appropriate options
curl_setopt($ch[$k], CURLOPT_URL, $url);
curl_setopt($ch[$k], CURLOPT_RETURNTRANSFER, true );
curl_setopt($ch[$k], CURLOPT_SSL_VERIFYPEER, false);
//add the handles
curl_multi_add_handle($mh,$ch[$k]);
}
//execute the handles
do {
curl_multi_exec($mh, $running);
} while ($running);
//Return the content for all urls
foreach($chunk AS $k => $url){
$result = curl_multi_getcontent($ch[$k]);
//get info about the request
$error = curl_getinfo($ch[$k], CURLINFO_HTTP_CODE);
//error handling if not 200 ok code
if($error != 200){
//when its one of the following errors try again later.
if($error == 429 || $error == 500 || $error == 503 || $error == 504){
$again[] = $url;
} else {
$errors[] = array("Url" => $url, "errornbr" => $error);
}
//no errors
} else {
//parse the json
$decoded = json_decode($result,true);
//////////////////////////////
// Process/manipulate all data
//////////////////////////////
$sql = "INSERT INTO `Information` (`id`, `one`, `two`, `three` ...)
VALUES " . implode(",",$sqlInformation) . "";
if (mysqli_query($connect, $sql)) {
echo "New information is created successfully";
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($connect);
}
curl_multi_remove_handle($mh, $ch[$k]);
curl_close($ch[$k]);
}
curl_multi_close($mh);
?>
include ('connect.php');
set_time_limit(0);
$api_key = '***';
$ids = mysqli_query($connect, "SELECT Id FROM `table` LIMIT 2000");
$urls = array();
while($result = mysqli_fetch_array($ids))
{
$urls[] = 'https://website.com/api/json/' . $ids['id'] . '?api_key=' . $api_key;
}
//number of urls called simultaneously
$threads = 10;
//create the multiple cURL handle
$mh = curl_multi_init();
while($urls){
//take the first chunk ($threads) of all urls
$chunk = array_splice($urls, 0, $threads);
//create a cURL resource for all urls in this chunk
foreach($chunk AS $k => $url){
//create cURL resources
$ch[$k] = curl_init();
//set URL and other appropriate options
curl_setopt($ch[$k], CURLOPT_URL, $url);
curl_setopt($ch[$k], CURLOPT_RETURNTRANSFER, true );
curl_setopt($ch[$k], CURLOPT_SSL_VERIFYPEER, false);
//add the handles
curl_multi_add_handle($mh,$ch[$k]);
}
//execute the handles
do {
curl_multi_exec($mh, $running);
} while ($running);
//Return the content for all urls
foreach($chunk AS $k => $url){
$result = curl_multi_getcontent($ch[$k]);
//get info about the request
$error = curl_getinfo($ch[$k], CURLINFO_HTTP_CODE);
//error handling if not 200 ok code
if($error != 200){
//when its one of the following errors try again later.
if($error == 429 || $error == 500 || $error == 503 || $error == 504){
$again[] = $url;
} else {
$errors[] = array("Url" => $url, "errornbr" => $error);
}
//no errors
} else {
//parse the json
$decoded = json_decode($result,true);
//////////////////////////////
// Process/manipulate all data
//////////////////////////////
$sql = "INSERT INTO `Information` (`id`, `one`, `two`, `three` ...)
VALUES " . implode(",",$sqlInformation) . "";
if (mysqli_query($connect, $sql)) {
echo "New information is created successfully";
} else {
echo "Error: " . $sql . "<br>" . mysqli_error($connect);
}
curl_multi_remove_handle($mh, $ch[$k]);
curl_close($ch[$k]);
}
curl_multi_close($mh);
?>
Gewijzigd op 09/10/2015 20:01:02 door peter paul
Server timed out and closed the connection. To fix, check that “wait_timeout” mysql variable in your my.cnf configuration file is large enough.
Server dropped an incorrect or too large packet. If mysqld gets a packet that is too large or incorrect, it assumes that something has gone wrong with the client and closes the connection. To fix, you can increase the maximal packet size limit “max_allowed_packet” in my.cnf file, eg. set max_allowed_packet = 128M, then sudo /etc/init.d/mysql restart.
Check ook de MySQL server logs
https://dev.mysql.com/doc/refman/5.0/en/server-logs.html
Gewijzigd op 09/10/2015 23:54:34 door Aad B
- als je enkel refereert aan associatieve keys, gebruik dan fetch_assoc ipv fetch_array
- $urls is een array, while ($urls) { ... } is een beetje raar, in het while-statement zou een predikaat moeten staan (een bools statement), wellicht is while (count($urls) > 0) { ... } beter
- $again[] wordt nergens gebruikt, wat doe je hiermee?
- als het belangrijk is dat alle data of in het geheel wel ingevoerd wordt, of, als er iets misgaat, in het geheel niet, gebruik dan een transactie
Vragen:
- het laatste stuk is nogal vaag, hier ontbreken sluitingsaccolades, waar sluit je de multi_handler precies af? Het script eindigt ook niet dus?
- welke "mysql server has gone away" - die van jou zelf dan, en niet de remote db?
- API's hebben vaak (ongeschreven) regels ten aanzien van "fair use" en soms zelfs expliciete regels over aantal requests per tijdsinterval, met bovenstaande code ben je waarschijnlijk nogal hard aan het hameren op deze API, heb je overwogen om ergens een sleep() commando in te bouwen, en vervolgens een set_time_limit() in PHP en/of dat je dit script in een crontab draait zodat je hier ook langer over mag doen en niet op het resultaat staat te wachten, of heb je de data "realtime" nodig?
- daarnaast, hoe lang duurt het uitvoeren van dit script? als dit te lang duurt treedt er (zoals hierboven wordt genoemd) wellicht een timeout op in de connectie naar je database; er wordt nergens iets gezegd over duur van executie
- log meer in het script zelf (dump tekst naar het scherm, maak deze meer "verbose"), en houd bij hoe lang de uitvoer duurt, als je script steevast stopt na een bepaalde tijd en niet zozeer na X requests dan treedt er waarschijnlijk (aan jouw zijde) een timeout op
Thomas van den Heuvel op 10/10/2015 14:40:23:
- $urls is een array, while ($urls) { ... } is een beetje raar, in het while-statement zou een predikaat moeten staan (een bools statement), wellicht is while (count($urls) > 0) { ... } beter
while() verwacht geen predicaat, maar een expressie. Het is hooguit een persoonlijke voorkeur van jou om daar een predicaat te gebruiken.
In dit geval is $urls een geldige expressie. Je moet wel uitkijken dat je niet in een oneindige loop komt, maar dat moet je met count($urls) net zogoed.
Quote:
while() verwacht geen predicaat, maar een expressie. Het is hooguit een persoonlijke voorkeur van jou om daar een predicaat te gebruiken.
En daarin schuilt een gevaar, omdat de expressie uiteindelijk naar true of false moet evalueren. Je laat daar ruimte open voor interpretatie die je kunt voorkomen. Dat kan het verschil betekenen tussen een werkend programma of een programma met een oneindige loop / die voortijdig eindigt.
Simpelweg omdat PHP losjes omspringt met typen wil dat niet zeggen dat het verstandig is dat jij dat ook doet.
In het algemeen lijkt het mij handiger dat je in dat soort statements (if, while etc.) enkel booleans / boolse expressies gebruikt.
Daarnaast is het een goede gewoonte om zo expliciet mogelijk te zijn in je code. Wat betekent while ($urls)? Dat is nogal vaag / open voor interpretatie. while (count($urls) > 0) omschrijft veel nauwkeuriger wat er moet gelden (en is ook meteen zelf-documenterend).
Als dit eindigt impliceert dit tevens dat count($urls) <= 0 (en $urls is naar alle waarschijnlijkheid een array).
Na afloop van while ($urls) kun je enkel stellen dat $urls "evalueert tot iets equivalent aan false" (het type op dat moment is ook niet echt duidelijk). Dat is voor de bepaling van de toestand van je applicatie ook niet bepaald handig, vooral niet als je een heleboel van dit soort constructies hebt en moet gaan debuggen omdat er ergens iets fout is. Het pverzicht is dan vrij snel weg.
Een predicaat is trouwens ook een expressie.
Ik kan nou trouwens wel heel veel moeite doen om het aannemelijk te maken dat dit wellicht een beter plan is, maar je kunt het advies ook gewoon niet overnemen he. Het was maar een suggestie die iemand naar mijn mening in de richting van een betere programmeerhouding stuurt. Het mag dan mijn persoonlijke voorkeur zijn, maar dat wil niet zeggen dat dat per definitie slecht is. Ik heb nog geen argument tegen gezien en ik noem net een aantal argumenten voor het gebruik.
Your move.
Gewijzigd op 10/10/2015 22:01:08 door Thomas van den Heuvel
Quote:
En daarin schuilt een gevaar, omdat de expressie uiteindelijk naar true of false moet evalueren. Je laat daar ruimte open voor interpretatie die je kunt voorkomen.
PHP heeft stricte regels wat betreft type juggling, dus die ruimte voor interpretatie vind ik zelf wel meevallen. En meestal is het wel logisch wanneer iets naar true of false evalueert, alhoewel je bij strings de catch-22-situatie hebt dat een niet-lege string toch naar false kan evalueren.
Quote:
Daarnaast is het een goede gewoonte om zo expliciet mogelijk te zijn in je code. Wat betekent while ($urls)? Dat is nogal vaag / open voor interpretatie. while (count($urls) > 0) omschrijft veel nauwkeuriger wat er moet gelden (en is ook meteen zelf-documenterend).
Ik zou het zelf-documenterender vinden als de betreffende variabele niet $urls zou heten, maar $url_list, of wat dan ook. En dan vind ik while ($url_list) meteen een stuk duidelijker te lezen.
Quote:
Als dit eindigt impliceert dit tevens dat count($urls) <= 0 (en $urls is naar alle waarschijnlijkheid een array).
Het is dat 'naar alle waarschijnlijkheid' dat mij zorgen baart. Want $urls kan ook rustig een string zijn. Of worden. En de expressie count($urls) kan dan een ander resultaat geven dan de expressie $urls.
Hier merk je dat PHP eigenlijk een beetje een gemankeerd taaltje is. Normaal gesproken zou je testen of $urls[0] bestaat, maar aangezien PHP geen arrays kent (wat PHP een array noemt, is functioneel een hash en technisch gezien een linked list) is het niet gegarandeerd dat element [0] bestaat.
Ik vind het uberhaupt link (maar in PHP vooral) om een while-conditie afhankelijk te laten zijn van een array. De loop stopt alleen wanneer je de array volledig 'opgegeten' hebt, maar omdat PHP geen aparte sigil gebruikt voor arrays is het in principe mogelijk om een array te wijzigen in een ander datatype, waardoor er geen betrouwbare manier is om de loop-conditie te testen. (En ja, een goede programmeerstijl zou dat risico acceptabel klein kunnen maken.)
Veel beter zou ik het vinden om een vlagvariabele als $urls_available te gebruiken die in de loop op false wordt gezet zodra alle urls zijn verwerkt.
Quote:
Na afloop van while ($urls) kun je enkel stellen dat $urls "evalueert tot iets equivalent aan false" (het type op dat moment is ook niet echt duidelijk). Dat is voor de bepaling van de toestand van je applicatie ook niet bepaald handig, vooral niet als je een heleboel van dit soort constructies hebt en moet gaan debuggen omdat er ergens iets fout is. Het pverzicht is dan vrij snel weg.
Iets dat evalueert tot iets dat equivalent is aan false is goed genoeg voor mij. Het kan soms wel duidelijker zijn om iets heel expliciet op te schrijven, maar als je een heleboel van die ietsjes bij elkaar in de buurt hebt staan, loop je ook het risico dat je code júist door al die expliciet uitgeschreven tests onleesbaar wordt. In het kader van "don't state the obvious" vind ik het bij expressies die intuïtief al true of false zijn dan ook meestal geen toegevoegde waarde hebben om die test expliciet uit te schrijven. Een slimme keuze van variabele/functienamen doet de rest.
Quote:
Een predicaat is trouwens ook een expressie.
Je hebt mij niet horen zeggen dat dat niet zo is. ;-) Het omgekeerde is echter niet per definitie waar, en dat was het punt dat ik wilde maken. While() verwacht een expressie. Dat kán een predicaat zijn, maar ook iets heel anders.
Quote:
Ik kan nou trouwens wel heel veel moeite doen om het aannemelijk te maken dat dit wellicht een beter plan is, maar je kunt het advies ook gewoon niet overnemen he. Het was maar een suggestie die iemand naar mijn mening in de richting van een betere programmeerhouding stuurt. Het mag dan mijn persoonlijke voorkeur zijn, maar dat wil niet zeggen dat dat per definitie slecht is. Ik heb nog geen argument tegen gezien en ik noem net een aantal argumenten voor het gebruik.
Ik zeg niet dat een persoonlijke mening per se slecht is (ik zie vaak meningen langskomen die ik zelfs zeer goed vind), maar wat ik voornamelijk -in het algemeen- een probleem vind, is dat een persoonlijke mening vaak gebracht wordt alsof het de absolute waarheid is. Regelmatig lees ik in een topic dat PHP iets niet zou kunnen, terwijl de schrijver eigenlijk bedoelt dat hij het zelf liever niet op die manier zou doen. Volgens mij help je daar mensen ook niet echt mee om de taal te leren.