resque-mongo 1.4.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CONTRIBUTORS +24 -6
- data/HISTORY.md +65 -0
- data/README.markdown +34 -5
- data/Rakefile +1 -1
- data/bin/resque +2 -2
- data/bin/resque-web +6 -1
- data/deps.rip +2 -2
- data/docs/HOOKS.md +121 -0
- data/docs/PLUGINS.md +93 -0
- data/examples/demo/Rakefile +5 -0
- data/examples/monit/resque.monit +6 -0
- data/lib/resque.rb +94 -7
- data/lib/resque/errors.rb +3 -0
- data/lib/resque/failure.rb +3 -0
- data/lib/resque/failure/base.rb +3 -0
- data/lib/resque/failure/hoptoad.rb +29 -19
- data/lib/resque/failure/mongo.rb +10 -1
- data/lib/resque/helpers.rb +8 -2
- data/lib/resque/job.rb +107 -2
- data/lib/resque/plugin.rb +46 -0
- data/lib/resque/server.rb +30 -11
- data/lib/resque/server/public/ranger.js +50 -7
- data/lib/resque/server/public/style.css +8 -1
- data/lib/resque/server/test_helper.rb +19 -0
- data/lib/resque/server/views/failed.erb +17 -3
- data/lib/resque/server/views/key_sets.erb +20 -0
- data/lib/resque/server/views/{key.erb → key_string.erb} +2 -8
- data/lib/resque/server/views/queues.erb +5 -2
- data/lib/resque/server/views/stats.erb +2 -2
- data/lib/resque/server/views/workers.erb +1 -1
- data/lib/resque/server/views/working.erb +2 -0
- data/lib/resque/tasks.rb +1 -1
- data/lib/resque/version.rb +1 -1
- data/lib/resque/worker.rb +54 -15
- data/tasks/redis.rake +53 -29
- data/test/job_hooks_test.rb +302 -0
- data/test/job_plugins_test.rb +209 -0
- data/test/plugin_test.rb +116 -0
- data/test/resque-mongo_benchmark.rb +62 -0
- data/test/resque-web_test.rb +54 -0
- data/test/resque_test.rb +34 -0
- data/test/test_helper.rb +15 -0
- data/test/worker_test.rb +62 -2
- metadata +58 -23
@@ -0,0 +1,46 @@
|
|
1
|
+
module Resque
|
2
|
+
module Plugin
|
3
|
+
extend self
|
4
|
+
|
5
|
+
LintError = Class.new(RuntimeError)
|
6
|
+
|
7
|
+
# Ensure that your plugin conforms to good hook naming conventions.
|
8
|
+
#
|
9
|
+
# Resque::Plugin.lint(MyResquePlugin)
|
10
|
+
def lint(plugin)
|
11
|
+
hooks = before_hooks(plugin) + around_hooks(plugin) + after_hooks(plugin)
|
12
|
+
|
13
|
+
hooks.each do |hook|
|
14
|
+
if hook =~ /perform$/
|
15
|
+
raise LintError, "#{plugin}.#{hook} is not namespaced"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
failure_hooks(plugin).each do |hook|
|
20
|
+
if hook =~ /failure$/
|
21
|
+
raise LintError, "#{plugin}.#{hook} is not namespaced"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Given an object, returns a list `before_perform` hook names.
|
27
|
+
def before_hooks(job)
|
28
|
+
job.methods.grep(/^before_perform/).sort
|
29
|
+
end
|
30
|
+
|
31
|
+
# Given an object, returns a list `around_perform` hook names.
|
32
|
+
def around_hooks(job)
|
33
|
+
job.methods.grep(/^around_perform/).sort
|
34
|
+
end
|
35
|
+
|
36
|
+
# Given an object, returns a list `after_perform` hook names.
|
37
|
+
def after_hooks(job)
|
38
|
+
job.methods.grep(/^after_perform/).sort
|
39
|
+
end
|
40
|
+
|
41
|
+
# Given an object, returns a list `on_failure` hook names.
|
42
|
+
def failure_hooks(job)
|
43
|
+
job.methods.grep(/^on_failure/).sort
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/resque/server.rb
CHANGED
@@ -20,7 +20,7 @@ module Resque
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def current_page
|
23
|
-
url request.path_info.sub('/','')
|
23
|
+
url request.path_info.sub('/','')
|
24
24
|
end
|
25
25
|
|
26
26
|
def url(*path_parts)
|
@@ -32,13 +32,14 @@ module Resque
|
|
32
32
|
request.env['SCRIPT_NAME']
|
33
33
|
end
|
34
34
|
|
35
|
-
def class_if_current(
|
36
|
-
'class="current"' if current_page.
|
35
|
+
def class_if_current(path = '')
|
36
|
+
'class="current"' if current_page[0, path.size] == path
|
37
37
|
end
|
38
38
|
|
39
39
|
def tab(name)
|
40
40
|
dname = name.to_s.downcase
|
41
|
-
|
41
|
+
path = url(dname)
|
42
|
+
"<li #{class_if_current(path)}><a href='#{path}'>#{name}</a></li>"
|
42
43
|
end
|
43
44
|
|
44
45
|
def tabs
|
@@ -55,19 +56,23 @@ module Resque
|
|
55
56
|
Resque.redis.scard(key)
|
56
57
|
when 'string'
|
57
58
|
Resque.redis.get(key).length
|
59
|
+
when 'zset'
|
60
|
+
Resque.redis.zcard(key)
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
61
|
-
def redis_get_value_as_array(key)
|
64
|
+
def redis_get_value_as_array(key, start=0)
|
62
65
|
case Resque.redis.type(key)
|
63
66
|
when 'none'
|
64
67
|
[]
|
65
68
|
when 'list'
|
66
|
-
Resque.redis.lrange(key,
|
69
|
+
Resque.redis.lrange(key, start, start + 20)
|
67
70
|
when 'set'
|
68
|
-
Resque.redis.smembers(key)
|
71
|
+
Resque.redis.smembers(key)[start..(start + 20)]
|
69
72
|
when 'string'
|
70
73
|
[Resque.redis.get(key)]
|
74
|
+
when 'zset'
|
75
|
+
Resque.redis.zrange(key, start, start + 20)
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
@@ -85,7 +90,7 @@ module Resque
|
|
85
90
|
ensure
|
86
91
|
@partial = false
|
87
92
|
end
|
88
|
-
|
93
|
+
|
89
94
|
def poll
|
90
95
|
if @polling
|
91
96
|
text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
|
@@ -94,14 +99,14 @@ module Resque
|
|
94
99
|
end
|
95
100
|
"<p class='poll'>#{text}</p>"
|
96
101
|
end
|
97
|
-
|
102
|
+
|
98
103
|
end
|
99
104
|
|
100
105
|
def show(page, layout = true)
|
101
106
|
begin
|
102
107
|
erb page.to_sym, {:layout => layout}, :resque => Resque
|
103
108
|
rescue Errno::ECONNREFUSED
|
104
|
-
erb :error, {:layout => false}, :error => "Can't connect to
|
109
|
+
erb :error, {:layout => false}, :error => "Can't connect to Mongo! (#{Resque.mongo.server})"
|
105
110
|
end
|
106
111
|
end
|
107
112
|
|
@@ -120,6 +125,11 @@ module Resque
|
|
120
125
|
end
|
121
126
|
end
|
122
127
|
|
128
|
+
post "/queues/:id/remove" do
|
129
|
+
Resque.remove_queue(params[:id])
|
130
|
+
redirect u('queues')
|
131
|
+
end
|
132
|
+
|
123
133
|
%w( overview workers ).each do |page|
|
124
134
|
get "/#{page}.poll" do
|
125
135
|
content_type "text/plain"
|
@@ -135,11 +145,20 @@ module Resque
|
|
135
145
|
show :failed
|
136
146
|
end
|
137
147
|
end
|
138
|
-
|
148
|
+
|
139
149
|
post "/failed/clear" do
|
140
150
|
Resque::Failure.clear
|
141
151
|
redirect u('failed')
|
142
152
|
end
|
153
|
+
|
154
|
+
get "/failed/requeue/:index" do
|
155
|
+
Resque::Failure.requeue(params[:index])
|
156
|
+
if request.xhr?
|
157
|
+
return Resque::Failure.all(params[:index])['retried_at']
|
158
|
+
else
|
159
|
+
redirect u('failed')
|
160
|
+
end
|
161
|
+
end
|
143
162
|
|
144
163
|
get "/stats" do
|
145
164
|
redirect url("/stats/resque")
|
@@ -1,24 +1,67 @@
|
|
1
|
-
var poll_interval = 2;
|
2
|
-
|
3
1
|
$(function() {
|
2
|
+
var poll_interval = 2
|
3
|
+
|
4
|
+
var relatizer = function(){
|
5
|
+
var dt = $(this).text(), relatized = $.relatizeDate(this)
|
6
|
+
if ($(this).parents("a").length > 0 || $(this).is("a")) {
|
7
|
+
$(this).relatizeDate()
|
8
|
+
if (!$(this).attr('title')) {
|
9
|
+
$(this).attr('title', dt)
|
10
|
+
}
|
11
|
+
} else {
|
12
|
+
$(this)
|
13
|
+
.text('')
|
14
|
+
.append( $('<a href="#" class="toggle_format" title="' + dt + '" />')
|
15
|
+
.append('<span class="date_time">' + dt +
|
16
|
+
'</span><span class="relatized_time">' +
|
17
|
+
relatized + '</span>') )
|
18
|
+
}
|
19
|
+
};
|
20
|
+
|
21
|
+
$('.time').each(relatizer);
|
22
|
+
|
23
|
+
$('.time a.toggle_format .date_time').hide()
|
24
|
+
|
25
|
+
var format_toggler = function(){
|
26
|
+
$('.time a.toggle_format span').toggle()
|
27
|
+
$(this).attr('title', $('span:hidden',this).text())
|
28
|
+
return false
|
29
|
+
};
|
30
|
+
|
31
|
+
$('.time a.toggle_format').click(format_toggler);
|
4
32
|
|
5
|
-
$('.time').relatizeDate()
|
6
33
|
$('.backtrace').click(function() {
|
7
34
|
$(this).next().toggle()
|
8
35
|
return false
|
9
36
|
})
|
10
|
-
|
37
|
+
|
11
38
|
$('a[rel=poll]').click(function() {
|
12
39
|
var href = $(this).attr('href')
|
13
40
|
$(this).parent().text('Starting...')
|
14
41
|
$("#main").addClass('polling')
|
42
|
+
|
15
43
|
setInterval(function() {
|
16
|
-
$.ajax({dataType:'text', type:'get', url:href, success:function(data) {
|
17
|
-
$('#main').html(data)
|
44
|
+
$.ajax({dataType: 'text', type: 'get', url: href, success: function(data) {
|
45
|
+
$('#main').html(data)
|
18
46
|
$('#main .time').relatizeDate()
|
19
47
|
}})
|
20
48
|
}, poll_interval * 1000)
|
49
|
+
|
21
50
|
return false
|
22
51
|
})
|
23
|
-
|
52
|
+
|
53
|
+
$('ul.failed a[rel=retry]').click(function() {
|
54
|
+
var href = $(this).attr('href');
|
55
|
+
$(this).text('Retrying...');
|
56
|
+
var parent = $(this).parent();
|
57
|
+
$.ajax({dataType: 'text', type: 'get', url: href, success: function(data) {
|
58
|
+
parent.html('Retried <b><span class="time">' + data + '</span></b>');
|
59
|
+
relatizer.apply($('.time', parent));
|
60
|
+
$('.date_time', parent).hide();
|
61
|
+
$('a.toggle_format span', parent).click(format_toggler);
|
62
|
+
}});
|
63
|
+
return false;
|
64
|
+
})
|
65
|
+
|
66
|
+
|
24
67
|
})
|
@@ -8,6 +8,8 @@ body { padding:0; margin:0; }
|
|
8
8
|
.header ul li a:hover { background:#333;}
|
9
9
|
.header ul li.current a { background:#429234; font-weight:bold; color:#fff;}
|
10
10
|
|
11
|
+
.header .namespace { position: absolute; right: 75px; top: 10px; color: #7A7A7A; }
|
12
|
+
|
11
13
|
.subnav { padding:2px 5% 7px 5%; background:#429234; font-size:90%;}
|
12
14
|
.subnav li { display:inline;}
|
13
15
|
.subnav li a { color:#fff; text-decoration:none; margin-right:10px; display:inline-block; background:#55ad46; padding:5px; -webkit-border-radius:3px; -moz-border-radius:3px;}
|
@@ -31,6 +33,8 @@ body { padding:0; margin:0; }
|
|
31
33
|
|
32
34
|
#main table.queues { width:40%;}
|
33
35
|
#main table.queues td.queue { font-weight:bold; width:50%;}
|
36
|
+
#main table.queues tr.failed td { border-top:2px solid; font-size:90%; }
|
37
|
+
|
34
38
|
#main table.queues tr.failed td { background:#ebffed; border-top:2px solid #6fd380; font-size:90%; color:#6fd380;}
|
35
39
|
#main table.queues tr.failed td a{ color:#6fd380;}
|
36
40
|
|
@@ -64,6 +68,7 @@ body { padding:0; margin:0; }
|
|
64
68
|
#main ul.failed li {background:-webkit-gradient(linear, left top, left bottom, from(#efefef), to(#fff)) #efefef; margin-top:10px; padding:10px; overflow:hidden; -webkit-border-radius:5px; border:1px solid #ccc; }
|
65
69
|
#main ul.failed li dl dt {font-size:80%; color:#999; width:60px; float:left; padding-top:1px; text-align:right;}
|
66
70
|
#main ul.failed li dl dd {margin-bottom:10px; margin-left:70px;}
|
71
|
+
#main ul.failed li dl dd .retry { float: right; }
|
67
72
|
#main ul.failed li dl dd code, #main ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%;}
|
68
73
|
#main ul.failed li dl dd.error a {font-family:Monaco, "Courier New", monospace; font-size:90%; }
|
69
74
|
#main ul.failed li dl dd.error pre { margin-top:3px; line-height:1.3;}
|
@@ -72,4 +77,6 @@ body { padding:0; margin:0; }
|
|
72
77
|
#main p.pagination a.less { float:left;}
|
73
78
|
#main p.pagination a.more { float:right;}
|
74
79
|
|
75
|
-
#main form
|
80
|
+
#main form {float:right; margin-top:-10px;}
|
81
|
+
|
82
|
+
#main .time a.toggle_format {text-decoration:none;}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
require 'resque/server'
|
3
|
+
|
4
|
+
module Resque
|
5
|
+
module TestHelper
|
6
|
+
class Test::Unit::TestCase
|
7
|
+
include Rack::Test::Methods
|
8
|
+
def app
|
9
|
+
Resque::Server.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.should_respond_with_success
|
13
|
+
test "should respond with success" do
|
14
|
+
assert last_response.ok?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
<%start = params[:start].to_i %>
|
2
|
-
<%failed = Resque::Failure.all(start, 20)%>
|
2
|
+
<%failed = [Resque::Failure.all(start, 20)].flatten %>
|
3
|
+
<% index = 0 %>
|
3
4
|
|
4
5
|
<h1>Failed Jobs</h1>
|
5
6
|
<%unless failed.empty?%>
|
@@ -12,18 +13,30 @@
|
|
12
13
|
|
13
14
|
<ul class='failed'>
|
14
15
|
<%for job in failed%>
|
16
|
+
<% index += 1 %>
|
15
17
|
<li>
|
16
18
|
<dl>
|
17
19
|
<dt>Worker</dt>
|
18
|
-
<dd
|
20
|
+
<dd>
|
21
|
+
<a href="<%= url(:workers, job['worker']) %>"><%= job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%= job['queue'] %></b > at <b><span class="time"><%= job['failed_at'] %></span></b>
|
22
|
+
<div class='retry'>
|
23
|
+
<% if job['retried_at'] %>
|
24
|
+
Retried <b><span class="time"><%= job['retried_at'] %></span></b>
|
25
|
+
<% else %>
|
26
|
+
<a href="<%= u "failed/requeue/#{start + index - 1}" %>" rel="retry">Retry</a>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
</dd>
|
19
30
|
<dt>Class</dt>
|
20
31
|
<dd><code><%= job['payload']['class'] %></code></dd>
|
21
32
|
<dt>Arguments</dt>
|
22
33
|
<dd><pre><%=h show_args(job['payload']['args']) %></pre></dd>
|
34
|
+
<dt>Exception</td>
|
35
|
+
<dd><code><%= job['exception'] %></code></dd>
|
23
36
|
<dt>Error</dt>
|
24
37
|
<dd class='error'>
|
25
38
|
<a href="#" class="backtrace"><%= h(job['error']) %></a>
|
26
|
-
<pre style='display:none'><%=h job['backtrace'].join("\n") %></pre>
|
39
|
+
<pre style='display:none'><%=h job['backtrace'].join("\n") %></pre>
|
27
40
|
</dd>
|
28
41
|
</dl>
|
29
42
|
<div class='r'>
|
@@ -33,3 +46,4 @@
|
|
33
46
|
</ul>
|
34
47
|
|
35
48
|
<%= partial :next_more, :start => start, :size => size %>
|
49
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<% if key = params[:key] %>
|
2
|
+
|
3
|
+
<p class='sub'>
|
4
|
+
Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = redis_get_size(key) %></b>
|
5
|
+
</p>
|
6
|
+
|
7
|
+
|
8
|
+
<h1>Key "<%= key %>" is a <%= resque.redis.type key %></h1>
|
9
|
+
<table>
|
10
|
+
<% for row in redis_get_value_as_array(key, start) %>
|
11
|
+
<tr>
|
12
|
+
<td>
|
13
|
+
<%= row %>
|
14
|
+
</td>
|
15
|
+
</tr>
|
16
|
+
<% end %>
|
17
|
+
</table>
|
18
|
+
|
19
|
+
<%= partial :next_more, :start => start, :size => size %>
|
20
|
+
<% end %>
|
@@ -1,17 +1,11 @@
|
|
1
1
|
<% if key = params[:key] %>
|
2
|
-
|
3
2
|
<h1>Key "<%= key %>" is a <%= resque.redis.type key %></h1>
|
4
3
|
<h2>size: <%= redis_get_size(key) %></h2>
|
5
4
|
<table>
|
6
|
-
<% for row in redis_get_value_as_array(key) %>
|
7
5
|
<tr>
|
8
6
|
<td>
|
9
|
-
<%=
|
7
|
+
<%= redis_get_value_as_array(key) %>
|
10
8
|
</td>
|
11
9
|
</tr>
|
12
|
-
<% end %>
|
13
10
|
</table>
|
14
|
-
|
15
|
-
<% else %>
|
16
|
-
|
17
|
-
<% end %>
|
11
|
+
<% end %>
|
@@ -3,6 +3,9 @@
|
|
3
3
|
<% if queue = params[:id] %>
|
4
4
|
|
5
5
|
<h1>Pending jobs on <span class='hl'><%= queue %></span></h1>
|
6
|
+
<form method="POST" action="<%=u "/queues/#{queue}/remove" %>" class='remove-queue'>
|
7
|
+
<input type='submit' name='' value='Remove Queue' />
|
8
|
+
</form>
|
6
9
|
<p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue)%></b> jobs</p>
|
7
10
|
<table class='jobs'>
|
8
11
|
<tr>
|
@@ -37,10 +40,10 @@
|
|
37
40
|
<td class='size'><%= resque.size queue %></td>
|
38
41
|
</tr>
|
39
42
|
<% end %>
|
40
|
-
<tr class=
|
43
|
+
<tr class="<%= Resque::Failure.count.zero? ? "failed" : "failure" %>">
|
41
44
|
<td class='queue failed'><a class="queue" href="<%= url :failed %>">failed</a></td>
|
42
45
|
<td class='size'><%= Resque::Failure.count %></td>
|
43
46
|
</tr>
|
44
47
|
</table>
|
45
48
|
|
46
|
-
<% end %>
|
49
|
+
<% end %>
|
@@ -18,7 +18,7 @@
|
|
18
18
|
<% host, pid, queues = worker.to_s.split(':') %>
|
19
19
|
<td><%= host %></td>
|
20
20
|
<td><%= pid %></td>
|
21
|
-
<td><span class="time"><%= worker.started %></
|
21
|
+
<td><span class="time"><%= worker.started %></span></td>
|
22
22
|
<td class='queues'><%= queues.split(',').map { |q| '<a class="queue-tag" href="' + u("/queues/#{q}") + '">' + q + '</a>'}.join('') %></td>
|
23
23
|
<td><%= worker.processed %></td>
|
24
24
|
<td><%= worker.failed %></td>
|
@@ -45,6 +45,8 @@
|
|
45
45
|
|
46
46
|
<% for worker in workers.sort_by { |w| w.job['run_at'] ? w.job['run_at'] : '' } %>
|
47
47
|
<% job = worker.job %>
|
48
|
+
<% next if worker.idle? %>
|
49
|
+
|
48
50
|
<tr>
|
49
51
|
<td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
50
52
|
<% host, pid, queues = worker.to_s.split(':') %>
|
data/lib/resque/tasks.rb
CHANGED
data/lib/resque/version.rb
CHANGED