resque_sqs 1.25.2
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.
- checksums.yaml +7 -0
- data/HISTORY.md +467 -0
- data/LICENSE +20 -0
- data/README.markdown +866 -0
- data/Rakefile +70 -0
- data/bin/resque-sqs +81 -0
- data/bin/resque-sqs-web +27 -0
- data/lib/resque_sqs/errors.rb +13 -0
- data/lib/resque_sqs/failure/airbrake.rb +33 -0
- data/lib/resque_sqs/failure/base.rb +73 -0
- data/lib/resque_sqs/failure/multiple.rb +59 -0
- data/lib/resque_sqs/failure/redis.rb +108 -0
- data/lib/resque_sqs/failure/redis_multi_queue.rb +89 -0
- data/lib/resque_sqs/failure.rb +113 -0
- data/lib/resque_sqs/helpers.rb +107 -0
- data/lib/resque_sqs/job.rb +346 -0
- data/lib/resque_sqs/log_formatters/quiet_formatter.rb +7 -0
- data/lib/resque_sqs/log_formatters/verbose_formatter.rb +7 -0
- data/lib/resque_sqs/log_formatters/very_verbose_formatter.rb +8 -0
- data/lib/resque_sqs/logging.rb +18 -0
- data/lib/resque_sqs/plugin.rb +66 -0
- data/lib/resque_sqs/server/helpers.rb +52 -0
- data/lib/resque_sqs/server/public/favicon.ico +0 -0
- data/lib/resque_sqs/server/public/idle.png +0 -0
- data/lib/resque_sqs/server/public/jquery-1.3.2.min.js +19 -0
- data/lib/resque_sqs/server/public/jquery.relatize_date.js +95 -0
- data/lib/resque_sqs/server/public/poll.png +0 -0
- data/lib/resque_sqs/server/public/ranger.js +78 -0
- data/lib/resque_sqs/server/public/reset.css +44 -0
- data/lib/resque_sqs/server/public/style.css +91 -0
- data/lib/resque_sqs/server/public/working.png +0 -0
- data/lib/resque_sqs/server/test_helper.rb +19 -0
- data/lib/resque_sqs/server/views/error.erb +1 -0
- data/lib/resque_sqs/server/views/failed.erb +29 -0
- data/lib/resque_sqs/server/views/failed_job.erb +50 -0
- data/lib/resque_sqs/server/views/failed_queues_overview.erb +24 -0
- data/lib/resque_sqs/server/views/key_sets.erb +19 -0
- data/lib/resque_sqs/server/views/key_string.erb +11 -0
- data/lib/resque_sqs/server/views/layout.erb +44 -0
- data/lib/resque_sqs/server/views/next_more.erb +22 -0
- data/lib/resque_sqs/server/views/overview.erb +4 -0
- data/lib/resque_sqs/server/views/queues.erb +58 -0
- data/lib/resque_sqs/server/views/stats.erb +62 -0
- data/lib/resque_sqs/server/views/workers.erb +109 -0
- data/lib/resque_sqs/server/views/working.erb +72 -0
- data/lib/resque_sqs/server.rb +271 -0
- data/lib/resque_sqs/stat.rb +57 -0
- data/lib/resque_sqs/tasks.rb +83 -0
- data/lib/resque_sqs/vendor/utf8_util/utf8_util_18.rb +91 -0
- data/lib/resque_sqs/vendor/utf8_util/utf8_util_19.rb +5 -0
- data/lib/resque_sqs/vendor/utf8_util.rb +20 -0
- data/lib/resque_sqs/version.rb +3 -0
- data/lib/resque_sqs/worker.rb +779 -0
- data/lib/resque_sqs.rb +479 -0
- data/lib/tasks/redis_sqs.rake +161 -0
- data/lib/tasks/resque_sqs.rake +2 -0
- data/test/airbrake_test.rb +27 -0
- data/test/failure_base_test.rb +15 -0
- data/test/job_hooks_test.rb +465 -0
- data/test/job_plugins_test.rb +230 -0
- data/test/logging_test.rb +24 -0
- data/test/plugin_test.rb +116 -0
- data/test/redis-test-cluster.conf +115 -0
- data/test/redis-test.conf +115 -0
- data/test/resque-web_test.rb +59 -0
- data/test/resque_failure_redis_test.rb +19 -0
- data/test/resque_hook_test.rb +165 -0
- data/test/resque_test.rb +278 -0
- data/test/stdout +42 -0
- data/test/test_helper.rb +228 -0
- data/test/worker_test.rb +1080 -0
- metadata +202 -0
|
@@ -0,0 +1,107 @@
|
|
|
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.respond_to?(:adapter)
|
|
6
|
+
raise "Please install the yajl-ruby or json gem" if MultiJson.adapter.to_s == 'MultiJson::Adapters::OkJson'
|
|
7
|
+
elsif MultiJson.respond_to?(:engine)
|
|
8
|
+
raise "Please install the yajl-ruby or json gem" if MultiJson.engine.to_s == 'MultiJson::Engines::OkJson'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ResqueSqs
|
|
12
|
+
# Methods used by various classes in ResqueSqs.
|
|
13
|
+
module Helpers
|
|
14
|
+
def self.extended(parent_class)
|
|
15
|
+
warn("ResqueSqs::Helpers will be gone with no replacement in Resque 2.0.0.")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.included(parent_class)
|
|
19
|
+
warn("ResqueSqs::Helpers will be gone with no replacement in Resque 2.0.0.")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class DecodeException < StandardError; end
|
|
23
|
+
|
|
24
|
+
# Direct access to the Redis instance.
|
|
25
|
+
def redis
|
|
26
|
+
# No infinite recursions, please.
|
|
27
|
+
# Some external libraries depend on ResqueSqs::Helpers being mixed into
|
|
28
|
+
# Resque, but this method causes recursions. If we have a super method,
|
|
29
|
+
# assume it is canonical. (see #1150)
|
|
30
|
+
return super if defined?(super)
|
|
31
|
+
|
|
32
|
+
ResqueSqs.redis
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Given a Ruby object, returns a string suitable for storage in a
|
|
36
|
+
# queue.
|
|
37
|
+
def encode(object)
|
|
38
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
|
39
|
+
MultiJson.dump object
|
|
40
|
+
else
|
|
41
|
+
MultiJson.encode object
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Given a string, returns a Ruby object.
|
|
46
|
+
def decode(object)
|
|
47
|
+
return unless object
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
|
51
|
+
MultiJson.load object
|
|
52
|
+
else
|
|
53
|
+
MultiJson.decode object
|
|
54
|
+
end
|
|
55
|
+
rescue ::MultiJson::DecodeError => e
|
|
56
|
+
raise DecodeException, e.message, e.backtrace
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Given a word with dashes, returns a camel cased version of it.
|
|
61
|
+
#
|
|
62
|
+
# classify('job-name') # => 'JobName'
|
|
63
|
+
def classify(dashed_word)
|
|
64
|
+
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Tries to find a constant with the name specified in the argument string:
|
|
68
|
+
#
|
|
69
|
+
# constantize("Module") # => Module
|
|
70
|
+
# constantize("Test::Unit") # => Test::Unit
|
|
71
|
+
#
|
|
72
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
|
73
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
|
74
|
+
# account:
|
|
75
|
+
#
|
|
76
|
+
# C = 'outside'
|
|
77
|
+
# module M
|
|
78
|
+
# C = 'inside'
|
|
79
|
+
# C # => 'inside'
|
|
80
|
+
# constantize("C") # => 'outside', same as ::C
|
|
81
|
+
# end
|
|
82
|
+
#
|
|
83
|
+
# NameError is raised when the constant is unknown.
|
|
84
|
+
def constantize(camel_cased_word)
|
|
85
|
+
camel_cased_word = camel_cased_word.to_s
|
|
86
|
+
|
|
87
|
+
if camel_cased_word.include?('-')
|
|
88
|
+
camel_cased_word = classify(camel_cased_word)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
names = camel_cased_word.split('::')
|
|
92
|
+
names.shift if names.empty? || names.first.empty?
|
|
93
|
+
|
|
94
|
+
constant = Object
|
|
95
|
+
names.each do |name|
|
|
96
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
|
97
|
+
|
|
98
|
+
if constant.const_defined?(name, *args)
|
|
99
|
+
constant = constant.const_get(name)
|
|
100
|
+
else
|
|
101
|
+
constant = constant.const_missing(name)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
constant
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
module ResqueSqs
|
|
2
|
+
# A ResqueSqs::Job represents a unit of work. Each job lives on a
|
|
3
|
+
# single queue and has an associated payload object. The payload
|
|
4
|
+
# is a hash with two attributes: `class` and `args`. The `class` is
|
|
5
|
+
# the name of the Ruby class which should be used to run the
|
|
6
|
+
# job. The `args` are an array of arguments which should be passed
|
|
7
|
+
# to the Ruby class's `perform` class-level method.
|
|
8
|
+
#
|
|
9
|
+
# You can manually run a job using this code:
|
|
10
|
+
#
|
|
11
|
+
# job = ResqueSqs::Job.reserve(:high)
|
|
12
|
+
# klass = ResqueSqs::Job.constantize(job.payload['class'])
|
|
13
|
+
# klass.perform(*job.payload['args'])
|
|
14
|
+
class Job
|
|
15
|
+
def redis
|
|
16
|
+
ResqueSqs.redis
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.redis
|
|
20
|
+
ResqueSqs.redis
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Given a Ruby object, returns a string suitable for storage in a
|
|
24
|
+
# queue.
|
|
25
|
+
def encode(object)
|
|
26
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
|
27
|
+
MultiJson.dump object
|
|
28
|
+
else
|
|
29
|
+
MultiJson.encode object
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Given a string, returns a Ruby object.
|
|
34
|
+
def decode(object)
|
|
35
|
+
return unless object
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
|
39
|
+
MultiJson.load object
|
|
40
|
+
else
|
|
41
|
+
MultiJson.decode object
|
|
42
|
+
end
|
|
43
|
+
rescue ::MultiJson::DecodeError => e
|
|
44
|
+
raise DecodeException, e.message, e.backtrace
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Given a Ruby object, returns a string suitable for storage in a
|
|
49
|
+
# queue.
|
|
50
|
+
def self.encode(object)
|
|
51
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
|
52
|
+
MultiJson.dump object
|
|
53
|
+
else
|
|
54
|
+
MultiJson.encode object
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Given a string, returns a Ruby object.
|
|
59
|
+
def self.decode(object)
|
|
60
|
+
return unless object
|
|
61
|
+
|
|
62
|
+
begin
|
|
63
|
+
if MultiJson.respond_to?(:dump) && MultiJson.respond_to?(:load)
|
|
64
|
+
MultiJson.load object
|
|
65
|
+
else
|
|
66
|
+
MultiJson.decode object
|
|
67
|
+
end
|
|
68
|
+
rescue ::MultiJson::DecodeError => e
|
|
69
|
+
raise DecodeException, e.message, e.backtrace
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Given a word with dashes, returns a camel cased version of it.
|
|
74
|
+
#
|
|
75
|
+
# classify('job-name') # => 'JobName'
|
|
76
|
+
def classify(dashed_word)
|
|
77
|
+
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Tries to find a constant with the name specified in the argument string:
|
|
81
|
+
#
|
|
82
|
+
# constantize("Module") # => Module
|
|
83
|
+
# constantize("Test::Unit") # => Test::Unit
|
|
84
|
+
#
|
|
85
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
|
86
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
|
87
|
+
# account:
|
|
88
|
+
#
|
|
89
|
+
# C = 'outside'
|
|
90
|
+
# module M
|
|
91
|
+
# C = 'inside'
|
|
92
|
+
# C # => 'inside'
|
|
93
|
+
# constantize("C") # => 'outside', same as ::C
|
|
94
|
+
# end
|
|
95
|
+
#
|
|
96
|
+
# NameError is raised when the constant is unknown.
|
|
97
|
+
def constantize(camel_cased_word)
|
|
98
|
+
camel_cased_word = camel_cased_word.to_s
|
|
99
|
+
|
|
100
|
+
if camel_cased_word.include?('-')
|
|
101
|
+
camel_cased_word = classify(camel_cased_word)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
names = camel_cased_word.split('::')
|
|
105
|
+
names.shift if names.empty? || names.first.empty?
|
|
106
|
+
|
|
107
|
+
constant = Object
|
|
108
|
+
names.each do |name|
|
|
109
|
+
args = Module.method(:const_get).arity != 1 ? [false] : []
|
|
110
|
+
|
|
111
|
+
if constant.const_defined?(name, *args)
|
|
112
|
+
constant = constant.const_get(name)
|
|
113
|
+
else
|
|
114
|
+
constant = constant.const_missing(name)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
constant
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Raise ResqueSqs::Job::DontPerform from a before_perform hook to
|
|
121
|
+
# abort the job.
|
|
122
|
+
DontPerform = Class.new(StandardError)
|
|
123
|
+
|
|
124
|
+
# The worker object which is currently processing this job.
|
|
125
|
+
attr_accessor :worker
|
|
126
|
+
|
|
127
|
+
# The name of the queue from which this job was pulled (or is to be
|
|
128
|
+
# placed)
|
|
129
|
+
attr_reader :queue
|
|
130
|
+
|
|
131
|
+
# This job's associated payload object.
|
|
132
|
+
attr_reader :payload
|
|
133
|
+
|
|
134
|
+
def initialize(queue, payload)
|
|
135
|
+
@queue = queue
|
|
136
|
+
@payload = payload
|
|
137
|
+
@failure_hooks_ran = false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Creates a job by placing it on a queue. Expects a string queue
|
|
141
|
+
# name, a string class name, and an optional array of arguments to
|
|
142
|
+
# pass to the class' `perform` method.
|
|
143
|
+
#
|
|
144
|
+
# Raises an exception if no queue or class is given.
|
|
145
|
+
def self.create(queue, klass, *args)
|
|
146
|
+
ResqueSqs.validate(klass, queue)
|
|
147
|
+
|
|
148
|
+
if ResqueSqs.inline?
|
|
149
|
+
# Instantiating a ResqueSqs::Job and calling perform on it so callbacks run
|
|
150
|
+
# decode(encode(args)) to ensure that args are normalized in the same manner as a non-inline job
|
|
151
|
+
new(:inline, {'class' => klass, 'args' => decode(encode(args))}).perform
|
|
152
|
+
else
|
|
153
|
+
ResqueSqs.push(queue, :class => klass.to_s, :args => args)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Removes a job from a queue. Expects a string queue name, a
|
|
158
|
+
# string class name, and, optionally, args.
|
|
159
|
+
#
|
|
160
|
+
# Returns the number of jobs destroyed.
|
|
161
|
+
#
|
|
162
|
+
# If no args are provided, it will remove all jobs of the class
|
|
163
|
+
# provided.
|
|
164
|
+
#
|
|
165
|
+
# That is, for these two jobs:
|
|
166
|
+
#
|
|
167
|
+
# { 'class' => 'UpdateGraph', 'args' => ['defunkt'] }
|
|
168
|
+
# { 'class' => 'UpdateGraph', 'args' => ['mojombo'] }
|
|
169
|
+
#
|
|
170
|
+
# The following call will remove both:
|
|
171
|
+
#
|
|
172
|
+
# ResqueSqs::Job.destroy(queue, 'UpdateGraph')
|
|
173
|
+
#
|
|
174
|
+
# Whereas specifying args will only remove the 2nd job:
|
|
175
|
+
#
|
|
176
|
+
# ResqueSqs::Job.destroy(queue, 'UpdateGraph', 'mojombo')
|
|
177
|
+
#
|
|
178
|
+
# This method can be potentially very slow and memory intensive,
|
|
179
|
+
# depending on the size of your queue, as it loads all jobs into
|
|
180
|
+
# a Ruby array before processing.
|
|
181
|
+
def self.destroy(queue, klass, *args)
|
|
182
|
+
klass = klass.to_s
|
|
183
|
+
queue = "queue:#{queue}"
|
|
184
|
+
destroyed = 0
|
|
185
|
+
|
|
186
|
+
if args.empty?
|
|
187
|
+
redis.lrange(queue, 0, -1).each do |string|
|
|
188
|
+
if decode(string)['class'] == klass
|
|
189
|
+
destroyed += redis.lrem(queue, 0, string).to_i
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
else
|
|
193
|
+
destroyed += redis.lrem(queue, 0, encode(:class => klass, :args => args))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
destroyed
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Given a string queue name, returns an instance of ResqueSqs::Job
|
|
200
|
+
# if any jobs are available. If not, returns nil.
|
|
201
|
+
def self.reserve(queue)
|
|
202
|
+
return unless payload = ResqueSqs.pop(queue)
|
|
203
|
+
new(queue, payload)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Attempts to perform the work represented by this job instance.
|
|
207
|
+
# Calls #perform on the class given in the payload with the
|
|
208
|
+
# arguments given in the payload.
|
|
209
|
+
def perform
|
|
210
|
+
job = payload_class
|
|
211
|
+
job_args = args || []
|
|
212
|
+
job_was_performed = false
|
|
213
|
+
|
|
214
|
+
begin
|
|
215
|
+
# Execute before_perform hook. Abort the job gracefully if
|
|
216
|
+
# ResqueSqs::DontPerform is raised.
|
|
217
|
+
begin
|
|
218
|
+
before_hooks.each do |hook|
|
|
219
|
+
job.send(hook, *job_args)
|
|
220
|
+
end
|
|
221
|
+
rescue DontPerform
|
|
222
|
+
return false
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Execute the job. Do it in an around_perform hook if available.
|
|
226
|
+
if around_hooks.empty?
|
|
227
|
+
job.perform(*job_args)
|
|
228
|
+
job_was_performed = true
|
|
229
|
+
else
|
|
230
|
+
# We want to nest all around_perform plugins, with the last one
|
|
231
|
+
# finally calling perform
|
|
232
|
+
stack = around_hooks.reverse.inject(nil) do |last_hook, hook|
|
|
233
|
+
if last_hook
|
|
234
|
+
lambda do
|
|
235
|
+
job.send(hook, *job_args) { last_hook.call }
|
|
236
|
+
end
|
|
237
|
+
else
|
|
238
|
+
lambda do
|
|
239
|
+
job.send(hook, *job_args) do
|
|
240
|
+
result = job.perform(*job_args)
|
|
241
|
+
job_was_performed = true
|
|
242
|
+
result
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
stack.call
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Execute after_perform hook
|
|
251
|
+
after_hooks.each do |hook|
|
|
252
|
+
job.send(hook, *job_args)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Return true if the job was performed
|
|
256
|
+
return job_was_performed
|
|
257
|
+
|
|
258
|
+
# If an exception occurs during the job execution, look for an
|
|
259
|
+
# on_failure hook then re-raise.
|
|
260
|
+
rescue Object => e
|
|
261
|
+
run_failure_hooks(e)
|
|
262
|
+
raise e
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Returns the actual class constant represented in this job's payload.
|
|
267
|
+
def payload_class
|
|
268
|
+
@payload_class ||= constantize(@payload['class'])
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Returns the payload class as a string without raising NameError
|
|
272
|
+
def payload_class_name
|
|
273
|
+
payload_class.to_s
|
|
274
|
+
rescue NameError
|
|
275
|
+
'No Name'
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def has_payload_class?
|
|
279
|
+
payload_class != Object
|
|
280
|
+
rescue NameError
|
|
281
|
+
false
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Returns an array of args represented in this job's payload.
|
|
285
|
+
def args
|
|
286
|
+
@payload['args']
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Given an exception object, hands off the needed parameters to
|
|
290
|
+
# the Failure module.
|
|
291
|
+
def fail(exception)
|
|
292
|
+
run_failure_hooks(exception)
|
|
293
|
+
Failure.create \
|
|
294
|
+
:payload => payload,
|
|
295
|
+
:exception => exception,
|
|
296
|
+
:worker => worker,
|
|
297
|
+
:queue => queue
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# Creates an identical job, essentially placing this job back on
|
|
301
|
+
# the queue.
|
|
302
|
+
def recreate
|
|
303
|
+
self.class.create(queue, payload_class, *args)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# String representation
|
|
307
|
+
def inspect
|
|
308
|
+
obj = @payload
|
|
309
|
+
"(Job{%s} | %s | %s)" % [ @queue, obj['class'], obj['args'].inspect ]
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Equality
|
|
313
|
+
def ==(other)
|
|
314
|
+
queue == other.queue &&
|
|
315
|
+
payload_class == other.payload_class &&
|
|
316
|
+
args == other.args
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def before_hooks
|
|
320
|
+
@before_hooks ||= Plugin.before_hooks(payload_class)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def around_hooks
|
|
324
|
+
@around_hooks ||= Plugin.around_hooks(payload_class)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def after_hooks
|
|
328
|
+
@after_hooks ||= Plugin.after_hooks(payload_class)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def failure_hooks
|
|
332
|
+
@failure_hooks ||= Plugin.failure_hooks(payload_class)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def run_failure_hooks(exception)
|
|
336
|
+
begin
|
|
337
|
+
job_args = args || []
|
|
338
|
+
if has_payload_class?
|
|
339
|
+
failure_hooks.each { |hook| payload_class.send(hook, exception, *job_args) } unless @failure_hooks_ran
|
|
340
|
+
end
|
|
341
|
+
ensure
|
|
342
|
+
@failure_hooks_ran = true
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module ResqueSqs
|
|
2
|
+
# Include this module in classes you wish to have logging facilities
|
|
3
|
+
module Logging
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
# Thunk to the logger's own log method (if configured)
|
|
7
|
+
def self.log(severity, message)
|
|
8
|
+
ResqueSqs.logger.__send__(severity, message) if ResqueSqs.logger
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Log level aliases
|
|
12
|
+
def debug(message); Logging.log :debug, message; end
|
|
13
|
+
def info(message); Logging.log :info, message; end
|
|
14
|
+
def warn(message); Logging.log :warn, message; end
|
|
15
|
+
def error(message); Logging.log :error, message; end
|
|
16
|
+
def fatal(message); Logging.log :fatal, message; end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module ResqueSqs
|
|
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
|
+
# ResqueSqs::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
|
+
|
|
46
|
+
# Given an object, returns a list `after_enqueue` hook names.
|
|
47
|
+
def after_enqueue_hooks(job)
|
|
48
|
+
job.methods.grep(/^after_enqueue/).sort
|
|
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
|
|
55
|
+
|
|
56
|
+
# Given an object, returns a list `after_dequeue` hook names.
|
|
57
|
+
def after_dequeue_hooks(job)
|
|
58
|
+
job.methods.grep(/^after_dequeue/).sort
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Given an object, returns a list `before_dequeue` hook names.
|
|
62
|
+
def before_dequeue_hooks(job)
|
|
63
|
+
job.methods.grep(/^before_dequeue/).sort
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
ResqueSqs::Server.helpers do
|
|
2
|
+
####################
|
|
3
|
+
#failed.erb helpers#
|
|
4
|
+
####################
|
|
5
|
+
|
|
6
|
+
def failed_date_format
|
|
7
|
+
"%Y/%m/%d %T %z"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def failed_multiple_queues?
|
|
11
|
+
return @multiple_failed_queues if defined?(@multiple_failed_queues)
|
|
12
|
+
@multiple_failed_queues = ResqueSqs::Failure.queues.size > 1
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def failed_size
|
|
16
|
+
@failed_size ||= ResqueSqs::Failure.count(params[:queue], params[:class])
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def failed_per_page
|
|
20
|
+
@failed_per_page = if params[:class]
|
|
21
|
+
failed_size
|
|
22
|
+
else
|
|
23
|
+
20
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def failed_start_at
|
|
28
|
+
params[:start].to_i
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def failed_end_at
|
|
32
|
+
if failed_start_at + failed_per_page > failed_size
|
|
33
|
+
failed_size
|
|
34
|
+
else
|
|
35
|
+
failed_start_at + failed_per_page
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def failed_order
|
|
40
|
+
params[:order] || 'desc'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def failed_class_counts(queue = params[:queue])
|
|
44
|
+
classes = Hash.new(0)
|
|
45
|
+
ResqueSqs::Failure.each(0, ResqueSqs::Failure.count(queue), queue) do |_, item|
|
|
46
|
+
class_name = item['payload']['class'] if item['payload']
|
|
47
|
+
class_name ||= "nil"
|
|
48
|
+
classes[class_name] += 1
|
|
49
|
+
end
|
|
50
|
+
classes
|
|
51
|
+
end
|
|
52
|
+
end
|
|
Binary file
|
|
Binary file
|