File Coverage

blib/lib/Mojolicious/Plugin/Minion/Overview/Backend/mysql.pm
Criterion Covered Total %
statement 6 100 6.0
branch 0 10 0.0
condition 0 4 0.0
subroutine 2 14 14.2
pod 10 10 100.0
total 18 138 13.0


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Minion::Overview::Backend::mysql;
2 1     1   7 use Mojo::Base 'Mojolicious::Plugin::Minion::Overview::Backend';
  1         2  
  1         7  
3              
4 1     1   572 use Mojo::JSON qw(decode_json);
  1         22931  
  1         1810  
5              
6              
7             =head2 failed_jobs
8              
9             Search failed jobs
10              
11             =cut
12              
13             sub failed_jobs {
14 0     0 1   return shift->where('state', 'failed')
15             ->jobs();
16             }
17              
18             =head2 job_runtime_metrics
19              
20             Job runtime metrics
21              
22             =cut
23              
24             sub job_runtime_metrics {
25 0     0 1   my ($self, $job) = @_;
26              
27 0           my $start = $self->start;
28              
29 0           my $sql = <
30             SELECT
31             `minion_jobs`.`started` AS `x`,
32             TIME_TO_SEC(TIMEDIFF(COALESCE(`minion_jobs`.`finished`, NOW()), `minion_jobs`.`started`)) AS `y`,
33             `minion_jobs`.`state`
34             FROM `minion_jobs`
35             WHERE
36             `minion_jobs`.`task` = ?
37             AND `minion_jobs`.`created` >= $start
38             ORDER BY `minion_jobs`.`created`
39             LIMIT 1000
40             SQL
41            
42 0           my $collection = $self->db->query($sql, $job)->hashes;
43              
44 0           return $collection;
45             }
46              
47             =head2 job_throughput_metrics
48              
49             Job throughput metrics
50              
51             =cut
52              
53             sub job_throughput_metrics {
54 0     0 1   my ($self, $job) = @_;
55              
56 0           my $start = $self->start;
57              
58 0           my $sql = <
59             SELECT
60             DATE_FORMAT(`minion_jobs`.`started`, "%Y-%m-%d %H:00:00") AS `x`,
61             COUNT(*) AS `y`,
62             `minion_jobs`.`state`
63             FROM `minion_jobs`
64             WHERE
65             `minion_jobs`.`task` = ?
66             AND `minion_jobs`.`created` >= $start
67             GROUP BY `x`, `minion_jobs`.`state`
68             ORDER BY `x` ASC
69             SQL
70            
71 0           my $collection = $self->db->query($sql, $job)->hashes;
72              
73 0           return $collection;
74             }
75              
76             =head2 jobs
77              
78             Search jobs
79              
80             =cut
81              
82             sub jobs {
83 0     0 1   my $self = shift;
84              
85 0           my @where = ('`minion_jobs`.`created` >= ' . $self->start);
86 0           my @params;
87              
88             # Search by term
89 0 0         if (my $term = $self->query->{ term }) {
90 0           push(@where, 'CONCAT(`minion_jobs`.`task`, CAST(`minion_jobs`.`notes` AS CHAR)) LIKE ?');
91 0           push(@params, '%' . $term . '%');
92             }
93              
94             # Search where fields
95 0           for my $field (keys(%{ $self->query->{ where } })) {
  0            
96 0           push(@where, "`$field` = ?");
97 0           push(@params, $self->query->{ where }->{ $field });
98             }
99              
100             # Search tags
101 0           for my $tag (@{ $self->query->{ tags } }) {
  0            
102 0           push(@where, '(`minion_jobs`.`notes` LIKE ? OR `minion_jobs`.`task` = ?)');
103 0           push(@params, '%"tags":[%' . $tag . '%]%', $tag);
104             }
105              
106 0           my $where_clause = join("\n and ", @where);
107              
108 0           my $sql_count = <
109             SELECT
110             count(*) AS `total`
111             FROM `minion_jobs`
112             LEFT JOIN `minion_jobs_depends` ON `minion_jobs_depends`.`child_id` = `minion_jobs`.`id`
113             WHERE
114             $where_clause
115             SQL
116              
117 0           my $total = $self->db->query($sql_count, @params)->hash->{ total };
118              
119 0           my $sql = <
120             SELECT
121             `minion_jobs`.*,
122             `minion_jobs_depends`.`parent_id`,
123             TIME_TO_SEC(TIMEDIFF(IF(`minion_jobs`.`finished` = '0000-00-00 00:00:00', NOW(), `minion_jobs`.`finished`), IF(`minion_jobs`.`started` = '0000-00-00 00:00:00', NOW(), `minion_jobs`.`started`))) AS `runtime`
124             FROM `minion_jobs`
125             LEFT JOIN `minion_jobs_depends` ON `minion_jobs_depends`.`child_id` = `minion_jobs`.`id`
126             WHERE
127             $where_clause
128             ORDER BY `minion_jobs`.`created` DESC
129             LIMIT ?
130             OFFSET ?
131             SQL
132              
133 0           my $offset = ($self->query->{ page } - 1) * $self->query->{ limit };
134              
135 0           push(@params, $self->query->{ limit }, $offset);
136            
137 0           my $collection = $self->db->query($sql, @params)->hashes;
138 0           my $count = scalar(@$collection);
139              
140             $collection->each(sub {
141 0     0     my $object = shift;
142              
143 0 0         $object->{ tags } = eval { decode_json($object->{ notes })->{ tags } || [$object->{ task }] };
  0            
144 0           });
145              
146             my $response = {
147             results => $collection,
148             query => {
149 0           %{ $self->query },
150             total => $total,
151             prev_page => {
152             term => $self->query->{ term },
153             tags => $self->query->{ tags },
154             page => ($self->query->{ page } - 1) || 0,
155 0           %{ $self->query->{ where } }
156             },
157             next_page => {
158             term => $self->query->{ term },
159             tags => $self->query->{ tags },
160             page => $count < $self->query->{ limit } ? $self->query->{ page } : $self->query->{ page } + 1,
161 0 0 0       %{ $self->query->{ where } }
  0            
162             }
163             },
164             };
165              
166             # Clear query
167 0           $self->clear_query;
168              
169 0           return $response;
170             }
171              
172             =head2 overview
173              
174             Dashboard overview
175              
176             =cut
177              
178             sub overview {
179 0     0 1   my $self = shift;
180              
181 0           my $start = $self->start;
182              
183 0           my $stats_sql = <
184             SELECT
185             COALESCE(SUM(IF(`minion_jobs`.`state` = 'finished', 1, 0)), 0) AS `finished`,
186             COALESCE(SUM(IF(`minion_jobs`.`state` = 'failed', 1, 0)), 0) AS `failed`,
187             COALESCE(SUM(IF(`minion_jobs`.`state` = 'active', 1, 0)), 0) AS `active`,
188             COALESCE(SUM(IF(`minion_jobs`.`state` = 'inactive', 1, 0)), 0) AS `inactive`
189             FROM `minion_jobs`
190             WHERE
191             `minion_jobs`.`created` >= $start
192             SQL
193            
194 0           my $jobs = $self->db->query($stats_sql)->hash;
195              
196 0           my $workers_sql = <
197             SELECT
198             COUNT(*) AS `workers`
199             FROM `minion_workers`
200             SQL
201            
202 0           my $workers = $self->db->query($workers_sql)->hash->{ workers };
203              
204             return [
205             {
206             title => 'Finished jobs',
207             count => $jobs->{ finished },
208             },
209             {
210             title => 'Failed jobs',
211             count => $jobs->{ failed },
212             },
213             {
214             title => 'Active jobs',
215             count => $jobs->{ active },
216             },
217             {
218             title => 'Inactive jobs',
219             count => $jobs->{ inactive },
220             },
221             {
222 0           title => 'Active workers',
223             count => $workers,
224             },
225             ];
226             }
227              
228             =head2 unique_jobs
229              
230             Search the list of unique jobs
231              
232             =cut
233              
234             sub unique_jobs {
235 0     0 1   my $self = shift;
236              
237 0           my @where = ('`minion_jobs`.`created` >= ' . $self->start, "`minion_jobs`.`state` IN ('finished', 'failed')");
238 0           my @params;
239              
240             # Search by term
241 0 0         if (my $term = $self->query->{ term }) {
242 0           push(@where, 'CONCAT(`minion_jobs`.`task`, CAST(`minion_jobs`.`notes` AS CHAR)) LIKE ?');
243 0           push(@params, '%' . $term . '%');
244             }
245              
246 0           my $where_clause = join("\n and ", @where);
247              
248 0           my $sql_count = <
249             SELECT
250             COUNT(DISTINCT(`minion_jobs`.`task`)) AS `total`
251             FROM `minion_jobs`
252             WHERE
253             $where_clause
254             SQL
255              
256 0           my $total = $self->db->query($sql_count)->hash->{ total };
257              
258 0           my $sql = <
259             SELECT
260             `task`,
261             CAST(COALESCE(`finished_in` / `finished`, 0) AS DECIMAL(10,2)) AS `finished`,
262             CAST(COALESCE(`failed_in` / `failed`, 0) AS DECIMAL(10,2)) AS `failed`
263             FROM (
264             SELECT
265             `task`,
266             SUM(IF(`minion_jobs`.`state` = 'finished', TIME_TO_SEC(TIMEDIFF(`minion_jobs`.`finished`, `minion_jobs`.`started`)), 0)) AS `finished_in`,
267             SUM(IF(`minion_jobs`.`state` = 'finished', 1, 0)) AS `finished`,
268             SUM(IF(`minion_jobs`.`state` = 'failed', TIME_TO_SEC(TIMEDIFF(`minion_jobs`.`finished`, `minion_jobs`.`started`)), 0)) AS `failed_in`,
269             SUM(IF(`minion_jobs`.`state` = 'failed', 1, 0)) AS `failed`
270              
271             FROM `minion_jobs`
272             WHERE
273             $where_clause
274             GROUP BY `minion_jobs`.`task`
275             LIMIT ?
276             OFFSET ?
277             ) AS `metrics`
278             ORDER BY COALESCE(`finished_in` / `finished`, 0) DESC
279             SQL
280              
281 0           my $offset = ($self->query->{ page } - 1) * $self->query->{ limit };
282            
283 0           push(@params, $self->query->{ limit }, $offset);
284            
285 0           my $collection = $self->db->query($sql, @params)->hashes;
286 0           my $count = scalar(@$collection);
287              
288             my $response = {
289             results => $collection,
290             query => {
291 0           %{ $self->query },
292             total => $total,
293              
294             prev_page => {
295             term => $self->query->{ term },
296             tags => $self->query->{ tags },
297             page => ($self->query->{ page } - 1) || 0,
298 0           %{ $self->query->{ where } }
299             },
300             next_page => {
301             term => $self->query->{ term },
302             tags => $self->query->{ tags },
303             page => $count < $self->query->{ limit } ? $self->query->{ page } : $self->query->{ page } + 1,
304 0 0 0       %{ $self->query->{ where } }
  0            
305             }
306             },
307             };
308            
309             # Clear query
310 0           $self->clear_query;
311              
312 0           return $response;
313             }
314              
315             =head2 worker
316              
317             Find a worker by id
318              
319             =cut
320              
321             sub worker {
322 0     0 1   my ($self, $id) = @_;
323              
324              
325 0           my $sql = <
326             SELECT
327             `minion_workers`.*,
328             CAST(AVG(TIME_TO_SEC(TIMEDIFF(`minion_jobs`.`started`, `minion_jobs`.`created`))) AS DECIMAL(10, 2)) AS `wait`
329             FROM `minion_workers`
330             INNER JOIN `minion_jobs` on `minion_jobs`.`worker` = `minion_workers`.`id`
331             WHERE
332             `minion_workers`.`id` = ?
333             SQL
334              
335 0           my $worker = $self->db->query($sql, $id)->hash;
336              
337 0           return $worker;
338             }
339              
340             =head2 worker_waittime_metrics
341              
342             Worker waittime metrics
343              
344             =cut
345              
346             sub worker_waittime_metrics {
347 0     0 1   my ($self, $worker_id) = @_;
348              
349 0           my $start = $self->start;
350              
351 0           my $sql = <
352             SELECT
353             `minion_jobs`.`created` AS `x`,
354             TIME_TO_SEC(TIMEDIFF(`minion_jobs`.`started`, `minion_jobs`.`created`)) AS `y`,
355             `minion_jobs`.`state`
356             FROM `minion_jobs`
357             INNER JOIN `minion_workers` on `minion_workers`.`id` = `minion_jobs`.`worker`
358             WHERE
359             `minion_workers`.`id` = ?
360             AND `minion_jobs`.`created` >= $start
361             ORDER BY `minion_jobs`.`created`
362             LIMIT 1000
363             SQL
364            
365 0           my $collection = $self->db->query($sql, $worker_id)->hashes;
366              
367 0           return $collection;
368             }
369              
370             =head2 worker_throughput_metrics
371              
372             Worker throughput metrics
373              
374             =cut
375              
376             sub worker_throughput_metrics {
377 0     0 1   my ($self, $worker_id) = @_;
378              
379 0           my $start = $self->start;
380              
381 0           my $sql = <
382             SELECT
383             DATE_FORMAT(`minion_jobs`.`started`, "%Y-%m-%d %H:00:00") AS `x`,
384             COUNT(*) AS `y`,
385             `minion_jobs`.`state`
386             FROM `minion_jobs`
387             INNER JOIN `minion_workers` on `minion_workers`.`id` = `minion_jobs`.`worker`
388             WHERE
389             `minion_workers`.`id` = ?
390             AND `minion_jobs`.`created` >= $start
391             GROUP BY `x`, `state`
392             ORDER BY `x` ASC
393             SQL
394            
395 0           my $collection = $self->db->query($sql, $worker_id)->hashes;
396              
397 0           return $collection;
398             }
399              
400             =head2 workers
401              
402             Get workers information
403              
404             =cut
405              
406             sub workers {
407 0     0 1   my $self = shift;
408              
409 0           my $sql = <
410             SELECT
411             `minion_workers`.*,
412             CAST(AVG(TIME_TO_SEC(TIMEDIFF(`minion_jobs`.`started`, `minion_jobs`.`created`))) AS DECIMAL(10, 2)) AS `wait`
413             FROM `minion_workers`
414             INNER JOIN `minion_jobs` on `minion_jobs`.`worker` = `minion_workers`.`id`
415             GROUP BY `minion_workers`.`id`
416             SQL
417              
418 0           my $stats_sql = <
419             SELECT
420             COUNT(*) AS `performed`,
421             COALESCE(SUM(IF(`minion_jobs`.`state` = 'active', 1, 0)), 0) AS `active`,
422             COALESCE(SUM(IF(`minion_jobs`.`state` = 'finished', 1, 0)), 0) AS `finished`,
423             COALESCE(SUM(IF(`minion_jobs`.`state` = 'failed', 1, 0)), 0) AS `failed`
424             FROM `minion_jobs`
425             INNER JOIN `minion_workers` on `minion_workers`.`id` = `minion_jobs`.`worker`
426             WHERE
427             `minion_workers`.`id` = ?
428             SQL
429            
430 0           my $collection = $self->db->query($sql)->hashes;
431              
432             $collection->each(sub {
433 0     0     my $object = shift;
434              
435 0           $object->{ status } = eval { decode_json($object->{ status }) };
  0            
436 0           $object->{ jobs_stats } = $self->db->query($stats_sql, $object->{ id })->hash;
437 0           });
438              
439 0           return $collection;
440             }
441              
442             1;