redis-8.x-1.x-dev/src/Lock/RedisLock.php

src/Lock/RedisLock.php
<?php

namespace Drupal\redis\Lock;

use Drupal\Core\Lock\LockBackendAbstract;
use Drupal\redis\Client\Relay;
use Drupal\redis\ClientFactory;
use Drupal\redis\ClientInterface;
use Drupal\redis\RedisPrefixTrait;

/**
 * PhpRedis lock backend implementation.
 */
class RedisLock extends LockBackendAbstract {

  use RedisPrefixTrait;

  /**
   * @var \Drupal\redis\ClientInterface
   */
  protected ClientInterface $client;

  /**
   * Creates a PhpRedis cache backend.
   */
  public function __construct(ClientFactory $factory, bool $persistent) {
    $this->client = $factory->getClient();

    if ($persistent) {
      // Set the lockId to a fixed string to make the lock ID the same across
      // multiple requests. The lock ID is used as a page token to relate all the
      // locks set during a request to each other.
      // @see \Drupal\Core\Lock\LockBackendInterface::getLockId()
      $this->lockId = 'persistent';
    }
    else {
      // __destruct() is causing problems with garbage collections, register a
      // shutdown function instead.
      drupal_register_shutdown_function([$this, 'releaseAll']);
    }

    // Don't cache locks in runtime memory.
    $this->client->addIgnorePattern($this->getKey('*'));
  }

  /**
   * Generate a redis key name for the current lock name.
   *
   * @param string $name
   *   Lock name.
   *
   * @return string
   *   The redis key for the given lock.
   */
  protected function getKey($name) {
    return $this->getPrefix() . ':lock:' . $name;
  }

  /**
   * {@inheritdoc}
   */
  public function acquire($name, $timeout = 30.0) {
    $key    = $this->getKey($name);
    $id     = $this->getLockId();

    // Insure that the timeout is at least 1 ms.
    $timeout = max($timeout, 0.001);

    // If we already have the lock, check for his owner and attempt a new EXPIRE
    // command on it.
    if (isset($this->locks[$name])) {

      // Create a new transaction, for atomicity.
      $this->client->watch($key);

      // Global tells us we are the owner, but in real life it could have expired
      // and another process could have taken it, check that.
      if ($this->client->get($key) != $id) {
        // Explicit UNWATCH we are not going to run the MULTI/EXEC block.
        $this->client->unwatch();
        unset($this->locks[$name]);
        return FALSE;
      }

      $this->client->multi();
      $this->client->set($key, $id, ['px' => (int) ($timeout * 1000)]);
      $result = $this->client->exec();

      // If the set failed, someone else wrote the key, we failed to acquire
      // the lock.
      if (FALSE === $result) {
        unset($this->locks[$name]);
        // Explicit transaction release which also frees the WATCH'ed key.
        $this->client->discard();
        return FALSE;
      }

      return ($this->locks[$name] = TRUE);
    }
    else {
      // Use a SET with microsecond expiration and the NX flag, which will only
      // succeed if the key does not exist yet.
      $result = $this->client->set($key, $id, ['nx', 'px' => (int) ($timeout * 1000)]);

      // If the result is FALSE, we failed to acquire the lock.
      if (FALSE === $result) {
        return FALSE;
      }

      // Register the lock.
      return ($this->locks[$name] = TRUE);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function lockMayBeAvailable($name) {
    $key    = $this->getKey($name);
    $value = $this->client->get($key);

    return $value === FALSE || $value === NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function release($name) {
    $key    = $this->getKey($name);
    $id     = $this->getLockId();

    unset($this->locks[$name]);

    // Ensure the lock deletion is an atomic transaction. If another thread
    // manages to removes all lock, we can not alter it anymore else we will
    // release the lock for the other thread and cause race conditions.
    $this->client->watch($key);

    if ($this->client->get($key) == $id) {
      $this->client->multi();
      $this->client->del($key);
      $this->client->exec();
    }
    else {
      $this->client->unwatch();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function releaseAll($lock_id = NULL) {
    // We can afford to deal with a slow algorithm here, this should not happen
    // on normal run because we should have removed manually all our locks.
    foreach ($this->locks as $name => $foo) {
      $this->release($name);
    }
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc