Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[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

Open
wants to merge 11 commits into
base: 7.4
Choose a base branch
Loading
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add dsn support
  • Loading branch information
rtek committed Apr 11, 2022
commit 6954fc646b659ca29e4b6ee25bc5e620436d76a0
2 changes: 1 addition & 1 deletion 2 src/Symfony/Component/Lock/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CHANGELOG
=========
6.1
6.2
---
* Add `MysqlStore` based on MySQL `GET_LOCK()` functionality

Expand Down
51 changes: 39 additions & 12 deletions 51 src/Symfony/Component/Lock/Store/MysqlStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,25 @@
*/
class MysqlStore implements PersistingStoreInterface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rename it to MysqlLocksStore to indicate Mysql user locks are used (not to be confused with data storage in Pdo store

{
private \PDO $conn;
private ?\PDO $conn;

public function __construct(\PDO $conn)
{
if ('mysql' !== $driver = $conn->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new InvalidArgumentException(sprintf('The adapter "%s" does not support the "%s" driver.', __CLASS__, $driver));
}
private ?string $dsn;

private array $options;

if (\PDO::ERRMODE_EXCEPTION !== $conn->getAttribute(\PDO::ATTR_ERRMODE)) {
throw new InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __METHOD__));
public function __construct(\PDO|string $connOrDsn, array $options = [])
{
if ($connOrDsn instanceof \PDO) {
$this->conn = $connOrDsn;
$this->assertMysqlDriver();
if (\PDO::ERRMODE_EXCEPTION !== $this->conn->getAttribute(\PDO::ATTR_ERRMODE)) {
throw new InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __METHOD__));
}
} else {
$this->dsn = $connOrDsn;
}

$this->conn = $conn;
$this->options = $options;
}

public function save(Key $key): void
Expand All @@ -44,11 +50,9 @@ public function save(Key $key): void
return;
}

// todo ? check that mysql > 5.7.3

// mysql limits lock name length to 64 chars
$name = (string) $key;
$name = \strlen($name) > 64 ? hash('sha256', $name) : $name;
$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))');
Copy link
Member

@GromNaN GromNaN Apr 10, 2022

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.

return self::$storeRegistry[$namespace] ?? self::$storeRegistry[$namespace] = new InMemoryStore();

Copy link
Contributor Author

@rtek rtek Apr 11, 2022

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:

  1. A combined store is used with with Mysql and Postgres
  2. A the same key is passed to two LockFactory::createLockFromKey, one using Mysql and the other using Postgres

I moved the state into the store since the tests assert that exists() is bound to the Key instance, while save() is bound to the Key::$resource. Does this work?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. They should not share the same instance of InMemoryStore

$stmt->bindValue(':name', $name, \PDO::PARAM_STR);
Expand Down Expand Up @@ -103,4 +107,27 @@ public function exists(Key $key): bool

return 1 === $stmt->fetchColumn();
}

private function getConnection(): \PDO
{
if (!$this->conn) {
$this->conn = new \PDO(
$this->dsn,
$this->options['db_username'] ?? null,
$this->options['db_password'] ?? null,
$this->options['db_connection_options'] ?? null
);
$this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$this->assertMysqlDriver();
}

return $this->conn;
}

private function assertMysqlDriver(): void
{
if ('mysql' !== $driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME)) {
throw new InvalidArgumentException(sprintf('The adapter "%s" does not support the "%s" driver.', __CLASS__, $driver));
}
}
}
17 changes: 16 additions & 1 deletion 17 src/Symfony/Component/Lock/Tests/Store/MysqlStoreTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class MysqlStoreTest extends AbstractStoreTest
{
protected function getPdo(): \PDO
protected function getEnv(): array
{
if (!$host = getenv('MYSQL_HOST')) {
$this->markTestSkipped('Missing MYSQL_HOST env variable');
Expand All @@ -24,6 +24,13 @@ protected function getPdo(): \PDO
$this->markTestSkipped('Missing MYSQL_PASSWORD env variable');
}

return [$host, $user, $pass];
}

protected function getPdo(): \PDO
{
[$host, $user, $pass] = $this->getEnv();

return new \PDO('mysql:host='.$host, $user, $pass);
}

Expand Down Expand Up @@ -73,4 +80,12 @@ public function testOtherConflictException()
$this->assertStringContainsString('acquired by other', $e->getMessage());
}
}

public function testDsnConstructor()
{
$this->expectNotToPerformAssertions();

[$host, $user, $pass] = $this->getEnv();
new MysqlStore("mysql:$host", ['db_username' => $user, 'db_password' => $pass]);
}
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.