Something went wrong. Try again.
@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
Something went wrong. Try again.
8.7 kB · 291 lines
PHP
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292<?php
final class PhabricatorDaemonConsoleController extends PhabricatorDaemonController {
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer();
$window_start = (time() - (60 * 15));
// Assume daemons spend about 250ms second in overhead per task acquiring // leases and doing other bookkeeping. This is probably an over-estimation, // but we'd rather show that utilization is too high than too low. $lease_overhead = 0.250;
$completed = id(new PhabricatorWorkerArchiveTaskQuery()) ->withDateModifiedSince($window_start) ->execute();
$failed = id(new PhabricatorWorkerActiveTask())->loadAllWhere( 'failureTime > %d', $window_start);
$usage_total = 0; $usage_start = PHP_INT_MAX;
$completed_info = array(); foreach ($completed as $completed_task) { $class = $completed_task->getTaskClass(); if (empty($completed_info[$class])) { $completed_info[$class] = array( 'n' => 0, 'duration' => 0, 'queueTime' => 0, ); } $completed_info[$class]['n']++; $duration = $completed_task->getDuration(); $completed_info[$class]['duration'] += $duration;
// NOTE: Duration is in microseconds, but we're just using seconds to // compute utilization. $usage_total += $lease_overhead + ($duration / 1000000); $usage_start = min($usage_start, $completed_task->getDateModified());
$date_archived = $completed_task->getArchivedEpoch(); $queue_seconds = $date_archived - $completed_task->getDateCreated();
// Don't measure queue time for tasks that completed in the same // epoch-second they were created in. if ($queue_seconds > 0) { $sec_in_us = phutil_units('1 second in microseconds'); $queue_us = $queue_seconds * $sec_in_us; $queue_exclusive_us = $queue_us - $duration; $queue_exclusive_seconds = $queue_exclusive_us / $sec_in_us; $rounded = floor($queue_exclusive_seconds); $completed_info[$class]['queueTime'] += $rounded; } }
$completed_info = isort($completed_info, 'n');
$rows = array(); foreach ($completed_info as $class => $info) { $duration_avg = new PhutilNumber((int)($info['duration'] / $info['n'])); $queue_avg = new PhutilNumber((int)($info['queueTime'] / $info['n'])); $rows[] = array( $class, number_format($info['n']), pht('%s us', $duration_avg), pht('%s s', $queue_avg), ); }
if ($failed) { // Add the time it takes to restart the daemons. This includes a guess // about other overhead of 2X. $restart_delay = PhutilDaemonHandle::getWaitBeforeRestart(); $usage_total += $restart_delay * count($failed) * 2; foreach ($failed as $failed_task) { $usage_start = min($usage_start, $failed_task->getFailureTime()); }
$rows[] = array( phutil_tag('em', array(), pht('Temporary Failures')), count($failed), null, null, ); }
$logs = id(new PhabricatorDaemonLogQuery()) ->setViewer($viewer) ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) ->setAllowStatusWrites(true) ->execute();
$taskmasters = 0; foreach ($logs as $log) { if ($log->getDaemon() == 'PhabricatorTaskmasterDaemon') { $taskmasters++; } }
if ($taskmasters && $usage_total) { // Total number of wall-time seconds the daemons have been running since // the oldest event. For very short times round up to 15s so we don't // render any ridiculous numbers if you reload the page immediately after // restarting the daemons. $available_time = $taskmasters * max(15, (time() - $usage_start));
// Percentage of those wall-time seconds we can account for, which the // daemons spent doing work: $used_time = ($usage_total / $available_time);
$rows[] = array( phutil_tag('em', array(), pht('Queue Utilization (Approximate)')), sprintf('%.1f%%', 100 * $used_time), null, null, ); }
$completed_table = new AphrontTableView($rows); $completed_table->setNoDataString( pht('No tasks have completed in the last 15 minutes.')); $completed_table->setHeaders( array( pht('Class'), pht('Count'), pht('Average Duration'), pht('Average Queue Time'), )); $completed_table->setColumnClasses( array( 'wide', 'n', 'n', 'n', ));
$completed_panel = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Recently Completed Tasks (Last 15m)')) ->setTable($completed_table);
$daemon_table = id(new PhabricatorDaemonLogListView()) ->setUser($viewer) ->setDaemonLogs($logs);
$daemon_panel = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Active Daemons')) ->setTable($daemon_table);
$tasks = id(new PhabricatorWorkerLeaseQuery()) ->setSkipLease(true) ->withLeasedTasks(true) ->setLimit(100) ->execute();
$tasks_table = id(new PhabricatorDaemonTasksTableView()) ->setTasks($tasks) ->setNoDataString(pht('No tasks are leased by workers.'));
$leased_panel = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Leased Tasks')) ->setTable($tasks_table);
$task_table = new PhabricatorWorkerActiveTask(); $queued = queryfx_all( $task_table->establishConnection('r'), 'SELECT taskClass, count(*) N FROM %T GROUP BY taskClass ORDER BY N DESC', $task_table->getTableName());
$rows = array(); foreach ($queued as $row) { $rows[] = array( $row['taskClass'], number_format($row['N']), ); }
$queued_table = new AphrontTableView($rows); $queued_table->setHeaders( array( pht('Class'), pht('Count'), )); $queued_table->setColumnClasses( array( 'wide', 'n', )); $queued_table->setNoDataString(pht('Task queue is empty.'));
$queued_panel = new PHUIObjectBoxView(); $queued_panel->setHeaderText(pht('Queued Tasks')); $queued_panel->setTable($queued_table);
$upcoming = id(new PhabricatorWorkerLeaseQuery()) ->setLimit(10) ->setSkipLease(true) ->execute();
$upcoming_panel = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Next In Queue')) ->setTable( id(new PhabricatorDaemonTasksTableView()) ->setTasks($upcoming) ->setNoDataString(pht('Task queue is empty.')));
$triggers = id(new PhabricatorWorkerTriggerQuery()) ->setViewer($viewer) ->setOrder(PhabricatorWorkerTriggerQuery::ORDER_EXECUTION) ->withNextEventBetween(0, null) ->needEvents(true) ->setLimit(10) ->execute();
$triggers_table = $this->buildTriggersTable($triggers);
$triggers_panel = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Upcoming Triggers')) ->setTable($triggers_table);
$crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Console'));
$nav = $this->buildSideNavView(); $nav->selectFilter('/'); $nav->appendChild( array( $crumbs, $completed_panel, $daemon_panel, $queued_panel, $leased_panel, $upcoming_panel, $triggers_panel, ));
return $this->newPage() ->setTitle(pht('Console')) ->appendChild($nav);
}
private function buildTriggersTable(array $triggers) { $viewer = $this->getViewer();
$rows = array(); foreach ($triggers as $trigger) { $event = $trigger->getEvent(); if ($event) { $last_epoch = $event->getLastEventEpoch(); $next_epoch = $event->getNextEventEpoch(); } else { $last_epoch = null; $next_epoch = null; }
$rows[] = array( $trigger->getID(), $trigger->getClockClass(), $trigger->getActionClass(), $last_epoch ? phabricator_datetime($last_epoch, $viewer) : null, $next_epoch ? phabricator_datetime($next_epoch, $viewer) : null, ); }
return id(new AphrontTableView($rows)) ->setNoDataString(pht('There are no upcoming event triggers.')) ->setHeaders( array( pht('ID'), pht('Clock'), pht('Action'), pht('Last'), pht('Next'), )) ->setColumnClasses( array( '', '', 'wide', 'date', 'date', )); }
}