nfo-resque-mongo 1.15.1 → 1.17.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/HISTORY.md +32 -0
- data/README.markdown +135 -62
- data/Rakefile +6 -2
- data/lib/nfo-resque-mongo.rb +2 -0
- data/lib/resque.rb +20 -6
- data/lib/resque/failure/hoptoad.rb +22 -113
- data/lib/resque/failure/mongo.rb +6 -1
- data/lib/resque/failure/multiple.rb +5 -1
- data/lib/resque/failure/redis.rb +51 -0
- data/lib/resque/helpers.rb +13 -17
- data/lib/resque/plugin.rb +5 -0
- data/lib/resque/server.rb +18 -8
- data/lib/resque/server/public/favicon.ico +0 -0
- data/lib/resque/server/public/style.css +2 -2
- data/lib/resque/server/views/failed.erb +3 -2
- data/lib/resque/server/views/queues.erb +1 -1
- data/lib/resque/server/views/stats.erb +15 -1
- data/lib/resque/server/views/working.erb +10 -6
- data/lib/resque/version.rb +1 -1
- data/lib/resque/worker.rb +9 -4
- data/{tasks → lib/tasks}/redis.rake +0 -0
- data/{tasks → lib/tasks}/resque.rake +0 -0
- data/test/hoptoad_test.rb +25 -0
- data/test/job_hooks_test.rb +40 -0
- data/test/queue_stats_test.rb +1 -1
- data/test/resque_test.rb +1 -1
- data/test/test_helper.rb +5 -2
- metadata +66 -61
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
|
1
|
+
begin
|
2
|
+
require 'hoptoad_notifier'
|
3
|
+
rescue LoadError
|
4
|
+
raise "Can't find 'hoptoad_notifier' gem. Please add it to your Gemfile or install it."
|
5
|
+
end
|
4
6
|
|
5
7
|
module Resque
|
6
8
|
module Failure
|
@@ -10,27 +12,20 @@ module Resque
|
|
10
12
|
#
|
11
13
|
# require 'resque/failure/hoptoad'
|
12
14
|
#
|
13
|
-
# Resque::Failure::
|
14
|
-
#
|
15
|
-
# config.secure = true
|
15
|
+
# Resque::Failure::Multiple.classes = [Resque::Failure::Redis, Resque::Failure::Hoptoad]
|
16
|
+
# Resque::Failure.backend = Resque::Failure::Multiple
|
16
17
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# config.proxy_port = 8080
|
18
|
+
# Once you've configured resque to use the Hoptoad failure backend,
|
19
|
+
# you'll want to setup an initializer to configure the Hoptoad.
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
21
|
+
# HoptoadNotifier.configure do |config|
|
22
|
+
# config.api_key = 'your_key_here'
|
23
|
+
# end
|
24
|
+
# For more information see https://github.com/thoughtbot/hoptoad_notifier
|
24
25
|
class Hoptoad < Base
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
class << self
|
29
|
-
attr_accessor :secure, :api_key
|
30
|
-
attr_accessor :proxy_host, :proxy_port, :proxy_user, :proxy_pass
|
31
|
-
attr_accessor :server_environment
|
32
|
-
attr_accessor :host, :port
|
33
|
-
attr_accessor :http_read_timeout, :http_open_timeout
|
26
|
+
def self.configure(&block)
|
27
|
+
Resque::Failure.backend = self
|
28
|
+
HoptoadNotifier.configure(&block)
|
34
29
|
end
|
35
30
|
|
36
31
|
def self.count
|
@@ -39,101 +34,15 @@ module Resque
|
|
39
34
|
Stat[:failed]
|
40
35
|
end
|
41
36
|
|
42
|
-
def self.configure
|
43
|
-
yield self
|
44
|
-
Resque::Failure.backend = self
|
45
|
-
end
|
46
|
-
|
47
37
|
def save
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
self.class.proxy_user, self.class.proxy_pass
|
55
|
-
http = request.new(url.host, url.port)
|
56
|
-
headers = {
|
57
|
-
'Content-type' => 'text/xml',
|
58
|
-
'Accept' => 'text/xml, application/xml'
|
59
|
-
}
|
60
|
-
|
61
|
-
http.read_timeout = self.class.http_read_timeout || 5 # seconds
|
62
|
-
http.open_timeout = self.class.http_open_timeout || 2 # seconds
|
63
|
-
|
64
|
-
http.use_ssl = use_ssl?
|
65
|
-
|
66
|
-
begin
|
67
|
-
response = http.post(url.path, xml, headers)
|
68
|
-
rescue TimeoutError => e
|
69
|
-
log "Timeout while contacting the Hoptoad server."
|
70
|
-
end
|
71
|
-
|
72
|
-
case response
|
73
|
-
when Net::HTTPSuccess then
|
74
|
-
log "Hoptoad Success: #{response.class}"
|
75
|
-
else
|
76
|
-
body = response.body if response.respond_to? :body
|
77
|
-
log "Hoptoad Failure: #{response.class}\n#{body}"
|
78
|
-
end
|
38
|
+
HoptoadNotifier.notify_or_ignore(exception,
|
39
|
+
:parameters => {
|
40
|
+
:payload_class => payload['class'].to_s,
|
41
|
+
:payload_args => payload['args'].inspect
|
42
|
+
}
|
43
|
+
)
|
79
44
|
end
|
80
45
|
|
81
|
-
def xml
|
82
|
-
x = Builder::XmlMarkup.new
|
83
|
-
x.instruct!
|
84
|
-
x.notice :version=>"2.0" do
|
85
|
-
x.tag! "api-key", api_key
|
86
|
-
x.notifier do
|
87
|
-
x.name "Resqueue"
|
88
|
-
x.version "0.1"
|
89
|
-
x.url "http://github.com/defunkt/resque"
|
90
|
-
end
|
91
|
-
x.error do
|
92
|
-
x.tag! "class", exception.class.name
|
93
|
-
x.message "#{exception.class.name}: #{exception.message}"
|
94
|
-
x.backtrace do
|
95
|
-
fill_in_backtrace_lines(x)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
x.request do
|
99
|
-
x.url queue.to_s
|
100
|
-
x.component worker.to_s
|
101
|
-
x.params do
|
102
|
-
x.var :key=>"payload_class" do
|
103
|
-
x.text! payload["class"].to_s
|
104
|
-
end
|
105
|
-
x.var :key=>"payload_args" do
|
106
|
-
x.text! payload["args"].to_s
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
x.tag!("server-environment") do
|
111
|
-
x.tag!("environment-name",server_environment)
|
112
|
-
x.tag!("project-root", "RAILS_ROOT")
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def fill_in_backtrace_lines(x)
|
119
|
-
Array(exception.backtrace).each do |unparsed_line|
|
120
|
-
_, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
|
121
|
-
x.line :file => file,:number => number
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def use_ssl?
|
126
|
-
self.class.secure
|
127
|
-
end
|
128
|
-
|
129
|
-
def api_key
|
130
|
-
self.class.api_key
|
131
|
-
end
|
132
|
-
|
133
|
-
def server_environment
|
134
|
-
return self.class.server_environment if self.class.server_environment
|
135
|
-
defined?(RAILS_ENV) ? RAILS_ENV : (ENV['RACK_ENV'] || 'development')
|
136
|
-
end
|
137
46
|
end
|
138
47
|
end
|
139
48
|
end
|
data/lib/resque/failure/mongo.rb
CHANGED
@@ -11,7 +11,7 @@ module Resque
|
|
11
11
|
:payload => payload,
|
12
12
|
:exception => exception.class.to_s,
|
13
13
|
:error => exception.to_s,
|
14
|
-
:backtrace => Array(exception.backtrace),
|
14
|
+
:backtrace => filter_backtrace(Array(exception.backtrace)),
|
15
15
|
:worker => worker.to_s,
|
16
16
|
:queue => queue
|
17
17
|
}
|
@@ -87,6 +87,11 @@ module Resque
|
|
87
87
|
item = all(index)
|
88
88
|
Resque.mongo_failures.remove(:_id => item['_id'])
|
89
89
|
end
|
90
|
+
|
91
|
+
def filter_backtrace(backtrace)
|
92
|
+
index = backtrace.index { |item| item.include?('/lib/resque/job.rb') }
|
93
|
+
backtrace.first(index.to_i)
|
94
|
+
end
|
90
95
|
end
|
91
96
|
end
|
92
97
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Resque
|
2
|
+
module Failure
|
3
|
+
# A Failure backend that stores exceptions in Redis. Very simple but
|
4
|
+
# works out of the box, along with support in the Resque web app.
|
5
|
+
class Redis < Base
|
6
|
+
def save
|
7
|
+
data = {
|
8
|
+
:failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S"),
|
9
|
+
:payload => payload,
|
10
|
+
:exception => exception.class.to_s,
|
11
|
+
:error => exception.to_s,
|
12
|
+
:backtrace => filter_backtrace(Array(exception.backtrace)),
|
13
|
+
:worker => worker.to_s,
|
14
|
+
:queue => queue
|
15
|
+
}
|
16
|
+
data = Resque.encode(data)
|
17
|
+
Resque.redis.rpush(:failed, data)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.count
|
21
|
+
Resque.redis.llen(:failed).to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.all(start = 0, count = 1)
|
25
|
+
Resque.list_range(:failed, start, count)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.clear
|
29
|
+
Resque.redis.del(:failed)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.requeue(index)
|
33
|
+
item = all(index)
|
34
|
+
item['retried_at'] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
|
35
|
+
Resque.redis.lset(:failed, index, Resque.encode(item))
|
36
|
+
Job.create(item['queue'], item['payload']['class'], *item['payload']['args'])
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.remove(index)
|
40
|
+
id = rand(0xffffff)
|
41
|
+
Resque.redis.lset(:failed, index, id)
|
42
|
+
Resque.redis.lrem(:failed, 1, id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def filter_backtrace(backtrace)
|
46
|
+
index = backtrace.index { |item| item.include?('/lib/resque/job.rb') }
|
47
|
+
backtrace.first(index.to_i)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/resque/helpers.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
# OkJson won't work because it doesn't serialize symbols
|
4
|
+
# in the same way yajl and json do.
|
5
|
+
if MultiJson.engine.to_s == 'MultiJson::Engines::OkJson'
|
6
|
+
raise "Please install the yajl-ruby or json gem"
|
7
|
+
end
|
8
|
+
|
1
9
|
module Resque
|
2
10
|
# Methods used by various classes in Resque.
|
3
11
|
module Helpers
|
@@ -23,29 +31,17 @@ module Resque
|
|
23
31
|
# Given a Ruby object, returns a string suitable for storage in a
|
24
32
|
# queue.
|
25
33
|
def encode(object)
|
26
|
-
|
27
|
-
Yajl::Encoder.encode(object)
|
28
|
-
else
|
29
|
-
object.to_json
|
30
|
-
end
|
34
|
+
::MultiJson.encode(object)
|
31
35
|
end
|
32
36
|
|
33
37
|
# Given a string, returns a Ruby object.
|
34
38
|
def decode(object)
|
35
39
|
return unless object
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
raise DecodeException, e
|
42
|
-
end
|
43
|
-
else
|
44
|
-
begin
|
45
|
-
JSON.parse(object)
|
46
|
-
rescue JSON::ParserError => e
|
47
|
-
raise DecodeException, e
|
48
|
-
end
|
41
|
+
begin
|
42
|
+
::MultiJson.decode(object)
|
43
|
+
rescue ::MultiJson::DecodeError => e
|
44
|
+
raise DecodeException, e
|
49
45
|
end
|
50
46
|
end
|
51
47
|
|
data/lib/resque/plugin.rb
CHANGED
@@ -47,5 +47,10 @@ module Resque
|
|
47
47
|
def after_enqueue_hooks(job)
|
48
48
|
job.methods.grep(/^after_enqueue/).sort
|
49
49
|
end
|
50
|
+
|
51
|
+
# Given an object, returns a list `before_enqueue` hook names.
|
52
|
+
def before_enqueue_hooks(job)
|
53
|
+
job.methods.grep(/^before_enqueue/).sort
|
54
|
+
end
|
50
55
|
end
|
51
56
|
end
|
data/lib/resque/server.rb
CHANGED
@@ -2,6 +2,7 @@ require 'sinatra/base'
|
|
2
2
|
require 'erb'
|
3
3
|
require 'resque'
|
4
4
|
require 'resque/version'
|
5
|
+
require 'time'
|
5
6
|
|
6
7
|
module Resque
|
7
8
|
class Server < Sinatra::Base
|
@@ -118,6 +119,7 @@ module Resque
|
|
118
119
|
end
|
119
120
|
|
120
121
|
def show(page, layout = true)
|
122
|
+
response["Cache-Control"] = "max-age=0, private, must-revalidate"
|
121
123
|
begin
|
122
124
|
erb page.to_sym, {:layout => layout}, :resque => Resque
|
123
125
|
rescue Errno::ECONNREFUSED
|
@@ -125,11 +127,27 @@ module Resque
|
|
125
127
|
end
|
126
128
|
end
|
127
129
|
|
130
|
+
def show_for_polling(page)
|
131
|
+
content_type "text/html"
|
132
|
+
@polling = true
|
133
|
+
show(page.to_sym, false).gsub(/\s{1,}/, ' ')
|
134
|
+
end
|
135
|
+
|
128
136
|
# to make things easier on ourselves
|
129
137
|
get "/?" do
|
130
138
|
redirect url_path(:overview)
|
131
139
|
end
|
132
140
|
|
141
|
+
%w( overview workers ).each do |page|
|
142
|
+
get "/#{page}.poll" do
|
143
|
+
show_for_polling(page)
|
144
|
+
end
|
145
|
+
|
146
|
+
get "/#{page}/:id.poll" do
|
147
|
+
show_for_polling(page)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
133
151
|
%w( overview queues working workers key ).each do |page|
|
134
152
|
get "/#{page}" do
|
135
153
|
show page
|
@@ -145,14 +163,6 @@ module Resque
|
|
145
163
|
redirect u('queues')
|
146
164
|
end
|
147
165
|
|
148
|
-
%w( overview workers ).each do |page|
|
149
|
-
get "/#{page}.poll" do
|
150
|
-
content_type "text/html"
|
151
|
-
@polling = true
|
152
|
-
show(page.to_sym, false).gsub(/\s{1,}/, ' ')
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
166
|
get "/failed" do
|
157
167
|
if Resque::Failure.url
|
158
168
|
redirect Resque::Failure.url
|
Binary file
|
@@ -59,7 +59,7 @@ body { padding:0; margin:0; }
|
|
59
59
|
#main table.stats th { font-size:100%; width:40%; color:#000;}
|
60
60
|
#main hr { border:0; border-top:5px solid #efefef; margin:15px 0;}
|
61
61
|
|
62
|
-
#footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
|
62
|
+
#footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
|
63
63
|
#footer p a { color:#999;}
|
64
64
|
|
65
65
|
#main p.poll { background:url(poll.png) no-repeat 0 2px; padding:3px 0; padding-left:23px; float:right; font-size:85%; }
|
@@ -73,7 +73,7 @@ body { padding:0; margin:0; }
|
|
73
73
|
#main ul.failed li.hover dl dd .retried .remove { display:block; }
|
74
74
|
#main ul.failed li dl dd .controls { display:none; float:right; }
|
75
75
|
#main ul.failed li.hover dl dd .controls { display:block; }
|
76
|
-
#main ul.failed li dl dd code, #main ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%;}
|
76
|
+
#main ul.failed li dl dd code, #main ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%; white-space: pre-wrap;}
|
77
77
|
#main ul.failed li dl dd.error a {font-family:Monaco, "Courier New", monospace; font-size:90%; }
|
78
78
|
#main ul.failed li dl dd.error pre { margin-top:3px; line-height:1.3;}
|
79
79
|
|
@@ -11,6 +11,7 @@
|
|
11
11
|
<h1>Search Results</h1>
|
12
12
|
<%end%>
|
13
13
|
<% index = 0 %>
|
14
|
+
<% date_format = "%Y/%m/%d %T %z" %>
|
14
15
|
|
15
16
|
<%unless failed.empty?%>
|
16
17
|
<form method="POST" action="<%=u 'failed/clear'%>" class="clear-failed" style="display: none">
|
@@ -35,10 +36,10 @@
|
|
35
36
|
<% else %>
|
36
37
|
<dt>Worker</dt>
|
37
38
|
<dd>
|
38
|
-
<a href="<%= u(: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>
|
39
|
+
<a href="<%= u(:workers, job['worker']) %>"><%= job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%= job['queue'] %></b > at <b><span class="time"><%= Time.parse(job['failed_at']).strftime(date_format) %></span></b>
|
39
40
|
<% if job['retried_at'] %>
|
40
41
|
<div class='retried'>
|
41
|
-
Retried <b><span class="time"><%= job['retried_at'] %></span></b>
|
42
|
+
Retried <b><span class="time"><%= Time.parse(job['retried_at']).strftime(date_format) %></span></b>
|
42
43
|
<a href="<%= u "failed/remove/#{start + index - 1}" %>" class="remove" rel="remove">Remove</a>
|
43
44
|
</div>
|
44
45
|
<% else %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
<h1>Pending jobs on <span class='hl'><%= queue %></span></h1>
|
6
6
|
<form method="POST" action="<%=u "/queues/#{queue}/remove" %>" class='remove-queue'>
|
7
|
-
<input type='submit' name='' value='Remove Queue' />
|
7
|
+
<input type='submit' name='' value='Remove Queue' onclick='return confirm("Are you absolutely sure? This cannot be undone.");' />
|
8
8
|
</form>
|
9
9
|
<p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue)%></b> jobs</p>
|
10
10
|
<table class='jobs'>
|
@@ -2,8 +2,13 @@
|
|
2
2
|
|
3
3
|
<% if params[:key] %>
|
4
4
|
|
5
|
+
<h1>Not supported in resque-mongo yet</h1>
|
6
|
+
<% if false %><!-- No supported in resque-mongo -->
|
7
|
+
|
5
8
|
<%= partial resque.redis.type(params[:key]).eql?("string") ? :key_string : :key_sets %>
|
6
9
|
|
10
|
+
<% end %>
|
11
|
+
|
7
12
|
<% elsif params[:id] == "resque" %>
|
8
13
|
|
9
14
|
<h1><%= resque %></h1>
|
@@ -22,6 +27,9 @@
|
|
22
27
|
|
23
28
|
<% elsif params[:id] == 'redis' %>
|
24
29
|
|
30
|
+
<h1>Not supported in resque-mongo yet</h1>
|
31
|
+
<% if false %><!-- No supported in resque-mongo -->
|
32
|
+
|
25
33
|
<h1><%= resque.redis_id %></h1>
|
26
34
|
<table class='stats'>
|
27
35
|
<% for key, value in resque.redis.info.to_a.sort_by { |i| i[0].to_s } %>
|
@@ -36,10 +44,14 @@
|
|
36
44
|
<% end %>
|
37
45
|
</table>
|
38
46
|
|
47
|
+
<% end %>
|
48
|
+
|
39
49
|
<% elsif params[:id] == 'keys' %>
|
40
50
|
|
51
|
+
<h1>Not supported in resque-mongo yet</h1>
|
52
|
+
<% if false %><!-- No supported in resque-mongo -->
|
53
|
+
|
41
54
|
<h1>Keys owned by <%= resque %></h1>
|
42
|
-
<p class='sub'>(All keys are actually prefixed with "<%= Resque.redis.namespace %>:")</p>
|
43
55
|
<table class='stats'>
|
44
56
|
<tr>
|
45
57
|
<th>key</th>
|
@@ -57,6 +69,8 @@
|
|
57
69
|
<% end %>
|
58
70
|
</table>
|
59
71
|
|
72
|
+
<% end %>
|
73
|
+
|
60
74
|
<% else %>
|
61
75
|
|
62
76
|
<% end %>
|