Skip to content

Commit

Permalink
allow for locks to be managed from the admin ui
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Dec 10, 2017
1 parent 3a1d694 commit 9c5d94d
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 2 deletions.
3 changes: 3 additions & 0 deletions Changes
@@ -1,5 +1,8 @@

8.06 2017-12-11
- Added active_locks field to stats methods in Minion, Minion::Backend and
Minion::Backend::Pg again.
- Improved Mojolicious::Plugin::Minion::Admin with support for locks.

8.05 2017-12-10
- Removed active_locks field from stats methods again since it did not work
Expand Down
6 changes: 6 additions & 0 deletions lib/Minion.pm
Expand Up @@ -633,6 +633,12 @@ These fields are currently available:
Number of jobs in C<active> state.
=item active_locks
active_locks => 100
Number of active named locks.
=item active_workers
active_workers => 100
Expand Down
6 changes: 6 additions & 0 deletions lib/Minion/Backend.pm
Expand Up @@ -619,6 +619,12 @@ These fields are currently available:
Number of jobs in C<active> state.
=item active_locks
active_locks => 100
Number of active named locks.
=item active_workers
active_workers => 100
Expand Down
8 changes: 8 additions & 0 deletions lib/Minion/Backend/Pg.pm
Expand Up @@ -215,6 +215,8 @@ sub stats {
count(*) filter (where state = 'finished') as finished_jobs,
count(*) filter (where state = 'inactive'
and delayed > now()) as delayed_jobs,
(select count(*) from minion_locks where expires > now())
as active_locks,
count(distinct worker) filter (where state = 'active') as active_workers,
(select case when is_called then last_value else 0 end
from minion_jobs_id_seq) as enqueued_jobs,
Expand Down Expand Up @@ -854,6 +856,12 @@ These fields are currently available:
Number of jobs in C<active> state.
=item active_locks
active_locks => 100
Number of active named locks.
=item active_workers
active_workers => 100
Expand Down
41 changes: 40 additions & 1 deletion lib/Mojolicious/Plugin/Minion/Admin.pm
Expand Up @@ -23,6 +23,8 @@ sub register {
$prefix->get('/stats' => \&_stats)->name('minion_stats');
$prefix->get('/jobs' => \&_list_jobs)->name('minion_jobs');
$prefix->patch('/jobs' => \&_manage_jobs)->name('minion_manage_jobs');
$prefix->get('/locks' => \&_list_locks)->name('minion_locks');
$prefix->delete('/locks' => \&_unlock)->name('minion_unlock');
$prefix->get('/workers' => \&_list_workers)->name('minion_workers');
}

Expand Down Expand Up @@ -53,6 +55,27 @@ sub _list_jobs {
);
}

sub _list_locks {
my $c = shift;

my $validation = $c->validation;
$validation->optional('limit')->num;
$validation->optional('offset')->num;
$validation->optional('name');
my $options = {name => $validation->param('name')};
my $limit = $validation->param('limit') || 10;
my $offset = $validation->param('offset') || 0;

my $results = $c->minion->backend->list_locks($offset, $limit, $options);
$c->render(
'minion/locks',
locks => $results->{locks},
total => $results->{total},
limit => $limit,
offset => $offset
);
}

sub _list_workers {
my $c = shift;

Expand Down Expand Up @@ -99,7 +122,7 @@ sub _manage_jobs {
else { $c->flash(success => 'All selected jobs removed.') }
}
elsif ($do eq 'stop') {
$minion->backend->broadcast(stop => [$_]) for @$ids;
$minion->broadcast(stop => [$_]) for @$ids;
$c->flash(info => 'Trying to stop all selected jobs.');
}

Expand All @@ -111,6 +134,22 @@ sub _stats {
$c->render(json => $c->minion->stats);
}

sub _unlock {
my $c = shift;

my $validation = $c->validation;
$validation->required('name');

$c->redirect_to('minion_locks') if $validation->has_error;

my $names = $validation->every_param('name');
my $minion = $c->minion;
$minion->unlock($_) for @$names;
$c->flash(info => 'Released all selected named locks.');

$c->redirect_to('minion_locks');
}

1;

=encoding utf8
Expand Down
7 changes: 7 additions & 0 deletions lib/Mojolicious/Plugin/Minion/resources/public/minion/app.css
Expand Up @@ -73,4 +73,11 @@ div.stats .stats-body {
.center-md ul {
float: none !important;
}
.navbar-nav {
margin-left: -15px !important;
}
.navbar-right {
float: left !important;
margin-left: -15px !important;
}
}
Expand Up @@ -22,6 +22,7 @@ function pollStats(url) {
$('.minion-stats-failed-jobs').html(data.failed_jobs);
$('.minion-stats-finished-jobs').html(data.finished_jobs);
$('.minion-stats-inactive-jobs').html(data.inactive_jobs);
$('.minion-stats-active-locks').html(data.active_locks);
$('.minion-stats-workers').html(data.active_workers + data.inactive_workers);
pageStats(data);
setTimeout(function () { pollStats(url) }, 3000);
Expand Down
Expand Up @@ -79,6 +79,15 @@
</span>
</a>
</li>
<li>
<a href="<%= url_for 'minion_locks' %>">
Locks
<span class="label label-pill label-default
minion-stats-active-locks">
0
</span>
</a>
</li>
<li>
<a href="<%= url_for 'minion_workers' %>">
Workers
Expand All @@ -89,7 +98,7 @@
</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right hidden-sm">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="<%= url_for $return_to %>">
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
Expand Down
@@ -0,0 +1,61 @@
% layout 'minion', title => 'Minion - Locks';

%= include 'minion/_notifications'

%= form_for 'minion_unlock' => begin
<div class="row center-md">
<div class="col-md-4 btn-group" role="group">
<button class="btn btn-default" type="submit">
<i class="fas fa-lock"></i> Unlock
</button>
</div>
<div class="col-md-4">
<p class="text-center">
% if (@$locks) {
<%= $offset + 1 %>-<%= $offset + @$locks %> of <%= $total %>
% }
</p>
</div>
<div class="col-md-4">
%= include 'minion/_limit'
</div>
</div>

<div class="row">
<div class="col-md-12">
<table class="table">
<thead>
<tr>
<th><input class="checkall" data-check="name" type="checkbox"></th>
<th>Name</th>
<th>Expires</th>
</tr>
</thead>
% my $i = 0;
% for my $lock (@$locks) {
% $i++;
% my $base = url_with->query(offset => 0);
<tbody>
<tr>
<td>
<input type="checkbox" name="name" value="<%= $lock->{name} %>">
</td>
<td>
<a href="<%= url_for->query([name => $lock->{name}]) %>">
<%= $lock->{name} %>
</a>
</td>
<td class="from-now"><%= $lock->{expires} %></td>
</tr>
</tbody>
% }
</table>
</div>
</div>
% end

<div class="row">
<div class="col-md-12">
%= include 'minion/_pagination'
</div>
</div>
3 changes: 3 additions & 0 deletions t/pg.t
Expand Up @@ -168,6 +168,7 @@ ok $minion->unlock('baz'), 'unlocked';
ok !$minion->unlock('baz'), 'not unlocked again';

# List locks
is $minion->stats->{active_locks}, 1, 'one active lock';
$results = $minion->backend->list_locks(0, 2);
is $results->{locks}[0]{name}, 'yada', 'right name';
like $results->{locks}[0]{expires}, qr/^[\d.]+$/, 'expires';
Expand All @@ -177,6 +178,7 @@ $minion->unlock('yada');
$minion->lock('yada', 3600, {limit => 2});
$minion->lock('test', 3600, {limit => 1});
$minion->lock('yada', 3600, {limit => 2});
is $minion->stats->{active_locks}, 3, 'three active locks';
$results = $minion->backend->list_locks(1, 1);
is $results->{locks}[0]{name}, 'test', 'right name';
like $results->{locks}[0]{expires}, qr/^[\d.]+$/, 'expires';
Expand Down Expand Up @@ -234,6 +236,7 @@ is $stats->{failed_jobs}, 0, 'no failed jobs';
is $stats->{finished_jobs}, 0, 'no finished jobs';
is $stats->{inactive_jobs}, 0, 'no inactive jobs';
is $stats->{delayed_jobs}, 0, 'no delayed jobs';
is $stats->{active_locks}, 0, 'no active locks';
ok $stats->{uptime}, 'has uptime';
$worker = $minion->worker->register;
is $minion->stats->{inactive_workers}, 1, 'one inactive worker';
Expand Down

0 comments on commit 9c5d94d

Please sign in to comment.