2012-12-25 15:09:51 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
2015-06-05 02:27:31 +02:00
|
|
|
*
|
|
|
|
* @task request Request Cache
|
2014-05-22 19:47:00 +02:00
|
|
|
* @task immutable Immutable Cache
|
|
|
|
* @task setup Setup Cache
|
2014-05-25 20:41:16 +02:00
|
|
|
* @task compress Compression
|
2012-12-25 15:09:51 +01:00
|
|
|
*/
|
2015-06-15 10:02:26 +02:00
|
|
|
final class PhabricatorCaches extends Phobject {
|
2012-12-25 15:09:51 +01:00
|
|
|
|
2015-06-05 02:27:31 +02:00
|
|
|
private static $requestCache;
|
|
|
|
|
2013-03-23 00:28:06 +01:00
|
|
|
public static function getNamespace() {
|
|
|
|
return PhabricatorEnv::getEnvConfig('phabricator.cache-namespace');
|
|
|
|
}
|
2012-12-25 15:09:51 +01:00
|
|
|
|
Implement a chunked, APC-backed graph cache
Summary:
Ref T2683. This is a refinement and simplification of D5257. In particular:
- D5257 only cached the commit chain, not path changes. This meant that we had to go issue an awkward query (which was slow on Facebook's install) periodically while reading the cache. This was reasonable locally but killed performance at FB scale. Instead, we can include path information in the cache. It is very rare that this is large except in Subversion, and we do not need to use this cache in Subversion. In other VCSes, the scale of this data is quite small (a handful of bytes per commit on average).
- D5257 required a large, slow offline computation step. This relies on D9044 to populate parent data so we can build the cache online at will, and let it expire with normal LRU/LFU/whatever semantics. We need this parent data for other reasons anyway.
- D5257 separated graph chunks per-repository. This change assumes we'll be able to pull stuff from APC most of the time and that the cost of switching chunks is not very large, so we can just build one chunk cache across all repositories. This allows the cache to be simpler.
- D5257 needed an offline cache, and used a unique cache structure. Since this one can be built online it can mostly use normal cache code.
- This also supports online appends to the cache.
- Finally, this has a timeout to guarantee a ceiling on the worst case: the worst case is something like a query for a file that has never existed, in a repository which receives exactly 1 commit every time other repositories receive 4095 commits, on a cold cache. If we hit cases like this we can bail after warming the cache up a bit and fall back to asking the VCS for an answer.
This cache isn't perfect, but I believe it will give us substantial gains in the average case. It can often satisfy "average-looking" queries in 4-8ms, and pathological-ish queries in 20ms on my machine; `hg` usually can't even start up in less than 100ms. The major thing that's attractive about this approach is that it does not require anything external or complicated, and will "just work", even producing reasonble improvements for users without APC.
In followups, I'll modify queries to use this cache and see if it holds up in more realistic workloads.
Test Plan:
- Used `bin/repository cache` to examine the behavior of this cache.
- Did some profiling/testing from the web UI using `debug.php`.
- This //appears// to provide a reasonable fast way to issue this query very quickly in the average case, without the various issues that plagued D5257.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley, jhurwitz
Maniphest Tasks: T2683
Differential Revision: https://secure.phabricator.com/D9045
2014-05-10 19:10:13 +02:00
|
|
|
private static function newStackFromCaches(array $caches) {
|
|
|
|
$caches = self::addNamespaceToCaches($caches);
|
|
|
|
$caches = self::addProfilerToCaches($caches);
|
|
|
|
return id(new PhutilKeyValueCacheStack())
|
|
|
|
->setCaches($caches);
|
|
|
|
}
|
2014-05-22 19:47:00 +02:00
|
|
|
|
2015-06-05 02:27:31 +02:00
|
|
|
/* -( Request Cache )------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a request cache stack.
|
|
|
|
*
|
|
|
|
* This cache stack is destroyed after each logical request. In particular,
|
|
|
|
* it is destroyed periodically by the daemons, while `static` caches are
|
|
|
|
* not.
|
|
|
|
*
|
|
|
|
* @return PhutilKeyValueCacheStack Request cache stack.
|
|
|
|
*/
|
|
|
|
public static function getRequestCache() {
|
|
|
|
if (!self::$requestCache) {
|
|
|
|
self::$requestCache = new PhutilInRequestKeyValueCache();
|
|
|
|
}
|
|
|
|
return self::$requestCache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy the request cache.
|
|
|
|
*
|
|
|
|
* This is called at the beginning of each logical request.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public static function destroyRequestCache() {
|
|
|
|
self::$requestCache = null;
|
|
|
|
}
|
|
|
|
|
2014-05-22 19:47:00 +02:00
|
|
|
|
2015-06-05 02:27:31 +02:00
|
|
|
/* -( Immutable Cache )---------------------------------------------------- */
|
2014-05-22 19:47:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets an immutable cache stack.
|
|
|
|
*
|
|
|
|
* This stack trades mutability away for improved performance. Normally, it is
|
|
|
|
* APC + DB.
|
|
|
|
*
|
|
|
|
* In the general case with multiple web frontends, this stack can not be
|
|
|
|
* cleared, so it is only appropriate for use if the value of a given key is
|
|
|
|
* permanent and immutable.
|
|
|
|
*
|
|
|
|
* @return PhutilKeyValueCacheStack Best immutable stack available.
|
|
|
|
* @task immutable
|
|
|
|
*/
|
|
|
|
public static function getImmutableCache() {
|
|
|
|
static $cache;
|
|
|
|
if (!$cache) {
|
|
|
|
$caches = self::buildImmutableCaches();
|
|
|
|
$cache = self::newStackFromCaches($caches);
|
|
|
|
}
|
|
|
|
return $cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build the immutable cache stack.
|
|
|
|
*
|
|
|
|
* @return list<PhutilKeyValueCache> List of caches.
|
|
|
|
* @task immutable
|
|
|
|
*/
|
|
|
|
private static function buildImmutableCaches() {
|
|
|
|
$caches = array();
|
|
|
|
|
2014-08-06 00:12:28 +02:00
|
|
|
$apc = new PhutilAPCKeyValueCache();
|
2014-05-22 19:47:00 +02:00
|
|
|
if ($apc->isAvailable()) {
|
|
|
|
$caches[] = $apc;
|
|
|
|
}
|
|
|
|
|
|
|
|
$caches[] = new PhabricatorKeyValueDatabaseCache();
|
|
|
|
|
|
|
|
return $caches;
|
|
|
|
}
|
Implement a chunked, APC-backed graph cache
Summary:
Ref T2683. This is a refinement and simplification of D5257. In particular:
- D5257 only cached the commit chain, not path changes. This meant that we had to go issue an awkward query (which was slow on Facebook's install) periodically while reading the cache. This was reasonable locally but killed performance at FB scale. Instead, we can include path information in the cache. It is very rare that this is large except in Subversion, and we do not need to use this cache in Subversion. In other VCSes, the scale of this data is quite small (a handful of bytes per commit on average).
- D5257 required a large, slow offline computation step. This relies on D9044 to populate parent data so we can build the cache online at will, and let it expire with normal LRU/LFU/whatever semantics. We need this parent data for other reasons anyway.
- D5257 separated graph chunks per-repository. This change assumes we'll be able to pull stuff from APC most of the time and that the cost of switching chunks is not very large, so we can just build one chunk cache across all repositories. This allows the cache to be simpler.
- D5257 needed an offline cache, and used a unique cache structure. Since this one can be built online it can mostly use normal cache code.
- This also supports online appends to the cache.
- Finally, this has a timeout to guarantee a ceiling on the worst case: the worst case is something like a query for a file that has never existed, in a repository which receives exactly 1 commit every time other repositories receive 4095 commits, on a cold cache. If we hit cases like this we can bail after warming the cache up a bit and fall back to asking the VCS for an answer.
This cache isn't perfect, but I believe it will give us substantial gains in the average case. It can often satisfy "average-looking" queries in 4-8ms, and pathological-ish queries in 20ms on my machine; `hg` usually can't even start up in less than 100ms. The major thing that's attractive about this approach is that it does not require anything external or complicated, and will "just work", even producing reasonble improvements for users without APC.
In followups, I'll modify queries to use this cache and see if it holds up in more realistic workloads.
Test Plan:
- Used `bin/repository cache` to examine the behavior of this cache.
- Did some profiling/testing from the web UI using `debug.php`.
- This //appears// to provide a reasonable fast way to issue this query very quickly in the average case, without the various issues that plagued D5257.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley, jhurwitz
Maniphest Tasks: T2683
Differential Revision: https://secure.phabricator.com/D9045
2014-05-10 19:10:13 +02:00
|
|
|
|
2016-10-21 16:23:58 +02:00
|
|
|
public static function getMutableCache() {
|
|
|
|
static $cache;
|
|
|
|
if (!$cache) {
|
|
|
|
$caches = self::buildMutableCaches();
|
|
|
|
$cache = self::newStackFromCaches($caches);
|
|
|
|
}
|
|
|
|
return $cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function buildMutableCaches() {
|
|
|
|
$caches = array();
|
|
|
|
|
|
|
|
$caches[] = new PhabricatorKeyValueDatabaseCache();
|
|
|
|
|
|
|
|
return $caches;
|
|
|
|
}
|
|
|
|
|
Implement a chunked, APC-backed graph cache
Summary:
Ref T2683. This is a refinement and simplification of D5257. In particular:
- D5257 only cached the commit chain, not path changes. This meant that we had to go issue an awkward query (which was slow on Facebook's install) periodically while reading the cache. This was reasonable locally but killed performance at FB scale. Instead, we can include path information in the cache. It is very rare that this is large except in Subversion, and we do not need to use this cache in Subversion. In other VCSes, the scale of this data is quite small (a handful of bytes per commit on average).
- D5257 required a large, slow offline computation step. This relies on D9044 to populate parent data so we can build the cache online at will, and let it expire with normal LRU/LFU/whatever semantics. We need this parent data for other reasons anyway.
- D5257 separated graph chunks per-repository. This change assumes we'll be able to pull stuff from APC most of the time and that the cost of switching chunks is not very large, so we can just build one chunk cache across all repositories. This allows the cache to be simpler.
- D5257 needed an offline cache, and used a unique cache structure. Since this one can be built online it can mostly use normal cache code.
- This also supports online appends to the cache.
- Finally, this has a timeout to guarantee a ceiling on the worst case: the worst case is something like a query for a file that has never existed, in a repository which receives exactly 1 commit every time other repositories receive 4095 commits, on a cold cache. If we hit cases like this we can bail after warming the cache up a bit and fall back to asking the VCS for an answer.
This cache isn't perfect, but I believe it will give us substantial gains in the average case. It can often satisfy "average-looking" queries in 4-8ms, and pathological-ish queries in 20ms on my machine; `hg` usually can't even start up in less than 100ms. The major thing that's attractive about this approach is that it does not require anything external or complicated, and will "just work", even producing reasonble improvements for users without APC.
In followups, I'll modify queries to use this cache and see if it holds up in more realistic workloads.
Test Plan:
- Used `bin/repository cache` to examine the behavior of this cache.
- Did some profiling/testing from the web UI using `debug.php`.
- This //appears// to provide a reasonable fast way to issue this query very quickly in the average case, without the various issues that plagued D5257.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley, jhurwitz
Maniphest Tasks: T2683
Differential Revision: https://secure.phabricator.com/D9045
2014-05-10 19:10:13 +02:00
|
|
|
|
2016-12-06 12:30:06 +01:00
|
|
|
/* -( Runtime Cache )------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a runtime cache stack.
|
|
|
|
*
|
|
|
|
* This stack is just APC. It's fast, it's effectively immutable, and it
|
|
|
|
* gets thrown away when the webserver restarts.
|
|
|
|
*
|
|
|
|
* This cache is suitable for deriving runtime caches, like a map of Conduit
|
|
|
|
* method names to provider classes.
|
|
|
|
*
|
|
|
|
* @return PhutilKeyValueCacheStack Best runtime stack available.
|
|
|
|
*/
|
|
|
|
public static function getRuntimeCache() {
|
|
|
|
static $cache;
|
|
|
|
if (!$cache) {
|
|
|
|
$caches = self::buildRuntimeCaches();
|
|
|
|
$cache = self::newStackFromCaches($caches);
|
|
|
|
}
|
|
|
|
return $cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static function buildRuntimeCaches() {
|
|
|
|
$caches = array();
|
|
|
|
|
|
|
|
$apc = new PhutilAPCKeyValueCache();
|
|
|
|
if ($apc->isAvailable()) {
|
|
|
|
$caches[] = $apc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $caches;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Implement a chunked, APC-backed graph cache
Summary:
Ref T2683. This is a refinement and simplification of D5257. In particular:
- D5257 only cached the commit chain, not path changes. This meant that we had to go issue an awkward query (which was slow on Facebook's install) periodically while reading the cache. This was reasonable locally but killed performance at FB scale. Instead, we can include path information in the cache. It is very rare that this is large except in Subversion, and we do not need to use this cache in Subversion. In other VCSes, the scale of this data is quite small (a handful of bytes per commit on average).
- D5257 required a large, slow offline computation step. This relies on D9044 to populate parent data so we can build the cache online at will, and let it expire with normal LRU/LFU/whatever semantics. We need this parent data for other reasons anyway.
- D5257 separated graph chunks per-repository. This change assumes we'll be able to pull stuff from APC most of the time and that the cost of switching chunks is not very large, so we can just build one chunk cache across all repositories. This allows the cache to be simpler.
- D5257 needed an offline cache, and used a unique cache structure. Since this one can be built online it can mostly use normal cache code.
- This also supports online appends to the cache.
- Finally, this has a timeout to guarantee a ceiling on the worst case: the worst case is something like a query for a file that has never existed, in a repository which receives exactly 1 commit every time other repositories receive 4095 commits, on a cold cache. If we hit cases like this we can bail after warming the cache up a bit and fall back to asking the VCS for an answer.
This cache isn't perfect, but I believe it will give us substantial gains in the average case. It can often satisfy "average-looking" queries in 4-8ms, and pathological-ish queries in 20ms on my machine; `hg` usually can't even start up in less than 100ms. The major thing that's attractive about this approach is that it does not require anything external or complicated, and will "just work", even producing reasonble improvements for users without APC.
In followups, I'll modify queries to use this cache and see if it holds up in more realistic workloads.
Test Plan:
- Used `bin/repository cache` to examine the behavior of this cache.
- Did some profiling/testing from the web UI using `debug.php`.
- This //appears// to provide a reasonable fast way to issue this query very quickly in the average case, without the various issues that plagued D5257.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley, jhurwitz
Maniphest Tasks: T2683
Differential Revision: https://secure.phabricator.com/D9045
2014-05-10 19:10:13 +02:00
|
|
|
/* -( Repository Graph Cache )--------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
public static function getRepositoryGraphL1Cache() {
|
|
|
|
static $cache;
|
|
|
|
if (!$cache) {
|
|
|
|
$caches = self::buildRepositoryGraphL1Caches();
|
|
|
|
$cache = self::newStackFromCaches($caches);
|
|
|
|
}
|
|
|
|
return $cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function buildRepositoryGraphL1Caches() {
|
|
|
|
$caches = array();
|
|
|
|
|
2014-08-06 00:12:28 +02:00
|
|
|
$request = new PhutilInRequestKeyValueCache();
|
2014-05-12 21:34:04 +02:00
|
|
|
$request->setLimit(32);
|
|
|
|
$caches[] = $request;
|
|
|
|
|
2014-08-06 00:12:28 +02:00
|
|
|
$apc = new PhutilAPCKeyValueCache();
|
Implement a chunked, APC-backed graph cache
Summary:
Ref T2683. This is a refinement and simplification of D5257. In particular:
- D5257 only cached the commit chain, not path changes. This meant that we had to go issue an awkward query (which was slow on Facebook's install) periodically while reading the cache. This was reasonable locally but killed performance at FB scale. Instead, we can include path information in the cache. It is very rare that this is large except in Subversion, and we do not need to use this cache in Subversion. In other VCSes, the scale of this data is quite small (a handful of bytes per commit on average).
- D5257 required a large, slow offline computation step. This relies on D9044 to populate parent data so we can build the cache online at will, and let it expire with normal LRU/LFU/whatever semantics. We need this parent data for other reasons anyway.
- D5257 separated graph chunks per-repository. This change assumes we'll be able to pull stuff from APC most of the time and that the cost of switching chunks is not very large, so we can just build one chunk cache across all repositories. This allows the cache to be simpler.
- D5257 needed an offline cache, and used a unique cache structure. Since this one can be built online it can mostly use normal cache code.
- This also supports online appends to the cache.
- Finally, this has a timeout to guarantee a ceiling on the worst case: the worst case is something like a query for a file that has never existed, in a repository which receives exactly 1 commit every time other repositories receive 4095 commits, on a cold cache. If we hit cases like this we can bail after warming the cache up a bit and fall back to asking the VCS for an answer.
This cache isn't perfect, but I believe it will give us substantial gains in the average case. It can often satisfy "average-looking" queries in 4-8ms, and pathological-ish queries in 20ms on my machine; `hg` usually can't even start up in less than 100ms. The major thing that's attractive about this approach is that it does not require anything external or complicated, and will "just work", even producing reasonble improvements for users without APC.
In followups, I'll modify queries to use this cache and see if it holds up in more realistic workloads.
Test Plan:
- Used `bin/repository cache` to examine the behavior of this cache.
- Did some profiling/testing from the web UI using `debug.php`.
- This //appears// to provide a reasonable fast way to issue this query very quickly in the average case, without the various issues that plagued D5257.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley, jhurwitz
Maniphest Tasks: T2683
Differential Revision: https://secure.phabricator.com/D9045
2014-05-10 19:10:13 +02:00
|
|
|
if ($apc->isAvailable()) {
|
|
|
|
$caches[] = $apc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $caches;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function getRepositoryGraphL2Cache() {
|
|
|
|
static $cache;
|
|
|
|
if (!$cache) {
|
|
|
|
$caches = self::buildRepositoryGraphL2Caches();
|
|
|
|
$cache = self::newStackFromCaches($caches);
|
|
|
|
}
|
|
|
|
return $cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function buildRepositoryGraphL2Caches() {
|
|
|
|
$caches = array();
|
|
|
|
$caches[] = new PhabricatorKeyValueDatabaseCache();
|
|
|
|
return $caches;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-12 16:31:53 +02:00
|
|
|
/* -( Server State Cache )------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Highly specialized cache for storing server process state.
|
|
|
|
*
|
|
|
|
* We use this cache to track initial steps in the setup phase, before
|
|
|
|
* configuration is loaded.
|
|
|
|
*
|
|
|
|
* This cache does NOT use the cache namespace (it must be accessed before
|
|
|
|
* we build configuration), and is global across all instances on the host.
|
|
|
|
*
|
|
|
|
* @return PhutilKeyValueCacheStack Best available server state cache stack.
|
|
|
|
* @task setup
|
|
|
|
*/
|
|
|
|
public static function getServerStateCache() {
|
|
|
|
static $cache;
|
|
|
|
if (!$cache) {
|
|
|
|
$caches = self::buildSetupCaches('phabricator-server');
|
|
|
|
|
|
|
|
// NOTE: We are NOT adding a cache namespace here! This cache is shared
|
|
|
|
// across all instances on the host.
|
|
|
|
|
|
|
|
$caches = self::addProfilerToCaches($caches);
|
|
|
|
$cache = id(new PhutilKeyValueCacheStack())
|
|
|
|
->setCaches($caches);
|
|
|
|
|
|
|
|
}
|
|
|
|
return $cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-12-25 15:09:51 +01:00
|
|
|
/* -( Setup Cache )-------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Highly specialized cache for performing setup checks. We use this cache
|
2013-01-22 19:32:26 +01:00
|
|
|
* to determine if we need to run expensive setup checks when the page
|
2012-12-25 15:09:51 +01:00
|
|
|
* loads. Without it, we would need to run these checks every time.
|
|
|
|
*
|
|
|
|
* Normally, this cache is just APC. In the absence of APC, this cache
|
|
|
|
* degrades into a slow, quirky on-disk cache.
|
|
|
|
*
|
|
|
|
* NOTE: Do not use this cache for anything else! It is not a general-purpose
|
|
|
|
* cache!
|
|
|
|
*
|
|
|
|
* @return PhutilKeyValueCacheStack Most qualified available cache stack.
|
|
|
|
* @task setup
|
|
|
|
*/
|
|
|
|
public static function getSetupCache() {
|
|
|
|
static $cache;
|
|
|
|
if (!$cache) {
|
2016-09-12 16:31:53 +02:00
|
|
|
$caches = self::buildSetupCaches('phabricator-setup');
|
Implement a chunked, APC-backed graph cache
Summary:
Ref T2683. This is a refinement and simplification of D5257. In particular:
- D5257 only cached the commit chain, not path changes. This meant that we had to go issue an awkward query (which was slow on Facebook's install) periodically while reading the cache. This was reasonable locally but killed performance at FB scale. Instead, we can include path information in the cache. It is very rare that this is large except in Subversion, and we do not need to use this cache in Subversion. In other VCSes, the scale of this data is quite small (a handful of bytes per commit on average).
- D5257 required a large, slow offline computation step. This relies on D9044 to populate parent data so we can build the cache online at will, and let it expire with normal LRU/LFU/whatever semantics. We need this parent data for other reasons anyway.
- D5257 separated graph chunks per-repository. This change assumes we'll be able to pull stuff from APC most of the time and that the cost of switching chunks is not very large, so we can just build one chunk cache across all repositories. This allows the cache to be simpler.
- D5257 needed an offline cache, and used a unique cache structure. Since this one can be built online it can mostly use normal cache code.
- This also supports online appends to the cache.
- Finally, this has a timeout to guarantee a ceiling on the worst case: the worst case is something like a query for a file that has never existed, in a repository which receives exactly 1 commit every time other repositories receive 4095 commits, on a cold cache. If we hit cases like this we can bail after warming the cache up a bit and fall back to asking the VCS for an answer.
This cache isn't perfect, but I believe it will give us substantial gains in the average case. It can often satisfy "average-looking" queries in 4-8ms, and pathological-ish queries in 20ms on my machine; `hg` usually can't even start up in less than 100ms. The major thing that's attractive about this approach is that it does not require anything external or complicated, and will "just work", even producing reasonble improvements for users without APC.
In followups, I'll modify queries to use this cache and see if it holds up in more realistic workloads.
Test Plan:
- Used `bin/repository cache` to examine the behavior of this cache.
- Did some profiling/testing from the web UI using `debug.php`.
- This //appears// to provide a reasonable fast way to issue this query very quickly in the average case, without the various issues that plagued D5257.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley, jhurwitz
Maniphest Tasks: T2683
Differential Revision: https://secure.phabricator.com/D9045
2014-05-10 19:10:13 +02:00
|
|
|
$cache = self::newStackFromCaches($caches);
|
2012-12-25 15:09:51 +01:00
|
|
|
}
|
|
|
|
return $cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task setup
|
|
|
|
*/
|
2016-09-12 16:31:53 +02:00
|
|
|
private static function buildSetupCaches($cache_name) {
|
Automatically sever databases after prolonged unreachability
Summary:
Ref T4571. When a database goes down briefly, we fall back to replicas.
However, this fallback is slow (not good for users) and keeps sending a lot of traffic to the master (might be bad if the root cause is load-related).
Keep track of recent connections and fully degrade into "severed" mode if we see a sequence of failures over a reasonable period of time. In this mode, we send much less traffic to the master (faster for users; less load for the database).
We do send a little bit of traffic still, and if the master recovers we'll recover back into normal mode seeing several connections in a row succeed.
This is similar to what most load balancers do when pulling web servers in and out of pools.
For now, the specific numbers are:
- We do at most one health check every 3 seconds.
- If 5 checks in a row fail or succeed, we sever or un-sever the database (so it takes about 15 seconds to switch modes).
- If the database is currently marked unhealthy, we reduce timeouts and retries when connecting to it.
Test Plan:
- Configured a bad `master`.
- Browsed around for a bit, initially saw "unrechable master" errors.
- After about 15 seconds, saw "major interruption" errors instead.
- Fixed the config for `master`.
- Browsed around for a while longer.
- After about 15 seconds, things recovered.
- Used "Cluster Databases" console to keep an eye on health checks: it now shows how many recent health checks were good:
{F1213397}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T4571
Differential Revision: https://secure.phabricator.com/D15677
2016-04-10 23:18:09 +02:00
|
|
|
// If this is the CLI, just build a setup cache.
|
|
|
|
if (php_sapi_name() == 'cli') {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
2012-12-25 15:09:51 +01:00
|
|
|
// In most cases, we should have APC. This is an ideal cache for our
|
|
|
|
// purposes -- it's fast and empties on server restart.
|
2014-08-06 00:12:28 +02:00
|
|
|
$apc = new PhutilAPCKeyValueCache();
|
2012-12-25 15:09:51 +01:00
|
|
|
if ($apc->isAvailable()) {
|
|
|
|
return array($apc);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't have APC, build a poor approximation on disk. This is still
|
|
|
|
// much better than nothing; some setup steps are quite slow.
|
2016-09-12 16:31:53 +02:00
|
|
|
$disk_path = self::getSetupCacheDiskCachePath($cache_name);
|
2012-12-25 15:09:51 +01:00
|
|
|
if ($disk_path) {
|
2014-08-06 00:12:28 +02:00
|
|
|
$disk = new PhutilOnDiskKeyValueCache();
|
2012-12-25 15:09:51 +01:00
|
|
|
$disk->setCacheFile($disk_path);
|
2013-04-13 16:09:32 +02:00
|
|
|
$disk->setWait(0.1);
|
2012-12-25 15:09:51 +01:00
|
|
|
if ($disk->isAvailable()) {
|
|
|
|
return array($disk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task setup
|
|
|
|
*/
|
2016-09-12 16:31:53 +02:00
|
|
|
private static function getSetupCacheDiskCachePath($name) {
|
2012-12-25 15:09:51 +01:00
|
|
|
// The difficulty here is in choosing a path which will change on server
|
|
|
|
// restart (we MUST have this property), but as rarely as possible
|
|
|
|
// otherwise (we desire this property to give the cache the best hit rate
|
|
|
|
// we can).
|
|
|
|
|
Make disk-based setup caches more correct (but slower)
Summary:
Fixes T9599. When APC/APCu are not available, we fall back to a disk-based cache.
We try to share this cache across webserver processes like APC/APCu would be shared in order to improve performance, but are just kind of guessing how to coordinate it. From T9599, it sounds like we don't always get this right in every configuration.
Since this is complicated and error prone, just stop trying to do this. This cache has bad performance anyway (no production install should be using it), and we have much better APC/APCu setup instructions now than we did when I wrote this. Just using the PID is simpler and more correct.
Test Plan:
- Artificially disabled APC.
- Reloaded the page, saw all the setup stuff run.
- Reloaded the page, saw no setup stuff run (i.e., cache was hit).
- Restarted the webserver.
- Reloaded the page, saw all the setup stuff run.
- Reloaded again, got a cache hit.
I don't really know how to reproduce the exact problem with the parent PID not working, but from T9599 it sounds like this fixed the issue and from my test plan we still appear to get correct behavior in the standard/common case.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T9599
Differential Revision: https://secure.phabricator.com/D14302
2015-10-19 20:14:46 +02:00
|
|
|
// Unfortunately, we don't have a very good strategy for minimizing the
|
|
|
|
// churn rate of the cache. We previously tried to use the parent process
|
|
|
|
// PID in some cases, but this was not reliable. See T9599 for one case of
|
|
|
|
// this.
|
2012-12-25 15:09:51 +01:00
|
|
|
|
|
|
|
$pid_basis = getmypid();
|
|
|
|
|
|
|
|
// If possible, we also want to know when the process launched, so we can
|
|
|
|
// drop the cache if a process restarts but gets the same PID an earlier
|
|
|
|
// process had. "/proc" is not available everywhere (e.g., not on OSX), but
|
|
|
|
// check if we have it.
|
|
|
|
$epoch_basis = null;
|
|
|
|
$stat = @stat("/proc/{$pid_basis}");
|
|
|
|
if ($stat !== false) {
|
|
|
|
$epoch_basis = $stat['ctime'];
|
|
|
|
}
|
|
|
|
|
|
|
|
$tmp_dir = sys_get_temp_dir();
|
|
|
|
|
2016-09-12 16:31:53 +02:00
|
|
|
$tmp_path = $tmp_dir.DIRECTORY_SEPARATOR.$name;
|
2012-12-25 15:09:51 +01:00
|
|
|
if (!file_exists($tmp_path)) {
|
|
|
|
@mkdir($tmp_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
$is_ok = self::testTemporaryDirectory($tmp_path);
|
|
|
|
if (!$is_ok) {
|
|
|
|
$tmp_path = $tmp_dir;
|
|
|
|
$is_ok = self::testTemporaryDirectory($tmp_path);
|
|
|
|
if (!$is_ok) {
|
|
|
|
// We can't find anywhere to write the cache, so just bail.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$tmp_name = 'setup-'.$pid_basis;
|
|
|
|
if ($epoch_basis) {
|
|
|
|
$tmp_name .= '.'.$epoch_basis;
|
|
|
|
}
|
|
|
|
$tmp_name .= '.cache';
|
|
|
|
|
|
|
|
return $tmp_path.DIRECTORY_SEPARATOR.$tmp_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @task setup
|
|
|
|
*/
|
|
|
|
private static function testTemporaryDirectory($dir) {
|
|
|
|
if (!@file_exists($dir)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!@is_dir($dir)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!@is_writable($dir)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-12-31 02:04:38 +01:00
|
|
|
private static function addProfilerToCaches(array $caches) {
|
|
|
|
foreach ($caches as $key => $cache) {
|
|
|
|
$pcache = new PhutilKeyValueCacheProfiler($cache);
|
|
|
|
$pcache->setProfiler(PhutilServiceProfiler::getInstance());
|
|
|
|
$caches[$key] = $pcache;
|
|
|
|
}
|
|
|
|
return $caches;
|
|
|
|
}
|
|
|
|
|
2013-04-13 16:09:32 +02:00
|
|
|
private static function addNamespaceToCaches(array $caches) {
|
2015-05-13 22:50:28 +02:00
|
|
|
$namespace = self::getNamespace();
|
2013-04-13 16:09:32 +02:00
|
|
|
if (!$namespace) {
|
|
|
|
return $caches;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($caches as $key => $cache) {
|
|
|
|
$ncache = new PhutilKeyValueCacheNamespace($cache);
|
|
|
|
$ncache->setNamespace($namespace);
|
|
|
|
$caches[$key] = $ncache;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $caches;
|
|
|
|
}
|
|
|
|
|
2014-05-25 20:41:16 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Deflate a value, if deflation is available and has an impact.
|
|
|
|
*
|
|
|
|
* If the value is larger than 1KB, we have `gzdeflate()`, we successfully
|
|
|
|
* can deflate it, and it benefits from deflation, we deflate it. Otherwise
|
|
|
|
* we leave it as-is.
|
|
|
|
*
|
|
|
|
* Data can later be inflated with @{method:inflateData}.
|
|
|
|
*
|
|
|
|
* @param string String to attempt to deflate.
|
|
|
|
* @return string|null Deflated string, or null if it was not deflated.
|
|
|
|
* @task compress
|
|
|
|
*/
|
|
|
|
public static function maybeDeflateData($value) {
|
|
|
|
$len = strlen($value);
|
|
|
|
if ($len <= 1024) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!function_exists('gzdeflate')) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$deflated = gzdeflate($value);
|
|
|
|
if ($deflated === false) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$deflated_len = strlen($deflated);
|
|
|
|
if ($deflated_len >= ($len / 2)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $deflated;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inflate data previously deflated by @{method:maybeDeflateData}.
|
|
|
|
*
|
|
|
|
* @param string Deflated data, from @{method:maybeDeflateData}.
|
|
|
|
* @return string Original, uncompressed data.
|
|
|
|
* @task compress
|
|
|
|
*/
|
|
|
|
public static function inflateData($value) {
|
|
|
|
if (!function_exists('gzinflate')) {
|
|
|
|
throw new Exception(
|
2015-05-22 09:27:56 +02:00
|
|
|
pht(
|
|
|
|
'%s is not available; unable to read deflated data!',
|
|
|
|
'gzinflate()'));
|
2014-05-25 20:41:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$value = gzinflate($value);
|
|
|
|
if ($value === false) {
|
|
|
|
throw new Exception(pht('Failed to inflate data!'));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-25 15:09:51 +01:00
|
|
|
}
|