-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Lock] Add MysqlStore
#45982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 7.4
Are you sure you want to change the base?
[Lock] Add MysqlStore
#45982
Changes from 1 commit
6a18831
f849ecb
2f26e01
6954fc6
4730f91
d28db69
2def8dd
cda417a
827545a
9a32d39
e2f0faa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,9 @@ class MysqlStore implements PersistingStoreInterface | |
|
||
private array $options; | ||
|
||
/** @var bool[] */ | ||
private static array $locksAcquired = []; | ||
|
||
public function __construct(\PDO|string $connOrDsn, array $options = []) | ||
{ | ||
if ($connOrDsn instanceof \PDO) { | ||
|
@@ -46,22 +49,20 @@ public function __construct(\PDO|string $connOrDsn, array $options = []) | |
|
||
public function save(Key $key): void | ||
{ | ||
if ($key->hasState(__CLASS__)) { | ||
$id = $this->getLockId($key); | ||
|
||
if (self::$locksAcquired[$id] ?? false) { | ||
return; | ||
} | ||
|
||
// mysql limits lock name length to 64 chars | ||
$name = (string) $key; | ||
$name = \strlen($name) > 64 ? hash('xxh128', $name) : $name; | ||
|
||
$stmt = $this->conn->prepare('SELECT IF(IS_USED_LOCK(:name) = CONNECTION_ID(), -1, GET_LOCK(:name, 0))'); | ||
$stmt->bindValue(':name', $name, \PDO::PARAM_STR); | ||
$stmt->bindValue(':name', self::getLockName($key), \PDO::PARAM_STR); | ||
$stmt->execute(); | ||
$result = $stmt->fetchColumn(); | ||
|
||
// lock acquired | ||
if (1 === $result) { | ||
$key->setState(__CLASS__, $name); | ||
self::$locksAcquired[$id] = true; | ||
|
||
return; | ||
} | ||
|
@@ -84,28 +85,16 @@ public function putOffExpiration(Key $key, float $ttl): void | |
|
||
public function delete(Key $key): void | ||
{ | ||
if (!$key->hasState(__CLASS__)) { | ||
return; | ||
} | ||
|
||
$stmt = $this->conn->prepare('DO RELEASE_LOCK(:name)'); | ||
$stmt->bindValue(':name', $key->getState(__CLASS__), \PDO::PARAM_STR); | ||
$stmt->bindValue(':name', self::getLockName($key), \PDO::PARAM_STR); | ||
$stmt->execute(); | ||
|
||
$key->removeState(__CLASS__); | ||
unset(self::$locksAcquired[$this->getLockId($key)]); | ||
} | ||
|
||
public function exists(Key $key): bool | ||
{ | ||
if (!$key->hasState(__CLASS__)) { | ||
return false; | ||
} | ||
|
||
$stmt = $this->conn->prepare('SELECT IF(IS_USED_LOCK(:name) = CONNECTION_ID(), 1, 0)'); | ||
$stmt->bindValue(':name', $key->getState(__CLASS__), \PDO::PARAM_STR); | ||
$stmt->execute(); | ||
|
||
return 1 === $stmt->fetchColumn(); | ||
return self::$locksAcquired[$this->getLockId($key)] ?? false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's wrong, the implementation must assert that the lock is still present in the backend. (Ie the database restart, the network is down, ...) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This wont be possible since GET_LOCK is bound to the connection so its always released when the connection is terminated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well.. This means, that each time the server restarts, the application might retrieve the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should do it. I cant add this test case to the since it requires rebooting mysql but I did run it manually to confirm proper handling even if the connection id is the same
|
||
} | ||
|
||
private function getConnection(): \PDO | ||
|
@@ -130,4 +119,17 @@ private function assertMysqlDriver(): void | |
throw new InvalidArgumentException(sprintf('The adapter "%s" does not support the "%s" driver.', __CLASS__, $driver)); | ||
} | ||
} | ||
|
||
private function getLockId(Key $key): string | ||
{ | ||
return spl_object_id($this->getConnection()).'_'.spl_object_id($key); | ||
} | ||
|
||
private static function getLockName(Key $key): string | ||
{ | ||
// mysql limits lock name length to 64 chars | ||
$name = (string) $key; | ||
|
||
return \strlen($name) > 64 ? hash('xxh128', $name) : $name; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hashing only longer names will mean that there is 2 resources that share the same MySQL lock name |
||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PostgreSqlStore
uses the InMemoryStore to avoid duplicate lock on the same connection.symfony/src/Symfony/Component/Lock/Store/PostgreSqlStore.php
Line 288 in 1f514f9
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate on this requirement?
Im concerned that using the
InMemoryStore
can cause unexpected behavior in situations where either:LockFactory::createLockFromKey
, one using Mysql and the other using PostgresI moved the state into the store since the tests assert that
exists()
is bound to theKey
instance, whilesave()
is bound to theKey::$resource
. Does this work?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InMemoryStore