Skip to content

Commit 07d7db9

Browse files
committed
Merge remote-tracking branch 'l3/ACP2E-2345' into Tier4-PR-Delivery-11-18-23
2 parents aca3ba4 + 5990498 commit 07d7db9

File tree

2 files changed

+107
-13
lines changed

2 files changed

+107
-13
lines changed

lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,13 @@ public function beginTransaction()
328328
}
329329
if ($this->_transactionLevel === 0) {
330330
$this->logger->startTimer();
331-
parent::beginTransaction();
332-
$this->logger->logStats(LoggerInterface::TYPE_TRANSACTION, 'BEGIN');
331+
try {
332+
$this->performQuery(function () {
333+
parent::beginTransaction();
334+
});
335+
} finally {
336+
$this->logger->logStats(LoggerInterface::TYPE_TRANSACTION, 'BEGIN');
337+
}
333338
}
334339
++$this->_transactionLevel;
335340
return $this;
@@ -598,9 +603,30 @@ protected function _checkDdlTransaction($sql)
598603
* @return \Zend_Db_Statement_Pdo|void
599604
* @throws Zend_Db_Adapter_Exception To re-throw \PDOException.
600605
* @throws Zend_Db_Statement_Exception
601-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
602606
*/
603607
protected function _query($sql, $bind = [])
608+
{
609+
$result = null;
610+
try {
611+
$this->_checkDdlTransaction($sql);
612+
$this->_prepareQuery($sql, $bind);
613+
$this->logger->startTimer();
614+
$result = $this->performQuery(fn () => parent::query($sql, $bind));
615+
} finally {
616+
$this->logger->logStats(LoggerInterface::TYPE_QUERY, $sql, $bind, $result);
617+
}
618+
619+
return $result;
620+
}
621+
622+
/**
623+
* Execute query and reconnect if needed.
624+
*
625+
* @param callable $queryExecutor
626+
* @return \Zend_Db_Statement_Pdo|void
627+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
628+
*/
629+
private function performQuery(callable $queryExecutor)
604630
{
605631
$connectionErrors = [
606632
2006, // SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
@@ -609,22 +635,15 @@ protected function _query($sql, $bind = [])
609635
$triesCount = 0;
610636
do {
611637
$retry = false;
612-
$this->logger->startTimer();
613638
try {
614-
$this->_checkDdlTransaction($sql);
615-
$this->_prepareQuery($sql, $bind);
616-
$result = parent::query($sql, $bind);
617-
$this->logger->logStats(LoggerInterface::TYPE_QUERY, $sql, $bind, $result);
618-
return $result;
639+
return $queryExecutor();
619640
} catch (\Exception $e) {
620641
// Finalize broken query
621642
$profiler = $this->getProfiler();
622643
if ($profiler instanceof Profiler) {
623-
/** @var Profiler $profiler */
624644
$profiler->queryEndLast();
625645
}
626646

627-
/** @var $pdoException \PDOException */
628647
$pdoException = null;
629648
if ($e instanceof \PDOException) {
630649
$pdoException = $e;
@@ -641,12 +660,10 @@ protected function _query($sql, $bind = [])
641660
$retry = true;
642661
$triesCount++;
643662
$this->closeConnection();
644-
645663
$this->_connect();
646664
}
647665

648666
if (!$retry) {
649-
$this->logger->logStats(LoggerInterface::TYPE_QUERY, $sql, $bind);
650667
$this->logger->critical($e);
651668
// rethrow custom exception if needed
652669
if ($pdoException && isset($this->exceptionMap[$pdoException->errorInfo[1]])) {

lib/internal/Magento/Framework/DB/Test/Unit/Adapter/Pdo/MysqlTest.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,4 +866,81 @@ private function invokeModelMethod(MockObject $adapter, string $method, array $p
866866

867867
return $method->invokeArgs($adapter, $parameters);
868868
}
869+
870+
/**
871+
* @dataProvider retryExceptionDataProvider
872+
* @param \Exception $exception
873+
* @return void
874+
*/
875+
public function testBeginTransactionWithReconnect(\Exception $exception): void
876+
{
877+
$adapter = $this->getMysqlPdoAdapterMock(['_connect', '_beginTransaction', '_rollBack']);
878+
$adapter->expects(self::exactly(4))
879+
->method('_connect');
880+
$adapter->expects(self::once())
881+
->method('_rollBack');
882+
883+
$matcher = self::exactly(2);
884+
$adapter->expects($matcher)
885+
->method('_beginTransaction')
886+
->willReturnCallback(
887+
function () use ($matcher, $exception) {
888+
if ($matcher->getInvocationCount() === 1) {
889+
throw $exception;
890+
}
891+
}
892+
);
893+
$adapter->beginTransaction();
894+
$adapter->rollBack();
895+
}
896+
897+
/**
898+
* @return array[]
899+
*/
900+
public function retryExceptionDataProvider(): array
901+
{
902+
$serverHasGoneAwayException = new \PDOException();
903+
$serverHasGoneAwayException->errorInfo = [1 => 2006];
904+
$lostConnectionException = new \PDOException();
905+
$lostConnectionException->errorInfo = [1 => 2013];
906+
907+
return [
908+
[$serverHasGoneAwayException],
909+
[$lostConnectionException],
910+
[new \Zend_Db_Statement_Exception('', 0, $serverHasGoneAwayException)],
911+
[new \Zend_Db_Statement_Exception('', 0, $lostConnectionException)],
912+
];
913+
}
914+
915+
/**
916+
* @dataProvider exceptionDataProvider
917+
* @param \Exception $exception
918+
* @return void
919+
*/
920+
public function testBeginTransactionWithoutReconnect(\Exception $exception): void
921+
{
922+
$this->expectException(\Exception::class);
923+
$adapter = $this->getMysqlPdoAdapterMock(['_connect', '_beginTransaction', '_rollBack']);
924+
$adapter->expects(self::once())
925+
->method('_connect');
926+
$adapter->expects(self::once())
927+
->method('_beginTransaction')
928+
->willThrowException($exception);
929+
$adapter->beginTransaction();
930+
}
931+
932+
/**
933+
* @return array[]
934+
*/
935+
public function exceptionDataProvider(): array
936+
{
937+
$pdoException = new \PDOException();
938+
$pdoException->errorInfo = [1 => 1213];
939+
940+
return [
941+
[$pdoException],
942+
[new \Zend_Db_Statement_Exception('', 0, $pdoException)],
943+
[new \Exception()],
944+
];
945+
}
869946
}

0 commit comments

Comments
 (0)