Skip to content

Commit bc1b55f

Browse files
committed
Rewrite logic to two keyed lock
1 parent c2240b3 commit bc1b55f

File tree

3 files changed

+68
-49
lines changed

3 files changed

+68
-49
lines changed

src/Locker/PostgresAdvisoryLocker.php

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
final class PostgresAdvisoryLocker
2121
{
2222
/**
23-
* Try to acquire transaction-level lock (recommended).
23+
* Acquire transaction-level lock (recommended).
2424
*/
25-
public function tryAcquireLockWithinTransaction(
25+
public function acquireLockWithinTransaction(
2626
PDO $dbConnection,
2727
PostgresLockId $postgresLockId,
28+
PostgresLockModeEnum $lockMode = PostgresLockModeEnum::Try,
2829
): bool {
2930
if ($dbConnection->inTransaction() === false) {
3031
$lockId = $postgresLockId->humanReadableValue;
@@ -34,12 +35,12 @@ public function tryAcquireLockWithinTransaction(
3435
);
3536
}
3637

37-
// TODO: Need to sanitize humanReadableValue?
38-
$statement = $dbConnection->prepare(
39-
<<<SQL
40-
SELECT PG_TRY_ADVISORY_XACT_LOCK(:class_id, :object_id); -- $postgresLockId->humanReadableValue
41-
SQL,
42-
);
38+
$sql = match ($lockMode) {
39+
PostgresLockModeEnum::Try => 'SELECT PG_TRY_ADVISORY_XACT_LOCK(:class_id, :object_id); -- ' . $postgresLockId->humanReadableValue,
40+
PostgresLockModeEnum::Block => 'SELECT PG_ADVISORY_XACT_LOCK(:class_id, :object_id); -- ' . $postgresLockId->humanReadableValue,
41+
};
42+
43+
$statement = $dbConnection->prepare($sql);
4344
$statement->execute(
4445
[
4546
'class_id' => $postgresLockId->classId,
@@ -51,18 +52,19 @@ public function tryAcquireLockWithinTransaction(
5152
}
5253

5354
/**
54-
* Try to acquire session-level lock (use only if transaction-level lock not applicable).
55+
* Acquire session-level lock (use only if transaction-level lock not applicable).
5556
*/
56-
public function tryAcquireLockWithinSession(
57+
public function acquireLockWithinSession(
5758
PDO $dbConnection,
5859
PostgresLockId $postgresLockId,
60+
PostgresLockModeEnum $lockMode = PostgresLockModeEnum::Try,
5961
): bool {
60-
// TODO: Need to sanitize humanReadableValue?
61-
$statement = $dbConnection->prepare(
62-
<<<SQL
63-
SELECT PG_TRY_ADVISORY_LOCK(:class_id, :object_id); -- $postgresLockId->humanReadableValue
64-
SQL,
65-
);
62+
$sql = match ($lockMode) {
63+
PostgresLockModeEnum::Try => 'SELECT PG_TRY_ADVISORY_LOCK(:class_id, :object_id); -- ' . $postgresLockId->humanReadableValue,
64+
PostgresLockModeEnum::Block => 'SELECT PG_ADVISORY_LOCK(:class_id, :object_id); -- ' . $postgresLockId->humanReadableValue,
65+
};
66+
67+
$statement = $dbConnection->prepare($sql);
6668
$statement->execute(
6769
[
6870
'class_id' => $postgresLockId->classId,

src/Locker/PostgresLockModeEnum.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cog\DbLocker\Locker;
6+
7+
/**
8+
* AdvisoryLockMode defines the mode of advisory lock acquisition.
9+
*
10+
* - Try: Attempt to acquire the lock without blocking (pg_try_advisory_lock).
11+
* - Block: Acquire the lock, blocking until it becomes available (pg_advisory_lock).
12+
*/
13+
enum PostgresLockModeEnum
14+
{
15+
case Try;
16+
case Block;
17+
}

test/Integration/Locker/PostgresAdvisoryLockerTest.php

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function testItCanTryAcquireLockWithinSession(): void
3030
$dbConnection = $this->initPostgresPdoConnection();
3131
$postgresLockId = PostgresLockId::fromKeyValue('test');
3232

33-
$isLockAcquired = $locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId);
33+
$isLockAcquired = $locker->acquireLockWithinSession($dbConnection, $postgresLockId);
3434

3535
$this->assertTrue($isLockAcquired);
3636
$this->assertPgAdvisoryLocksCount(1);
@@ -44,7 +44,7 @@ public function testItCanTryAcquireLockFromIntKeysCornerCases(): void
4444
$dbConnection = $this->initPostgresPdoConnection();
4545
$postgresLockId = PostgresLockId::fromIntKeys(self::DB_INT32_VALUE_MIN, 0);
4646

47-
$isLockAcquired = $locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId);
47+
$isLockAcquired = $locker->acquireLockWithinSession($dbConnection, $postgresLockId);
4848

4949
$this->assertTrue($isLockAcquired);
5050
$this->assertPgAdvisoryLocksCount(1);
@@ -79,8 +79,8 @@ public function testItCanTryAcquireLockInSameConnectionOnlyOnce(): void
7979
$dbConnection = $this->initPostgresPdoConnection();
8080
$postgresLockId = PostgresLockId::fromKeyValue('test');
8181

82-
$isLockAcquired1 = $locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId);
83-
$isLockAcquired2 = $locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId);
82+
$isLockAcquired1 = $locker->acquireLockWithinSession($dbConnection, $postgresLockId);
83+
$isLockAcquired2 = $locker->acquireLockWithinSession($dbConnection, $postgresLockId);
8484

8585
$this->assertTrue($isLockAcquired1);
8686
$this->assertTrue($isLockAcquired2);
@@ -95,8 +95,8 @@ public function testItCanTryAcquireMultipleLocksInOneConnection(): void
9595
$postgresLockId1 = PostgresLockId::fromKeyValue('test1');
9696
$postgresLockId2 = PostgresLockId::fromKeyValue('test2');
9797

98-
$isLock1Acquired = $locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId1);
99-
$isLock2Acquired = $locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId2);
98+
$isLock1Acquired = $locker->acquireLockWithinSession($dbConnection, $postgresLockId1);
99+
$isLock2Acquired = $locker->acquireLockWithinSession($dbConnection, $postgresLockId2);
100100

101101
$this->assertTrue($isLock1Acquired);
102102
$this->assertTrue($isLock2Acquired);
@@ -111,9 +111,9 @@ public function testItCannotAcquireSameLockInTwoConnections(): void
111111
$dbConnection1 = $this->initPostgresPdoConnection();
112112
$dbConnection2 = $this->initPostgresPdoConnection();
113113
$postgresLockId = PostgresLockId::fromKeyValue('test');
114-
$locker->tryAcquireLockWithinSession($dbConnection1, $postgresLockId);
114+
$locker->acquireLockWithinSession($dbConnection1, $postgresLockId);
115115

116-
$isLockAcquired = $locker->tryAcquireLockWithinSession($dbConnection2, $postgresLockId);
116+
$isLockAcquired = $locker->acquireLockWithinSession($dbConnection2, $postgresLockId);
117117

118118
$this->assertFalse($isLockAcquired);
119119
$this->assertPgAdvisoryLocksCount(1);
@@ -125,7 +125,7 @@ public function testItCanReleaseLock(): void
125125
$locker = $this->initLocker();
126126
$dbConnection = $this->initPostgresPdoConnection();
127127
$postgresLockId = PostgresLockId::fromKeyValue('test');
128-
$locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId);
128+
$locker->acquireLockWithinSession($dbConnection, $postgresLockId);
129129

130130
$isLockReleased = $locker->releaseLockWithinSession($dbConnection, $postgresLockId);
131131

@@ -138,8 +138,8 @@ public function testItCanReleaseLockTwiceIfAcquiredTwice(): void
138138
$locker = $this->initLocker();
139139
$dbConnection = $this->initPostgresPdoConnection();
140140
$postgresLockId = PostgresLockId::fromKeyValue('test');
141-
$locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId);
142-
$locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId);
141+
$locker->acquireLockWithinSession($dbConnection, $postgresLockId);
142+
$locker->acquireLockWithinSession($dbConnection, $postgresLockId);
143143

144144
$isLockReleased1 = $locker->releaseLockWithinSession($dbConnection, $postgresLockId);
145145
$isLockReleased2 = $locker->releaseLockWithinSession($dbConnection, $postgresLockId);
@@ -155,10 +155,10 @@ public function testItCanTryAcquireLockInSecondConnectionAfterRelease(): void
155155
$dbConnection1 = $this->initPostgresPdoConnection();
156156
$dbConnection2 = $this->initPostgresPdoConnection();
157157
$postgresLockId = PostgresLockId::fromKeyValue('test');
158-
$locker->tryAcquireLockWithinSession($dbConnection1, $postgresLockId);
158+
$locker->acquireLockWithinSession($dbConnection1, $postgresLockId);
159159
$locker->releaseLockWithinSession($dbConnection1, $postgresLockId);
160160

161-
$isLockAcquired = $locker->tryAcquireLockWithinSession($dbConnection2, $postgresLockId);
161+
$isLockAcquired = $locker->acquireLockWithinSession($dbConnection2, $postgresLockId);
162162

163163
$this->assertTrue($isLockAcquired);
164164
$this->assertPgAdvisoryLocksCount(1);
@@ -171,11 +171,11 @@ public function testItCannotAcquireLockInSecondConnectionAfterOneReleaseTwiceLoc
171171
$dbConnection1 = $this->initPostgresPdoConnection();
172172
$dbConnection2 = $this->initPostgresPdoConnection();
173173
$postgresLockId = PostgresLockId::fromKeyValue('test');
174-
$locker->tryAcquireLockWithinSession($dbConnection1, $postgresLockId);
175-
$locker->tryAcquireLockWithinSession($dbConnection1, $postgresLockId);
174+
$locker->acquireLockWithinSession($dbConnection1, $postgresLockId);
175+
$locker->acquireLockWithinSession($dbConnection1, $postgresLockId);
176176

177177
$isLockReleased = $locker->releaseLockWithinSession($dbConnection1, $postgresLockId);
178-
$isLockAcquired = $locker->tryAcquireLockWithinSession($dbConnection2, $postgresLockId);
178+
$isLockAcquired = $locker->acquireLockWithinSession($dbConnection2, $postgresLockId);
179179

180180
$this->assertTrue($isLockReleased);
181181
$this->assertFalse($isLockAcquired);
@@ -202,7 +202,7 @@ public function testItCannotReleaseLockIfAcquiredInOtherConnection(): void
202202
$dbConnection1 = $this->initPostgresPdoConnection();
203203
$dbConnection2 = $this->initPostgresPdoConnection();
204204
$postgresLockId = PostgresLockId::fromKeyValue('test');
205-
$locker->tryAcquireLockWithinSession($dbConnection1, $postgresLockId);
205+
$locker->acquireLockWithinSession($dbConnection1, $postgresLockId);
206206

207207
$isLockReleased = $locker->releaseLockWithinSession($dbConnection2, $postgresLockId);
208208

@@ -217,8 +217,8 @@ public function testItCanReleaseAllLocksInConnection(): void
217217
$dbConnection = $this->initPostgresPdoConnection();
218218
$postgresLockId1 = PostgresLockId::fromKeyValue('test');
219219
$postgresLockId2 = PostgresLockId::fromKeyValue('test2');
220-
$locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId1);
221-
$locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId2);
220+
$locker->acquireLockWithinSession($dbConnection, $postgresLockId1);
221+
$locker->acquireLockWithinSession($dbConnection, $postgresLockId2);
222222

223223
$locker->releaseAllLocksWithinSession($dbConnection);
224224

@@ -244,10 +244,10 @@ public function testItCanReleaseAllLocksInConnectionButKeepsOtherLocks(): void
244244
$postgresLockId2 = PostgresLockId::fromKeyValue('test2');
245245
$postgresLockId3 = PostgresLockId::fromKeyValue('test3');
246246
$postgresLockId4 = PostgresLockId::fromKeyValue('test4');
247-
$locker->tryAcquireLockWithinSession($dbConnection1, $postgresLockId1);
248-
$locker->tryAcquireLockWithinSession($dbConnection1, $postgresLockId2);
249-
$locker->tryAcquireLockWithinSession($dbConnection2, $postgresLockId3);
250-
$locker->tryAcquireLockWithinSession($dbConnection2, $postgresLockId4);
247+
$locker->acquireLockWithinSession($dbConnection1, $postgresLockId1);
248+
$locker->acquireLockWithinSession($dbConnection1, $postgresLockId2);
249+
$locker->acquireLockWithinSession($dbConnection2, $postgresLockId3);
250+
$locker->acquireLockWithinSession($dbConnection2, $postgresLockId4);
251251

252252
$locker->releaseAllLocksWithinSession($dbConnection1);
253253

@@ -263,7 +263,7 @@ public function testItCanTryAcquireLockWithinTransaction(): void
263263
$postgresLockId = PostgresLockId::fromKeyValue('test');
264264
$dbConnection->beginTransaction();
265265

266-
$isLockAcquired = $locker->tryAcquireLockWithinTransaction($dbConnection, $postgresLockId);
266+
$isLockAcquired = $locker->acquireLockWithinTransaction($dbConnection, $postgresLockId);
267267

268268
$this->assertTrue($isLockAcquired);
269269
$this->assertPgAdvisoryLocksCount(1);
@@ -281,7 +281,7 @@ public function testItCannotAcquireLockWithinTransactionNotInTransaction(): void
281281
$dbConnection = $this->initPostgresPdoConnection();
282282
$postgresLockId = PostgresLockId::fromKeyValue('test');
283283

284-
$locker->tryAcquireLockWithinTransaction($dbConnection, $postgresLockId);
284+
$locker->acquireLockWithinTransaction($dbConnection, $postgresLockId);
285285
}
286286

287287
public function testItCannotAcquireLockInSecondConnectionIfTakenWithinTransaction(): void
@@ -291,9 +291,9 @@ public function testItCannotAcquireLockInSecondConnectionIfTakenWithinTransactio
291291
$dbConnection2 = $this->initPostgresPdoConnection();
292292
$postgresLockId = PostgresLockId::fromKeyValue('test');
293293
$dbConnection1->beginTransaction();
294-
$locker->tryAcquireLockWithinTransaction($dbConnection1, $postgresLockId);
294+
$locker->acquireLockWithinTransaction($dbConnection1, $postgresLockId);
295295

296-
$isLockAcquired = $locker->tryAcquireLockWithinSession($dbConnection2, $postgresLockId);
296+
$isLockAcquired = $locker->acquireLockWithinSession($dbConnection2, $postgresLockId);
297297

298298
$this->assertFalse($isLockAcquired);
299299
$this->assertPgAdvisoryLocksCount(1);
@@ -306,7 +306,7 @@ public function testItCanAutoReleaseLockAcquiredWithinTransactionOnCommit(): voi
306306
$dbConnection = $this->initPostgresPdoConnection();
307307
$postgresLockId = PostgresLockId::fromKeyValue('test');
308308
$dbConnection->beginTransaction();
309-
$locker->tryAcquireLockWithinTransaction($dbConnection, $postgresLockId);
309+
$locker->acquireLockWithinTransaction($dbConnection, $postgresLockId);
310310

311311
$dbConnection->commit();
312312

@@ -320,7 +320,7 @@ public function testItCanAutoReleaseLockAcquiredWithinTransactionOnRollback(): v
320320
$dbConnection = $this->initPostgresPdoConnection();
321321
$postgresLockId = PostgresLockId::fromKeyValue('test');
322322
$dbConnection->beginTransaction();
323-
$locker->tryAcquireLockWithinTransaction($dbConnection, $postgresLockId);
323+
$locker->acquireLockWithinTransaction($dbConnection, $postgresLockId);
324324

325325
$dbConnection->rollBack();
326326

@@ -334,7 +334,7 @@ public function testItCanAutoReleaseLockAcquiredWithinTransactionOnConnectionKil
334334
$dbConnection = $this->initPostgresPdoConnection();
335335
$postgresLockId = PostgresLockId::fromKeyValue('test');
336336
$dbConnection->beginTransaction();
337-
$locker->tryAcquireLockWithinTransaction($dbConnection, $postgresLockId);
337+
$locker->acquireLockWithinTransaction($dbConnection, $postgresLockId);
338338

339339
$dbConnection = null;
340340

@@ -347,7 +347,7 @@ public function testItCannotReleaseLockAcquiredWithinTransaction(): void
347347
$dbConnection = $this->initPostgresPdoConnection();
348348
$postgresLockId = PostgresLockId::fromKeyValue('test');
349349
$dbConnection->beginTransaction();
350-
$locker->tryAcquireLockWithinTransaction($dbConnection, $postgresLockId);
350+
$locker->acquireLockWithinTransaction($dbConnection, $postgresLockId);
351351

352352
$isLockReleased = $locker->releaseLockWithinSession($dbConnection, $postgresLockId);
353353

@@ -362,9 +362,9 @@ public function testItCannotReleaseAllLocksAcquiredWithinTransaction(): void
362362
$dbConnection = $this->initPostgresPdoConnection();
363363
$postgresLockId1 = PostgresLockId::fromKeyValue('test');
364364
$postgresLockId2 = PostgresLockId::fromKeyValue('test2');
365-
$locker->tryAcquireLockWithinSession($dbConnection, $postgresLockId1);
365+
$locker->acquireLockWithinSession($dbConnection, $postgresLockId1);
366366
$dbConnection->beginTransaction();
367-
$locker->tryAcquireLockWithinTransaction($dbConnection, $postgresLockId2);
367+
$locker->acquireLockWithinTransaction($dbConnection, $postgresLockId2);
368368

369369
$locker->releaseAllLocksWithinSession($dbConnection);
370370

0 commit comments

Comments
 (0)