Skip to content

Commit 0a29fe4

Browse files
committed
fix: doctrine transaction manager reconnection on server one away
Change-Id: I11e4b11563c6725e5b7671a10ec26484ea739bf1
1 parent 403df19 commit 0a29fe4

File tree

1 file changed

+55
-23
lines changed

1 file changed

+55
-23
lines changed

app/Services/Utils/DoctrineTransactionService.php

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@
1111
* See the License for the specific language governing permissions and
1212
* limitations under the License.
1313
**/
14+
15+
use Doctrine\DBAL\Exception\ConnectionLost;
16+
use Doctrine\DBAL\TransactionIsolationLevel;
1417
use Illuminate\Support\Facades\Log;
1518
use Closure;
1619
use LaravelDoctrine\ORM\Facades\Registry;
1720
use Doctrine\DBAL\Exception\RetryableException;
1821
use Exception;
1922
use Utils\Db\ITransactionService;
23+
2024
/**
2125
* Class DoctrineTransactionService
2226
* @package App\Services\Utils
@@ -40,14 +44,33 @@ public function __construct($manager_name)
4044
}
4145

4246
/**
43-
* Execute a Closure within a transaction.
44-
*
45-
* @param Closure $callback
46-
* @return mixed
47-
*
48-
* @throws \Exception
47+
* @param Exception $e
48+
* @return bool
49+
*/
50+
public function shouldReconnect(\Exception $e):bool
51+
{
52+
if($e instanceof RetryableException) return true;
53+
if($e instanceof ConnectionLost) return true;
54+
if($e instanceof \PDOException){
55+
switch(intval($e->getCode())){
56+
case 2006:
57+
Log::warning("DoctrineTransactionService::shouldReconnect: MySQL server has gone away!");
58+
return true;
59+
case 2002:
60+
Log::warning("DoctrineTransactionService::shouldReconnect: php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known!");
61+
return true;
62+
}
63+
}
64+
return false;
65+
}
66+
67+
/**
68+
* @param Closure $callback
69+
* @param int $isolationLevel
70+
* @return mixed|null
71+
* @throws Exception
4972
*/
50-
public function transaction(Closure $callback)
73+
public function transaction(Closure $callback, int $isolationLevel = TransactionIsolationLevel::READ_COMMITTED)
5174
{
5275
$retry = 0;
5376
$done = false;
@@ -60,32 +83,41 @@ public function transaction(Closure $callback)
6083
Log::warning("DoctrineTransactionService::transaction: entity manager is closed!, trying to re open...");
6184
$em = Registry::resetManager($this->manager_name);
6285
}
63-
86+
$em->getConnection()->setTransactionIsolation($isolationLevel);
6487
$em->getConnection()->beginTransaction(); // suspend auto-commit
6588
$result = $callback($this);
6689
$em->flush();
6790
$em->getConnection()->commit();
6891
$done = true;
6992
}
70-
catch (RetryableException $ex) {
71-
Log::warning($ex);
72-
Log::warning("DoctrineTransactionService::transaction Retrying ...");
93+
catch (Exception $ex) {
94+
95+
$retry++;
96+
$em->getConnection()->close();
7397
$em->close();
74-
$em->getConnection()->rollBack();
98+
if($em->getConnection()->isTransactionActive())
99+
$em->getConnection()->rollBack();
75100
Registry::resetManager($this->manager_name);
76-
Log::warning($ex);
77-
$retry++;
78-
if ($retry === self::MaxRetries) {
79-
Log::info(sprintf("DoctrineTransactionService::transaction Max Retry Reached %s", $retry));
80-
throw $ex;
101+
102+
if($this->shouldReconnect($ex)){
103+
Log::warning
104+
(
105+
sprintf
106+
(
107+
"DoctrineTransactionService::transaction should reconnect %s retry %s",
108+
$ex->getMessage(),
109+
$retry
110+
)
111+
);
112+
if ($retry === self::MaxRetries) {
113+
Log::warning(sprintf("DoctrineTransactionService::transaction Max Retry Reached %s", $retry));
114+
Log::error($ex);
115+
throw $ex;
116+
}
117+
continue;
81118
}
82-
}
83-
catch (Exception $ex) {
84-
Log::warning($ex);
85-
$em->close();
86119
Log::warning("DoctrineTransactionService::transaction rolling back TX");
87-
$em->getConnection()->rollBack();
88-
Registry::resetManager($this->manager_name);
120+
Log::error($ex);
89121
throw $ex;
90122
}
91123
}

0 commit comments

Comments
 (0)