Path: blob/master/src/applications/daemon/controller/PhabricatorDaemonConsoleController.php
12256 views
<?php12final class PhabricatorDaemonConsoleController3extends PhabricatorDaemonController {45public function handleRequest(AphrontRequest $request) {6$viewer = $this->getViewer();78$window_start = (time() - (60 * 15));910// Assume daemons spend about 250ms second in overhead per task acquiring11// leases and doing other bookkeeping. This is probably an over-estimation,12// but we'd rather show that utilization is too high than too low.13$lease_overhead = 0.250;1415$completed = id(new PhabricatorWorkerArchiveTaskQuery())16->withDateModifiedSince($window_start)17->execute();1819$failed = id(new PhabricatorWorkerActiveTask())->loadAllWhere(20'failureTime > %d',21$window_start);2223$usage_total = 0;24$usage_start = PHP_INT_MAX;2526$completed_info = array();27foreach ($completed as $completed_task) {28$class = $completed_task->getTaskClass();29if (empty($completed_info[$class])) {30$completed_info[$class] = array(31'n' => 0,32'duration' => 0,33'queueTime' => 0,34);35}36$completed_info[$class]['n']++;37$duration = $completed_task->getDuration();38$completed_info[$class]['duration'] += $duration;3940// NOTE: Duration is in microseconds, but we're just using seconds to41// compute utilization.42$usage_total += $lease_overhead + ($duration / 1000000);43$usage_start = min($usage_start, $completed_task->getDateModified());4445$date_archived = $completed_task->getArchivedEpoch();46$queue_seconds = $date_archived - $completed_task->getDateCreated();4748// Don't measure queue time for tasks that completed in the same49// epoch-second they were created in.50if ($queue_seconds > 0) {51$sec_in_us = phutil_units('1 second in microseconds');52$queue_us = $queue_seconds * $sec_in_us;53$queue_exclusive_us = $queue_us - $duration;54$queue_exclusive_seconds = $queue_exclusive_us / $sec_in_us;55$rounded = floor($queue_exclusive_seconds);56$completed_info[$class]['queueTime'] += $rounded;57}58}5960$completed_info = isort($completed_info, 'n');6162$rows = array();63foreach ($completed_info as $class => $info) {64$duration_avg = new PhutilNumber((int)($info['duration'] / $info['n']));65$queue_avg = new PhutilNumber((int)($info['queueTime'] / $info['n']));66$rows[] = array(67$class,68number_format($info['n']),69pht('%s us', $duration_avg),70pht('%s s', $queue_avg),71);72}7374if ($failed) {75// Add the time it takes to restart the daemons. This includes a guess76// about other overhead of 2X.77$restart_delay = PhutilDaemonHandle::getWaitBeforeRestart();78$usage_total += $restart_delay * count($failed) * 2;79foreach ($failed as $failed_task) {80$usage_start = min($usage_start, $failed_task->getFailureTime());81}8283$rows[] = array(84phutil_tag('em', array(), pht('Temporary Failures')),85count($failed),86null,87null,88);89}9091$logs = id(new PhabricatorDaemonLogQuery())92->setViewer($viewer)93->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)94->setAllowStatusWrites(true)95->execute();9697$taskmasters = 0;98foreach ($logs as $log) {99if ($log->getDaemon() == 'PhabricatorTaskmasterDaemon') {100$taskmasters++;101}102}103104if ($taskmasters && $usage_total) {105// Total number of wall-time seconds the daemons have been running since106// the oldest event. For very short times round up to 15s so we don't107// render any ridiculous numbers if you reload the page immediately after108// restarting the daemons.109$available_time = $taskmasters * max(15, (time() - $usage_start));110111// Percentage of those wall-time seconds we can account for, which the112// daemons spent doing work:113$used_time = ($usage_total / $available_time);114115$rows[] = array(116phutil_tag('em', array(), pht('Queue Utilization (Approximate)')),117sprintf('%.1f%%', 100 * $used_time),118null,119null,120);121}122123$completed_table = new AphrontTableView($rows);124$completed_table->setNoDataString(125pht('No tasks have completed in the last 15 minutes.'));126$completed_table->setHeaders(127array(128pht('Class'),129pht('Count'),130pht('Average Duration'),131pht('Average Queue Time'),132));133$completed_table->setColumnClasses(134array(135'wide',136'n',137'n',138'n',139));140141$completed_panel = id(new PHUIObjectBoxView())142->setHeaderText(pht('Recently Completed Tasks (Last 15m)'))143->setTable($completed_table);144145$daemon_table = id(new PhabricatorDaemonLogListView())146->setUser($viewer)147->setDaemonLogs($logs);148149$daemon_panel = id(new PHUIObjectBoxView())150->setHeaderText(pht('Active Daemons'))151->setTable($daemon_table);152153$tasks = id(new PhabricatorWorkerLeaseQuery())154->setSkipLease(true)155->withLeasedTasks(true)156->setLimit(100)157->execute();158159$tasks_table = id(new PhabricatorDaemonTasksTableView())160->setTasks($tasks)161->setNoDataString(pht('No tasks are leased by workers.'));162163$leased_panel = id(new PHUIObjectBoxView())164->setHeaderText(pht('Leased Tasks'))165->setTable($tasks_table);166167$task_table = new PhabricatorWorkerActiveTask();168$queued = queryfx_all(169$task_table->establishConnection('r'),170'SELECT taskClass, count(*) N FROM %T GROUP BY taskClass171ORDER BY N DESC',172$task_table->getTableName());173174$rows = array();175foreach ($queued as $row) {176$rows[] = array(177$row['taskClass'],178number_format($row['N']),179);180}181182$queued_table = new AphrontTableView($rows);183$queued_table->setHeaders(184array(185pht('Class'),186pht('Count'),187));188$queued_table->setColumnClasses(189array(190'wide',191'n',192));193$queued_table->setNoDataString(pht('Task queue is empty.'));194195$queued_panel = new PHUIObjectBoxView();196$queued_panel->setHeaderText(pht('Queued Tasks'));197$queued_panel->setTable($queued_table);198199$upcoming = id(new PhabricatorWorkerLeaseQuery())200->setLimit(10)201->setSkipLease(true)202->execute();203204$upcoming_panel = id(new PHUIObjectBoxView())205->setHeaderText(pht('Next In Queue'))206->setTable(207id(new PhabricatorDaemonTasksTableView())208->setTasks($upcoming)209->setNoDataString(pht('Task queue is empty.')));210211$triggers = id(new PhabricatorWorkerTriggerQuery())212->setViewer($viewer)213->setOrder(PhabricatorWorkerTriggerQuery::ORDER_EXECUTION)214->withNextEventBetween(0, null)215->needEvents(true)216->setLimit(10)217->execute();218219$triggers_table = $this->buildTriggersTable($triggers);220221$triggers_panel = id(new PHUIObjectBoxView())222->setHeaderText(pht('Upcoming Triggers'))223->setTable($triggers_table);224225$crumbs = $this->buildApplicationCrumbs();226$crumbs->addTextCrumb(pht('Console'));227228$nav = $this->buildSideNavView();229$nav->selectFilter('/');230$nav->appendChild(231array(232$crumbs,233$completed_panel,234$daemon_panel,235$queued_panel,236$leased_panel,237$upcoming_panel,238$triggers_panel,239));240241return $this->newPage()242->setTitle(pht('Console'))243->appendChild($nav);244245}246247private function buildTriggersTable(array $triggers) {248$viewer = $this->getViewer();249250$rows = array();251foreach ($triggers as $trigger) {252$event = $trigger->getEvent();253if ($event) {254$last_epoch = $event->getLastEventEpoch();255$next_epoch = $event->getNextEventEpoch();256} else {257$last_epoch = null;258$next_epoch = null;259}260261$rows[] = array(262$trigger->getID(),263$trigger->getClockClass(),264$trigger->getActionClass(),265$last_epoch ? phabricator_datetime($last_epoch, $viewer) : null,266$next_epoch ? phabricator_datetime($next_epoch, $viewer) : null,267);268}269270return id(new AphrontTableView($rows))271->setNoDataString(pht('There are no upcoming event triggers.'))272->setHeaders(273array(274pht('ID'),275pht('Clock'),276pht('Action'),277pht('Last'),278pht('Next'),279))280->setColumnClasses(281array(282'',283'',284'wide',285'date',286'date',287));288}289290}291292293