Skip to content

WireDatabasePDO's "execute" function does not re-create connection before retry #1302

@romaincazier

Description

@romaincazier

Short description of the issue

When a request takes too long, the MySQL server goes away thus making the next query fail. The execute function's comment states that it "will retry queries if they failed due to a lost connection", however it seems it just retries the query, which is still linked to the connection that has gone away.

Expected behavior

Even though we could play with the MySQL wait_timeout setting, it is not necessarily possible in some cases (e.g. shared hosting), making the retry aspect of Processwire's PDO highly valuable. But it doesn't work here.

From my understanding, once it detects a fail / retries, it should first re-create the connection and then re-construct the query based on this new connection.

Suggestion for a possible fix

What I tried to do, and seemed to work at first, was adding this code before the line 583:

if ($tryAgain > 0) {
  $newQuery = $this->wire('database')->prepare($query->queryString);
  foreach ($query->getParams() as $value) {
    $newQuery->bindValue($value["parameter"], $value["value"], $value["data_type"]);
  }
  $query = $newQuery;
}

Two things to note: I had to make every PDOStatement a WireDatabasePDOStatement, and in the WireDatabasePDOStatement file I created a params var populated with the values binded in the bindValue function:

$this->params[] = [
  "parameter" => $parameter,
  "value" => $value,
  "data_type" => $data_type,
];

And accessible with the getParams function mentionned above.

However, even if it worked for my code below (in the "Step to reproduce the issue"), I still had the 2006 MySQL error afterwards at this line.

Side note: this second error led me to the DatabaseQuery file which seems to provide the abstraction for what I'm describing above...? So maybe it could be there that we should retry the query ? I don't know at this point if every query in PW is a DatabaseQuery or not.

Step to reproduce the issue

Here is a code sample that triggers this error:

$page->of(false);
$title = $page->title;
$page->title = "Error";
$page->save();
sleep(60); // adjust according to your server's wait_timeout setting
$page->title = $title;
$page->save();

Setup/Environment

Server Details

Software Version
ProcessWire 3.0.165
PHP 7.3.25
Webserver Apache
MySQL Server 5.7.30-log
MySQL Client mysqlnd 5.0.12-dev - 20150407 - $Id: 7cc7cc96e675f6d72e5cf0f267f48e167c2abb23 $
Server Settings
Parameter Value
allow_url_fopen
max_execution_time 60 (changeable)
max_input_nesting_level 64
max_input_time 0
max_input_vars 3000
memory_limit 640M
post_max_size 300M
upload_max_filesize 300M
xdebug
xdebug.max_nesting_level
mod_rewrite
mod_security
EXIF Support 1
FreeType 1
GD Settings
Parameter Value
Version bundled (2.1.0 compatible)
GIF 1
JPG 1
PNG 1
WebP 1
iMagick Settings
Parameter Value
Version 6.8.9
GIF 1
JPG 1
PNG 1
SVG 1
PDF 1
WebP
Module Details
Module ClassName Version
AdminThemeUikitModified 0.3.1
ImageCreateVariations 1.0.0
ProcessTracyAdminer 1.0.7
TracyDebugger 4.21.23

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions