protected function run() { $this->setEngines(PhabricatorFactEngine::loadAllEngines()); while (!$this->shouldExit()) { PhabricatorCaches::destroyRequestCache(); $iterators = $this->getAllApplicationIterators(); foreach ($iterators as $iterator_name => $iterator) { $this->processIteratorWithCursor($iterator_name, $iterator); } $this->processAggregates(); $this->log(pht('Zzz...')); $this->sleep(60 * 5); } }
private function runLoop() { do { PhabricatorCaches::destroyRequestCache(); $this->stillWorking(); $messages = $this->protocolAdapter->getNextMessages($this->pollFrequency); if (count($messages) > 0) { foreach ($messages as $message) { $this->routeMessage($message); } } foreach ($this->handlers as $handler) { $handler->runBackgroundTasks(); } } while (!$this->shouldExit()); }
public function testRequestCache() { $cache = PhabricatorCaches::getRequestCache(); $test_key = 'unit.' . Filesystem::readRandomCharacters(8); $default_value = pht('Default'); $new_value = pht('New Value'); $this->assertEqual($default_value, $cache->getKey($test_key, $default_value)); // Set a key, verify it persists. $cache = PhabricatorCaches::getRequestCache(); $cache->setKey($test_key, $new_value); $this->assertEqual($new_value, $cache->getKey($test_key, $default_value)); // Refetch the cache, verify it's really a cache. $cache = PhabricatorCaches::getRequestCache(); $this->assertEqual($new_value, $cache->getKey($test_key, $default_value)); // Destroy the cache. PhabricatorCaches::destroyRequestCache(); // Now, the value should be missing again. $cache = PhabricatorCaches::getRequestCache(); $this->assertEqual($default_value, $cache->getKey($test_key, $default_value)); }
protected function run() { do { PhabricatorCaches::destroyRequestCache(); $tasks = id(new PhabricatorWorkerLeaseQuery())->setLimit(1)->execute(); if ($tasks) { $this->willBeginWork(); foreach ($tasks as $task) { $id = $task->getID(); $class = $task->getTaskClass(); $this->log(pht('Working on task %d (%s)...', $id, $class)); $task = $task->executeTask(); $ex = $task->getExecutionException(); if ($ex) { if ($ex instanceof PhabricatorWorkerPermanentFailureException) { throw new PhutilProxyException(pht('Permanent failure while executing Task ID %d.', $id), $ex); } else { if ($ex instanceof PhabricatorWorkerYieldException) { $this->log(pht('Task %s yielded.', $id)); } else { $this->log(pht('Task %d failed!', $id)); throw new PhutilProxyException(pht('Error while executing Task ID %d.', $id), $ex); } } } else { $this->log(pht('Task %s complete! Moved to archive.', $id)); } } $sleep = 0; } else { // When there's no work, sleep for one second. The pool will // autoscale down if we're continuously idle for an extended period // of time. $this->willBeginIdle(); $sleep = 1; } $this->sleep($sleep); } while (!$this->shouldExit()); }
protected function run() { // The trigger daemon is a low-level infrastructure daemon which schedules // and executes chronological events. Examples include a subscription which // generates a bill on the 12th of every month, or a reminder email 15 // minutes before a meeting. // Only one trigger daemon can run at a time, and very little work should // happen in the daemon process. In general, triggered events should // just schedule a task into the normal daemon worker queue and then // return. This allows the real work to take longer to execute without // disrupting other triggers. // The trigger mechanism guarantees that events will execute exactly once, // but does not guarantee that they will execute at precisely the specified // time. Under normal circumstances, they should execute within a minute or // so of the desired time, so this mechanism can be used for things like // meeting reminders. // If the trigger queue backs up (for example, because it is overwhelmed by // trigger updates, doesn't run for a while, or a trigger action is written // inefficiently) or the daemon queue backs up (usually for similar // reasons), events may execute an arbitrarily long time after they were // scheduled to execute. In some cases (like billing a subscription) this // may be desirable; in other cases (like sending a meeting reminder) the // action may want to check the current time and see if the event is still // relevant. // The trigger daemon works in two phases: // // 1. A scheduling phase processes recently updated triggers and // schedules them for future execution. For example, this phase would // see that a meeting trigger had been changed recently, determine // when the reminder for it should execute, and then schedule the // action to execute at that future date. // 2. An execution phase runs the actions for any scheduled events which // are due to execute. // // The major goal of this design is to deliver on the guarantee that events // will execute exactly once. It prevents race conditions in scheduling // and execution by ensuring there is only one writer for either of these // phases. Without this separation of responsibilities, web processes // trying to reschedule events after an update could race with other web // processes or the daemon. // We want to start the first GC cycle right away, not wait 4 hours. $this->nextCollection = PhabricatorTime::getNow(); do { PhabricatorCaches::destroyRequestCache(); $lock = PhabricatorGlobalLock::newLock('trigger'); try { $lock->lock(5); } catch (PhutilLockException $ex) { throw new PhutilProxyException(pht('Another process is holding the trigger lock. Usually, this ' . 'means another copy of the trigger daemon is running elsewhere. ' . 'Multiple processes are not permitted to update triggers ' . 'simultaneously.'), $ex); } // Run the scheduling phase. This finds updated triggers which we have // not scheduled yet and schedules them. $last_version = $this->loadCurrentCursor(); $head_version = $this->loadCurrentVersion(); // The cursor points at the next record to process, so we can only skip // this step if we're ahead of the version number. if ($last_version <= $head_version) { $this->scheduleTriggers($last_version); } // Run the execution phase. This finds events which are due to execute // and runs them. $this->executeTriggers(); $lock->unlock(); $sleep_duration = $this->getSleepDuration(); $sleep_duration = $this->runNuanceImportCursors($sleep_duration); $sleep_duration = $this->runGarbageCollection($sleep_duration); $sleep_duration = $this->runCalendarNotifier($sleep_duration); $this->sleep($sleep_duration); } while (!$this->shouldExit()); }
/** * @task pull */ protected function run() { $argv = $this->getArgv(); array_unshift($argv, __CLASS__); $args = new PhutilArgumentParser($argv); $args->parse(array(array('name' => 'no-discovery', 'help' => pht('Pull only, without discovering commits.')), array('name' => 'not', 'param' => 'repository', 'repeat' => true, 'help' => pht('Do not pull __repository__.')), array('name' => 'repositories', 'wildcard' => true, 'help' => pht('Pull specific __repositories__ instead of all.')))); $no_discovery = $args->getArg('no-discovery'); $include = $args->getArg('repositories'); $exclude = $args->getArg('not'); // Each repository has an individual pull frequency; after we pull it, // wait that long to pull it again. When we start up, try to pull everything // serially. $retry_after = array(); $min_sleep = 15; $max_futures = 4; $futures = array(); $queue = array(); while (!$this->shouldExit()) { PhabricatorCaches::destroyRequestCache(); $pullable = $this->loadPullableRepositories($include, $exclude); // If any repositories have the NEEDS_UPDATE flag set, pull them // as soon as possible. $need_update_messages = $this->loadRepositoryUpdateMessages(true); foreach ($need_update_messages as $message) { $repo = idx($pullable, $message->getRepositoryID()); if (!$repo) { continue; } $this->log(pht('Got an update message for repository "%s"!', $repo->getMonogram())); $retry_after[$message->getRepositoryID()] = time(); } // If any repositories were deleted, remove them from the retry timer map // so we don't end up with a retry timer that never gets updated and // causes us to sleep for the minimum amount of time. $retry_after = array_select_keys($retry_after, array_keys($pullable)); // Figure out which repositories we need to queue for an update. foreach ($pullable as $id => $repository) { $monogram = $repository->getMonogram(); if (isset($futures[$id])) { $this->log(pht('Repository "%s" is currently updating.', $monogram)); continue; } if (isset($queue[$id])) { $this->log(pht('Repository "%s" is already queued.', $monogram)); continue; } $after = idx($retry_after, $id, 0); if ($after > time()) { $this->log(pht('Repository "%s" is not due for an update for %s second(s).', $monogram, new PhutilNumber($after - time()))); continue; } if (!$after) { $this->log(pht('Scheduling repository "%s" for an initial update.', $monogram)); } else { $this->log(pht('Scheduling repository "%s" for an update (%s seconds overdue).', $monogram, new PhutilNumber(time() - $after))); } $queue[$id] = $after; } // Process repositories in the order they became candidates for updates. asort($queue); // Dequeue repositories until we hit maximum parallelism. while ($queue && count($futures) < $max_futures) { foreach ($queue as $id => $time) { $repository = idx($pullable, $id); if (!$repository) { $this->log(pht('Repository %s is no longer pullable; skipping.', $id)); unset($queue[$id]); continue; } $monogram = $repository->getMonogram(); $this->log(pht('Starting update for repository "%s".', $monogram)); unset($queue[$id]); $futures[$id] = $this->buildUpdateFuture($repository, $no_discovery); break; } } if ($queue) { $this->log(pht('Not enough process slots to schedule the other %s ' . 'repository(s) for updates yet.', new PhutilNumber(count($queue)))); } if ($futures) { $iterator = id(new FutureIterator($futures))->setUpdateInterval($min_sleep); foreach ($iterator as $id => $future) { $this->stillWorking(); if ($future === null) { $this->log(pht('Waiting for updates to complete...')); $this->stillWorking(); if ($this->loadRepositoryUpdateMessages()) { $this->log(pht('Interrupted by pending updates!')); break; } continue; } unset($futures[$id]); $retry_after[$id] = $this->resolveUpdateFuture($pullable[$id], $future, $min_sleep); // We have a free slot now, so go try to fill it. break; } // Jump back into prioritization if we had any futures to deal with. continue; } $this->waitForUpdates($min_sleep, $retry_after); } }
<?php phabricator_startup(); try { PhabricatorStartup::beginStartupPhase('libraries'); PhabricatorStartup::loadCoreLibraries(); PhabricatorStartup::beginStartupPhase('purge'); PhabricatorCaches::destroyRequestCache(); PhabricatorStartup::beginStartupPhase('sink'); $sink = new AphrontPHPHTTPSink(); try { PhabricatorStartup::beginStartupPhase('run'); AphrontApplicationConfiguration::runHTTPRequest($sink); } catch (Exception $ex) { try { $response = new AphrontUnhandledExceptionResponse(); $response->setException($ex); PhabricatorStartup::endOutputCapture(); $sink->writeResponse($response); } catch (Exception $response_exception) { // If we hit a rendering exception, ignore it and throw the original // exception. It is generally more interesting and more likely to be // the root cause. throw $ex; } } } catch (Exception $ex) { PhabricatorStartup::didEncounterFatalException('Core Exception', $ex, false); } function phabricator_startup() {