Skip to content
Snippets Groups Projects
Commit f156c3d1 authored by catch's avatar catch
Browse files

Issue #2872276 by damiankloip, dawehner: Database lock backend should normalize the lock name

parent 9165b058
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\Core\Lock; namespace Drupal\Core\Lock;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Database\Connection; use Drupal\Core\Database\Connection;
use Drupal\Core\Database\IntegrityConstraintViolationException; use Drupal\Core\Database\IntegrityConstraintViolationException;
use Drupal\Core\Database\SchemaObjectExistsException; use Drupal\Core\Database\SchemaObjectExistsException;
...@@ -42,6 +43,8 @@ public function __construct(Connection $database) { ...@@ -42,6 +43,8 @@ public function __construct(Connection $database) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function acquire($name, $timeout = 30.0) { public function acquire($name, $timeout = 30.0) {
$name = $this->normalizeName($name);
// Insure that the timeout is at least 1 ms. // Insure that the timeout is at least 1 ms.
$timeout = max($timeout, 0.001); $timeout = max($timeout, 0.001);
$expire = microtime(TRUE) + $timeout; $expire = microtime(TRUE) + $timeout;
...@@ -106,6 +109,8 @@ public function acquire($name, $timeout = 30.0) { ...@@ -106,6 +109,8 @@ public function acquire($name, $timeout = 30.0) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function lockMayBeAvailable($name) { public function lockMayBeAvailable($name) {
$name = $this->normalizeName($name);
try { try {
$lock = $this->database->query('SELECT expire, value FROM {semaphore} WHERE name = :name', [':name' => $name])->fetchAssoc(); $lock = $this->database->query('SELECT expire, value FROM {semaphore} WHERE name = :name', [':name' => $name])->fetchAssoc();
} }
...@@ -136,6 +141,8 @@ public function lockMayBeAvailable($name) { ...@@ -136,6 +141,8 @@ public function lockMayBeAvailable($name) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function release($name) { public function release($name) {
$name = $this->normalizeName($name);
unset($this->locks[$name]); unset($this->locks[$name]);
try { try {
$this->database->delete('semaphore') $this->database->delete('semaphore')
...@@ -203,6 +210,33 @@ protected function catchException(\Exception $e) { ...@@ -203,6 +210,33 @@ protected function catchException(\Exception $e) {
} }
} }
/**
* Normalizes a lock name in order to comply with database limitations.
*
* @param string $name
* The passed in lock name.
*
* @return string
* An ASCII-encoded lock name that is at most 255 characters long.
*/
protected function normalizeName($name) {
// Nothing to do if the name is a US ASCII string of 255 characters or less.
$name_is_ascii = mb_check_encoding($name, 'ASCII');
if (strlen($name) <= 255 && $name_is_ascii) {
return $name;
}
// Return a string that uses as much as possible of the original name with
// the hash appended.
$hash = Crypt::hashBase64($name);
if (!$name_is_ascii) {
return $hash;
}
return substr($name, 0, 255 - strlen($hash)) . $hash;
}
/** /**
* Defines the schema for the semaphore table. * Defines the schema for the semaphore table.
*/ */
......
...@@ -47,6 +47,21 @@ public function testBackendLockRelease() { ...@@ -47,6 +47,21 @@ public function testBackendLockRelease() {
$this->assertTrue($success, 'Could acquire second lock a second time within the same request.'); $this->assertTrue($success, 'Could acquire second lock a second time within the same request.');
$this->lock->release('lock_b'); $this->lock->release('lock_b');
// Test acquiring an releasing a lock with a long key (over 255 chars).
$long_key = 'long_key:BZoMiSf9IIPULsJ98po18TxJ6T4usd3MZrLE0d3qMgG6iAgDlOi1G3oMap7zI5df84l7LtJBg4bOj6XvpO6vDRmP5h5QbA0Bj9rVFiPIPAIQZ9qFvJqTALiK1OR3GpOkWQ4vgEA4LkY0UfznrWBeuK7IWZfv1um6DLosnVXd1z1cJjvbEUqYGJj92rwHfhYihLm8IO9t3P2gAvEkH5Mhc8GBoiTsIDnP01Te1kxGFHO3RuvJIxPnHmZtSdBggmuVN7x9';
$success = $this->lock->acquire($long_key);
$this->assertTrue($success, 'Could acquire long key lock.');
// This function is not part of the backend, but the default database
// backend implement it, we can here use it safely.
$is_free = $this->lock->lockMayBeAvailable($long_key);
$this->assertFalse($is_free, 'Long key lock is unavailable.');
$this->lock->release($long_key);
$is_free = $this->lock->lockMayBeAvailable($long_key);
$this->assertTrue($is_free, 'Long key lock has been released.');
} }
/** /**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment