Skip to content

Commit 066e2bd

Browse files
authored
Merge pull request #705 from utopia-php/feat-enable-reconnect
Enable reconnection + retry
2 parents dbecdf8 + 9a046f2 commit 066e2bd

2 files changed

Lines changed: 60 additions & 39 deletions

File tree

src/Database/PDO.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Utopia\Database;
44

55
use InvalidArgumentException;
6+
use Utopia\CLI\Console;
67

78
/**
89
* A PDO wrapper that forwards method calls to the internal PDO instance.
@@ -41,7 +42,27 @@ public function __construct(
4142
*/
4243
public function __call(string $method, array $args): mixed
4344
{
44-
return $this->pdo->{$method}(...$args);
45+
try {
46+
return $this->pdo->{$method}(...$args);
47+
} catch (\Throwable $e) {
48+
if (Connection::hasError($e)) {
49+
Console::warning('[Database] ' . $e->getMessage());
50+
Console::warning('[Database] Lost connection detected. Reconnecting...');
51+
52+
$inTransaction = $this->pdo->inTransaction();
53+
54+
// Attempt to reconnect
55+
$this->reconnect();
56+
57+
// If we weren't in a transaction, also retry the query
58+
// In a transaction we can't retry as the state is attached to the previous connection
59+
if (!$inTransaction) {
60+
return $this->pdo->{$method}(...$args);
61+
}
62+
}
63+
64+
throw $e;
65+
}
4566
}
4667

4768
/**

tests/unit/PDOTest.php

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -41,44 +41,44 @@ public function testMethodCallIsForwardedToPDO(): void
4141
$this->assertSame($pdoStatementMock, $result);
4242
}
4343

44-
// public function testLostConnectionRetriesCall(): void
45-
// {
46-
// $dsn = 'sqlite::memory:';
47-
// $pdoWrapper = $this->getMockBuilder(PDO::class)
48-
// ->setConstructorArgs([$dsn, null, null, []])
49-
// ->onlyMethods(['reconnect'])
50-
// ->getMock();
51-
//
52-
// $pdoMock = $this->getMockBuilder(\PDO::class)
53-
// ->disableOriginalConstructor()
54-
// ->getMock();
55-
// $pdoStatementMock = $this->getMockBuilder(\PDOStatement::class)
56-
// ->disableOriginalConstructor()
57-
// ->getMock();
58-
//
59-
// $pdoMock->expects($this->exactly(2))
60-
// ->method('query')
61-
// ->with('SELECT 1')
62-
// ->will($this->onConsecutiveCalls(
63-
// $this->throwException(new \Exception("Lost connection")),
64-
// $pdoStatementMock
65-
// ));
66-
//
67-
// $reflection = new ReflectionClass($pdoWrapper);
68-
// $pdoProperty = $reflection->getProperty('pdo');
69-
// $pdoProperty->setAccessible(true);
70-
// $pdoProperty->setValue($pdoWrapper, $pdoMock);
71-
//
72-
// $pdoWrapper->expects($this->once())
73-
// ->method('reconnect')
74-
// ->willReturnCallback(function () use ($pdoWrapper, $pdoMock, $pdoProperty) {
75-
// $pdoProperty->setValue($pdoWrapper, $pdoMock);
76-
// });
77-
//
78-
// $result = $pdoWrapper->query('SELECT 1');
79-
//
80-
// $this->assertSame($pdoStatementMock, $result);
81-
// }
44+
public function testLostConnectionRetriesCall(): void
45+
{
46+
$dsn = 'sqlite::memory:';
47+
$pdoWrapper = $this->getMockBuilder(PDO::class)
48+
->setConstructorArgs([$dsn, null, null, []])
49+
->onlyMethods(['reconnect'])
50+
->getMock();
51+
52+
$pdoMock = $this->getMockBuilder(\PDO::class)
53+
->disableOriginalConstructor()
54+
->getMock();
55+
$pdoStatementMock = $this->getMockBuilder(\PDOStatement::class)
56+
->disableOriginalConstructor()
57+
->getMock();
58+
59+
$pdoMock->expects($this->exactly(2))
60+
->method('query')
61+
->with('SELECT 1')
62+
->will($this->onConsecutiveCalls(
63+
$this->throwException(new \Exception("Lost connection")),
64+
$pdoStatementMock
65+
));
66+
67+
$reflection = new ReflectionClass($pdoWrapper);
68+
$pdoProperty = $reflection->getProperty('pdo');
69+
$pdoProperty->setAccessible(true);
70+
$pdoProperty->setValue($pdoWrapper, $pdoMock);
71+
72+
$pdoWrapper->expects($this->once())
73+
->method('reconnect')
74+
->willReturnCallback(function () use ($pdoWrapper, $pdoMock, $pdoProperty) {
75+
$pdoProperty->setValue($pdoWrapper, $pdoMock);
76+
});
77+
78+
$result = $pdoWrapper->query('SELECT 1');
79+
80+
$this->assertSame($pdoStatementMock, $result);
81+
}
8282

8383
public function testNonLostConnectionExceptionIsRethrown(): void
8484
{

0 commit comments

Comments
 (0)