@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Provide `bin/garbage` for interacting with garbage collection

Summary:
Fixes T9494. This:

- Removes all the random GC.x.y.z config.
- Puts it all in one place that's locked and which you use `bin/garbage set-policy ...` to adjust.
- Makes every TTL-based GC configurable.
- Simplifies the code in the actual GCs.

Test Plan:
- Ran `bin/garbage collect` to collect some garbage, until it stopped collecting.
- Ran `bin/garbage set-policy ...` to shorten policy. Saw change in web UI. Ran `bin/garbage collect` again and saw it collect more garbage.
- Set policy to indefinite and saw it not collect garabge.
- Set policy to default and saw it reflected in web UI / `collect`.
- Ran `bin/phd debug trigger` and saw all GCs fire with reasonable looking queries.
- Read new docs.

{F857928}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9494

Differential Revision: https://secure.phabricator.com/D14219

+486 -188
+1
bin/garbage
··· 1 + ../scripts/setup/manage_garbage.php
+21
scripts/setup/manage_garbage.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + $root = dirname(dirname(dirname(__FILE__))); 5 + require_once $root.'/scripts/__init_script__.php'; 6 + 7 + $args = new PhutilArgumentParser($argv); 8 + $args->setTagline(pht('manage garbage colletors')); 9 + $args->setSynopsis(<<<EOSYNOPSIS 10 + **garbage** __command__ [__options__] 11 + Manage garbage collectors. 12 + 13 + EOSYNOPSIS 14 + ); 15 + $args->parseStandardArguments(); 16 + 17 + $workflows = id(new PhutilClassMapQuery()) 18 + ->setAncestorClass('PhabricatorGarbageCollectorManagementWorkflow') 19 + ->execute(); 20 + $workflows[] = new PhutilHelpArgumentWorkflow(); 21 + $args->parseWorkflows($workflows);
+6 -2
src/__phutil_library_map__.php
··· 2197 2197 'PhabricatorFundApplication' => 'applications/fund/application/PhabricatorFundApplication.php', 2198 2198 'PhabricatorGDSetupCheck' => 'applications/config/check/PhabricatorGDSetupCheck.php', 2199 2199 'PhabricatorGarbageCollector' => 'infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php', 2200 - 'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php', 2200 + 'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php', 2201 + 'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php', 2202 + 'PhabricatorGarbageCollectorManagementWorkflow' => 'infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php', 2201 2203 'PhabricatorGestureUIExample' => 'applications/uiexample/examples/PhabricatorGestureUIExample.php', 2202 2204 'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php', 2203 2205 'PhabricatorGitHubAuthProvider' => 'applications/auth/provider/PhabricatorGitHubAuthProvider.php', ··· 6197 6199 'PhabricatorFundApplication' => 'PhabricatorApplication', 6198 6200 'PhabricatorGDSetupCheck' => 'PhabricatorSetupCheck', 6199 6201 'PhabricatorGarbageCollector' => 'Phobject', 6200 - 'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions', 6202 + 'PhabricatorGarbageCollectorManagementCollectWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow', 6203 + 'PhabricatorGarbageCollectorManagementSetPolicyWorkflow' => 'PhabricatorGarbageCollectorManagementWorkflow', 6204 + 'PhabricatorGarbageCollectorManagementWorkflow' => 'PhabricatorManagementWorkflow', 6201 6205 'PhabricatorGestureUIExample' => 'PhabricatorUIExample', 6202 6206 'PhabricatorGitGraphStream' => 'PhabricatorRepositoryGraphStream', 6203 6207 'PhabricatorGitHubAuthProvider' => 'PhabricatorOAuth2AuthProvider',
+2 -2
src/applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php
··· 6 6 const COLLECTORCONST = 'auth.sessions'; 7 7 8 8 public function getCollectorName() { 9 - return pht('Auth Sessions'); 9 + return pht('Authentication Sessions'); 10 10 } 11 11 12 12 public function hasAutomaticPolicy() { 13 13 return true; 14 14 } 15 15 16 - public function collectGarbage() { 16 + protected function collectGarbage() { 17 17 $session_table = new PhabricatorAuthSession(); 18 18 $conn_w = $session_table->establishConnection('w'); 19 19
+2 -2
src/applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php
··· 6 6 const COLLECTORCONST = 'auth.tokens'; 7 7 8 8 public function getCollectorName() { 9 - return pht('Auth Tokens'); 9 + return pht('Authentication Tokens'); 10 10 } 11 11 12 12 public function hasAutomaticPolicy() { 13 13 return true; 14 14 } 15 15 16 - public function collectGarbage() { 16 + protected function collectGarbage() { 17 17 $session_table = new PhabricatorAuthTemporaryToken(); 18 18 $conn_w = $session_table->establishConnection('w'); 19 19
+2 -8
src/applications/cache/garbagecollector/PhabricatorCacheGeneralGarbageCollector.php
··· 13 13 return phutil_units('30 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $key = 'gcdaemon.ttl.general-cache'; 18 - $ttl = PhabricatorEnv::getEnvConfig($key); 19 - if ($ttl <= 0) { 20 - return false; 21 - } 22 - 16 + protected function collectGarbage() { 23 17 $cache = new PhabricatorKeyValueDatabaseCache(); 24 18 $conn_w = $cache->establishConnection('w'); 25 19 ··· 28 22 'DELETE FROM %T WHERE cacheCreated < %d 29 23 ORDER BY cacheCreated ASC LIMIT 100', 30 24 $cache->getTableName(), 31 - time() - $ttl); 25 + $this->getGarbageEpoch()); 32 26 33 27 return ($conn_w->getAffectedRows() == 100); 34 28 }
+2 -8
src/applications/cache/garbagecollector/PhabricatorCacheMarkupGarbageCollector.php
··· 13 13 return phutil_units('30 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $key = 'gcdaemon.ttl.markup-cache'; 18 - $ttl = PhabricatorEnv::getEnvConfig($key); 19 - if ($ttl <= 0) { 20 - return false; 21 - } 22 - 16 + protected function collectGarbage() { 23 17 $table = new PhabricatorMarkupCache(); 24 18 $conn_w = $table->establishConnection('w'); 25 19 ··· 27 21 $conn_w, 28 22 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', 29 23 $table->getTableName(), 30 - time() - $ttl); 24 + $this->getGarbageEpoch()); 31 25 32 26 return ($conn_w->getAffectedRows() == 100); 33 27 }
+2 -2
src/applications/cache/garbagecollector/PhabricatorCacheTTLGarbageCollector.php
··· 13 13 return true; 14 14 } 15 15 16 - public function collectGarbage() { 16 + protected function collectGarbage() { 17 17 $cache = new PhabricatorKeyValueDatabaseCache(); 18 18 $conn_w = $cache->establishConnection('w'); 19 19 ··· 22 22 'DELETE FROM %T WHERE cacheExpires < %d 23 23 ORDER BY cacheExpires ASC LIMIT 100', 24 24 $cache->getTableName(), 25 - time()); 25 + PhabricatorTime::getNow()); 26 26 27 27 return ($conn_w->getAffectedRows() == 100); 28 28 }
+3 -8
src/applications/conduit/garbagecollector/ConduitConnectionGarbageCollector.php
··· 13 13 return phutil_units('180 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $key = 'gcdaemon.ttl.conduit-logs'; 18 - $ttl = PhabricatorEnv::getEnvConfig($key); 19 - if ($ttl <= 0) { 20 - return false; 21 - } 22 - 16 + protected function collectGarbage() { 23 17 $table = new PhabricatorConduitConnectionLog(); 24 18 $conn_w = $table->establishConnection('w'); 19 + 25 20 queryfx( 26 21 $conn_w, 27 22 'DELETE FROM %T WHERE dateCreated < %d 28 23 ORDER BY dateCreated ASC LIMIT 100', 29 24 $table->getTableName(), 30 - time() - $ttl); 25 + $this->getGarbageEpoch()); 31 26 32 27 return ($conn_w->getAffectedRows() == 100); 33 28 }
+3 -8
src/applications/conduit/garbagecollector/ConduitLogGarbageCollector.php
··· 13 13 return phutil_units('180 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $key = 'gcdaemon.ttl.conduit-logs'; 18 - $ttl = PhabricatorEnv::getEnvConfig($key); 19 - if ($ttl <= 0) { 20 - return false; 21 - } 22 - 16 + protected function collectGarbage() { 23 17 $table = new PhabricatorConduitMethodCallLog(); 24 18 $conn_w = $table->establishConnection('w'); 19 + 25 20 queryfx( 26 21 $conn_w, 27 22 'DELETE FROM %T WHERE dateCreated < %d 28 23 ORDER BY dateCreated ASC LIMIT 100', 29 24 $table->getTableName(), 30 - time() - $ttl); 25 + $this->getGarbageEpoch()); 31 26 32 27 return ($conn_w->getAffectedRows() == 100); 33 28 }
+2 -1
src/applications/conduit/garbagecollector/ConduitTokenGarbageCollector.php
··· 13 13 return true; 14 14 } 15 15 16 - public function collectGarbage() { 16 + protected function collectGarbage() { 17 17 $table = new PhabricatorConduitToken(); 18 18 $conn_w = $table->establishConnection('w'); 19 + 19 20 queryfx( 20 21 $conn_w, 21 22 'DELETE FROM %T WHERE expires <= %d
+12
src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
··· 176 176 'Inbound mail addresses are now configured for each application '. 177 177 'in the Applications tool.'); 178 178 179 + $gc_reason = pht( 180 + 'Garbage collectors are now configured with "%s".', 181 + 'bin/garbage set-policy'); 182 + 179 183 $ancient_config += array( 180 184 'phid.external-loaders' => 181 185 pht( ··· 280 284 'auth.login-message' => pht( 281 285 'This configuration option has been replaced with a modular '. 282 286 'handler. See T9346.'), 287 + 288 + 'gcdaemon.ttl.herald-transcripts' => $gc_reason, 289 + 'gcdaemon.ttl.daemon-logs' => $gc_reason, 290 + 'gcdaemon.ttl.differential-parse-cache' => $gc_reason, 291 + 'gcdaemon.ttl.markup-cache' => $gc_reason, 292 + 'gcdaemon.ttl.task-archive' => $gc_reason, 293 + 'gcdaemon.ttl.general-cache' => $gc_reason, 294 + 'gcdaemon.ttl.conduit-logs' => $gc_reason, 283 295 ); 284 296 285 297 return $ancient_config;
+20 -2
src/applications/config/module/PhabricatorConfigCollectorsModule.php
··· 17 17 $collectors = msort($collectors, 'getCollectorConstant'); 18 18 19 19 $rows = array(); 20 + $rowc = array(); 20 21 foreach ($collectors as $key => $collector) { 22 + $class = null; 21 23 if ($collector->hasAutomaticPolicy()) { 22 24 $policy_view = phutil_tag('em', array(), pht('Automatic')); 23 25 } else { 24 - $policy = $collector->getDefaultRetentionPolicy(); 26 + $policy = $collector->getRetentionPolicy(); 25 27 if ($policy === null) { 26 28 $policy_view = pht('Indefinite'); 27 29 } else { ··· 30 32 '%s Day(s)', 31 33 new PhutilNumber($days)); 32 34 } 35 + 36 + $default = $collector->getDefaultRetentionPolicy(); 37 + if ($policy !== $default) { 38 + $class = 'highlighted'; 39 + $policy_view = phutil_tag('strong', array(), $policy_view); 40 + } 33 41 } 34 42 43 + $rowc[] = $class; 35 44 $rows[] = array( 36 45 $collector->getCollectorConstant(), 37 46 $collector->getCollectorName(), ··· 40 49 } 41 50 42 51 $table = id(new AphrontTableView($rows)) 52 + ->setRowClasses($rowc) 43 53 ->setHeaders( 44 54 array( 45 55 pht('Constant'), ··· 53 63 null, 54 64 )); 55 65 66 + $header = id(new PHUIHeaderView()) 67 + ->setHeader(pht('Garbage Collectors')) 68 + ->setSubheader( 69 + pht( 70 + 'Collectors with custom policies are highlighted. Use '. 71 + '%s to change retention policies.', 72 + phutil_tag('tt', array(), 'bin/garbage set-policy'))); 73 + 56 74 return id(new PHUIObjectBoxView()) 57 - ->setHeaderText(pht('Garbage Collectors')) 75 + ->setHeader($header) 58 76 ->setTable($table); 59 77 } 60 78
-70
src/applications/config/option/PhabricatorGarbageCollectorConfigOptions.php
··· 1 - <?php 2 - 3 - final class PhabricatorGarbageCollectorConfigOptions 4 - extends PhabricatorApplicationConfigOptions { 5 - 6 - public function getName() { 7 - return pht('Garbage Collector'); 8 - } 9 - 10 - public function getDescription() { 11 - return pht('Configure the GC for old logs, caches, etc.'); 12 - } 13 - 14 - public function getFontIcon() { 15 - return 'fa-trash-o'; 16 - } 17 - 18 - public function getGroup() { 19 - return 'core'; 20 - } 21 - 22 - public function getOptions() { 23 - 24 - $options = array( 25 - 'gcdaemon.ttl.herald-transcripts' => array( 26 - 30, 27 - pht('Number of seconds to retain Herald transcripts for.'), 28 - ), 29 - 'gcdaemon.ttl.daemon-logs' => array( 30 - 7, 31 - pht('Number of seconds to retain Daemon logs for.'), 32 - ), 33 - 'gcdaemon.ttl.differential-parse-cache' => array( 34 - 14, 35 - pht('Number of seconds to retain Differential parse caches for.'), 36 - ), 37 - 'gcdaemon.ttl.markup-cache' => array( 38 - 30, 39 - pht('Number of seconds to retain Markup cache entries for.'), 40 - ), 41 - 'gcdaemon.ttl.task-archive' => array( 42 - 14, 43 - pht('Number of seconds to retain archived background tasks for.'), 44 - ), 45 - 'gcdaemon.ttl.general-cache' => array( 46 - 30, 47 - pht('Number of seconds to retain general cache entries for.'), 48 - ), 49 - 'gcdaemon.ttl.conduit-logs' => array( 50 - 180, 51 - pht('Number of seconds to retain Conduit call logs for.'), 52 - ), 53 - ); 54 - 55 - $result = array(); 56 - foreach ($options as $key => $spec) { 57 - list($default_days, $description) = $spec; 58 - $result[] = $this 59 - ->newOption($key, 'int', $default_days * (24 * 60 * 60)) 60 - ->setDescription($description) 61 - ->addExample((7 * 24 * 60 * 60), pht('Retain for 1 week')) 62 - ->addExample((14 * 24 * 60 * 60), pht('Retain for 2 weeks')) 63 - ->addExample((30 * 24 * 60 * 60), pht('Retain for 30 days')) 64 - ->addExample((60 * 24 * 60 * 60), pht('Retain for 60 days')) 65 - ->addExample(0, pht('Retain indefinitely')); 66 - } 67 - return $result; 68 - } 69 - 70 - }
+11
src/applications/config/option/PhabricatorPHDConfigOptions.php
··· 80 80 'and the daemons. Primarily, this is a way to suppress the '. 81 81 '"Daemons and Web Have Different Config" setup issue on a per '. 82 82 'config key basis.')), 83 + $this->newOption('phd.garbage-collection', 'wild', array()) 84 + ->setLocked(true) 85 + ->setLockedMessage( 86 + pht( 87 + 'This option can not be edited from the web UI. Use %s to adjust '. 88 + 'garbage collector policies.', 89 + phutil_tag('tt', array(), 'bin/garbage set-policy'))) 90 + ->setSummary(pht('Retention policies for garbage collection.')) 91 + ->setDescription( 92 + pht( 93 + 'Customizes retention policies for garbage collectors.')), 83 94 ); 84 95 } 85 96
+2 -7
src/applications/daemon/garbagecollector/PhabricatorDaemonLogEventGarbageCollector.php
··· 13 13 return phutil_units('7 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.daemon-logs'); 18 - if ($ttl <= 0) { 19 - return false; 20 - } 21 - 16 + protected function collectGarbage() { 22 17 $table = new PhabricatorDaemonLogEvent(); 23 18 $conn_w = $table->establishConnection('w'); 24 19 ··· 26 21 $conn_w, 27 22 'DELETE FROM %T WHERE epoch < %d LIMIT 100', 28 23 $table->getTableName(), 29 - time() - $ttl); 24 + $this->getGarbageEpoch()); 30 25 31 26 return ($conn_w->getAffectedRows() == 100); 32 27 }
+2 -7
src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php
··· 13 13 return phutil_units('7 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.daemon-logs'); 18 - if ($ttl <= 0) { 19 - return false; 20 - } 21 - 16 + protected function collectGarbage() { 22 17 $table = new PhabricatorDaemonLog(); 23 18 $conn_w = $table->establishConnection('w'); 24 19 ··· 26 21 $conn_w, 27 22 'DELETE FROM %T WHERE dateCreated < %d AND status != %s LIMIT 100', 28 23 $table->getTableName(), 29 - time() - $ttl, 24 + $this->getGarbageEpoch(), 30 25 PhabricatorDaemonLog::STATUS_RUNNING); 31 26 32 27 return ($conn_w->getAffectedRows() == 100);
+3 -9
src/applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php
··· 13 13 return phutil_units('14 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $key = 'gcdaemon.ttl.task-archive'; 18 - $ttl = PhabricatorEnv::getEnvConfig($key); 19 - if ($ttl <= 0) { 20 - return false; 21 - } 22 - 16 + protected function collectGarbage() { 23 17 $table = new PhabricatorWorkerArchiveTask(); 24 18 $data_table = new PhabricatorWorkerTaskData(); 25 19 $conn_w = $table->establishConnection('w'); 26 20 27 21 $tasks = id(new PhabricatorWorkerArchiveTaskQuery()) 28 - ->withDateCreatedBefore(time() - $ttl) 22 + ->withDateCreatedBefore($this->getGarbageEpoch()) 23 + ->setLimit(100) 29 24 ->execute(); 30 - 31 25 if (!$tasks) { 32 26 return false; 33 27 }
+2 -8
src/applications/differential/garbagecollector/DifferentialParseCacheGarbageCollector.php
··· 13 13 return phutil_units('14 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $key = 'gcdaemon.ttl.differential-parse-cache'; 18 - $ttl = PhabricatorEnv::getEnvConfig($key); 19 - if ($ttl <= 0) { 20 - return false; 21 - } 22 - 16 + protected function collectGarbage() { 23 17 $table = new DifferentialChangeset(); 24 18 $conn_w = $table->establishConnection('w'); 25 19 ··· 27 21 $conn_w, 28 22 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', 29 23 DifferentialChangeset::TABLE_CACHE, 30 - time() - $ttl); 24 + $this->getGarbageEpoch()); 31 25 32 26 return ($conn_w->getAffectedRows() == 100); 33 27 }
+2 -5
src/applications/drydock/garbagecollector/DrydockLogGarbageCollector.php
··· 13 13 return phutil_units('30 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 16 + protected function collectGarbage() { 17 17 $log_table = new DrydockLog(); 18 18 $conn_w = $log_table->establishConnection('w'); 19 19 20 - $now = PhabricatorTime::getNow(); 21 - $ttl = phutil_units('30 days in seconds'); 22 - 23 20 queryfx( 24 21 $conn_w, 25 22 'DELETE FROM %T WHERE epoch <= %d LIMIT 100', 26 23 $log_table->getTableName(), 27 - $now - $ttl); 24 + $this->getGarbageEpoch()); 28 25 29 26 return ($conn_w->getAffectedRows() == 100); 30 27 }
+2 -2
src/applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php
··· 13 13 return true; 14 14 } 15 15 16 - public function collectGarbage() { 16 + protected function collectGarbage() { 17 17 $files = id(new PhabricatorFile())->loadAllWhere( 18 18 'ttl < %d LIMIT 100', 19 - time()); 19 + PhabricatorTime::getNow()); 20 20 21 21 foreach ($files as $file) { 22 22 $file->delete();
+2 -7
src/applications/herald/garbagecollector/HeraldTranscriptGarbageCollector.php
··· 13 13 return phutil_units('30 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = PhabricatorEnv::getEnvConfig('gcdaemon.ttl.herald-transcripts'); 18 - if ($ttl <= 0) { 19 - return false; 20 - } 21 - 16 + protected function collectGarbage() { 22 17 $table = new HeraldTranscript(); 23 18 $conn_w = $table->establishConnection('w'); 24 19 ··· 33 28 WHERE garbageCollected = 0 AND time < %d 34 29 LIMIT 100', 35 30 $table->getTableName(), 36 - time() - $ttl); 31 + $this->getGarbageEpoch()); 37 32 38 33 return ($conn_w->getAffectedRows() == 100); 39 34 }
+2 -4
src/applications/metamta/garbagecollector/MetaMTAMailReceivedGarbageCollector.php
··· 13 13 return phutil_units('90 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = phutil_units('90 days in seconds'); 18 - 16 + protected function collectGarbage() { 19 17 $table = new PhabricatorMetaMTAReceivedMail(); 20 18 $conn_w = $table->establishConnection('w'); 21 19 ··· 23 21 $conn_w, 24 22 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', 25 23 $table->getTableName(), 26 - time() - $ttl); 24 + $this->getGarbageEpoch()); 27 25 28 26 return ($conn_w->getAffectedRows() == 100); 29 27 }
+2 -4
src/applications/metamta/garbagecollector/MetaMTAMailSentGarbageCollector.php
··· 13 13 return phutil_units('90 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = phutil_units('90 days in seconds'); 18 - 16 + protected function collectGarbage() { 19 17 $mails = id(new PhabricatorMetaMTAMail())->loadAllWhere( 20 18 'dateCreated < %d LIMIT 100', 21 - PhabricatorTime::getNow() - $ttl); 19 + $this->getGarbageEpoch()); 22 20 23 21 foreach ($mails as $mail) { 24 22 $mail->delete();
+2 -4
src/applications/multimeter/garbagecollector/MultimeterEventGarbageCollector.php
··· 13 13 return phutil_units('90 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = phutil_units('90 days in seconds'); 18 - 16 + protected function collectGarbage() { 19 17 $table = new MultimeterEvent(); 20 18 $conn_w = $table->establishConnection('w'); 21 19 ··· 23 21 $conn_w, 24 22 'DELETE FROM %T WHERE epoch < %d LIMIT 100', 25 23 $table->getTableName(), 26 - PhabricatorTime::getNow() - $ttl); 24 + $this->getGarbageEpoch()); 27 25 28 26 return ($conn_w->getAffectedRows() == 100); 29 27 }
+2 -4
src/applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php
··· 13 13 return phutil_units('90 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = 90 * 24 * 60 * 60; 18 - 16 + protected function collectGarbage() { 19 17 $table = new PhabricatorFeedStoryNotification(); 20 18 $conn_w = $table->establishConnection('w'); 21 19 ··· 24 22 'DELETE FROM %T WHERE chronologicalKey < (%d << 32) 25 23 ORDER BY chronologicalKey ASC LIMIT 100', 26 24 $table->getTableName(), 27 - time() - $ttl); 25 + $this->getGarbageEpoch()); 28 26 29 27 return ($conn_w->getAffectedRows() == 100); 30 28 }
+2 -4
src/applications/people/garbagecollector/PeopleUserLogGarbageCollector.php
··· 13 13 return phutil_units('180 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = phutil_units('180 days in seconds'); 18 - 16 + protected function collectGarbage() { 19 17 $table = new PhabricatorUserLog(); 20 18 $conn_w = $table->establishConnection('w'); 21 19 ··· 23 21 $conn_w, 24 22 'DELETE FROM %T WHERE dateCreated < %d LIMIT 100', 25 23 $table->getTableName(), 26 - time() - $ttl); 24 + $this->getGarbageEpoch()); 27 25 28 26 return ($conn_w->getAffectedRows() == 100); 29 27 }
+2 -4
src/applications/system/garbagecollector/PhabricatorSystemActionGarbageCollector.php
··· 13 13 return phutil_units('3 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = phutil_units('3 days in seconds'); 18 - 16 + protected function collectGarbage() { 19 17 $table = new PhabricatorSystemActionLog(); 20 18 $conn_w = $table->establishConnection('w'); 21 19 ··· 23 21 $conn_w, 24 22 'DELETE FROM %T WHERE epoch < %d LIMIT 100', 25 23 $table->getTableName(), 26 - time() - $ttl); 24 + $this->getGarbageEpoch()); 27 25 28 26 return ($conn_w->getAffectedRows() == 100); 29 27 }
+2 -4
src/applications/system/garbagecollector/PhabricatorSystemDestructionGarbageCollector.php
··· 13 13 return phutil_units('90 days in seconds'); 14 14 } 15 15 16 - public function collectGarbage() { 17 - $ttl = phutil_units('90 days in seconds'); 18 - 16 + protected function collectGarbage() { 19 17 $table = new PhabricatorSystemDestructionLog(); 20 18 $conn_w = $table->establishConnection('w'); 21 19 ··· 23 21 $conn_w, 24 22 'DELETE FROM %T WHERE epoch < %d LIMIT 100', 25 23 $table->getTableName(), 26 - time() - $ttl); 24 + $this->getGarbageEpoch()); 27 25 28 26 return ($conn_w->getAffectedRows() == 100); 29 27 }
+68
src/docs/user/configuration/managing_garbage.diviner
··· 1 + @title Managing Garbage Collection 2 + @group config 3 + 4 + Understanding and configuring garbage collection. 5 + 6 + Overview 7 + ======== 8 + 9 + Phabricator generates various logs and caches during normal operation. Some of 10 + these logs and caches are usually of very little use after some time has 11 + passed, so they are deleted automatically (often after a month or two) in a 12 + process called "garbage collection". 13 + 14 + Garbage collection is performed automatically by the daemons. You can review 15 + all of the installed garbage collectors by browsing to {nav Config > Garbage 16 + Collectors}. 17 + 18 + 19 + Configuring Retention Policies 20 + ============================== 21 + 22 + You can reconfigure the data retention policies for most collectors. 23 + 24 + The default retention polcies should be suitable for most installs. However, 25 + you might want to **decrease** retention to reduce the amount of disk space 26 + used by some high-volume log that you don't find particularly interesting, or 27 + to adhere to an organizational data retention policy. 28 + 29 + Alternatively, you might want to **increase** retention if you want to retain 30 + some logs for a longer period of time, perhaps for auditing or analytic 31 + purposes. 32 + 33 + You can review the current retention policies in 34 + {nav Config > Garbage Collectors}. To change a policy, use 35 + `bin/garbage set-policy` to select a new policy: 36 + 37 + ``` 38 + phabricator/ $ ./bin/garbage set-policy --collector cache.markup --days 7 39 + ``` 40 + 41 + You can use `--days` to select how long data is retained for. You can also use 42 + `--indefinite` to set an indefinite retention policy. This will stop the 43 + garbage collector from cleaning up any data. Finally, you can use `--default` 44 + to restore the default policy. 45 + 46 + Your changes should be reflected in the web UI immediately, and will take 47 + effect in the actual collector **the next time the daemons are restarted**. 48 + 49 + 50 + Troubleshooting 51 + =============== 52 + 53 + You can manually run a collector with `bin/garbage collect`. 54 + 55 + ``` 56 + phabricator/ $ ./bin/garbage collect --collector cache.general 57 + ``` 58 + 59 + By using the `--trace` flag, you can inspect the operation of the collector 60 + in detail. 61 + 62 + 63 + Next Steps 64 + ========== 65 + 66 + Continue by: 67 + 68 + - exploring other daemon topics with @{article:Managing Daemons with phd}.
+71 -1
src/infrastructure/daemon/garbagecollector/PhabricatorGarbageCollector.php
··· 47 47 48 48 49 49 /** 50 + * Get the effective retention policy. 51 + * 52 + * @return int|null Lifetime, or `null` for indefinite retention. 53 + * @task info 54 + */ 55 + public function getRetentionPolicy() { 56 + if ($this->hasAutomaticPolicy()) { 57 + throw new Exception( 58 + pht( 59 + 'Can not get retention policy of collector with automatic '. 60 + 'policy.')); 61 + } 62 + 63 + $config = PhabricatorEnv::getEnvConfig('phd.garbage-collection'); 64 + $const = $this->getCollectorConstant(); 65 + 66 + return idx($config, $const, $this->getDefaultRetentionPolicy()); 67 + } 68 + 69 + 70 + 71 + /** 50 72 * Get a unique string constant identifying this collector. 51 73 * 52 74 * @return string Collector constant. ··· 61 83 62 84 63 85 /** 86 + * Run the collector. 87 + * 88 + * @return bool True if there is more garbage to collect. 89 + * @task collect 90 + */ 91 + final public function runCollector() { 92 + // Don't do anything if this collector is configured with an indefinite 93 + // retention policy. 94 + if (!$this->hasAutomaticPolicy()) { 95 + $policy = $this->getRetentionPolicy(); 96 + if (!$policy) { 97 + return false; 98 + } 99 + } 100 + 101 + return $this->collectGarbage(); 102 + } 103 + 104 + 105 + /** 64 106 * Collect garbage from whatever source this GC handles. 65 107 * 66 108 * @return bool True if there is more garbage to collect. 67 109 * @task collect 68 110 */ 69 - abstract public function collectGarbage(); 111 + abstract protected function collectGarbage(); 112 + 113 + 114 + /** 115 + * Get the most recent epoch timestamp that is considered garbage. 116 + * 117 + * Records older than this should be collected. 118 + * 119 + * @return int Most recent garbage timestamp. 120 + * @task collect 121 + */ 122 + final protected function getGarbageEpoch() { 123 + if ($this->hasAutomaticPolicy()) { 124 + throw new Exception( 125 + pht( 126 + 'Can not get garbage epoch for a collector with an automatic '. 127 + 'collection policy.')); 128 + } 129 + 130 + $ttl = $this->getRetentionPolicy(); 131 + if (!$ttl) { 132 + throw new Exception( 133 + pht( 134 + 'Can not get garbage epoch for a collector with an indefinite '. 135 + 'retention policy.')); 136 + } 137 + 138 + return (PhabricatorTime::getNow() - $ttl); 139 + } 70 140 71 141 72 142 /**
+50
src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementCollectWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorGarbageCollectorManagementCollectWorkflow 4 + extends PhabricatorGarbageCollectorManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('collect') 9 + ->setExamples('**collect** --collector __collector__') 10 + ->setSynopsis( 11 + pht('Run a garbage collector in the foreground.')) 12 + ->setArguments( 13 + array( 14 + array( 15 + 'name' => 'collector', 16 + 'param' => 'const', 17 + 'help' => pht( 18 + 'Constant identifying the garbage collector to run.'), 19 + ), 20 + )); 21 + } 22 + 23 + public function execute(PhutilArgumentParser $args) { 24 + $collector = $this->getCollector($args->getArg('collector')); 25 + 26 + echo tsprintf( 27 + "%s\n", 28 + pht('Collecting "%s" garbage...', $collector->getCollectorName())); 29 + 30 + $any = false; 31 + while (true) { 32 + $more = $collector->runCollector(); 33 + if ($more) { 34 + $any = true; 35 + } else { 36 + break; 37 + } 38 + } 39 + 40 + if ($any) { 41 + $message = pht('Finished collecting all the garbage.'); 42 + } else { 43 + $message = pht('Could not find any garbage to collect.'); 44 + } 45 + echo tsprintf("\n%s\n", $message); 46 + 47 + return 0; 48 + } 49 + 50 + }
+141
src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementSetPolicyWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorGarbageCollectorManagementSetPolicyWorkflow 4 + extends PhabricatorGarbageCollectorManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('set-policy') 9 + ->setExamples( 10 + "**set-policy** --collector __collector__ --days 30\n". 11 + "**set-policy** --collector __collector__ --indefinite\n". 12 + "**set-policy** --collector __collector__ --default") 13 + ->setSynopsis( 14 + pht( 15 + 'Change retention policies for a garbage collector.')) 16 + ->setArguments( 17 + array( 18 + array( 19 + 'name' => 'collector', 20 + 'param' => 'const', 21 + 'help' => pht( 22 + 'Constant identifying the garbage collector.'), 23 + ), 24 + array( 25 + 'name' => 'indefinite', 26 + 'help' => pht( 27 + 'Set an indefinite retention policy.'), 28 + ), 29 + array( 30 + 'name' => 'default', 31 + 'help' => pht( 32 + 'Use the default retention policy.'), 33 + ), 34 + array( 35 + 'name' => 'days', 36 + 'param' => 'count', 37 + 'help' => pht( 38 + 'Retain data for the specified number of days.'), 39 + ), 40 + )); 41 + } 42 + 43 + public function execute(PhutilArgumentParser $args) { 44 + $config_key = 'phd.garbage-collection'; 45 + 46 + $collector = $this->getCollector($args->getArg('collector')); 47 + 48 + $days = $args->getArg('days'); 49 + $indefinite = $args->getArg('indefinite'); 50 + $default = $args->getArg('default'); 51 + 52 + $count = 0; 53 + if ($days !== null) { 54 + $count++; 55 + } 56 + if ($indefinite) { 57 + $count++; 58 + } 59 + if ($default) { 60 + $count++; 61 + } 62 + 63 + if (!$count) { 64 + throw new PhutilArgumentUsageException( 65 + pht( 66 + 'Choose a policy with "%s", "%s" or "%s".', 67 + '--days', 68 + '--indefinite', 69 + '--default')); 70 + } 71 + 72 + if ($count > 1) { 73 + throw new PhutilArgumentUsageException( 74 + pht( 75 + 'Options "%s", "%s" and "%s" represent mutually exclusive ways '. 76 + 'to choose a policy. Specify only one.', 77 + '--days', 78 + '--indefinite', 79 + '--default')); 80 + } 81 + 82 + if ($days !== null) { 83 + $days = (int)$days; 84 + if ($days < 1) { 85 + throw new PhutilArgumentUsageException( 86 + pht( 87 + 'Specify a positive number of days to retain data for.')); 88 + } 89 + } 90 + 91 + $collector_const = $collector->getCollectorConstant(); 92 + $value = PhabricatorEnv::getEnvConfig($config_key); 93 + 94 + if ($days !== null) { 95 + echo tsprintf( 96 + "%s\n", 97 + pht( 98 + 'Setting retention policy for "%s" to %s day(s).', 99 + $collector->getCollectorName(), 100 + new PhutilNumber($days))); 101 + 102 + $value[$collector_const] = phutil_units($days.' days in seconds'); 103 + } else if ($indefinite) { 104 + echo tsprintf( 105 + "%s\n", 106 + pht( 107 + 'Setting "%s" to be retained indefinitely.', 108 + $collector->getCollectorName())); 109 + 110 + $value[$collector_const] = null; 111 + } else { 112 + echo tsprintf( 113 + "%s\n", 114 + pht( 115 + 'Restoring "%s" to the default retention policy.', 116 + $collector->getCollectorName())); 117 + 118 + unset($value[$collector_const]); 119 + } 120 + 121 + id(new PhabricatorConfigLocalSource()) 122 + ->setKeys( 123 + array( 124 + $config_key => $value, 125 + )); 126 + 127 + echo tsprintf( 128 + "%s\n", 129 + pht( 130 + 'Wrote new policy to local configuration.')); 131 + 132 + echo tsprintf( 133 + "%s\n", 134 + pht( 135 + 'This change will take effect the next time the daemons are '. 136 + 'restarted.')); 137 + 138 + return 0; 139 + } 140 + 141 + }
+32
src/infrastructure/daemon/garbagecollector/management/PhabricatorGarbageCollectorManagementWorkflow.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorGarbageCollectorManagementWorkflow 4 + extends PhabricatorManagementWorkflow { 5 + 6 + protected function getCollector($const) { 7 + $collectors = PhabricatorGarbageCollector::getAllCollectors(); 8 + 9 + $collector_list = array_keys($collectors); 10 + sort($collector_list); 11 + $collector_list = implode(', ', $collector_list); 12 + 13 + if (!$const) { 14 + throw new PhutilArgumentUsageException( 15 + pht( 16 + 'Specify a collector with "%s". Valid collectors are: %s.', 17 + '--collector', 18 + $collector_list)); 19 + } 20 + 21 + if (empty($collectors[$const])) { 22 + throw new PhutilArgumentUsageException( 23 + pht( 24 + 'No such collector "%s". Choose a valid collector: %s.', 25 + $const, 26 + $collector_list)); 27 + } 28 + 29 + return $collectors[$const]; 30 + } 31 + 32 + }
+1 -1
src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php
··· 356 356 // If we're in a collection cycle, continue collection. 357 357 if ($this->garbageCollectors) { 358 358 foreach ($this->garbageCollectors as $key => $collector) { 359 - $more_garbage = $collector->collectGarbage(); 359 + $more_garbage = $collector->runCollector(); 360 360 if (!$more_garbage) { 361 361 unset($this->garbageCollectors[$key]); 362 362 }
+5
src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
··· 1393 1393 '%s Days', 1394 1394 ), 1395 1395 1396 + 'Setting retention policy for "%s" to %s day(s).' => array( 1397 + 'Setting retention policy for "%s" to one day.', 1398 + 'Setting retention policy for "%s" to %s days.', 1399 + ), 1400 + 1396 1401 ); 1397 1402 } 1398 1403