Skip to content

Commit da9bd84

Browse files
authored
Improve PostgreSQL advisory lock key hashing (#64)
1 parent 7e6c619 commit da9bd84

File tree

2 files changed

+25
-21
lines changed

2 files changed

+25
-21
lines changed

src/mutex/PgAdvisoryLockMutex.php

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44

55
namespace malkusch\lock\mutex;
66

7+
use malkusch\lock\util\LockUtil;
8+
79
class PgAdvisoryLockMutex extends LockMutex
810
{
911
/** @var \PDO */
1012
private $pdo;
1113

12-
/** @var int */
13-
private $key1;
14-
15-
/** @var int */
16-
private $key2;
14+
/** @var array{int, int} */
15+
private array $key;
1716

1817
/**
1918
* @throws \RuntimeException
@@ -22,32 +21,33 @@ public function __construct(\PDO $PDO, string $name)
2221
{
2322
$this->pdo = $PDO;
2423

25-
$hashed_name = hash('sha256', $name, true);
24+
[$keyBytes1, $keyBytes2] = str_split(md5(LockUtil::getInstance()->getKeyPrefix() . ':' . $name, true), 4);
25+
26+
// https://github.com/php/php-src/issues/17068
27+
$unpackToSignedIntLeFx = static function (string $v) {
28+
$unpacked = unpack('va/Cb/cc', $v);
2629

27-
[$bytes1, $bytes2] = str_split($hashed_name, 4);
30+
return $unpacked['a'] | ($unpacked['b'] << 16) | ($unpacked['c'] << 24);
31+
};
2832

29-
$this->key1 = unpack('i', $bytes1)[1];
30-
$this->key2 = unpack('i', $bytes2)[1];
33+
$this->key = [
34+
$unpackToSignedIntLeFx($keyBytes1),
35+
$unpackToSignedIntLeFx($keyBytes2),
36+
];
3137
}
3238

3339
#[\Override]
3440
protected function lock(): void
3541
{
3642
$statement = $this->pdo->prepare('SELECT pg_advisory_lock(?, ?)');
3743

38-
$statement->execute([
39-
$this->key1,
40-
$this->key2,
41-
]);
44+
$statement->execute($this->key);
4245
}
4346

4447
#[\Override]
4548
protected function unlock(): void
4649
{
4750
$statement = $this->pdo->prepare('SELECT pg_advisory_unlock(?, ?)');
48-
$statement->execute([
49-
$this->key1,
50-
$this->key2,
51-
]);
51+
$statement->execute($this->key);
5252
}
5353
}

tests/mutex/PgAdvisoryLockMutexTest.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ protected function setUp(): void
2424

2525
$this->pdo = $this->createMock(\PDO::class);
2626

27-
$this->mutex = new PgAdvisoryLockMutex($this->pdo, 'test');
27+
$this->mutex = new PgAdvisoryLockMutex($this->pdo, 'test-one-negative-key');
2828
}
2929

3030
private function isPhpunit9x(): bool
@@ -52,11 +52,14 @@ public function testAcquireLock(): void
5252
}
5353

5454
foreach ($arguments as $v) {
55+
self::assertLessThan(1 << 32, $v);
56+
self::assertGreaterThanOrEqual(-(1 << 32), $v);
5557
self::assertIsInt($v);
5658
}
5759

5860
return true;
59-
})
61+
}),
62+
[533558444, -1716795572]
6063
));
6164

6265
\Closure::bind(static fn ($mutex) => $mutex->lock(), null, PgAdvisoryLockMutex::class)($this->mutex);
@@ -83,12 +86,13 @@ public function testReleaseLock(): void
8386

8487
foreach ($arguments as $v) {
8588
self::assertLessThan(1 << 32, $v);
86-
self::assertGreaterThan(-(1 << 32), $v);
89+
self::assertGreaterThanOrEqual(-(1 << 32), $v);
8790
self::assertIsInt($v);
8891
}
8992

9093
return true;
91-
})
94+
}),
95+
[533558444, -1716795572]
9296
));
9397

9498
\Closure::bind(static fn ($mutex) => $mutex->unlock(), null, PgAdvisoryLockMutex::class)($this->mutex);

0 commit comments

Comments
 (0)