Skip to content

Commit c2ce745

Browse files
simon-watiaunicolas-grekas
authored andcommitted
[Lock] Release PostgreSqlStore connection lock on failure
1 parent c31c09f commit c2ce745

File tree

2 files changed

+68
-20
lines changed

2 files changed

+68
-20
lines changed

Store/PostgreSqlStore.php

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

83-
$sql = 'SELECT pg_try_advisory_lock(:key)';
84-
$stmt = $this->getConnection()->prepare($sql);
85-
$stmt->bindValue(':key', $this->getHashedKey($key));
86-
$result = $stmt->execute();
83+
$lockAcquired = false;
8784

88-
// Check if lock is acquired
89-
if (true === (\is_object($result) ? $result->fetchOne() : $stmt->fetchColumn())) {
90-
$key->markUnserializable();
91-
// release sharedLock in case of promotion
92-
$this->unlockShared($key);
85+
try {
86+
$sql = 'SELECT pg_try_advisory_lock(:key)';
87+
$stmt = $this->getConnection()->prepare($sql);
88+
$stmt->bindValue(':key', $this->getHashedKey($key));
89+
$result = $stmt->execute();
9390

94-
return;
91+
// Check if lock is acquired
92+
if (true === (\is_object($result) ? $result->fetchOne() : $stmt->fetchColumn())) {
93+
$key->markUnserializable();
94+
// release sharedLock in case of promotion
95+
$this->unlockShared($key);
96+
97+
$lockAcquired = true;
98+
99+
return;
100+
}
101+
} finally {
102+
if (!$lockAcquired) {
103+
$this->getInternalStore()->delete($key);
104+
}
95105
}
96106

97107
throw new LockConflictedException();
@@ -102,19 +112,29 @@ public function saveRead(Key $key)
102112
// prevent concurrency within the same connection
103113
$this->getInternalStore()->saveRead($key);
104114

105-
$sql = 'SELECT pg_try_advisory_lock_shared(:key)';
106-
$stmt = $this->getConnection()->prepare($sql);
115+
$lockAcquired = false;
107116

108-
$stmt->bindValue(':key', $this->getHashedKey($key));
109-
$result = $stmt->execute();
117+
try {
118+
$sql = 'SELECT pg_try_advisory_lock_shared(:key)';
119+
$stmt = $this->getConnection()->prepare($sql);
120+
121+
$stmt->bindValue(':key', $this->getHashedKey($key));
122+
$result = $stmt->execute();
110123

111-
// Check if lock is acquired
112-
if (true === (\is_object($result) ? $result->fetchOne() : $stmt->fetchColumn())) {
113-
$key->markUnserializable();
114-
// release lock in case of demotion
115-
$this->unlock($key);
124+
// Check if lock is acquired
125+
if (true === (\is_object($result) ? $result->fetchOne() : $stmt->fetchColumn())) {
126+
$key->markUnserializable();
127+
// release lock in case of demotion
128+
$this->unlock($key);
116129

117-
return;
130+
$lockAcquired = true;
131+
132+
return;
133+
}
134+
} finally {
135+
if (!$lockAcquired) {
136+
$this->getInternalStore()->delete($key);
137+
}
118138
}
119139

120140
throw new LockConflictedException();

Tests/Store/PostgreSqlStoreTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Lock\Tests\Store;
1313

1414
use Symfony\Component\Lock\Exception\InvalidArgumentException;
15+
use Symfony\Component\Lock\Exception\LockConflictedException;
1516
use Symfony\Component\Lock\Key;
1617
use Symfony\Component\Lock\PersistingStoreInterface;
1718
use Symfony\Component\Lock\Store\PostgreSqlStore;
@@ -50,4 +51,31 @@ public function testInvalidDriver()
5051
$this->expectExceptionMessage('The adapter "Symfony\Component\Lock\Store\PostgreSqlStore" does not support');
5152
$store->exists(new Key('foo'));
5253
}
54+
55+
public function testSaveAfterConflict()
56+
{
57+
$store1 = $this->getStore();
58+
$store2 = $this->getStore();
59+
60+
$key = new Key(uniqid(__METHOD__, true));
61+
62+
$store1->save($key);
63+
$this->assertTrue($store1->exists($key));
64+
65+
$lockConflicted = false;
66+
67+
try {
68+
$store2->save($key);
69+
} catch (LockConflictedException $lockConflictedException) {
70+
$lockConflicted = true;
71+
}
72+
73+
$this->assertTrue($lockConflicted);
74+
$this->assertFalse($store2->exists($key));
75+
76+
$store1->delete($key);
77+
78+
$store2->save($key);
79+
$this->assertTrue($store2->exists($key));
80+
}
5381
}

0 commit comments

Comments
 (0)