Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/repository/engine/PhabricatorRepositoryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryEngine.php
index 244ed0cd45..fed5b09521 100644
--- a/src/applications/repository/engine/PhabricatorRepositoryEngine.php
+++ b/src/applications/repository/engine/PhabricatorRepositoryEngine.php
@@ -1,204 +1,203 @@
<?php
/**
* @task config Configuring Repository Engines
* @task internal Internals
*/
abstract class PhabricatorRepositoryEngine extends Phobject {
private $repository;
private $verbose;
/**
* @task config
*/
public function setRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
/**
* @task config
*/
protected function getRepository() {
if ($this->repository === null) {
throw new PhutilInvalidStateException('setRepository');
}
return $this->repository;
}
/**
* @task config
*/
public function setVerbose($verbose) {
$this->verbose = $verbose;
return $this;
}
/**
* @task config
*/
public function getVerbose() {
return $this->verbose;
}
public function getViewer() {
return PhabricatorUser::getOmnipotentUser();
}
protected function newRepositoryLock(
PhabricatorRepository $repository,
$lock_key,
$lock_device_only) {
- $lock_parts = array();
- $lock_parts[] = $lock_key;
- $lock_parts[] = $repository->getID();
+ $lock_parts = array(
+ 'repositoryPHID' => $repository->getPHID(),
+ );
if ($lock_device_only) {
$device = AlmanacKeys::getLiveDevice();
if ($device) {
- $lock_parts[] = $device->getID();
+ $lock_parts['devicePHID'] = $device->getPHID();
}
}
- $lock_name = implode(':', $lock_parts);
- return PhabricatorGlobalLock::newLock($lock_name);
+ return PhabricatorGlobalLock::newLock($lock_key, $lock_parts);
}
/**
* Verify that the "origin" remote exists, and points at the correct URI.
*
* This catches or corrects some types of misconfiguration, and also repairs
* an issue where Git 1.7.1 does not create an "origin" for `--bare` clones.
* See T4041.
*
* @param PhabricatorRepository Repository to verify.
* @return void
*/
protected function verifyGitOrigin(PhabricatorRepository $repository) {
try {
list($remotes) = $repository->execxLocalCommand(
'remote show -n origin');
} catch (CommandException $ex) {
throw new PhutilProxyException(
pht(
'Expected to find a Git working copy at path "%s", but the '.
'path exists and is not a valid working copy. If you remove '.
'this directory, the daemons will automatically recreate it '.
'correctly. Phabricator will not destroy the directory for you '.
'because it can not be sure that it does not contain important '.
'data.',
$repository->getLocalPath()),
$ex);
}
$matches = null;
if (!preg_match('/^\s*Fetch URL:\s*(.*?)\s*$/m', $remotes, $matches)) {
throw new Exception(
pht(
"Expected '%s' in '%s'.",
'Fetch URL',
'git remote show -n origin'));
}
$remote_uri = $matches[1];
$expect_remote = $repository->getRemoteURI();
if ($remote_uri == 'origin') {
// If a remote does not exist, git pretends it does and prints out a
// made up remote where the URI is the same as the remote name. This is
// definitely not correct.
// Possibly, we should use `git remote --verbose` instead, which does not
// suffer from this problem (but is a little more complicated to parse).
$valid = false;
$exists = false;
} else {
$normal_type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
$remote_normal = id(new PhabricatorRepositoryURINormalizer(
$normal_type_git,
$remote_uri))->getNormalizedPath();
$expect_normal = id(new PhabricatorRepositoryURINormalizer(
$normal_type_git,
$expect_remote))->getNormalizedPath();
$valid = ($remote_normal == $expect_normal);
$exists = true;
}
// These URIs may have plaintext HTTP credentials. If they do, censor
// them for display. See T12945.
$display_remote = phutil_censor_credentials($remote_uri);
$display_expect = phutil_censor_credentials($expect_remote);
if (!$valid) {
if (!$exists) {
// If there's no "origin" remote, just create it regardless of how
// strongly we own the working copy. There is almost no conceivable
// scenario in which this could do damage.
$this->log(
pht(
'Remote "origin" does not exist. Creating "origin", with '.
'URI "%s".',
$expect_remote));
$repository->execxLocalCommand(
'remote add origin %P',
$repository->getRemoteURIEnvelope());
// NOTE: This doesn't fetch the origin (it just creates it), so we won't
// know about origin branches until the next "pull" happens. That's fine
// for our purposes, but might impact things in the future.
} else {
if ($repository->canDestroyWorkingCopy()) {
// Bad remote, but we can try to repair it.
$this->log(
pht(
'Remote "origin" exists, but is pointed at the wrong URI, "%s". '.
'Resetting origin URI to "%s.',
$remote_uri,
$expect_remote));
$repository->execxLocalCommand(
'remote set-url origin %P',
$repository->getRemoteURIEnvelope());
} else {
// Bad remote and we aren't comfortable repairing it.
$message = pht(
'Working copy at "%s" has a mismatched origin URI, "%s". '.
'The expected origin URI is "%s". Fix your configuration, or '.
'set the remote URI correctly. To avoid breaking anything, '.
'Phabricator will not automatically fix this.',
$repository->getLocalPath(),
$display_remote,
$display_expect);
throw new Exception($message);
}
}
}
}
/**
* @task internal
*/
protected function log($pattern /* ... */) {
if ($this->getVerbose()) {
$console = PhutilConsole::getConsole();
$argv = func_get_args();
array_unshift($argv, "%s\n");
call_user_func_array(array($console, 'writeOut'), $argv);
}
return $this;
}
}
diff --git a/src/infrastructure/util/PhabricatorGlobalLock.php b/src/infrastructure/util/PhabricatorGlobalLock.php
index 26e11d1899..8aecb40873 100644
--- a/src/infrastructure/util/PhabricatorGlobalLock.php
+++ b/src/infrastructure/util/PhabricatorGlobalLock.php
@@ -1,164 +1,180 @@
<?php
/**
* Global, MySQL-backed lock. This is a high-reliability, low-performance
* global lock.
*
* The lock is maintained by using GET_LOCK() in MySQL, and automatically
* released when the connection terminates. Thus, this lock can safely be used
* to control access to shared resources without implementing any sort of
* timeout or override logic: the lock can't normally be stuck in a locked state
* with no process actually holding the lock.
*
* However, acquiring the lock is moderately expensive (several network
* roundtrips). This makes it unsuitable for tasks where lock performance is
* important.
*
* $lock = PhabricatorGlobalLock::newLock('example');
* $lock->lock();
* do_contentious_things();
* $lock->unlock();
*
* NOTE: This lock is not completely global; it is namespaced to the active
* storage namespace so that unit tests running in separate table namespaces
* are isolated from one another.
*
* @task construct Constructing Locks
* @task impl Implementation
*/
final class PhabricatorGlobalLock extends PhutilLock {
+ private $parameters;
private $conn;
private $isExternalConnection = false;
private static $pool = array();
/* -( Constructing Locks )------------------------------------------------- */
- public static function newLock($name) {
+ public static function newLock($name, $parameters = array()) {
$namespace = PhabricatorLiskDAO::getStorageNamespace();
$namespace = PhabricatorHash::digestToLength($namespace, 20);
- $full_name = 'ph:'.$namespace.':'.$name;
-
- $length_limit = 64;
- if (strlen($full_name) > $length_limit) {
- throw new Exception(
- pht(
- 'Lock name "%s" is too long (full lock name is "%s"). The '.
- 'full lock name must not be longer than %s bytes.',
- $name,
- $full_name,
- new PhutilNumber($length_limit)));
+ $parts = array();
+ ksort($parameters);
+ foreach ($parameters as $key => $parameter) {
+ if (!preg_match('/^[a-zA-Z0-9]+\z/', $key)) {
+ throw new Exception(
+ pht(
+ 'Lock parameter key "%s" must be alphanumeric.',
+ $key));
+ }
+
+ if (!is_scalar($parameter) && !is_null($parameter)) {
+ throw new Exception(
+ pht(
+ 'Lock parameter for key "%s" must be a scalar.',
+ $key));
+ }
+
+ $value = phutil_json_encode($parameter);
+ $parts[] = "{$key}={$value}";
}
+ $parts = implode(', ', $parts);
+ $local = "{$name}({$parts})";
+ $local = PhabricatorHash::digestToLength($local, 20);
+
+ $full_name = "ph:{$namespace}:{$local}";
$lock = self::getLock($full_name);
if (!$lock) {
$lock = new PhabricatorGlobalLock($full_name);
self::registerLock($lock);
+
+ $lock->parameters = $parameters;
}
return $lock;
}
/**
* Use a specific database connection for locking.
*
* By default, `PhabricatorGlobalLock` will lock on the "repository" database
* (somewhat arbitrarily). In most cases this is fine, but this method can
* be used to lock on a specific connection.
*
* @param AphrontDatabaseConnection
* @return this
*/
public function useSpecificConnection(AphrontDatabaseConnection $conn) {
$this->conn = $conn;
$this->isExternalConnection = true;
return $this;
}
/* -( Implementation )----------------------------------------------------- */
protected function doLock($wait) {
$conn = $this->conn;
if (!$conn) {
// Try to reuse a connection from the connection pool.
$conn = array_pop(self::$pool);
}
if (!$conn) {
// NOTE: Using the 'repository' database somewhat arbitrarily, mostly
// 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);
}
// NOTE: Since MySQL will disconnect us if we're idle for too long, we set
// the wait_timeout to an enormous value, to allow us to hold the
// connection open indefinitely (or, at least, for 24 days).
$max_allowed_timeout = 2147483;
queryfx($conn, 'SET wait_timeout = %d', $max_allowed_timeout);
$lock_name = $this->getName();
$result = queryfx_one(
$conn,
'SELECT GET_LOCK(%s, %f)',
$lock_name,
$wait);
$ok = head($result);
if (!$ok) {
throw new PhutilLockException($lock_name);
}
$conn->rememberLock($lock_name);
$this->conn = $conn;
}
protected function doUnlock() {
$lock_name = $this->getName();
$conn = $this->conn;
try {
$result = queryfx_one(
$conn,
'SELECT RELEASE_LOCK(%s)',
$lock_name);
$conn->forgetLock($lock_name);
} catch (Exception $ex) {
$result = array(null);
}
$ok = head($result);
if (!$ok) {
// TODO: We could throw here, but then this lock doesn't get marked
// unlocked and we throw again later when exiting. It also doesn't
// particularly matter for any current applications. For now, just
// swallow the error.
}
$this->conn = null;
$this->isExternalConnection = false;
if (!$this->isExternalConnection) {
$conn->close();
self::$pool[] = $conn;
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Nov 6, 2:10 AM (19 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
321388
Default Alt Text
(12 KB)

Event Timeline