Exemplo n.º 1
0
 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);
     }
 }
Exemplo n.º 2
0
 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);
     }
 }
Exemplo n.º 7
0
<?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()
{