Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/util/PhabricatorGlobalLock.php
Show First 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | /* -( Connection Pool )---------------------------------------------------- */ | ||||
public static function getConnectionPoolSize() { | public static function getConnectionPoolSize() { | ||||
return count(self::$pool); | return count(self::$pool); | ||||
} | } | ||||
public static function clearConnectionPool() { | public static function clearConnectionPool() { | ||||
self::$pool = array(); | self::$pool = array(); | ||||
} | } | ||||
public static function newConnection() { | |||||
// NOTE: Use of the "repository" database is somewhat arbitrary, mostly | |||||
// because the first client of locks was the repository daemons. | |||||
// We must always use the same database for all locks, because different | |||||
// databases may be on different hosts if the database is partitioned. | |||||
// However, we don't access any tables so we could use any valid database. | |||||
// We could build a database-free connection instead, but that's kind of | |||||
// messy and unusual. | |||||
$dao = new PhabricatorRepository(); | |||||
// NOTE: Using "force_new" to make sure each lock is on its own connection. | |||||
// See T13627. This is critically important in versions of MySQL older | |||||
// than MySQL 5.7, because they can not hold more than one lock per | |||||
// connection simultaneously. | |||||
return $dao->establishConnection('w', $force_new = true); | |||||
} | |||||
/* -( Implementation )----------------------------------------------------- */ | /* -( Implementation )----------------------------------------------------- */ | ||||
protected function doLock($wait) { | protected function doLock($wait) { | ||||
$conn = $this->conn; | $conn = $this->conn; | ||||
if (!$conn) { | if (!$conn) { | ||||
if ($this->externalConnection) { | if ($this->externalConnection) { | ||||
$conn = $this->externalConnection; | $conn = $this->externalConnection; | ||||
} | } | ||||
} | } | ||||
if (!$conn) { | if (!$conn) { | ||||
// Try to reuse a connection from the connection pool. | // Try to reuse a connection from the connection pool. | ||||
$conn = array_pop(self::$pool); | $conn = array_pop(self::$pool); | ||||
} | } | ||||
if (!$conn) { | if (!$conn) { | ||||
// NOTE: Using the 'repository' database somewhat arbitrarily, mostly | $conn = self::newConnection(); | ||||
// because the first client of locks is the repository daemons. We must | |||||
// always use the same database for all locks, but don't access any | |||||
// tables so we could use any valid database. We could build a | |||||
// database-free connection instead, but that's kind of messy and we | |||||
// might forget about it in the future if we vertically partition the | |||||
// application. | |||||
$dao = new PhabricatorRepository(); | |||||
// NOTE: Using "force_new" to make sure each lock is on its own | |||||
// connection. | |||||
$conn = $dao->establishConnection('w', $force_new = true); | |||||
} | } | ||||
// See T13627. We must never hold more than one lock per connection, so | // See T13627. We must never hold more than one lock per connection, so | ||||
// make sure this connection has no existing locks. (Normally, we should | // make sure this connection has no existing locks. (Normally, we should | ||||
// only be able to get here if callers explicitly provide the same external | // only be able to get here if callers explicitly provide the same external | ||||
// connection to multiple locks.) | // connection to multiple locks.) | ||||
if ($conn->isHoldingAnyLock()) { | if ($conn->isHoldingAnyLock()) { | ||||
Show All 26 Lines | if (!$ok) { | ||||
// is still good. We're done with it, so add it to the pool, just as we | // is still good. We're done with it, so add it to the pool, just as we | ||||
// would if we were releasing the lock. | // would if we were releasing the lock. | ||||
// If we don't do this, we may establish a huge number of connections | // If we don't do this, we may establish a huge number of connections | ||||
// very rapidly if many workers try to acquire a lock at once. For | // very rapidly if many workers try to acquire a lock at once. For | ||||
// example, this can happen if there are a large number of webhook tasks | // example, this can happen if there are a large number of webhook tasks | ||||
// in the queue. | // in the queue. | ||||
// See T13627. If this is an external connection, don't put it into | |||||
// the shared connection pool. | |||||
if (!$this->externalConnection) { | |||||
self::$pool[] = $conn; | self::$pool[] = $conn; | ||||
} | |||||
throw id(new PhutilLockException($lock_name)) | throw id(new PhutilLockException($lock_name)) | ||||
->setHint($this->newHint($lock_name, $wait)); | ->setHint($this->newHint($lock_name, $wait)); | ||||
} | } | ||||
$conn->rememberLock($lock_name); | $conn->rememberLock($lock_name); | ||||
$this->conn = $conn; | $this->conn = $conn; | ||||
▲ Show 20 Lines • Show All 221 Lines • Show Last 20 Lines |