Skip to content

Commit 0913c18

Browse files
committed
fix(transaction): tolerate postgres cleanup rollback
1 parent d9ed545 commit 0913c18

2 files changed

Lines changed: 33 additions & 5 deletions

File tree

src/Database/Adapter/Postgres.php

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,18 @@ public function startTransaction(): bool
3838
{
3939
try {
4040
if ($this->inTransaction === 0) {
41-
if ($this->getPDO()->inTransaction()) {
42-
$this->getPDO()->rollBack();
43-
} else {
44-
// If no active transaction, this has no effect.
45-
$this->getPDO()->prepare('ROLLBACK')->execute();
41+
try {
42+
if ($this->getPDO()->inTransaction()) {
43+
$this->getPDO()->rollBack();
44+
} else {
45+
// If no active transaction, this has no effect.
46+
$this->getPDO()->prepare('ROLLBACK')->execute();
47+
}
48+
} catch (PDOException) {
49+
// A pooled connection can report a transaction it no longer
50+
// holds after a reconnect (e.g. Swoole PDOProxy keeps its own
51+
// counter), making this cleanup rollback throw. It is best
52+
// effort; swallow it and begin a fresh transaction below.
4653
}
4754

4855
$result = $this->getPDO()->beginTransaction();

tests/unit/SQLTransactionTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PDOException;
66
use PHPUnit\Framework\TestCase;
77
use Utopia\Database\Adapter\MySQL;
8+
use Utopia\Database\Adapter\Postgres;
89
use Utopia\Database\Exception\Transaction as TransactionException;
910

1011
class SQLTransactionTest extends TestCase
@@ -57,4 +58,24 @@ public function testStartTransactionDoesNotMaskBeginFailureAfterDesyncedRollback
5758

5859
$adapter->startTransaction();
5960
}
61+
62+
public function testPostgresStartTransactionRecoversFromDesyncedRollback(): void
63+
{
64+
$pdo = $this->getMockBuilder(\PDO::class)
65+
->disableOriginalConstructor()
66+
->getMock();
67+
68+
$pdo->method('inTransaction')->willReturn(true);
69+
$pdo->method('rollBack')->willThrowException(
70+
new PDOException('There is no active transaction')
71+
);
72+
$pdo->expects($this->once())
73+
->method('beginTransaction')
74+
->willReturn(true);
75+
76+
$adapter = new Postgres($pdo);
77+
78+
$this->assertTrue($adapter->startTransaction());
79+
$this->assertTrue($adapter->inTransaction());
80+
}
6081
}

0 commit comments

Comments
 (0)