qu-scheduler 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +2 -1
- data/README.md +191 -0
- data/lib/qu-scheduler.rb +1 -4
- data/lib/qu-scheduler/tasks.rb +35 -0
- data/lib/qu/extensions/scheduler.rb +102 -0
- data/lib/qu/extensions/scheduler/redis.rb +199 -0
- data/lib/qu/scheduler.rb +227 -0
- data/lib/qu/scheduler/version.rb +2 -2
- data/test/delayed_queue_test.rb +277 -0
- data/test/redis-test.conf +115 -0
- data/test/scheduler_args_test.rb +156 -0
- data/test/scheduler_hooks_test.rb +51 -0
- data/test/scheduler_test.rb +181 -0
- data/test/test_helper.rb +91 -7
- metadata +64 -76
- data/README.rdoc +0 -7
- data/lib/tasks/qu-scheduler_tasks.rake +0 -4
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/assets/javascripts/application.js +0 -9
- data/test/dummy/app/assets/stylesheets/application.css +0 -7
- data/test/dummy/app/controllers/application_controller.rb +0 -3
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -45
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/database.yml +0 -25
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -30
- data/test/dummy/config/environments/production.rb +0 -60
- data/test/dummy/config/environments/test.rb +0 -39
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/inflections.rb +0 -10
- data/test/dummy/config/initializers/mime_types.rb +0 -5
- data/test/dummy/config/initializers/secret_token.rb +0 -7
- data/test/dummy/config/initializers/session_store.rb +0 -8
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -5
- data/test/dummy/config/routes.rb +0 -58
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +0 -0
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -26
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +0 -6
data/lib/qu/scheduler.rb
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'qu'
|
2
|
+
require 'qu/scheduler/version'
|
3
|
+
require 'qu/extensions/scheduler'
|
4
|
+
require 'rufus/scheduler'
|
5
|
+
require 'thwait'
|
6
|
+
|
7
|
+
module Qu
|
8
|
+
class Scheduler
|
9
|
+
@@scheduled_jobs = {}
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# If set, will try to update the schedule in the loop
|
13
|
+
attr_accessor :dynamic
|
14
|
+
|
15
|
+
# Amount of time in seconds to sleep between polls of the delayed
|
16
|
+
# queue. Defaults to 5
|
17
|
+
attr_writer :poll_sleep_amount
|
18
|
+
|
19
|
+
# the Rufus::Scheduler jobs that are scheduled
|
20
|
+
def scheduled_jobs
|
21
|
+
@@scheduled_jobs
|
22
|
+
end
|
23
|
+
|
24
|
+
def poll_sleep_amount
|
25
|
+
@poll_sleep_amount ||= 5 # seconds
|
26
|
+
end
|
27
|
+
|
28
|
+
# Schedule all jobs and continually look for delayed jobs (never returns)
|
29
|
+
def run
|
30
|
+
set_process_title "starting"
|
31
|
+
|
32
|
+
# trap signals
|
33
|
+
register_signal_handlers
|
34
|
+
|
35
|
+
# Load the schedule into rufus
|
36
|
+
# If dynamic is set, load that schedule otherwise use normal load
|
37
|
+
if dynamic
|
38
|
+
reload_schedule!
|
39
|
+
else
|
40
|
+
load_schedule!
|
41
|
+
end
|
42
|
+
|
43
|
+
# Now start the scheduling part of the loop.
|
44
|
+
loop do
|
45
|
+
begin
|
46
|
+
handle_delayed_items
|
47
|
+
update_schedule if dynamic
|
48
|
+
rescue Errno::EAGAIN, Errno::ECONNRESET => e
|
49
|
+
warn e.message
|
50
|
+
end
|
51
|
+
poll_sleep
|
52
|
+
end
|
53
|
+
|
54
|
+
# never gets here.
|
55
|
+
end
|
56
|
+
|
57
|
+
# For all signals, set the shutdown flag and wait for current
|
58
|
+
# poll/enqueing to finish (should be almost istant). In the
|
59
|
+
# case of sleeping, exit immediately.
|
60
|
+
def register_signal_handlers
|
61
|
+
trap("TERM") { shutdown }
|
62
|
+
trap("INT") { shutdown }
|
63
|
+
|
64
|
+
begin
|
65
|
+
trap('QUIT') { shutdown }
|
66
|
+
trap('USR1') { print_schedule }
|
67
|
+
trap('USR2') { reload_schedule! }
|
68
|
+
rescue ArgumentError
|
69
|
+
warn "Signals QUIT, USR1 and USR2 not supported."
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def print_schedule
|
74
|
+
if rufus_scheduler
|
75
|
+
Qu.logger.info("Scheduling Info\tLast Run")
|
76
|
+
scheduler_jobs = rufus_scheduler.all_jobs
|
77
|
+
scheduler_jobs.each do |k, v|
|
78
|
+
Qu.logger.info("#{v.t}\t#{v.last}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Pulls the schedule from Qu.schedule and loads it into the
|
84
|
+
# rufus scheduler instance
|
85
|
+
def load_schedule!
|
86
|
+
Qu.backend.load_schedule!
|
87
|
+
end
|
88
|
+
|
89
|
+
# Loads a job schedule into the Rufus::Scheduler and stores it in @@scheduled_jobs
|
90
|
+
def load_schedule_job(name, config)
|
91
|
+
# If rails_env is set in the config, enforce ENV['RAILS_ENV'] as
|
92
|
+
# required for the jobs to be scheduled. If rails_env is missing, the
|
93
|
+
# job should be scheduled regardless of what ENV['RAILS_ENV'] is set
|
94
|
+
# to.
|
95
|
+
if config['rails_env'].nil? || rails_env_matches?(config)
|
96
|
+
Qu.logger.info("Scheduling #{name}")
|
97
|
+
interval_defined = false
|
98
|
+
interval_types = %w{cron every}
|
99
|
+
interval_types.each do |interval_type|
|
100
|
+
if !config[interval_type].nil? && config[interval_type].length > 0
|
101
|
+
@@scheduled_jobs[name] = rufus_scheduler.send(interval_type, config[interval_type]) do
|
102
|
+
Qu.logger.info("queueing #{config['class']} (#{name})")
|
103
|
+
handle_errors { enqueue_from_config(config) }
|
104
|
+
end
|
105
|
+
interval_defined = true
|
106
|
+
break
|
107
|
+
end
|
108
|
+
end
|
109
|
+
unless interval_defined
|
110
|
+
Qu.logger.warn("no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns true if the given schedule config hash matches the current
|
116
|
+
# ENV['RAILS_ENV']
|
117
|
+
def rails_env_matches?(config)
|
118
|
+
config['rails_env'] && ENV['RAILS_ENV'] && config['rails_env'].gsub(/\s/,'').split(',').include?(ENV['RAILS_ENV'])
|
119
|
+
end
|
120
|
+
|
121
|
+
# Handles queueing delayed items
|
122
|
+
# at_time - Time to start scheduling items (default: now).
|
123
|
+
def handle_delayed_items(at_time=nil)
|
124
|
+
item = nil
|
125
|
+
if timestamp = Qu.backend.next_delayed_timestamp(at_time)
|
126
|
+
set_process_title "processing delayed jobs"
|
127
|
+
while !timestamp.nil?
|
128
|
+
enqueue_delayed_items_for_timestamp(timestamp)
|
129
|
+
timestamp = Qu.backend.next_delayed_timestamp(at_time)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Enqueues all delayed jobs for a timestamp
|
135
|
+
def enqueue_delayed_items_for_timestamp(timestamp)
|
136
|
+
item = nil
|
137
|
+
begin
|
138
|
+
handle_shutdown do
|
139
|
+
if item = Qu.backend.next_item_for_timestamp(timestamp)
|
140
|
+
Qu.logger.debug("queuing #{item['klass']} [delayed]")
|
141
|
+
handle_errors { enqueue_from_config(item) }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
# continue processing until there are no more ready items in this timestamp
|
145
|
+
end while !item.nil?
|
146
|
+
end
|
147
|
+
|
148
|
+
def handle_shutdown
|
149
|
+
exit if @shutdown
|
150
|
+
yield
|
151
|
+
exit if @shutdown
|
152
|
+
end
|
153
|
+
|
154
|
+
def handle_errors
|
155
|
+
begin
|
156
|
+
yield
|
157
|
+
rescue Exception => e
|
158
|
+
Qu.logger.fatal("#{e.class.name}: #{e.message}")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Enqueues a job based on a config hash
|
163
|
+
def enqueue_from_config(job_config)
|
164
|
+
payload = Payload.new(job_config)
|
165
|
+
if job_klass = job_config['custom_job_class']
|
166
|
+
# The custom job class API must offer a static "scheduled" method. If the custom
|
167
|
+
# job class can not be constantized (via a requeue call from the web perhaps), fall
|
168
|
+
# back to enqueing normally via Qu::Job.create.
|
169
|
+
begin
|
170
|
+
Qu::Payload.new.send(:constantize, job_klass).scheduled(payload.queue, job_klass, *payload.args)
|
171
|
+
rescue NameError
|
172
|
+
# Note that the custom job class (job_config['custom_job_class']) is the one enqueued
|
173
|
+
Qu.backend.enqueue(Payload.new(:klass => job_klass, :args => payload.args))
|
174
|
+
end
|
175
|
+
else
|
176
|
+
Qu.backend.enqueue(payload)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def rufus_scheduler
|
181
|
+
@rufus_scheduler ||= Rufus::Scheduler.start_new
|
182
|
+
end
|
183
|
+
|
184
|
+
# Stops old rufus scheduler and creates a new one. Returns the new
|
185
|
+
# rufus scheduler
|
186
|
+
def clear_schedule!
|
187
|
+
rufus_scheduler.stop
|
188
|
+
@rufus_scheduler = nil
|
189
|
+
@@scheduled_jobs = {}
|
190
|
+
rufus_scheduler
|
191
|
+
end
|
192
|
+
|
193
|
+
def reload_schedule!
|
194
|
+
set_process_title "reloading schedule"
|
195
|
+
clear_schedule!
|
196
|
+
load_schedule!
|
197
|
+
end
|
198
|
+
|
199
|
+
def unschedule_job(name)
|
200
|
+
if scheduled_jobs[name]
|
201
|
+
Qu.logger.debug("Removing schedule #{name}")
|
202
|
+
scheduled_jobs[name].unschedule
|
203
|
+
@@scheduled_jobs.delete(name)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Sleeps and returns true
|
208
|
+
def poll_sleep
|
209
|
+
@sleeping = true
|
210
|
+
handle_shutdown { sleep poll_sleep_amount }
|
211
|
+
@sleeping = false
|
212
|
+
true
|
213
|
+
end
|
214
|
+
|
215
|
+
# Sets the shutdown flag, exits if sleeping
|
216
|
+
def shutdown
|
217
|
+
@shutdown = true
|
218
|
+
exit if @sleeping
|
219
|
+
end
|
220
|
+
|
221
|
+
def set_process_title(string)
|
222
|
+
Qu.logger.info(string)
|
223
|
+
$0 = "qu-scheduler-#{Qu::Scheduler::VERSION}: #{string}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
data/lib/qu/scheduler/version.rb
CHANGED
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
context "DelayedQueue" do
|
4
|
+
|
5
|
+
setup do
|
6
|
+
Qu.backend.redis.flushall
|
7
|
+
end
|
8
|
+
|
9
|
+
test "enqueue_at adds correct list and zset" do
|
10
|
+
timestamp = Time.now - 1 # 1 second ago (in the past, should come out right away)
|
11
|
+
|
12
|
+
assert_equal(0, Qu.backend.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should be empty to start")
|
13
|
+
|
14
|
+
Qu.enqueue_at(timestamp, SomeIvarJob, "path")
|
15
|
+
|
16
|
+
# Confirm the correct keys were added
|
17
|
+
assert_equal(1, Qu.backend.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should have one entry now")
|
18
|
+
assert_equal(1, Qu.backend.redis.zcard(:delayed_queue_schedule), "The delayed_queue_schedule should have 1 entry now")
|
19
|
+
|
20
|
+
read_timestamp = Qu.backend.next_delayed_timestamp
|
21
|
+
|
22
|
+
# Confirm the timestamp came out correctly
|
23
|
+
assert_equal(timestamp.to_i, read_timestamp, "The timestamp we pull out of redis should match the one we put in")
|
24
|
+
item = Qu.backend.next_item_for_timestamp(read_timestamp)
|
25
|
+
|
26
|
+
# Confirm the item came out correctly
|
27
|
+
assert_equal('SomeIvarJob', item['klass'], "Should be the same class that we queued")
|
28
|
+
assert_equal(["path"], item['args'], "Should have the same arguments that we queued")
|
29
|
+
|
30
|
+
# And now confirm the keys are gone
|
31
|
+
assert(!Qu.backend.redis.exists("delayed:#{timestamp.to_i}"))
|
32
|
+
assert_equal(0, Qu.backend.redis.zcard(:delayed_queue_schedule), "delayed queue should be empty")
|
33
|
+
end
|
34
|
+
|
35
|
+
test "a job in the future doesn't come out" do
|
36
|
+
timestamp = Time.now + 600 # 10 minutes from now (in the future, shouldn't come out)
|
37
|
+
|
38
|
+
assert_equal(0, Qu.backend.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should be empty to start")
|
39
|
+
|
40
|
+
Qu.enqueue_at(timestamp, SomeIvarJob, "path")
|
41
|
+
|
42
|
+
# Confirm the correct keys were added
|
43
|
+
assert_equal(1, Qu.backend.redis.llen("delayed:#{timestamp.to_i}").to_i, "delayed queue should have one entry now")
|
44
|
+
assert_equal(1, Qu.backend.redis.zcard(:delayed_queue_schedule), "The delayed_queue_schedule should have 1 entry now")
|
45
|
+
|
46
|
+
read_timestamp = Qu.backend.next_delayed_timestamp
|
47
|
+
|
48
|
+
assert_nil(read_timestamp, "No timestamps should be ready for queueing")
|
49
|
+
end
|
50
|
+
|
51
|
+
test "a job in the future comes out if you want it to" do
|
52
|
+
timestamp = Time.now + 600 # 10 minutes from now
|
53
|
+
|
54
|
+
Qu.enqueue_at(timestamp, SomeIvarJob, "path")
|
55
|
+
|
56
|
+
read_timestamp = Qu.backend.next_delayed_timestamp(timestamp)
|
57
|
+
|
58
|
+
assert_equal(timestamp.to_i, read_timestamp, "The timestamp we pull out of redis should match the one we put in")
|
59
|
+
end
|
60
|
+
|
61
|
+
test "enqueue_at and enqueue_in are equivelent" do
|
62
|
+
timestamp = Time.now + 60
|
63
|
+
|
64
|
+
Qu.enqueue_at(timestamp, SomeIvarJob, "path")
|
65
|
+
Qu.enqueue_in(timestamp - Time.now, SomeIvarJob, "path")
|
66
|
+
|
67
|
+
assert_equal(1, Qu.backend.redis.zcard(:delayed_queue_schedule), "should have one timestamp in the delayed queue")
|
68
|
+
assert_equal(2, Qu.backend.redis.llen("delayed:#{timestamp.to_i}"), "should have 2 items in the timestamp queue")
|
69
|
+
end
|
70
|
+
|
71
|
+
test "empty delayed_queue_peek returns empty array" do
|
72
|
+
assert_equal([], Qu.backend.delayed_queue_peek(0,20))
|
73
|
+
end
|
74
|
+
|
75
|
+
test "delayed_queue_peek returns stuff" do
|
76
|
+
t = Time.now
|
77
|
+
expected_timestamps = (1..5).to_a.map do |i|
|
78
|
+
(t + 60 + i).to_i
|
79
|
+
end
|
80
|
+
|
81
|
+
expected_timestamps.each do |timestamp|
|
82
|
+
Qu.backend.delayed_push(timestamp, Qu::Payload.new({:class => SomeIvarJob, :args => 'blah1'}))
|
83
|
+
end
|
84
|
+
|
85
|
+
timestamps = Qu.backend.delayed_queue_peek(2,3)
|
86
|
+
|
87
|
+
assert_equal(expected_timestamps[2,3], timestamps)
|
88
|
+
end
|
89
|
+
|
90
|
+
test "delayed_queue_schedule_size returns correct size" do
|
91
|
+
assert_equal(0, Qu.backend.delayed_queue_schedule_size)
|
92
|
+
Qu.enqueue_at(Time.now+60, SomeIvarJob)
|
93
|
+
assert_equal(1, Qu.backend.delayed_queue_schedule_size)
|
94
|
+
end
|
95
|
+
|
96
|
+
test "delayed_timestamp_size returns 0 when nothing is queue" do
|
97
|
+
t = Time.now + 60
|
98
|
+
assert_equal(0, Qu.backend.delayed_timestamp_size(t))
|
99
|
+
end
|
100
|
+
|
101
|
+
test "delayed_timestamp_size returns 1 when one thing is queued" do
|
102
|
+
t = Time.now + 60
|
103
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
104
|
+
assert_equal(1, Qu.backend.delayed_timestamp_size(t))
|
105
|
+
end
|
106
|
+
|
107
|
+
test "delayed_timestamp_peek returns empty array when nothings in it" do
|
108
|
+
t = Time.now + 60
|
109
|
+
assert_equal([], Qu.delayed_timestamp_peek(t, 0, 1), "make sure it's an empty array, not nil")
|
110
|
+
end
|
111
|
+
|
112
|
+
test "delayed_timestamp_peek returns an array containing one job when one thing is queued" do
|
113
|
+
t = Time.now + 60
|
114
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
115
|
+
assert_equal [{'args' => [], 'klass' => 'SomeIvarJob'}], Qu.delayed_timestamp_peek(t, 0, 1)
|
116
|
+
end
|
117
|
+
|
118
|
+
test "delayed_timestamp_peek returns an array of multiple jobs when more than one job is queued" do
|
119
|
+
t = Time.now + 60
|
120
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
121
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
122
|
+
job = {'args' => [], 'klass' => 'SomeIvarJob'}
|
123
|
+
assert_equal([job, job], Qu.delayed_timestamp_peek(t, 0, 2))
|
124
|
+
end
|
125
|
+
|
126
|
+
test "delayed_timestamp_peek only returns an array of one job if only asked for 1" do
|
127
|
+
t = Time.now + 60
|
128
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
129
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
130
|
+
job = {'args' => [], 'klass' => 'SomeIvarJob'}
|
131
|
+
assert_equal([job], Qu.delayed_timestamp_peek(t, 0, 1))
|
132
|
+
end
|
133
|
+
|
134
|
+
test "handle_delayed_items with no items" do
|
135
|
+
Qu::Scheduler.expects(:enqueue).never
|
136
|
+
Qu::Scheduler.handle_delayed_items
|
137
|
+
end
|
138
|
+
|
139
|
+
test "handle_delayed_item with items" do
|
140
|
+
t = Time.now - 60 # in the past
|
141
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
142
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
143
|
+
|
144
|
+
# 2 SomeIvarJob jobs should be created in the "ivar" queue
|
145
|
+
Qu.backend.expects(:enqueue).twice.with(all_of(instance_of(Qu::Payload), responds_with(:klass, SomeIvarJob), responds_with(:args, []), responds_with(:queue, 'ivar')))
|
146
|
+
Qu::Scheduler.handle_delayed_items
|
147
|
+
end
|
148
|
+
|
149
|
+
test "handle_delayed_items with items in the future" do
|
150
|
+
t = Time.now + 60 # in the future
|
151
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
152
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
153
|
+
|
154
|
+
# 2 SomeIvarJob jobs should be created in the "ivar" queue
|
155
|
+
Qu.backend.expects(:enqueue).twice.with(all_of(instance_of(Qu::Payload), responds_with(:klass, SomeIvarJob), responds_with(:args, []), responds_with(:queue, 'ivar')))
|
156
|
+
Qu::Scheduler.handle_delayed_items(t)
|
157
|
+
end
|
158
|
+
|
159
|
+
test "enqueue_delayed_items_for_timestamp creates jobs and empties the delayed queue" do
|
160
|
+
t = Time.now + 60
|
161
|
+
|
162
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
163
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
164
|
+
|
165
|
+
# 2 SomeIvarJob jobs should be created in the "ivar" queue
|
166
|
+
Qu.backend.expects(:enqueue).twice.with(all_of(instance_of(Qu::Payload), responds_with(:klass, SomeIvarJob), responds_with(:args, []), responds_with(:queue, 'ivar')))
|
167
|
+
|
168
|
+
Qu::Scheduler.enqueue_delayed_items_for_timestamp(t)
|
169
|
+
|
170
|
+
# delayed queue for timestamp should be empty
|
171
|
+
assert_equal(0, Qu.delayed_timestamp_peek(t, 0, 3).length)
|
172
|
+
end
|
173
|
+
|
174
|
+
test "handle_delayed_items works with out specifying queue (upgrade case)" do
|
175
|
+
t = Time.now - 60
|
176
|
+
Qu.backend.delayed_push(t, Qu::Payload.new(:klass => 'SomeIvarJob'))
|
177
|
+
|
178
|
+
# Since we didn't specify :queue when calling delayed_push, it will be forced
|
179
|
+
# to load the class to figure out the queue. This is the upgrade case from 1.0.4
|
180
|
+
# to 1.0.5.
|
181
|
+
Qu.backend.expects(:enqueue).once.with(all_of(instance_of(Qu::Payload), responds_with(:klass, SomeIvarJob), responds_with(:args, []), responds_with(:queue, 'ivar')))
|
182
|
+
|
183
|
+
Qu::Scheduler.handle_delayed_items
|
184
|
+
end
|
185
|
+
|
186
|
+
test "reset_delayed_queue clears the queue" do
|
187
|
+
t = Time.now + 120
|
188
|
+
4.times { Qu.enqueue_at(t, SomeIvarJob) }
|
189
|
+
4.times { Qu.enqueue_at(Time.now + rand(100), SomeIvarJob) }
|
190
|
+
|
191
|
+
Qu.backend.reset_delayed_queue
|
192
|
+
assert_equal(0, Qu.backend.delayed_queue_schedule_size)
|
193
|
+
end
|
194
|
+
|
195
|
+
test "remove_delayed removes job and returns the count" do
|
196
|
+
t = Time.now + 120
|
197
|
+
Qu.enqueue_at(t, SomeIvarJob)
|
198
|
+
|
199
|
+
assert_equal(1, Qu.backend.remove_delayed(SomeIvarJob))
|
200
|
+
end
|
201
|
+
|
202
|
+
test "remove_delayed doesn't remove things it shouldn't" do
|
203
|
+
t = Time.now + 120
|
204
|
+
Qu.enqueue_at(t, SomeIvarJob, "foo")
|
205
|
+
Qu.enqueue_at(t, SomeIvarJob, "bar")
|
206
|
+
Qu.enqueue_at(t, SomeIvarJob, "bar")
|
207
|
+
Qu.enqueue_at(t, SomeIvarJob, "baz")
|
208
|
+
|
209
|
+
assert_equal(0, Qu.backend.remove_delayed(SomeIvarJob))
|
210
|
+
end
|
211
|
+
|
212
|
+
test "remove_delayed respected param" do
|
213
|
+
t = Time.now + 120
|
214
|
+
Qu.enqueue_at(t, SomeIvarJob, "foo")
|
215
|
+
Qu.enqueue_at(t, SomeIvarJob, "bar")
|
216
|
+
Qu.enqueue_at(t, SomeIvarJob, "bar")
|
217
|
+
Qu.enqueue_at(t, SomeIvarJob, "baz")
|
218
|
+
|
219
|
+
assert_equal(2, Qu.backend.remove_delayed(SomeIvarJob, "bar"))
|
220
|
+
assert_equal(1, Qu.backend.delayed_queue_schedule_size)
|
221
|
+
end
|
222
|
+
|
223
|
+
test "remove_delayed removes items in different timestamps" do
|
224
|
+
t = Time.now + 120
|
225
|
+
Qu.enqueue_at(t, SomeIvarJob, "foo")
|
226
|
+
Qu.enqueue_at(t + 1, SomeIvarJob, "bar")
|
227
|
+
Qu.enqueue_at(t + 2, SomeIvarJob, "bar")
|
228
|
+
Qu.enqueue_at(t + 3, SomeIvarJob, "baz")
|
229
|
+
|
230
|
+
assert_equal(2, Qu.backend.remove_delayed(SomeIvarJob, "bar"))
|
231
|
+
assert_equal(2, Qu.backend.count_all_scheduled_jobs)
|
232
|
+
end
|
233
|
+
|
234
|
+
test "remove_delayed_job_from_timestamp removes instances of jobs at a given timestamp" do
|
235
|
+
t = Time.now + 120
|
236
|
+
Qu.enqueue_at(t, SomeIvarJob, "foo")
|
237
|
+
assert_equal 1, Qu.backend.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
|
238
|
+
assert_equal 0, Qu.backend.delayed_timestamp_size(t)
|
239
|
+
end
|
240
|
+
|
241
|
+
test "remove_delayed_job_from_timestamp doesn't remove items from other timestamps" do
|
242
|
+
t1 = Time.now + 120
|
243
|
+
t2 = t1 + 1
|
244
|
+
Qu.enqueue_at(t1, SomeIvarJob, "foo")
|
245
|
+
Qu.enqueue_at(t2, SomeIvarJob, "foo")
|
246
|
+
assert_equal 1, Qu.backend.remove_delayed_job_from_timestamp(t2, SomeIvarJob, "foo")
|
247
|
+
assert_equal 1, Qu.backend.delayed_timestamp_size(t1)
|
248
|
+
assert_equal 0, Qu.backend.delayed_timestamp_size(t2)
|
249
|
+
end
|
250
|
+
|
251
|
+
test "remove_delayed_job_from_timestamp removes nothing if there are no matches" do
|
252
|
+
t = Time.now + 120
|
253
|
+
assert_equal 0, Qu.backend.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
|
254
|
+
end
|
255
|
+
|
256
|
+
test "remove_delayed_job_from_timestamp only removes items that match args" do
|
257
|
+
t = Time.now + 120
|
258
|
+
Qu.enqueue_at(t, SomeIvarJob, "foo")
|
259
|
+
Qu.enqueue_at(t, SomeIvarJob, "bar")
|
260
|
+
assert_equal 1, Qu.backend.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
|
261
|
+
assert_equal 1, Qu.backend.delayed_timestamp_size(t)
|
262
|
+
end
|
263
|
+
|
264
|
+
test "remove_delayed_job_from_timestamp returns the number of items removed" do
|
265
|
+
t = Time.now + 120
|
266
|
+
Qu.enqueue_at(t, SomeIvarJob, "foo")
|
267
|
+
assert_equal 1, Qu.backend.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
|
268
|
+
end
|
269
|
+
|
270
|
+
test "remove_delayed_job_from_timestamp should cleanup the delayed timestamp list if not jobs are left" do
|
271
|
+
t = Time.now + 120
|
272
|
+
Qu.enqueue_at(t, SomeIvarJob, "foo")
|
273
|
+
assert_equal 1, Qu.backend.remove_delayed_job_from_timestamp(t, SomeIvarJob, "foo")
|
274
|
+
assert !Qu.backend.redis.exists("delayed:#{t.to_i}")
|
275
|
+
assert Qu.backend.delayed_queue_peek(0, 100).empty?
|
276
|
+
end
|
277
|
+
end
|