resque_admin-scheduler 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. metadata +63 -39
  3. data/bin/migrate_to_timestamps_set.rb +0 -16
  4. data/exe/resque-scheduler +0 -5
  5. data/lib/resque-scheduler.rb +0 -4
  6. data/lib/resque_admin/scheduler.rb +0 -447
  7. data/lib/resque_admin/scheduler/cli.rb +0 -147
  8. data/lib/resque_admin/scheduler/configuration.rb +0 -73
  9. data/lib/resque_admin/scheduler/delaying_extensions.rb +0 -324
  10. data/lib/resque_admin/scheduler/env.rb +0 -89
  11. data/lib/resque_admin/scheduler/extension.rb +0 -13
  12. data/lib/resque_admin/scheduler/failure_handler.rb +0 -11
  13. data/lib/resque_admin/scheduler/lock.rb +0 -4
  14. data/lib/resque_admin/scheduler/lock/base.rb +0 -61
  15. data/lib/resque_admin/scheduler/lock/basic.rb +0 -27
  16. data/lib/resque_admin/scheduler/lock/resilient.rb +0 -78
  17. data/lib/resque_admin/scheduler/locking.rb +0 -104
  18. data/lib/resque_admin/scheduler/logger_builder.rb +0 -72
  19. data/lib/resque_admin/scheduler/plugin.rb +0 -31
  20. data/lib/resque_admin/scheduler/scheduling_extensions.rb +0 -141
  21. data/lib/resque_admin/scheduler/server.rb +0 -268
  22. data/lib/resque_admin/scheduler/server/views/delayed.erb +0 -63
  23. data/lib/resque_admin/scheduler/server/views/delayed_schedules.erb +0 -20
  24. data/lib/resque_admin/scheduler/server/views/delayed_timestamp.erb +0 -26
  25. data/lib/resque_admin/scheduler/server/views/requeue-params.erb +0 -23
  26. data/lib/resque_admin/scheduler/server/views/scheduler.erb +0 -58
  27. data/lib/resque_admin/scheduler/server/views/search.erb +0 -72
  28. data/lib/resque_admin/scheduler/server/views/search_form.erb +0 -8
  29. data/lib/resque_admin/scheduler/signal_handling.rb +0 -40
  30. data/lib/resque_admin/scheduler/tasks.rb +0 -25
  31. data/lib/resque_admin/scheduler/util.rb +0 -39
  32. data/lib/resque_admin/scheduler/version.rb +0 -7
  33. data/tasks/resque_scheduler.rake +0 -2
@@ -1,147 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- require 'optparse'
4
-
5
- module ResqueAdmin
6
- module Scheduler
7
- CLI_OPTIONS_ENV_MAPPING = {
8
- app_name: 'APP_NAME',
9
- background: 'BACKGROUND',
10
- dynamic: 'DYNAMIC_SCHEDULE',
11
- env: 'RAILS_ENV',
12
- initializer_path: 'INITIALIZER_PATH',
13
- logfile: 'LOGFILE',
14
- logformat: 'LOGFORMAT',
15
- quiet: 'QUIET',
16
- pidfile: 'PIDFILE',
17
- poll_sleep_amount: 'RESQUE_SCHEDULER_INTERVAL',
18
- verbose: 'VERBOSE'
19
- }.freeze
20
-
21
- class Cli
22
- BANNER = <<-EOF.gsub(/ {6}/, '')
23
- Usage: resque-scheduler [options]
24
-
25
- Runs a resque scheduler process directly (rather than via rake).
26
-
27
- EOF
28
- OPTIONS = [
29
- {
30
- args: ['-n', '--app-name [APP_NAME]',
31
- 'Application name for procline'],
32
- callback: ->(options) { ->(n) { options[:app_name] = n } }
33
- },
34
- {
35
- args: ['-B', '--background', 'Run in the background [BACKGROUND]'],
36
- callback: ->(options) { ->(b) { options[:background] = b } }
37
- },
38
- {
39
- args: ['-D', '--dynamic-schedule',
40
- 'Enable dynamic scheduling [DYNAMIC_SCHEDULE]'],
41
- callback: ->(options) { ->(d) { options[:dynamic] = d } }
42
- },
43
- {
44
- args: ['-E', '--environment [RAILS_ENV]', 'Environment name'],
45
- callback: ->(options) { ->(e) { options[:env] = e } }
46
- },
47
- {
48
- args: ['-I', '--initializer-path [INITIALIZER_PATH]',
49
- 'Path to optional initializer ruby file'],
50
- callback: ->(options) { ->(i) { options[:initializer_path] = i } }
51
- },
52
- {
53
- args: ['-i', '--interval [RESQUE_SCHEDULER_INTERVAL]',
54
- 'Interval for checking if a scheduled job must run'],
55
- callback: ->(options) { ->(i) { options[:poll_sleep_amount] = i } }
56
- },
57
- {
58
- args: ['-l', '--logfile [LOGFILE]', 'Log file name'],
59
- callback: ->(options) { ->(l) { options[:logfile] = l } }
60
- },
61
- {
62
- args: ['-F', '--logformat [LOGFORMAT]', 'Log output format'],
63
- callback: ->(options) { ->(f) { options[:logformat] = f } }
64
- },
65
- {
66
- args: ['-P', '--pidfile [PIDFILE]', 'PID file name'],
67
- callback: ->(options) { ->(p) { options[:pidfile] = p } }
68
- },
69
- {
70
- args: ['-q', '--quiet', 'Run with minimal output [QUIET]'],
71
- callback: ->(options) { ->(q) { options[:quiet] = q } }
72
- },
73
- {
74
- args: ['-v', '--verbose', 'Run with verbose output [VERBOSE]'],
75
- callback: ->(options) { ->(v) { options[:verbose] = v } }
76
- }
77
- ].freeze
78
-
79
- def self.run!(argv = ARGV, env = ENV)
80
- new(argv, env).run!
81
- end
82
-
83
- def initialize(argv = ARGV, env = ENV)
84
- @argv = argv
85
- @env = env
86
- end
87
-
88
- def run!
89
- pre_run
90
- run_forever
91
- end
92
-
93
- def pre_run
94
- parse_options
95
- pre_setup
96
- setup_env
97
- end
98
-
99
- def parse_options
100
- option_parser.parse!(argv.dup)
101
- end
102
-
103
- def pre_setup
104
- if options[:initializer_path]
105
- load options[:initializer_path].to_s.strip
106
- else
107
- false
108
- end
109
- end
110
-
111
- def setup_env
112
- require_relative 'env'
113
- runtime_env.setup
114
- end
115
-
116
- def run_forever
117
- ResqueAdmin::Scheduler.run
118
- end
119
-
120
- private
121
-
122
- attr_reader :argv, :env
123
-
124
- def runtime_env
125
- @runtime_env ||= ResqueAdmin::Scheduler::Env.new(options)
126
- end
127
-
128
- def option_parser
129
- OptionParser.new do |opts|
130
- opts.banner = BANNER
131
- opts.version = ResqueAdmin::Scheduler::VERSION
132
- OPTIONS.each do |opt|
133
- opts.on(*opt[:args], &opt[:callback].call(options))
134
- end
135
- end
136
- end
137
-
138
- def options
139
- @options ||= {}.tap do |o|
140
- CLI_OPTIONS_ENV_MAPPING.each do |key, envvar|
141
- o[key] = env[envvar] if env.include?(envvar)
142
- end
143
- end
144
- end
145
- end
146
- end
147
- end
@@ -1,73 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- module ResqueAdmin
4
- module Scheduler
5
- module Configuration
6
- # Allows for block-style configuration
7
- def configure
8
- yield self
9
- end
10
-
11
- # Used in `#load_schedule_job`
12
- attr_writer :env
13
-
14
- def env
15
- return @env if @env
16
- @env ||= Rails.env if defined?(Rails) && Rails.respond_to?(:env)
17
- @env ||= ENV['RAILS_ENV']
18
- @env
19
- end
20
-
21
- # If true, logs more stuff...
22
- attr_writer :verbose
23
-
24
- def verbose
25
- @verbose ||= !!ENV['VERBOSE']
26
- end
27
-
28
- # If set, produces no output
29
- attr_writer :quiet
30
-
31
- def quiet
32
- @quiet ||= !!ENV['QUIET']
33
- end
34
-
35
- # If set, will write messages to the file
36
- attr_writer :logfile
37
-
38
- def logfile
39
- @logfile ||= ENV['LOGFILE']
40
- end
41
-
42
- # Sets whether to log in 'text' or 'json'
43
- attr_writer :logformat
44
-
45
- def logformat
46
- @logformat ||= ENV['LOGFORMAT']
47
- end
48
-
49
- # If set, will try to update the schedule in the loop
50
- attr_writer :dynamic
51
-
52
- def dynamic
53
- @dynamic ||= !!ENV['DYNAMIC_SCHEDULE']
54
- end
55
-
56
- # If set, will append the app name to procline
57
- attr_writer :app_name
58
-
59
- def app_name
60
- @app_name ||= ENV['APP_NAME']
61
- end
62
-
63
- # Amount of time in seconds to sleep between polls of the delayed
64
- # queue. Defaults to 5
65
- attr_writer :poll_sleep_amount
66
-
67
- def poll_sleep_amount
68
- @poll_sleep_amount ||=
69
- Float(ENV.fetch('RESQUE_SCHEDULER_INTERVAL', '5'))
70
- end
71
- end
72
- end
73
- end
@@ -1,324 +0,0 @@
1
- # vim:fileencoding=utf-8
2
- require 'resque'
3
- require_relative 'plugin'
4
- require_relative '../scheduler'
5
-
6
- module ResqueAdmin
7
- module Scheduler
8
- module DelayingExtensions
9
- # This method is nearly identical to +enqueue+ only it also
10
- # takes a timestamp which will be used to schedule the job
11
- # for queueing. Until timestamp is in the past, the job will
12
- # sit in the schedule list.
13
- def enqueue_at(timestamp, klass, *args)
14
- validate(klass)
15
- enqueue_at_with_queue(
16
- queue_from_class(klass), timestamp, klass, *args
17
- )
18
- end
19
-
20
- # Identical to +enqueue_at+, except you can also specify
21
- # a queue in which the job will be placed after the
22
- # timestamp has passed. It respects ResqueAdmin.inline option, by
23
- # creating the job right away instead of adding to the queue.
24
- def enqueue_at_with_queue(queue, timestamp, klass, *args)
25
- return false unless plugin.run_before_schedule_hooks(klass, *args)
26
-
27
- if ResqueAdmin.inline? || timestamp.to_i < Time.now.to_i
28
- # Just create the job and let resque perform it right away with
29
- # inline. If the class is a custom job class, call self#scheduled
30
- # on it. This allows you to do things like
31
- # ResqueAdmin.enqueue_at(timestamp, CustomJobClass, :opt1 => val1).
32
- # Otherwise, pass off to ResqueAdmin.
33
- if klass.respond_to?(:scheduled)
34
- klass.scheduled(queue, klass.to_s, *args)
35
- else
36
- ResqueAdmin::Job.create(queue, klass, *args)
37
- end
38
- else
39
- delayed_push(timestamp, job_to_hash_with_queue(queue, klass, args))
40
- end
41
-
42
- plugin.run_after_schedule_hooks(klass, *args)
43
- end
44
-
45
- # Identical to enqueue_at but takes number_of_seconds_from_now
46
- # instead of a timestamp.
47
- def enqueue_in(number_of_seconds_from_now, klass, *args)
48
- enqueue_at(Time.now + number_of_seconds_from_now, klass, *args)
49
- end
50
-
51
- # Identical to +enqueue_in+, except you can also specify
52
- # a queue in which the job will be placed after the
53
- # number of seconds has passed.
54
- def enqueue_in_with_queue(queue, number_of_seconds_from_now,
55
- klass, *args)
56
- enqueue_at_with_queue(queue, Time.now + number_of_seconds_from_now,
57
- klass, *args)
58
- end
59
-
60
- # Used internally to stuff the item into the schedule sorted list.
61
- # +timestamp+ can be either in seconds or a datetime object Insertion
62
- # if O(log(n)). Returns true if it's the first job to be scheduled at
63
- # that time, else false
64
- def delayed_push(timestamp, item)
65
- # First add this item to the list for this timestamp
66
- redis.rpush("delayed:#{timestamp.to_i}", encode(item))
67
-
68
- # Store the timestamps at with this item occurs
69
- redis.sadd("timestamps:#{encode(item)}", "delayed:#{timestamp.to_i}")
70
-
71
- # Now, add this timestamp to the zsets. The score and the value are
72
- # the same since we'll be querying by timestamp, and we don't have
73
- # anything else to store.
74
- redis.zadd :delayed_queue_schedule, timestamp.to_i, timestamp.to_i
75
- end
76
-
77
- # Returns an array of timestamps based on start and count
78
- def delayed_queue_peek(start, count)
79
- result = redis.zrange(:delayed_queue_schedule, start,
80
- start + count - 1)
81
- Array(result).map(&:to_i)
82
- end
83
-
84
- # Returns the size of the delayed queue schedule
85
- def delayed_queue_schedule_size
86
- redis.zcard :delayed_queue_schedule
87
- end
88
-
89
- # Returns the number of jobs for a given timestamp in the delayed queue
90
- # schedule
91
- def delayed_timestamp_size(timestamp)
92
- redis.llen("delayed:#{timestamp.to_i}").to_i
93
- end
94
-
95
- # Returns an array of delayed items for the given timestamp
96
- def delayed_timestamp_peek(timestamp, start, count)
97
- if 1 == count
98
- r = list_range "delayed:#{timestamp.to_i}", start, count
99
- r.nil? ? [] : [r]
100
- else
101
- list_range "delayed:#{timestamp.to_i}", start, count
102
- end
103
- end
104
-
105
- # Returns the next delayed queue timestamp
106
- # (don't call directly)
107
- def next_delayed_timestamp(at_time = nil)
108
- search_first_delayed_timestamp_in_range(nil, at_time || Time.now)
109
- end
110
-
111
- # Returns the next item to be processed for a given timestamp, nil if
112
- # done. (don't call directly)
113
- # +timestamp+ can either be in seconds or a datetime
114
- def next_item_for_timestamp(timestamp)
115
- key = "delayed:#{timestamp.to_i}"
116
-
117
- encoded_item = redis.lpop(key)
118
- redis.srem("timestamps:#{encoded_item}", key)
119
- item = decode(encoded_item)
120
-
121
- # If the list is empty, remove it.
122
- clean_up_timestamp(key, timestamp)
123
- item
124
- end
125
-
126
- # Clears all jobs created with enqueue_at or enqueue_in
127
- def reset_delayed_queue
128
- Array(redis.zrange(:delayed_queue_schedule, 0, -1)).each do |item|
129
- key = "delayed:#{item}"
130
- items = redis.lrange(key, 0, -1)
131
- redis.pipelined do
132
- items.each { |ts_item| redis.del("timestamps:#{ts_item}") }
133
- end
134
- redis.del key
135
- end
136
-
137
- redis.del :delayed_queue_schedule
138
- end
139
-
140
- # Given an encoded item, remove it from the delayed_queue
141
- def remove_delayed(klass, *args)
142
- search = encode(job_to_hash(klass, args))
143
- remove_delayed_job(search)
144
- end
145
-
146
- # Given an encoded item, enqueue it now
147
- def enqueue_delayed(klass, *args)
148
- hash = job_to_hash(klass, args)
149
- remove_delayed(klass, *args).times do
150
- ResqueAdmin::Scheduler.enqueue_from_config(hash)
151
- end
152
- end
153
-
154
- # Given a block, remove jobs that return true from a block
155
- #
156
- # This allows for removal of delayed jobs that have arguments matching
157
- # certain criteria
158
- def remove_delayed_selection(klass = nil)
159
- raise ArgumentError, 'Please supply a block' unless block_given?
160
-
161
- found_jobs = find_delayed_selection(klass) { |args| yield(args) }
162
- found_jobs.reduce(0) do |sum, encoded_job|
163
- sum + remove_delayed_job(encoded_job)
164
- end
165
- end
166
-
167
- # Given a block, enqueue jobs now that return true from a block
168
- #
169
- # This allows for enqueuing of delayed jobs that have arguments matching
170
- # certain criteria
171
- def enqueue_delayed_selection(klass = nil)
172
- raise ArgumentError, 'Please supply a block' unless block_given?
173
-
174
- found_jobs = find_delayed_selection(klass) { |args| yield(args) }
175
- found_jobs.reduce(0) do |sum, encoded_job|
176
- decoded_job = decode(encoded_job)
177
- klass = Util.constantize(decoded_job['class'])
178
- sum + enqueue_delayed(klass, *decoded_job['args'])
179
- end
180
- end
181
-
182
- # Given a block, find jobs that return true from a block
183
- #
184
- # This allows for finding of delayed jobs that have arguments matching
185
- # certain criteria
186
- def find_delayed_selection(klass = nil, &block)
187
- raise ArgumentError, 'Please supply a block' unless block_given?
188
-
189
- timestamps = redis.zrange(:delayed_queue_schedule, 0, -1)
190
-
191
- # Beyond 100 there's almost no improvement in speed
192
- found = timestamps.each_slice(100).map do |ts_group|
193
- jobs = redis.pipelined do |r|
194
- ts_group.each do |ts|
195
- r.lrange("delayed:#{ts}", 0, -1)
196
- end
197
- end
198
-
199
- jobs.flatten.select do |payload|
200
- payload_matches_selection?(decode(payload), klass, &block)
201
- end
202
- end
203
-
204
- found.flatten
205
- end
206
-
207
- # Given a timestamp and job (klass + args) it removes all instances and
208
- # returns the count of jobs removed.
209
- #
210
- # O(N) where N is the number of jobs scheduled to fire at the given
211
- # timestamp
212
- def remove_delayed_job_from_timestamp(timestamp, klass, *args)
213
- return 0 if ResqueAdmin.inline?
214
-
215
- key = "delayed:#{timestamp.to_i}"
216
- encoded_job = encode(job_to_hash(klass, args))
217
-
218
- redis.srem("timestamps:#{encoded_job}", key)
219
- count = redis.lrem(key, 0, encoded_job)
220
- clean_up_timestamp(key, timestamp)
221
-
222
- count
223
- end
224
-
225
- def count_all_scheduled_jobs
226
- total_jobs = 0
227
- Array(redis.zrange(:delayed_queue_schedule, 0, -1)).each do |ts|
228
- total_jobs += redis.llen("delayed:#{ts}").to_i
229
- end
230
- total_jobs
231
- end
232
-
233
- # Discover if a job has been delayed.
234
- # Examples
235
- # ResqueAdmin.delayed?(MyJob)
236
- # ResqueAdmin.delayed?(MyJob, id: 1)
237
- # Returns true if the job has been delayed
238
- def delayed?(klass, *args)
239
- !scheduled_at(klass, *args).empty?
240
- end
241
-
242
- # Returns delayed jobs schedule timestamp for +klass+, +args+.
243
- def scheduled_at(klass, *args)
244
- search = encode(job_to_hash(klass, args))
245
- redis.smembers("timestamps:#{search}").map do |key|
246
- key.tr('delayed:', '').to_i
247
- end
248
- end
249
-
250
- def last_enqueued_at(job_name, date)
251
- redis.hset('delayed:last_enqueued_at', job_name, date)
252
- end
253
-
254
- def get_last_enqueued_at(job_name)
255
- redis.hget('delayed:last_enqueued_at', job_name)
256
- end
257
-
258
- private
259
-
260
- def job_to_hash(klass, args)
261
- { class: klass.to_s, args: args, queue: queue_from_class(klass) }
262
- end
263
-
264
- def job_to_hash_with_queue(queue, klass, args)
265
- { class: klass.to_s, args: args, queue: queue }
266
- end
267
-
268
- def remove_delayed_job(encoded_job)
269
- return 0 if ResqueAdmin.inline?
270
-
271
- timestamps = redis.smembers("timestamps:#{encoded_job}")
272
-
273
- replies = redis.pipelined do
274
- timestamps.each do |key|
275
- redis.lrem(key, 0, encoded_job)
276
- redis.srem("timestamps:#{encoded_job}", key)
277
- end
278
- end
279
-
280
- return 0 if replies.nil? || replies.empty?
281
- replies.each_slice(2).map(&:first).inject(:+)
282
- end
283
-
284
- def clean_up_timestamp(key, timestamp)
285
- # Use a watch here to ensure nobody adds jobs to this delayed
286
- # queue while we're removing it.
287
- redis.watch(key) do
288
- if redis.llen(key).to_i == 0
289
- # If the list is empty, remove it.
290
- redis.multi do
291
- redis.del(key)
292
- redis.zrem(:delayed_queue_schedule, timestamp.to_i)
293
- end
294
- else
295
- redis.redis.unwatch
296
- end
297
- end
298
- end
299
-
300
- def search_first_delayed_timestamp_in_range(start_at, stop_at)
301
- start_at = start_at.nil? ? '-inf' : start_at.to_i
302
- stop_at = stop_at.nil? ? '+inf' : stop_at.to_i
303
-
304
- items = redis.zrangebyscore(
305
- :delayed_queue_schedule, start_at, stop_at,
306
- limit: [0, 1]
307
- )
308
- timestamp = items.nil? ? nil : Array(items).first
309
- timestamp.to_i unless timestamp.nil?
310
- end
311
-
312
- def payload_matches_selection?(decoded_payload, klass)
313
- return false if decoded_payload.nil?
314
- job_class = decoded_payload['class']
315
- relevant_class = (klass.nil? || klass.to_s == job_class)
316
- relevant_class && yield(decoded_payload['args'])
317
- end
318
-
319
- def plugin
320
- ResqueAdmin::Scheduler::Plugin
321
- end
322
- end
323
- end
324
- end