Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/daemon/controller/PhabricatorDaemonConsoleController.php
12256 views
1
<?php
2
3
final class PhabricatorDaemonConsoleController
4
extends PhabricatorDaemonController {
5
6
public function handleRequest(AphrontRequest $request) {
7
$viewer = $this->getViewer();
8
9
$window_start = (time() - (60 * 15));
10
11
// Assume daemons spend about 250ms second in overhead per task acquiring
12
// leases and doing other bookkeeping. This is probably an over-estimation,
13
// but we'd rather show that utilization is too high than too low.
14
$lease_overhead = 0.250;
15
16
$completed = id(new PhabricatorWorkerArchiveTaskQuery())
17
->withDateModifiedSince($window_start)
18
->execute();
19
20
$failed = id(new PhabricatorWorkerActiveTask())->loadAllWhere(
21
'failureTime > %d',
22
$window_start);
23
24
$usage_total = 0;
25
$usage_start = PHP_INT_MAX;
26
27
$completed_info = array();
28
foreach ($completed as $completed_task) {
29
$class = $completed_task->getTaskClass();
30
if (empty($completed_info[$class])) {
31
$completed_info[$class] = array(
32
'n' => 0,
33
'duration' => 0,
34
'queueTime' => 0,
35
);
36
}
37
$completed_info[$class]['n']++;
38
$duration = $completed_task->getDuration();
39
$completed_info[$class]['duration'] += $duration;
40
41
// NOTE: Duration is in microseconds, but we're just using seconds to
42
// compute utilization.
43
$usage_total += $lease_overhead + ($duration / 1000000);
44
$usage_start = min($usage_start, $completed_task->getDateModified());
45
46
$date_archived = $completed_task->getArchivedEpoch();
47
$queue_seconds = $date_archived - $completed_task->getDateCreated();
48
49
// Don't measure queue time for tasks that completed in the same
50
// epoch-second they were created in.
51
if ($queue_seconds > 0) {
52
$sec_in_us = phutil_units('1 second in microseconds');
53
$queue_us = $queue_seconds * $sec_in_us;
54
$queue_exclusive_us = $queue_us - $duration;
55
$queue_exclusive_seconds = $queue_exclusive_us / $sec_in_us;
56
$rounded = floor($queue_exclusive_seconds);
57
$completed_info[$class]['queueTime'] += $rounded;
58
}
59
}
60
61
$completed_info = isort($completed_info, 'n');
62
63
$rows = array();
64
foreach ($completed_info as $class => $info) {
65
$duration_avg = new PhutilNumber((int)($info['duration'] / $info['n']));
66
$queue_avg = new PhutilNumber((int)($info['queueTime'] / $info['n']));
67
$rows[] = array(
68
$class,
69
number_format($info['n']),
70
pht('%s us', $duration_avg),
71
pht('%s s', $queue_avg),
72
);
73
}
74
75
if ($failed) {
76
// Add the time it takes to restart the daemons. This includes a guess
77
// about other overhead of 2X.
78
$restart_delay = PhutilDaemonHandle::getWaitBeforeRestart();
79
$usage_total += $restart_delay * count($failed) * 2;
80
foreach ($failed as $failed_task) {
81
$usage_start = min($usage_start, $failed_task->getFailureTime());
82
}
83
84
$rows[] = array(
85
phutil_tag('em', array(), pht('Temporary Failures')),
86
count($failed),
87
null,
88
null,
89
);
90
}
91
92
$logs = id(new PhabricatorDaemonLogQuery())
93
->setViewer($viewer)
94
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
95
->setAllowStatusWrites(true)
96
->execute();
97
98
$taskmasters = 0;
99
foreach ($logs as $log) {
100
if ($log->getDaemon() == 'PhabricatorTaskmasterDaemon') {
101
$taskmasters++;
102
}
103
}
104
105
if ($taskmasters && $usage_total) {
106
// Total number of wall-time seconds the daemons have been running since
107
// the oldest event. For very short times round up to 15s so we don't
108
// render any ridiculous numbers if you reload the page immediately after
109
// restarting the daemons.
110
$available_time = $taskmasters * max(15, (time() - $usage_start));
111
112
// Percentage of those wall-time seconds we can account for, which the
113
// daemons spent doing work:
114
$used_time = ($usage_total / $available_time);
115
116
$rows[] = array(
117
phutil_tag('em', array(), pht('Queue Utilization (Approximate)')),
118
sprintf('%.1f%%', 100 * $used_time),
119
null,
120
null,
121
);
122
}
123
124
$completed_table = new AphrontTableView($rows);
125
$completed_table->setNoDataString(
126
pht('No tasks have completed in the last 15 minutes.'));
127
$completed_table->setHeaders(
128
array(
129
pht('Class'),
130
pht('Count'),
131
pht('Average Duration'),
132
pht('Average Queue Time'),
133
));
134
$completed_table->setColumnClasses(
135
array(
136
'wide',
137
'n',
138
'n',
139
'n',
140
));
141
142
$completed_panel = id(new PHUIObjectBoxView())
143
->setHeaderText(pht('Recently Completed Tasks (Last 15m)'))
144
->setTable($completed_table);
145
146
$daemon_table = id(new PhabricatorDaemonLogListView())
147
->setUser($viewer)
148
->setDaemonLogs($logs);
149
150
$daemon_panel = id(new PHUIObjectBoxView())
151
->setHeaderText(pht('Active Daemons'))
152
->setTable($daemon_table);
153
154
$tasks = id(new PhabricatorWorkerLeaseQuery())
155
->setSkipLease(true)
156
->withLeasedTasks(true)
157
->setLimit(100)
158
->execute();
159
160
$tasks_table = id(new PhabricatorDaemonTasksTableView())
161
->setTasks($tasks)
162
->setNoDataString(pht('No tasks are leased by workers.'));
163
164
$leased_panel = id(new PHUIObjectBoxView())
165
->setHeaderText(pht('Leased Tasks'))
166
->setTable($tasks_table);
167
168
$task_table = new PhabricatorWorkerActiveTask();
169
$queued = queryfx_all(
170
$task_table->establishConnection('r'),
171
'SELECT taskClass, count(*) N FROM %T GROUP BY taskClass
172
ORDER BY N DESC',
173
$task_table->getTableName());
174
175
$rows = array();
176
foreach ($queued as $row) {
177
$rows[] = array(
178
$row['taskClass'],
179
number_format($row['N']),
180
);
181
}
182
183
$queued_table = new AphrontTableView($rows);
184
$queued_table->setHeaders(
185
array(
186
pht('Class'),
187
pht('Count'),
188
));
189
$queued_table->setColumnClasses(
190
array(
191
'wide',
192
'n',
193
));
194
$queued_table->setNoDataString(pht('Task queue is empty.'));
195
196
$queued_panel = new PHUIObjectBoxView();
197
$queued_panel->setHeaderText(pht('Queued Tasks'));
198
$queued_panel->setTable($queued_table);
199
200
$upcoming = id(new PhabricatorWorkerLeaseQuery())
201
->setLimit(10)
202
->setSkipLease(true)
203
->execute();
204
205
$upcoming_panel = id(new PHUIObjectBoxView())
206
->setHeaderText(pht('Next In Queue'))
207
->setTable(
208
id(new PhabricatorDaemonTasksTableView())
209
->setTasks($upcoming)
210
->setNoDataString(pht('Task queue is empty.')));
211
212
$triggers = id(new PhabricatorWorkerTriggerQuery())
213
->setViewer($viewer)
214
->setOrder(PhabricatorWorkerTriggerQuery::ORDER_EXECUTION)
215
->withNextEventBetween(0, null)
216
->needEvents(true)
217
->setLimit(10)
218
->execute();
219
220
$triggers_table = $this->buildTriggersTable($triggers);
221
222
$triggers_panel = id(new PHUIObjectBoxView())
223
->setHeaderText(pht('Upcoming Triggers'))
224
->setTable($triggers_table);
225
226
$crumbs = $this->buildApplicationCrumbs();
227
$crumbs->addTextCrumb(pht('Console'));
228
229
$nav = $this->buildSideNavView();
230
$nav->selectFilter('/');
231
$nav->appendChild(
232
array(
233
$crumbs,
234
$completed_panel,
235
$daemon_panel,
236
$queued_panel,
237
$leased_panel,
238
$upcoming_panel,
239
$triggers_panel,
240
));
241
242
return $this->newPage()
243
->setTitle(pht('Console'))
244
->appendChild($nav);
245
246
}
247
248
private function buildTriggersTable(array $triggers) {
249
$viewer = $this->getViewer();
250
251
$rows = array();
252
foreach ($triggers as $trigger) {
253
$event = $trigger->getEvent();
254
if ($event) {
255
$last_epoch = $event->getLastEventEpoch();
256
$next_epoch = $event->getNextEventEpoch();
257
} else {
258
$last_epoch = null;
259
$next_epoch = null;
260
}
261
262
$rows[] = array(
263
$trigger->getID(),
264
$trigger->getClockClass(),
265
$trigger->getActionClass(),
266
$last_epoch ? phabricator_datetime($last_epoch, $viewer) : null,
267
$next_epoch ? phabricator_datetime($next_epoch, $viewer) : null,
268
);
269
}
270
271
return id(new AphrontTableView($rows))
272
->setNoDataString(pht('There are no upcoming event triggers.'))
273
->setHeaders(
274
array(
275
pht('ID'),
276
pht('Clock'),
277
pht('Action'),
278
pht('Last'),
279
pht('Next'),
280
))
281
->setColumnClasses(
282
array(
283
'',
284
'',
285
'wide',
286
'date',
287
'date',
288
));
289
}
290
291
}
292
293