Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5c5ac42

Browse files
bug #44828 [Lock] Release DoctrineDbalPostgreSqlStore connection lock on failure (simon-watiau)
This PR was squashed before being merged into the 5.4 branch. Discussion ---------- [Lock] Release DoctrineDbalPostgreSqlStore connection lock on failure | Q | A | ------------- | --- | Branch? | 5.4<!-- see below --> | Bug fix? | yes | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tickets | <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT | Doc PR | <!-- required for new features --> When using PostgreSQL advisory locks using the `DoctrineDbalPostgreSqlStore` A first lock is acquired in memory for same connection concurrencies, this `InMemoryStore` does not rely on TTL. When the advisory lock fails to be acquired, this first lock is not released. For long running processes, this cause the lock to not be acquirable again because the `InMemoryStore` will never release its lock. related to : #44723 (comment) Commits ------- fab5991 [Lock] Release DoctrineDbalPostgreSqlStore connection lock on failure
2 parents 62d2903 + fab5991 commit 5c5ac42

File tree

2 files changed

+67
-20
lines changed

2 files changed

+67
-20
lines changed

src/Symfony/Component/Lock/Store/DoctrineDbalPostgreSqlStore.php

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,28 @@ public function save(Key $key)
6262
// prevent concurrency within the same connection
6363
$this->getInternalStore()->save($key);
6464

65-
$sql = 'SELECT pg_try_advisory_lock(:key)';
66-
$result = $this->conn->executeQuery($sql, [
67-
'key' => $this->getHashedKey($key),
68-
]);
65+
$lockAcquired = false;
6966

70-
// Check if lock is acquired
71-
if (true === $result->fetchOne()) {
72-
$key->markUnserializable();
73-
// release sharedLock in case of promotion
74-
$this->unlockShared($key);
67+
try {
68+
$sql = 'SELECT pg_try_advisory_lock(:key)';
69+
$result = $this->conn->executeQuery($sql, [
70+
'key' => $this->getHashedKey($key),
71+
]);
7572

76-
return;
73+
// Check if lock is acquired
74+
if (true === $result->fetchOne()) {
75+
$key->markUnserializable();
76+
// release sharedLock in case of promotion
77+
$this->unlockShared($key);
78+
79+
$lockAcquired = true;
80+
81+
return;
82+
}
83+
} finally {
84+
if (!$lockAcquired) {
85+
$this->getInternalStore()->delete($key);
86+
}
7787
}
7888

7989
throw new LockConflictedException();
@@ -84,18 +94,28 @@ public function saveRead(Key $key)
8494
// prevent concurrency within the same connection
8595
$this->getInternalStore()->saveRead($key);
8696

87-
$sql = 'SELECT pg_try_advisory_lock_shared(:key)';
88-
$result = $this->conn->executeQuery($sql, [
89-
'key' => $this->getHashedKey($key),
90-
]);
97+
$lockAcquired = false;
98+
99+
try {
100+
$sql = 'SELECT pg_try_advisory_lock_shared(:key)';
101+
$result = $this->conn->executeQuery($sql, [
102+
'key' => $this->getHashedKey($key),
103+
]);
91104

92-
// Check if lock is acquired
93-
if (true === $result->fetchOne()) {
94-
$key->markUnserializable();
95-
// release lock in case of demotion
96-
$this->unlock($key);
105+
// Check if lock is acquired
106+
if (true === $result->fetchOne()) {
107+
$key->markUnserializable();
108+
// release lock in case of demotion
109+
$this->unlock($key);
97110

98-
return;
111+
$lockAcquired = true;
112+
113+
return;
114+
}
115+
} finally {
116+
if (!$lockAcquired) {
117+
$this->getInternalStore()->delete($key);
118+
}
99119
}
100120

101121
throw new LockConflictedException();

src/Symfony/Component/Lock/Tests/Store/DoctrineDbalPostgreSqlStoreTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\DBAL\DriverManager;
1515
use Symfony\Component\Lock\Exception\InvalidArgumentException;
16+
use Symfony\Component\Lock\Exception\LockConflictedException;
1617
use Symfony\Component\Lock\Key;
1718
use Symfony\Component\Lock\PersistingStoreInterface;
1819
use Symfony\Component\Lock\Store\DoctrineDbalPostgreSqlStore;
@@ -59,4 +60,30 @@ public function getInvalidDrivers()
5960
yield ['sqlite:///tmp/foo.db'];
6061
yield [DriverManager::getConnection(['url' => 'sqlite:///tmp/foo.db'])];
6162
}
63+
64+
public function testSaveAfterConflict()
65+
{
66+
$store1 = $this->getStore();
67+
$store2 = $this->getStore();
68+
69+
$key = new Key(uniqid(__METHOD__, true));
70+
71+
$store1->save($key);
72+
$this->assertTrue($store1->exists($key));
73+
74+
$lockConflicted = false;
75+
try {
76+
$store2->save($key);
77+
} catch (LockConflictedException $lockConflictedException) {
78+
$lockConflicted = true;
79+
}
80+
81+
$this->assertTrue($lockConflicted);
82+
$this->assertFalse($store2->exists($key));
83+
84+
$store1->delete($key);
85+
86+
$store2->save($key);
87+
$this->assertTrue($store2->exists($key));
88+
}
6289
}

0 commit comments

Comments
 (0)