mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-30 01:10:58 +01:00
Merge GC daemon into Trigger daemon
Summary: Fixes T7352. This reduces the memory footprint for instances by combining these two similar daemons into one daemon which handles the responsibilities of both. The fit isn't 100% perfect here but it's pretty close, and the GC daemon is fairly trivial. Test Plan: - Adjusted all the numbers to small numbers (5 second sleep, 120 second GC length). - Added a ton of logging. - Started trigger daemon. - Saw it run a GC cycle. - Saw it reschedule another cycle after 120 seconds (adjusted down from 4 hours). - Reverted all the logging/small numbers. - Ran `bin/phd start`, saw stable trigger daemon running. - Grepped for removed daemon class name. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T7352 Differential Revision: https://secure.phabricator.com/D11872
This commit is contained in:
parent
af303f458b
commit
a3518e19a5
5 changed files with 104 additions and 47 deletions
|
@ -1838,7 +1838,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php',
|
'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php',
|
||||||
'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php',
|
'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php',
|
||||||
'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php',
|
'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php',
|
||||||
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollectorDaemon.php',
|
|
||||||
'PhabricatorGestureUIExample' => 'applications/uiexample/examples/PhabricatorGestureUIExample.php',
|
'PhabricatorGestureUIExample' => 'applications/uiexample/examples/PhabricatorGestureUIExample.php',
|
||||||
'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
|
'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
|
||||||
'PhabricatorGitHubAuthProvider' => 'applications/auth/provider/PhabricatorGitHubAuthProvider.php',
|
'PhabricatorGitHubAuthProvider' => 'applications/auth/provider/PhabricatorGitHubAuthProvider.php',
|
||||||
|
@ -5122,7 +5121,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck',
|
'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck',
|
||||||
'PhabricatorGarbageCollector' => 'Phobject',
|
'PhabricatorGarbageCollector' => 'Phobject',
|
||||||
'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
|
|
||||||
'PhabricatorGestureUIExample' => 'PhabricatorUIExample',
|
'PhabricatorGestureUIExample' => 'PhabricatorUIExample',
|
||||||
'PhabricatorGitGraphStream' => 'PhabricatorRepositoryGraphStream',
|
'PhabricatorGitGraphStream' => 'PhabricatorRepositoryGraphStream',
|
||||||
'PhabricatorGitHubAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
'PhabricatorGitHubAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||||
|
|
|
@ -337,9 +337,6 @@ abstract class PhabricatorDaemonManagementWorkflow
|
||||||
array(
|
array(
|
||||||
'class' => 'PhabricatorRepositoryPullLocalDaemon',
|
'class' => 'PhabricatorRepositoryPullLocalDaemon',
|
||||||
),
|
),
|
||||||
array(
|
|
||||||
'class' => 'PhabricatorGarbageCollectorDaemon',
|
|
||||||
),
|
|
||||||
array(
|
array(
|
||||||
'class' => 'PhabricatorTriggerDaemon',
|
'class' => 'PhabricatorTriggerDaemon',
|
||||||
),
|
),
|
||||||
|
|
|
@ -70,7 +70,8 @@ You can get a list of launchable daemons with **phd list**:
|
||||||
- **PhabricatorTaskmasterDaemon** performs work from a task queue;
|
- **PhabricatorTaskmasterDaemon** performs work from a task queue;
|
||||||
- **PhabricatorRepositoryPullLocalDaemon** daemons track repositories, for
|
- **PhabricatorRepositoryPullLocalDaemon** daemons track repositories, for
|
||||||
more information see @{article:Diffusion User Guide}; and
|
more information see @{article:Diffusion User Guide}; and
|
||||||
- **PhabricatorGarbageCollectorDaemon** cleans up old logs and caches.
|
- **PhabricatorTriggerDaemon** schedules event triggers and cleans up old
|
||||||
|
logs and caches.
|
||||||
|
|
||||||
= Debugging and Tuning =
|
= Debugging and Tuning =
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ daemons launch, and split daemons across machines like this:
|
||||||
- `PhabricatorRepositoryPullLocalDaemon`: Run one copy on any machine.
|
- `PhabricatorRepositoryPullLocalDaemon`: Run one copy on any machine.
|
||||||
On each web frontend which is not running a normal copy, run a copy
|
On each web frontend which is not running a normal copy, run a copy
|
||||||
with the `--no-discovery` flag.
|
with the `--no-discovery` flag.
|
||||||
- `PhabricatorGarbageCollectorDaemon`: Run one copy on any machine.
|
- `PhabricatorTriggerDaemon`: Run one copy on any machine.
|
||||||
- `PhabricatorTaskmasterDaemon`: Run as many copies as you need to keep
|
- `PhabricatorTaskmasterDaemon`: Run as many copies as you need to keep
|
||||||
tasks from backing up. You can run them all on one machine or split them
|
tasks from backing up. You can run them all on one machine or split them
|
||||||
across machines.
|
across machines.
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collects old logs and caches to reduce the amount of data stored in the
|
|
||||||
* database.
|
|
||||||
*/
|
|
||||||
final class PhabricatorGarbageCollectorDaemon extends PhabricatorDaemon {
|
|
||||||
|
|
||||||
protected function run() {
|
|
||||||
$collectors = id(new PhutilSymbolLoader())
|
|
||||||
->setAncestorClass('PhabricatorGarbageCollector')
|
|
||||||
->loadObjects();
|
|
||||||
|
|
||||||
do {
|
|
||||||
foreach ($collectors as $name => $collector) {
|
|
||||||
$more_garbage = false;
|
|
||||||
do {
|
|
||||||
if ($more_garbage) {
|
|
||||||
$this->log(pht('Collecting more garbage with "%s".', $name));
|
|
||||||
} else {
|
|
||||||
$this->log(pht('Collecting garbage with "%s".', $name));
|
|
||||||
}
|
|
||||||
|
|
||||||
$more_garbage = $collector->collectGarbage();
|
|
||||||
$this->stillWorking();
|
|
||||||
} while ($more_garbage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We made it to the end of the run cycle of every GC, so we're more or
|
|
||||||
// less caught up. Ease off the GC loop so we don't keep doing table
|
|
||||||
// scans just to delete a handful of rows; wake up in a few hours.
|
|
||||||
$this->log(pht('All caught up, waiting for more garbage.'));
|
|
||||||
$this->sleep(4 * (60 * 60));
|
|
||||||
} while (!$this->shouldExit());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule and execute event triggers, which run code at specific times.
|
* Schedule and execute event triggers, which run code at specific times.
|
||||||
|
*
|
||||||
|
* Also performs garbage collection of old logs, caches, etc.
|
||||||
|
*
|
||||||
|
* @task garbage Garbage Collection
|
||||||
*/
|
*/
|
||||||
final class PhabricatorTriggerDaemon
|
final class PhabricatorTriggerDaemon
|
||||||
extends PhabricatorDaemon {
|
extends PhabricatorDaemon {
|
||||||
|
@ -9,6 +13,9 @@ final class PhabricatorTriggerDaemon
|
||||||
const COUNTER_VERSION = 'trigger.version';
|
const COUNTER_VERSION = 'trigger.version';
|
||||||
const COUNTER_CURSOR = 'trigger.cursor';
|
const COUNTER_CURSOR = 'trigger.cursor';
|
||||||
|
|
||||||
|
private $garbageCollectors;
|
||||||
|
private $nextCollection;
|
||||||
|
|
||||||
protected function run() {
|
protected function run() {
|
||||||
|
|
||||||
// The trigger daemon is a low-level infrastructure daemon which schedules
|
// The trigger daemon is a low-level infrastructure daemon which schedules
|
||||||
|
@ -54,6 +61,9 @@ final class PhabricatorTriggerDaemon
|
||||||
// trying to reschedule events after an update could race with other web
|
// trying to reschedule events after an update could race with other web
|
||||||
// processes or the daemon.
|
// processes or the daemon.
|
||||||
|
|
||||||
|
// We want to start the first GC cycle right away, not wait 4 hours.
|
||||||
|
$this->nextCollection = PhabricatorTime::getNow();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$lock = PhabricatorGlobalLock::newLock('trigger');
|
$lock = PhabricatorGlobalLock::newLock('trigger');
|
||||||
|
|
||||||
|
@ -86,7 +96,9 @@ final class PhabricatorTriggerDaemon
|
||||||
|
|
||||||
$lock->unlock();
|
$lock->unlock();
|
||||||
|
|
||||||
$this->sleep($this->getSleepDuration());
|
$sleep_duration = $this->getSleepDuration();
|
||||||
|
$sleep_duration = $this->runGarbageCollection($sleep_duration);
|
||||||
|
$this->sleep($sleep_duration);
|
||||||
} while (!$this->shouldExit());
|
} while (!$this->shouldExit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +260,7 @@ final class PhabricatorTriggerDaemon
|
||||||
* @return int Number of seconds to sleep for.
|
* @return int Number of seconds to sleep for.
|
||||||
*/
|
*/
|
||||||
private function getSleepDuration() {
|
private function getSleepDuration() {
|
||||||
$sleep = 60;
|
$sleep = 5;
|
||||||
|
|
||||||
$next_triggers = id(new PhabricatorWorkerTriggerQuery())
|
$next_triggers = id(new PhabricatorWorkerTriggerQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
|
@ -290,4 +302,91 @@ final class PhabricatorTriggerDaemon
|
||||||
id(new PhabricatorWorkerTrigger())->establishConnection('w'),
|
id(new PhabricatorWorkerTrigger())->establishConnection('w'),
|
||||||
$counter_name);
|
$counter_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Garbage Collection )------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the garbage collector for up to a specified number of seconds.
|
||||||
|
*
|
||||||
|
* @param int Number of seconds the GC may run for.
|
||||||
|
* @return int Number of seconds remaining in the time budget.
|
||||||
|
* @task garbage
|
||||||
|
*/
|
||||||
|
private function runGarbageCollection($duration) {
|
||||||
|
$run_until = (PhabricatorTime::getNow() + $duration);
|
||||||
|
|
||||||
|
// NOTE: We always run at least one GC cycle to make sure the GC can make
|
||||||
|
// progress even if the trigger queue is busy.
|
||||||
|
do {
|
||||||
|
$more_garbage = $this->updateGarbageCollection();
|
||||||
|
if (!$more_garbage) {
|
||||||
|
// If we don't have any more collection work to perform, we're all
|
||||||
|
// done.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (PhabricatorTime::getNow() <= $run_until);
|
||||||
|
|
||||||
|
$remaining = max(0, $run_until - PhabricatorTime::getNow());
|
||||||
|
|
||||||
|
return $remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update garbage collection, possibly collecting a small amount of garbage.
|
||||||
|
*
|
||||||
|
* @return bool True if there is more garbage to collect.
|
||||||
|
* @task garbage
|
||||||
|
*/
|
||||||
|
private function updateGarbageCollection() {
|
||||||
|
// If we're ready to start the next collection cycle, load all the
|
||||||
|
// collectors.
|
||||||
|
$next = $this->nextCollection;
|
||||||
|
if ($next && (PhabricatorTime::getNow() >= $next)) {
|
||||||
|
$this->nextCollection = null;
|
||||||
|
$this->garbageCollectors = $this->loadGarbageCollectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're in a collection cycle, continue collection.
|
||||||
|
if ($this->garbageCollectors) {
|
||||||
|
foreach ($this->garbageCollectors as $key => $collector) {
|
||||||
|
$more_garbage = $collector->collectGarbage();
|
||||||
|
if (!$more_garbage) {
|
||||||
|
unset($this->garbageCollectors[$key]);
|
||||||
|
}
|
||||||
|
// We only run one collection per call, to prevent triggers from being
|
||||||
|
// thrown too far off schedule if there's a lot of garbage to collect.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->garbageCollectors) {
|
||||||
|
// If we have more work to do, return true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, reschedule another cycle in 4 hours.
|
||||||
|
$now = PhabricatorTime::getNow();
|
||||||
|
$wait = phutil_units('4 hours in seconds');
|
||||||
|
$this->nextCollection = $now + $wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all of the available garbage collectors.
|
||||||
|
*
|
||||||
|
* @return list<PhabricatorGarbageCollector> Garbage collectors.
|
||||||
|
* @task garbage
|
||||||
|
*/
|
||||||
|
private function loadGarbageCollectors() {
|
||||||
|
return id(new PhutilSymbolLoader())
|
||||||
|
->setAncestorClass('PhabricatorGarbageCollector')
|
||||||
|
->loadObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue