tresque 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +186 -0
- data/Rakefile +1 -0
- data/lib/tresque.rb +24 -0
- data/lib/tresque/delay.rb +115 -0
- data/lib/tresque/delay_execution_worker.rb +58 -0
- data/lib/tresque/queue_lock.rb +70 -0
- data/lib/tresque/registry.rb +94 -0
- data/lib/tresque/resque_spec/ext.rb +92 -0
- data/lib/tresque/resque_spec/helpers.rb +25 -0
- data/lib/tresque/resque_spec/matchers.rb +318 -0
- data/lib/tresque/resque_spec/resque_spec.rb +201 -0
- data/lib/tresque/resque_spec/scheduler.rb +93 -0
- data/lib/tresque/spec.rb +2 -0
- data/lib/tresque/spec/delay.rb +27 -0
- data/lib/tresque/util.rb +16 -0
- data/lib/tresque/version.rb +3 -0
- data/lib/tresque/worker.rb +228 -0
- data/lib/tresque/worker_lock.rb +53 -0
- data/tresque.gemspec +30 -0
- metadata +141 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'resque-bus'
|
2
|
+
|
3
|
+
module TResque
|
4
|
+
class Registry
|
5
|
+
class << self
|
6
|
+
def default_weight
|
7
|
+
100
|
8
|
+
end
|
9
|
+
|
10
|
+
def class_hash
|
11
|
+
@class_hash ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def queue_hash
|
15
|
+
@queue_hash ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def class_list
|
19
|
+
class_hash.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
def worker(klass)
|
23
|
+
klass = klass.name if klass.is_a?(Class)
|
24
|
+
klass = klass.to_s
|
25
|
+
class_hash[klass] = 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def queue(queue_name, weight=nil)
|
29
|
+
queue_name = queue_name.to_s
|
30
|
+
if weight
|
31
|
+
# take higher weight
|
32
|
+
if !queue_hash[queue_name] || weight > queue_hash[queue_name]
|
33
|
+
queue_hash[queue_name] = weight
|
34
|
+
end
|
35
|
+
else
|
36
|
+
queue_hash[queue_name] ||= false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def weight(key)
|
41
|
+
if !self.queue_hash[key]
|
42
|
+
self.default_weight
|
43
|
+
else
|
44
|
+
self.queue_hash[key]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# called to know what queues to set
|
49
|
+
def queues
|
50
|
+
register_classes
|
51
|
+
register_bus
|
52
|
+
sorted_queues
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def register_classes
|
58
|
+
class_list.each do |klass_name|
|
59
|
+
klass = klass_name.constantize
|
60
|
+
queue(klass.queue)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def register_bus
|
65
|
+
manager = QueueBus::TaskManager.new(false)
|
66
|
+
manager.queue_names.each do |name|
|
67
|
+
queue(name)
|
68
|
+
end
|
69
|
+
queue("bus_incoming", 1)
|
70
|
+
end
|
71
|
+
|
72
|
+
def sorted_queues
|
73
|
+
array = queue_hash.keys.clone.shuffle
|
74
|
+
hash = {}
|
75
|
+
array.each do |key|
|
76
|
+
hash[key] = self.weight(key)
|
77
|
+
end
|
78
|
+
# sorted with highest weight first
|
79
|
+
array.sort!{ |x,y| hash[y] <=> hash[x] }
|
80
|
+
array
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :app_key
|
85
|
+
def initialize(app_key)
|
86
|
+
@app_key = Util.normalize(app_key)
|
87
|
+
end
|
88
|
+
|
89
|
+
def queue(name, weight=nil)
|
90
|
+
queue_name = "#{app_key}_#{name}"
|
91
|
+
::TResque::Registry.queue(queue_name, weight)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'resque'
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
class Job
|
5
|
+
class << self
|
6
|
+
alias :create_without_resque_spec :create
|
7
|
+
alias :destroy_without_resque_spec :destroy
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.create(queue, klass, *args)
|
11
|
+
return create_without_resque_spec(queue, klass, *args) if ResqueSpec.disable_ext
|
12
|
+
|
13
|
+
raise ::Resque::NoQueueError.new("Jobs must be placed onto a queue.") if !queue
|
14
|
+
raise ::Resque::NoClassError.new("Jobs must be given a class.") if klass.to_s.empty?
|
15
|
+
ResqueSpec.enqueue(queue, klass, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.destroy(queue, klass, *args)
|
19
|
+
return destroy_without_resque_spec(queue, klass, *args) if ResqueSpec.disable_ext
|
20
|
+
|
21
|
+
raise ::Resque::NoQueueError.new("Jobs must have been placed onto a queue.") if !queue
|
22
|
+
raise ::Resque::NoClassError.new("Jobs must have been given a class.") if klass.to_s.empty?
|
23
|
+
|
24
|
+
old_count = ResqueSpec.queue_by_name(queue).size
|
25
|
+
|
26
|
+
ResqueSpec.dequeue(queue, klass, *args)
|
27
|
+
|
28
|
+
old_count - ResqueSpec.queue_by_name(queue).size
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
alias :enqueue_without_resque_spec :enqueue
|
33
|
+
alias :enqueue_to_without_resque_spec :enqueue_to if Resque.respond_to? :enqueue_to
|
34
|
+
alias :reserve_without_resque_spec :reserve
|
35
|
+
alias :peek_without_resque_spec :peek
|
36
|
+
alias :size_without_resque_spec :size
|
37
|
+
|
38
|
+
def enqueue(klass, *args)
|
39
|
+
return enqueue_without_resque_spec(klass, *args) if ResqueSpec.disable_ext
|
40
|
+
|
41
|
+
enqueue_to(queue_from_class(klass), klass, *args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def enqueue_to(queue, klass, *args)
|
45
|
+
return enqueue_to_without_resque_spec(queue, klass, *args) if ResqueSpec.disable_ext
|
46
|
+
|
47
|
+
if ResqueSpec.inline
|
48
|
+
return if run_before_enqueue(klass, *args)
|
49
|
+
run_after_enqueue(klass, *args)
|
50
|
+
Job.create(queue, klass, *args)
|
51
|
+
else
|
52
|
+
return if run_before_enqueue(klass, *args)
|
53
|
+
Job.create(queue, klass, *args)
|
54
|
+
run_after_enqueue(klass, *args)
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def peek(queue, start = 0, count = 1)
|
60
|
+
return peek_without_resque_spec(queue, start, count) if ResqueSpec.disable_ext
|
61
|
+
ResqueSpec.peek(queue, start, count).map do |job|
|
62
|
+
job.inject({}) { |a, (k, v)| a[k.to_s] = v; a }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def reserve(queue_name)
|
67
|
+
return reserve_without_resque_spec(queue_name) if ResqueSpec.disable_ext
|
68
|
+
|
69
|
+
ResqueSpec.pop(queue_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def size(queue_name)
|
73
|
+
return size_without_resque_spec(queue_name) if ResqueSpec.disable_ext
|
74
|
+
|
75
|
+
ResqueSpec.queue_by_name(queue_name).count
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def run_after_enqueue(klass, *args)
|
81
|
+
Plugin.after_enqueue_hooks(klass).each do |hook|
|
82
|
+
klass.send(hook, *args)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def run_before_enqueue(klass, *args)
|
87
|
+
before_hooks = Plugin.before_enqueue_hooks(klass).collect do |hook|
|
88
|
+
klass.send(hook, *args)
|
89
|
+
end
|
90
|
+
before_hooks.any? { |result| result == false }
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ResqueSpec
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
def with_resque
|
5
|
+
original = ResqueSpec.inline
|
6
|
+
begin
|
7
|
+
ResqueSpec.inline = true
|
8
|
+
yield
|
9
|
+
ensure
|
10
|
+
ResqueSpec.inline = original
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def without_resque_spec
|
15
|
+
original = ResqueSpec.disable_ext
|
16
|
+
begin
|
17
|
+
ResqueSpec.disable_ext = true
|
18
|
+
yield
|
19
|
+
ensure
|
20
|
+
ResqueSpec.disable_ext = original
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require 'rspec/core'
|
2
|
+
require 'rspec/expectations'
|
3
|
+
require 'rspec/mocks'
|
4
|
+
|
5
|
+
module InQueueHelper
|
6
|
+
def self.extended(klass)
|
7
|
+
klass.instance_eval do
|
8
|
+
self.queue_name = nil
|
9
|
+
chain :in do |queue_name|
|
10
|
+
self.queue_name = queue_name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_accessor :queue_name
|
18
|
+
|
19
|
+
def queue(actual)
|
20
|
+
if @queue_name
|
21
|
+
ResqueSpec.queue_by_name(@queue_name)
|
22
|
+
else
|
23
|
+
ResqueSpec.queue_for(actual)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec::Matchers.define :have_queued do |*expected_args|
|
30
|
+
extend InQueueHelper
|
31
|
+
include InQueueHelper
|
32
|
+
chain :times do |num_times_queued|
|
33
|
+
@times = num_times_queued
|
34
|
+
@times_info = @times == 1 ? ' once' : " #{@times} times"
|
35
|
+
end
|
36
|
+
|
37
|
+
chain :once do |num_times_queued|
|
38
|
+
@times = 1
|
39
|
+
@times_info = ' once'
|
40
|
+
end
|
41
|
+
|
42
|
+
match do |actual|
|
43
|
+
matched = queue(actual).select do |entry|
|
44
|
+
klass = entry.fetch(:class)
|
45
|
+
args = entry.fetch(:args)
|
46
|
+
|
47
|
+
if expected_args.length == 0
|
48
|
+
klass.to_s == actual.to_s
|
49
|
+
elsif expected_args.length == 1 && expected_args[0].is_a?(Hash) && !expected_args[0].has_key?('tz') && !expected_args[0].has_key?('locale')
|
50
|
+
klass.to_s == actual.to_s && args[0].except('locale', 'tz') == expected_args[0]
|
51
|
+
else
|
52
|
+
klass.to_s == actual.to_s && expected_args == args
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if @times
|
57
|
+
matched.size == @times
|
58
|
+
else
|
59
|
+
matched.size > 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
failure_message do |actual|
|
64
|
+
"expected that #{actual} would have [#{expected_args.join(', ')}] queued#{@times_info}"
|
65
|
+
end
|
66
|
+
|
67
|
+
failure_message_when_negated do |actual|
|
68
|
+
"expected that #{actual} would not have [#{expected_args.join(', ')}] queued#{@times_info}"
|
69
|
+
end
|
70
|
+
|
71
|
+
description do
|
72
|
+
"have queued arguments of [#{expected_args.join(', ')}]#{@times_info}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
RSpec::Matchers.define :have_queue_size_of do |size|
|
77
|
+
extend InQueueHelper
|
78
|
+
include InQueueHelper
|
79
|
+
|
80
|
+
match do |actual|
|
81
|
+
queue(actual).size == size
|
82
|
+
end
|
83
|
+
|
84
|
+
failure_message do |actual|
|
85
|
+
"expected that #{actual} would have #{size} entries queued, but got #{queue(actual).size} instead"
|
86
|
+
end
|
87
|
+
|
88
|
+
failure_message_when_negated do |actual|
|
89
|
+
"expected that #{actual} would not have #{size} entries queued, but got #{queue(actual).size} instead"
|
90
|
+
end
|
91
|
+
|
92
|
+
description do
|
93
|
+
"have a queue size of #{size}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
RSpec::Matchers.define :have_queue_size_of_at_least do |size|
|
98
|
+
extend InQueueHelper
|
99
|
+
include InQueueHelper
|
100
|
+
match do |actual|
|
101
|
+
queue(actual).size >= size
|
102
|
+
end
|
103
|
+
|
104
|
+
failure_message do |actual|
|
105
|
+
"expected that #{actual} would have at least #{size} entries queued, but got #{queue(actual).size} instead"
|
106
|
+
end
|
107
|
+
|
108
|
+
failure_message_when_negated do |actual|
|
109
|
+
"expected that #{actual} would not have at least #{size} entries queued, but got #{queue(actual).size} instead"
|
110
|
+
end
|
111
|
+
|
112
|
+
description do
|
113
|
+
"have a queue size of at least #{size}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
module ScheduleQueueHelper
|
118
|
+
def self.extended(klass)
|
119
|
+
klass.instance_eval do
|
120
|
+
self.queue_name = nil
|
121
|
+
chain :queue do |queue_name|
|
122
|
+
self.queue_name = queue_name
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
attr_accessor :queue_name
|
128
|
+
|
129
|
+
def schedule_queue_for(actual)
|
130
|
+
if @queue_name
|
131
|
+
ResqueSpec.queue_by_name(@queue_name)
|
132
|
+
else
|
133
|
+
ResqueSpec.schedule_for(actual)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
RSpec::Matchers.define :have_scheduled do |*expected_args|
|
140
|
+
extend ScheduleQueueHelper
|
141
|
+
include ScheduleQueueHelper
|
142
|
+
chain :at do |timestamp|
|
143
|
+
@interval = nil
|
144
|
+
@time = timestamp
|
145
|
+
@time_info = "at #{@time}"
|
146
|
+
end
|
147
|
+
|
148
|
+
chain :in do |interval|
|
149
|
+
@time = nil
|
150
|
+
@interval = interval
|
151
|
+
@time_info = "in #{@interval} seconds"
|
152
|
+
end
|
153
|
+
|
154
|
+
match do |actual|
|
155
|
+
schedule_queue_for(actual).any? do |entry|
|
156
|
+
class_matches = entry[:class].to_s == actual.to_s
|
157
|
+
args_match = begin
|
158
|
+
if expected_args.length == 1 && expected_args[0].is_a?(Hash) && !expected_args[0].has_key?('tz') && !expected_args[0].has_key?('locale')
|
159
|
+
entry[:args][0].except('tz', 'locale') == expected_args[0]
|
160
|
+
else
|
161
|
+
entry[:args] == expected_args
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
time_matches = if @time
|
166
|
+
entry[:time] == @time
|
167
|
+
elsif @interval
|
168
|
+
entry[:time].to_i == (entry[:stored_at] + @interval).to_i
|
169
|
+
else
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
class_matches && args_match && time_matches
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
failure_message do |actual|
|
178
|
+
["expected that #{actual} would have [#{expected_args.join(', ')}] scheduled", @time_info].join(' ')
|
179
|
+
end
|
180
|
+
|
181
|
+
failure_message_when_negated do |actual|
|
182
|
+
["expected that #{actual} would not have [#{expected_args.join(', ')}] scheduled", @time_info].join(' ')
|
183
|
+
end
|
184
|
+
|
185
|
+
description do
|
186
|
+
"have scheduled arguments"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
RSpec::Matchers.define :have_scheduled_at do |*expected_args|
|
191
|
+
extend ScheduleQueueHelper
|
192
|
+
warn "DEPRECATION WARNING: have_scheduled_at(time, *args) is deprecated and will be removed in future. Please use have_scheduled(*args).at(time) instead."
|
193
|
+
|
194
|
+
match do |actual|
|
195
|
+
time = expected_args.first
|
196
|
+
other_args = expected_args[1..-1]
|
197
|
+
schedule_queue_for(actual).any? { |entry| entry[:class].to_s == actual.to_s && entry[:time] == time && other_args == entry[:args] }
|
198
|
+
end
|
199
|
+
|
200
|
+
failure_message do |actual|
|
201
|
+
"expected that #{actual} would have [#{expected_args.join(', ')}] scheduled"
|
202
|
+
end
|
203
|
+
|
204
|
+
failure_message_when_negated do |actual|
|
205
|
+
"expected that #{actual} would not have [#{expected_args.join(', ')}] scheduled"
|
206
|
+
end
|
207
|
+
|
208
|
+
description do
|
209
|
+
"have scheduled at the given time the arguments"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
RSpec::Matchers.define :have_schedule_size_of do |size|
|
214
|
+
extend ScheduleQueueHelper
|
215
|
+
|
216
|
+
match do |actual|
|
217
|
+
schedule_queue_for(actual).size == size
|
218
|
+
end
|
219
|
+
|
220
|
+
failure_message do |actual|
|
221
|
+
"expected that #{actual} would have #{size} scheduled entries, but got #{schedule_queue_for(actual).size} instead"
|
222
|
+
end
|
223
|
+
|
224
|
+
failure_message_when_negated do |actual|
|
225
|
+
"expected that #{actual} would have #{size} scheduled entries."
|
226
|
+
end
|
227
|
+
|
228
|
+
description do
|
229
|
+
"have schedule size of #{size}"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
RSpec::Matchers.define :have_schedule_size_of_at_least do |size|
|
234
|
+
extend ScheduleQueueHelper
|
235
|
+
|
236
|
+
match do |actual|
|
237
|
+
schedule_queue_for(actual).size >= size
|
238
|
+
end
|
239
|
+
|
240
|
+
failure_message do |actual|
|
241
|
+
"expected that #{actual} would have at least #{size} scheduled entries, but got #{schedule_queue_for(actual).size} instead"
|
242
|
+
end
|
243
|
+
|
244
|
+
failure_message_when_negated do |actual|
|
245
|
+
"expected that #{actual} would have at least #{size} scheduled entries."
|
246
|
+
end
|
247
|
+
|
248
|
+
description do
|
249
|
+
"have schedule size of #{size}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
RSpec::Matchers.define :have_delayed do |method_name|
|
255
|
+
extend InQueueHelper
|
256
|
+
include InQueueHelper
|
257
|
+
|
258
|
+
chain :until do |timestamp|
|
259
|
+
@when = timestamp.to_i
|
260
|
+
end
|
261
|
+
|
262
|
+
chain :with do |*args|
|
263
|
+
@args = args
|
264
|
+
end
|
265
|
+
|
266
|
+
chain :in_lock_namespace do |ns|
|
267
|
+
@lock_namespace = ns.to_s
|
268
|
+
end
|
269
|
+
|
270
|
+
chain :with_queue_lock do |lock|
|
271
|
+
@queue_lock = lock.to_s
|
272
|
+
end
|
273
|
+
|
274
|
+
chain :with_worker_lock do |lock|
|
275
|
+
@worker_lock = lock.to_s
|
276
|
+
end
|
277
|
+
|
278
|
+
match do |class_name|
|
279
|
+
|
280
|
+
class_name = case class_name
|
281
|
+
when Class
|
282
|
+
class_name.name
|
283
|
+
when String, Symbol
|
284
|
+
class_name
|
285
|
+
else # instances of objects
|
286
|
+
class_name.class.name
|
287
|
+
end
|
288
|
+
|
289
|
+
[@queue_name || ResqueSpec.queues].flatten.compact.each do |queue|
|
290
|
+
ResqueSpec.queue_by_name(queue).detect do |entry|
|
291
|
+
klass = entry[:class]
|
292
|
+
args = entry[:args]
|
293
|
+
data = args.first if args.is_a?(Array)
|
294
|
+
|
295
|
+
matched = data['class_name'] == class_name.to_s
|
296
|
+
matched &&= data['method_name'] == method_name.to_s
|
297
|
+
matched &&= data['run_at'] == @when if @when
|
298
|
+
matched &&= data['args'] == @args if @args
|
299
|
+
matched &&= data['lock_namespace'] == @lock_namespace if @lock_namespace
|
300
|
+
matched &&= data['queue_lock'] == @queue_lock if @queue_lock
|
301
|
+
matched &&= data['worker_lock'] == @worker_lock if @worker_lock
|
302
|
+
matched
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
failure_message do |actual|
|
308
|
+
"expected that #{method_name} would have been a delayed invocation"
|
309
|
+
end
|
310
|
+
|
311
|
+
failure_message_when_negated do |actual|
|
312
|
+
"expected that #{method_name} would not have been delayed"
|
313
|
+
end
|
314
|
+
|
315
|
+
description do
|
316
|
+
"#{method_name} should be deplayed"
|
317
|
+
end
|
318
|
+
end
|