Skip to content

Improvement of connection retry mechanism #27128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: 2.4-develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 86 additions & 95 deletions lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface, Rese

/**
* MEMORY engine type for MySQL tables
* @deprecated Use InnoDB/ExtraDB engine instead
*/
public const ENGINE_MEMORY = 'MEMORY';

Expand All @@ -87,7 +88,7 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface, Rese
*
* @var int
*/
protected $_transactionLevel = 0;
protected $_transactionLevel = 0;

/**
* Whether transaction was rolled back or not
Expand All @@ -101,28 +102,28 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface, Rese
*
* @var bool
*/
protected $_connectionFlagsSet = false;
protected $_connectionFlagsSet = false;

/**
* Tables DDL cache
*
* @var array
*/
protected $_ddlCache = [];
protected $_ddlCache = [];

/**
* SQL bind params. Used temporarily by regexp callback.
*
* @var array
*/
protected $_bindParams = [];
protected $_bindParams = [];

/**
* Autoincrement for bind value. Used by regexp callback.
*
* @var int
*/
protected $_bindIncrement = 0;
protected $_bindIncrement = 0;

/**
* Cache frontend adapter instance
Expand Down Expand Up @@ -528,6 +529,9 @@ private function createConnection()
/**
* Run RAW Query
*
* @deprecated
* Use \Magento\Framework\DB\Adapter\AdapterInterface::query instead
*
* @param string $sql
* @return \Zend_Db_Statement_Interface
* @throws \PDOException
Expand All @@ -551,6 +555,9 @@ public function rawQuery($sql)
/**
* Run RAW query and Fetch First row
*
* @deprecated
* Use \Magento\Framework\DB\Adapter\AdapterInterface::fetchRow instead
*
* @param string $sql
* @param string|int $field
* @return mixed|null
Expand Down Expand Up @@ -656,10 +663,13 @@ private function performQuery(callable $queryExecutor)
// Check to reconnect
if ($pdoException && $triesCount < self::MAX_CONNECTION_RETRIES
&& in_array($pdoException->errorInfo[1], $connectionErrors)
&& $this->getTransactionLevel() == 0 // Connect retry doesn't make sense in transaction
) {
$retry = true;
$triesCount++;
$this->closeConnection();
usleep($triesCount * 100000);

$this->_connect();
}

Expand Down Expand Up @@ -811,6 +821,7 @@ protected function _unQuote($string)
* Normalizes mixed positional-named bind to positional bind, and replaces named placeholders in query to
* '?' placeholders.
*
* @deprecated It's not used now
* @param string $sql
* @param array $bind
* @return $this
Expand Down Expand Up @@ -1538,42 +1549,6 @@ public function getIndexList($tableName, $schemaName = null)
return $ddl;
}

/**
* Remove duplicate entry for create key
*
* @param string $table
* @param array $fields
* @param string[] $ids
* @return $this
*/
protected function _removeDuplicateEntry($table, $fields, $ids)
{
$where = [];
$i = 0;
foreach ($fields as $field) {
$where[] = $this->quoteInto($field . '=?', $ids[$i++]);
}

if (!$where) {
return $this;
}
$whereCond = implode(' AND ', $where);
$sql = sprintf('SELECT COUNT(*) as `cnt` FROM `%s` WHERE %s', $table, $whereCond);

$cnt = $this->rawFetchRow($sql, 'cnt');
if ($cnt > 1) {
$sql = sprintf(
'DELETE FROM `%s` WHERE %s LIMIT %d',
$table,
$whereCond,
$cnt - 1
);
$this->rawQuery($sql);
}

return $this;
}

/**
* Creates and returns a new \Magento\Framework\DB\Select object for this adapter.
*
Expand Down Expand Up @@ -1969,47 +1944,51 @@ public function modifyColumnByDdl($tableName, $columnName, $definition, $flushDa
* Retrieve column data type by data from describe table
*
* @param array $column
* @return string|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @return string
* @throws \Zend_Db_Adapter_Exception
*/
protected function _getColumnTypeByDdl($column)
{
// phpstan:ignore
switch ($column['DATA_TYPE']) {
case 'bool':
return Table::TYPE_BOOLEAN;
case 'tinytext':
case 'char':
case 'varchar':
case 'text':
case 'mediumtext':
case 'longtext':
return Table::TYPE_TEXT;
case 'blob':
case 'mediumblob':
case 'longblob':
return Table::TYPE_BLOB;
case 'tinyint':
case 'smallint':
return Table::TYPE_SMALLINT;
case 'mediumint':
case 'int':
return Table::TYPE_INTEGER;
case 'bigint':
return Table::TYPE_BIGINT;
case 'datetime':
return Table::TYPE_DATETIME;
case 'timestamp':
return Table::TYPE_TIMESTAMP;
case 'date':
return Table::TYPE_DATE;
case 'float':
return Table::TYPE_FLOAT;
case 'decimal':
case 'numeric':
return Table::TYPE_DECIMAL;
static $mapping = [
'bool' => Table::TYPE_BOOLEAN,

'tinytext' => Table::TYPE_TEXT,
'char' => Table::TYPE_TEXT,
'varchar' => Table::TYPE_TEXT,
'text' => Table::TYPE_TEXT,
'mediumtext' => Table::TYPE_TEXT,
'longtext' => Table::TYPE_TEXT,

'blob' => Table::TYPE_BLOB,
'mediumblob' => Table::TYPE_BLOB,
'longblob' => Table::TYPE_BLOB,

'tinyint' => Table::TYPE_SMALLINT,
'smallint' => Table::TYPE_SMALLINT,

'mediumint' => Table::TYPE_INTEGER,
'int' => Table::TYPE_INTEGER,

'bigint' => Table::TYPE_BIGINT,

'datetime' => Table::TYPE_DATETIME,

'timestamp' => Table::TYPE_TIMESTAMP,

'date' => Table::TYPE_DATE,

'float' => Table::TYPE_FLOAT,

'decimal' => Table::TYPE_DECIMAL,
'numeric' => Table::TYPE_DECIMAL,
];

if (!isset($mapping[$column['DATA_TYPE']])) {
throw new \Zend_Db_Adapter_Exception('Unknown data type: ' . $column['DATA_TYPE']);
}
return null;

return $mapping[$column['DATA_TYPE']];
}

/**
Expand Down Expand Up @@ -2904,24 +2883,7 @@ public function addIndex(

$query .= sprintf(' ADD %s (%s)', $condition, $fieldSql);

$cycle = true;
while ($cycle === true) {
try {
$result = $this->rawQuery($query);
$cycle = false;
} catch (\Exception $e) {
if ($indexType !== null && in_array(strtolower($indexType), ['primary', 'unique'])) {
$match = [];
// phpstan:ignore
if (preg_match('#SQLSTATE\[23000\]: [^:]+: 1062[^\']+\'([\d.-]+)\'#', $e->getMessage(), $match)) {
$ids = explode('-', $match[1]);
$this->_removeDuplicateEntry($tableName, $fields, $ids);
continue;
}
}
throw $e;
}
}
$result = $this->rawQuery($query);

$this->resetDdlCache($tableName, $schemaName);

Expand Down Expand Up @@ -4239,6 +4201,35 @@ public function closeConnection()
parent::closeConnection();
}

/**
* @inheritDoc
*/
public function quote($value, $type = null)
{
$connectionErrors = [
2006, // SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
2013, // SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server during query
];
$triesCount = 0;
while (true) {
try {
return parent::quote($value, $type);
} catch (\PDOException $e) {
// Check to reconnect
if (in_array($e->errorInfo[1], $connectionErrors)
&& $this->getTransactionLevel() == 0
&& $triesCount < self::MAX_CONNECTION_RETRIES
) {
$triesCount++;
$this->closeConnection();
usleep($triesCount * 100000);
} else {
throw $e;
}
}
}
}

/**
* Disable show internals with var_dump
*
Expand Down
Loading