[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/infrastructure/util/ -> PhabricatorGlobalLock.php (source)

   1  <?php
   2  
   3  /**
   4   * Global, MySQL-backed lock. This is a high-reliability, low-performance
   5   * global lock.
   6   *
   7   * The lock is maintained by using GET_LOCK() in MySQL, and automatically
   8   * released when the connection terminates. Thus, this lock can safely be used
   9   * to control access to shared resources without implementing any sort of
  10   * timeout or override logic: the lock can't normally be stuck in a locked state
  11   * with no process actually holding the lock.
  12   *
  13   * However, acquiring the lock is moderately expensive (several network
  14   * roundtrips). This makes it unsuitable for tasks where lock performance is
  15   * important.
  16   *
  17   *    $lock = PhabricatorGlobalLock::newLock('example');
  18   *    $lock->lock();
  19   *      do_contentious_things();
  20   *    $lock->unlock();
  21   *
  22   * NOTE: This lock is not completely global; it is namespaced to the active
  23   * storage namespace so that unit tests running in separate table namespaces
  24   * are isolated from one another.
  25   *
  26   * @task construct  Constructing Locks
  27   * @task impl       Implementation
  28   */
  29  final class PhabricatorGlobalLock extends PhutilLock {
  30  
  31    private $lockname;
  32    private $conn;
  33  
  34    private static $pool = array();
  35  
  36  
  37  /* -(  Constructing Locks  )------------------------------------------------- */
  38  
  39  
  40    public static function newLock($name) {
  41      $namespace = PhabricatorLiskDAO::getStorageNamespace();
  42      $full_name = 'global:'.$namespace.':'.$name;
  43  
  44      $lock = self::getLock($full_name);
  45      if (!$lock) {
  46        $lock = new PhabricatorGlobalLock($full_name);
  47        $lock->lockname = $name;
  48        self::registerLock($lock);
  49      }
  50  
  51      return $lock;
  52    }
  53  
  54  
  55  /* -(  Implementation  )----------------------------------------------------- */
  56  
  57    protected function doLock($wait) {
  58      $conn = $this->conn;
  59  
  60      if (!$conn) {
  61        // Try to reuse a connection from the connection pool.
  62        $conn = array_pop(self::$pool);
  63      }
  64  
  65      if (!$conn) {
  66        // NOTE: Using the 'repository' database somewhat arbitrarily, mostly
  67        // because the first client of locks is the repository daemons. We must
  68        // always use the same database for all locks, but don't access any
  69        // tables so we could use any valid database. We could build a
  70        // database-free connection instead, but that's kind of messy and we
  71        // might forget about it in the future if we vertically partition the
  72        // application.
  73        $dao = new PhabricatorRepository();
  74  
  75        // NOTE: Using "force_new" to make sure each lock is on its own
  76        // connection.
  77        $conn = $dao->establishConnection('w', $force_new = true);
  78  
  79        // NOTE: Since MySQL will disconnect us if we're idle for too long, we set
  80        // the wait_timeout to an enormous value, to allow us to hold the
  81        // connection open indefinitely (or, at least, for 24 days).
  82        $max_allowed_timeout = 2147483;
  83        queryfx($conn, 'SET wait_timeout = %d', $max_allowed_timeout);
  84      }
  85  
  86      $result = queryfx_one(
  87        $conn,
  88        'SELECT GET_LOCK(%s, %f)',
  89        'phabricator:'.$this->lockname,
  90        $wait);
  91  
  92      $ok = head($result);
  93      if (!$ok) {
  94        throw new PhutilLockException($this->getName());
  95      }
  96  
  97      $this->conn = $conn;
  98    }
  99  
 100    protected function doUnlock() {
 101      queryfx(
 102        $this->conn,
 103        'SELECT RELEASE_LOCK(%s)',
 104        'phabricator:'.$this->lockname);
 105  
 106      $this->conn->close();
 107      self::$pool[] = $this->conn;
 108      $this->conn = null;
 109    }
 110  
 111  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1