resque-scheduler 2.5.5 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of resque-scheduler might be problematic. Click here for more details.

Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -6
  3. data/.rubocop.yml +18 -113
  4. data/.rubocop_todo.yml +29 -0
  5. data/.simplecov +3 -1
  6. data/.travis.yml +12 -4
  7. data/.vagrant-provision-as-vagrant.sh +15 -0
  8. data/.vagrant-provision.sh +23 -0
  9. data/.vagrant-skel/bash_profile +7 -0
  10. data/.vagrant-skel/bashrc +7 -0
  11. data/AUTHORS.md +5 -0
  12. data/Gemfile +1 -2
  13. data/HISTORY.md +18 -0
  14. data/README.md +39 -11
  15. data/ROADMAP.md +0 -6
  16. data/Rakefile +11 -19
  17. data/Vagrantfile +14 -0
  18. data/bin/resque-scheduler +2 -2
  19. data/examples/Rakefile +1 -1
  20. data/examples/config/initializers/resque-web.rb +2 -2
  21. data/examples/dynamic-scheduling/app/jobs/fix_schedules_job.rb +2 -4
  22. data/examples/dynamic-scheduling/app/jobs/send_email_job.rb +1 -1
  23. data/examples/dynamic-scheduling/app/models/user.rb +2 -2
  24. data/examples/dynamic-scheduling/lib/tasks/resque.rake +2 -2
  25. data/lib/resque-scheduler.rb +3 -1
  26. data/lib/resque/scheduler.rb +112 -168
  27. data/lib/resque/scheduler/cli.rb +144 -0
  28. data/lib/resque/scheduler/configuration.rb +73 -0
  29. data/lib/resque/scheduler/delaying_extensions.rb +278 -0
  30. data/lib/resque/scheduler/env.rb +61 -0
  31. data/lib/resque/scheduler/extension.rb +13 -0
  32. data/lib/resque/scheduler/lock.rb +2 -1
  33. data/lib/resque/scheduler/lock/base.rb +6 -2
  34. data/lib/resque/scheduler/lock/basic.rb +4 -5
  35. data/lib/resque/scheduler/lock/resilient.rb +30 -37
  36. data/lib/resque/scheduler/locking.rb +94 -0
  37. data/lib/resque/scheduler/logger_builder.rb +72 -0
  38. data/lib/resque/scheduler/plugin.rb +31 -0
  39. data/lib/resque/scheduler/scheduling_extensions.rb +150 -0
  40. data/lib/resque/scheduler/server.rb +246 -0
  41. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed.erb +2 -1
  42. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed_schedules.erb +0 -0
  43. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed_timestamp.erb +0 -0
  44. data/lib/{resque_scheduler → resque/scheduler}/server/views/requeue-params.erb +0 -0
  45. data/lib/{resque_scheduler → resque/scheduler}/server/views/scheduler.erb +16 -1
  46. data/lib/{resque_scheduler → resque/scheduler}/server/views/search.erb +2 -1
  47. data/lib/{resque_scheduler → resque/scheduler}/server/views/search_form.erb +0 -0
  48. data/lib/resque/scheduler/signal_handling.rb +40 -0
  49. data/lib/{resque_scheduler → resque/scheduler}/tasks.rb +3 -5
  50. data/lib/resque/scheduler/util.rb +41 -0
  51. data/lib/resque/scheduler/version.rb +7 -0
  52. data/resque-scheduler.gemspec +21 -19
  53. data/script/migrate_to_timestamps_set.rb +5 -3
  54. data/tasks/resque_scheduler.rake +1 -1
  55. data/test/cli_test.rb +26 -69
  56. data/test/delayed_queue_test.rb +262 -169
  57. data/test/env_test.rb +41 -0
  58. data/test/resque-web_test.rb +169 -48
  59. data/test/scheduler_args_test.rb +73 -41
  60. data/test/scheduler_hooks_test.rb +9 -8
  61. data/test/scheduler_locking_test.rb +55 -36
  62. data/test/scheduler_setup_test.rb +52 -15
  63. data/test/scheduler_task_test.rb +15 -10
  64. data/test/scheduler_test.rb +215 -114
  65. data/test/support/redis_instance.rb +32 -33
  66. data/test/test_helper.rb +33 -36
  67. data/test/util_test.rb +11 -0
  68. metadata +113 -57
  69. data/lib/resque/scheduler_locking.rb +0 -91
  70. data/lib/resque_scheduler.rb +0 -386
  71. data/lib/resque_scheduler/cli.rb +0 -160
  72. data/lib/resque_scheduler/logger_builder.rb +0 -72
  73. data/lib/resque_scheduler/plugin.rb +0 -28
  74. data/lib/resque_scheduler/server.rb +0 -183
  75. data/lib/resque_scheduler/util.rb +0 -34
  76. data/lib/resque_scheduler/version.rb +0 -5
  77. data/test/redis-test.conf +0 -108
@@ -1,160 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- require 'optparse'
4
-
5
- module ResqueScheduler
6
- class Cli
7
- BANNER = <<-EOF.gsub(/ {6}/, '')
8
- Usage: resque-scheduler [options]
9
-
10
- Runs a resque scheduler process directly (rather than via rake).
11
-
12
- EOF
13
- OPTIONS = [
14
- {
15
- args: ['-n', '--app-name [APP_NAME]', 'Application name for procline'],
16
- callback: ->(options) { ->(n) { options[:app_name] = n } }
17
- },
18
- {
19
- args: ['-B', '--background', 'Run in the background [BACKGROUND]'],
20
- callback: ->(options) { ->(b) { options[:background] = b } }
21
- },
22
- {
23
- args: ['-D', '--dynamic-schedule',
24
- 'Enable dynamic scheduling [DYNAMIC_SCHEDULE]'],
25
- callback: ->(options) { ->(d) { options[:dynamic] = d } }
26
- },
27
- {
28
- args: ['-E', '--environment [RAILS_ENV]', 'Environment name'],
29
- callback: ->(options) { ->(e) { options[:env] = e } }
30
- },
31
- {
32
- args: ['-I', '--initializer-path [INITIALIZER_PATH]',
33
- 'Path to optional initializer ruby file'],
34
- callback: ->(options) { ->(i) { options[:initializer_path] = i } }
35
- },
36
- {
37
- args: ['-i', '--interval [RESQUE_SCHEDULER_INTERVAL]',
38
- 'Interval for checking if a scheduled job must run'],
39
- callback: ->(options) { ->(i) { options[:poll_sleep_amount] = i } }
40
- },
41
- {
42
- args: ['-l', '--logfile [LOGFILE]', 'Log file name'],
43
- callback: ->(options) { ->(l) { options[:logfile] = l } }
44
- },
45
- {
46
- args: ['-F', '--logformat [LOGFORMAT]', 'Log output format'],
47
- callback: ->(options) { ->(f) { options[:logformat] = f } }
48
- },
49
- {
50
- args: ['-P', '--pidfile [PIDFILE]', 'PID file name'],
51
- callback: ->(options) { ->(p) { options[:pidfile] = p } }
52
- },
53
- {
54
- args: ['-q', '--quiet', 'Run with minimal output [QUIET] (or [MUTE])'],
55
- callback: ->(options) { ->(q) { options[:mute] = q } }
56
- },
57
- {
58
- args: ['-v', '--verbose', 'Run with verbose output [VERBOSE]'],
59
- callback: ->(options) { ->(v) { options[:verbose] = v } }
60
- }
61
- ].freeze
62
-
63
- def self.run!(argv = ARGV, env = ENV)
64
- new(argv, env).run!
65
- end
66
-
67
- def initialize(argv = ARGV, env = ENV)
68
- @argv = argv
69
- @env = env
70
- end
71
-
72
- def run!
73
- pre_run
74
- run_forever
75
- end
76
-
77
- def pre_run
78
- parse_options
79
- pre_setup
80
- setup_env
81
- end
82
-
83
- def parse_options
84
- OptionParser.new do |opts|
85
- opts.banner = BANNER
86
- OPTIONS.each do |opt|
87
- opts.on(*opt[:args], &(opt[:callback].call(options)))
88
- end
89
- end.parse!(argv.dup)
90
- end
91
-
92
- def pre_setup
93
- if options[:initializer_path]
94
- load options[:initializer_path].to_s.strip
95
- else
96
- false
97
- end
98
- end
99
-
100
- def setup_env
101
- require 'resque'
102
- require 'resque/scheduler'
103
-
104
- # Need to set this here for conditional Process.daemon redirect of
105
- # stderr/stdout to /dev/null
106
- Resque::Scheduler.mute = !!options[:mute]
107
-
108
- if options[:background]
109
- unless Process.respond_to?('daemon')
110
- abort 'background option is set, which requires ruby >= 1.9'
111
- end
112
-
113
- Process.daemon(true, !Resque::Scheduler.mute)
114
- Resque.redis.client.reconnect
115
- end
116
-
117
- File.open(options[:pidfile], 'w') do |f|
118
- f.puts $PROCESS_ID
119
- end if options[:pidfile]
120
-
121
- Resque::Scheduler.configure do |c|
122
- # These settings are somewhat redundant given the defaults present
123
- # in the attr reader methods. They are left here for clarity and
124
- # to serve as an example of how to use `.configure`.
125
-
126
- c.app_name = options[:app_name]
127
- c.dynamic = !!options[:dynamic]
128
- c.env = options[:env]
129
- c.logfile = options[:logfile]
130
- c.logformat = options[:logformat]
131
- c.poll_sleep_amount = Float(options[:poll_sleep_amount] || '5')
132
- c.verbose = !!options[:verbose]
133
- end
134
- end
135
-
136
- def run_forever
137
- Resque::Scheduler.run
138
- end
139
-
140
- private
141
-
142
- attr_reader :argv, :env
143
-
144
- def options
145
- @options ||= {
146
- app_name: env['APP_NAME'],
147
- background: env['BACKGROUND'],
148
- dynamic: env['DYNAMIC_SCHEDULE'],
149
- env: env['RAILS_ENV'],
150
- initializer_path: env['INITIALIZER_PATH'],
151
- logfile: env['LOGFILE'],
152
- logformat: env['LOGFORMAT'],
153
- mute: env['MUTE'] || env['QUIET'],
154
- pidfile: env['PIDFILE'],
155
- poll_sleep_amount: env['RESQUE_SCHEDULER_INTERVAL'],
156
- verbose: env['VERBOSE']
157
- }
158
- end
159
- end
160
- end
@@ -1,72 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- require 'mono_logger'
4
-
5
- module ResqueScheduler
6
- # Just builds a logger, with specified verbosity and destination.
7
- # The simplest example:
8
- #
9
- # ResqueScheduler::LoggerBuilder.new.build
10
- class LoggerBuilder
11
- # Initializes new instance of the builder
12
- #
13
- # Pass :opts Hash with
14
- # - :mute if logger needs to be silent for all levels. Default - false
15
- # - :verbose if there is a need in debug messages. Default - false
16
- # - :log_dev to output logs into a desired file. Default - STDOUT
17
- # - :format log format, either 'text' or 'json'. Default - 'text'
18
- #
19
- # Example:
20
- #
21
- # LoggerBuilder.new(
22
- # :mute => false, :verbose => true, :log_dev => 'log/scheduler.log'
23
- # )
24
- def initialize(opts={})
25
- @muted = !!opts[:mute]
26
- @verbose = !!opts[:verbose]
27
- @log_dev = opts[:log_dev] || $stdout
28
- @format = opts[:format] || 'text'
29
- end
30
-
31
- # Returns an instance of MonoLogger
32
- def build
33
- logger = MonoLogger.new(@log_dev)
34
- logger.level = level
35
- logger.formatter = send(:"#{@format}_formatter")
36
- logger
37
- end
38
-
39
- private
40
-
41
- def level
42
- if @verbose && !@muted
43
- MonoLogger::DEBUG
44
- elsif !@muted
45
- MonoLogger::INFO
46
- else
47
- MonoLogger::FATAL
48
- end
49
- end
50
-
51
- def text_formatter
52
- proc do |severity, datetime, progname, msg|
53
- "resque-scheduler: [#{severity}] #{datetime.iso8601}: #{msg}\n"
54
- end
55
- end
56
-
57
- def json_formatter
58
- proc do |severity, datetime, progname, msg|
59
- require 'json'
60
- JSON.dump(
61
- {
62
- :name => 'resque-scheduler',
63
- :progname => progname,
64
- :level => severity,
65
- :timestamp => datetime.iso8601,
66
- :msg => msg
67
- }
68
- ) + "\n"
69
- end
70
- end
71
- end
72
- end
@@ -1,28 +0,0 @@
1
- module ResqueScheduler
2
- module Plugin
3
- extend self
4
- def hooks(job, pattern)
5
- job.methods.grep(/^#{pattern}/).sort
6
- end
7
-
8
- def run_hooks(job, pattern, *args)
9
- results = hooks(job, pattern).collect do |hook|
10
- job.send(hook, *args)
11
- end
12
-
13
- results.all? { |result| result != false }
14
- end
15
-
16
- def run_before_delayed_enqueue_hooks(klass, *args)
17
- run_hooks(klass, 'before_delayed_enqueue', *args)
18
- end
19
-
20
- def run_before_schedule_hooks(klass, *args)
21
- run_hooks(klass, 'before_schedule', *args)
22
- end
23
-
24
- def run_after_schedule_hooks(klass, *args)
25
- run_hooks(klass, 'after_schedule', *args)
26
- end
27
- end
28
- end
@@ -1,183 +0,0 @@
1
- require 'resque_scheduler'
2
- require 'resque/server'
3
- require 'json'
4
-
5
- # Extend Resque::Server to add tabs
6
- module ResqueScheduler
7
- module Server
8
- def self.included(base)
9
- base.class_eval do
10
- helpers do
11
- def format_time(t)
12
- t.strftime('%Y-%m-%d %H:%M:%S %z')
13
- end
14
-
15
- def queue_from_class_name(class_name)
16
- Resque.queue_from_class(ResqueScheduler::Util.constantize(class_name))
17
- end
18
-
19
- def find_job(worker)
20
- worker = worker.downcase
21
- results = Array.new
22
-
23
- # Check working jobs
24
- working = Resque.working
25
- working = [working] unless working.is_a?(Array)
26
- work = working.select do |w|
27
- w.job && w.job["payload"] && w.job['payload']['class'].downcase.include?(worker)
28
- end
29
- work.each do |w|
30
- results += [w.job['payload'].merge({'queue' => w.job['queue'], 'where_at' => 'working'})]
31
- end
32
-
33
- # Check delayed Jobs
34
- dels = Array.new
35
- Resque.delayed_queue_peek(0, Resque.delayed_queue_schedule_size).each do |d|
36
- Resque.delayed_timestamp_peek(d, 0, Resque.delayed_timestamp_size(d)).each do |j|
37
- dels << j.merge!({'timestamp' => d})
38
- end
39
- end
40
- results += dels.select do |j|
41
- j['class'].downcase.include?(worker) && j.merge!({'where_at' => 'delayed'})
42
- end
43
-
44
- # Check Queues
45
- Resque.queues.each do |queue|
46
- queued = Resque.peek(queue, 0, Resque.size(queue))
47
- queued = [queued] unless queued.is_a?(Array)
48
- results += queued.select do |j|
49
- j['class'].downcase.include?(worker) && j.merge!({'queue' => queue, 'where_at' => 'queued'})
50
- end
51
- end
52
- results
53
- end
54
-
55
- def schedule_interval(config)
56
- if config['every']
57
- schedule_interval_every(config['every'])
58
- elsif config['cron']
59
- 'cron: ' + config['cron'].to_s
60
- else
61
- 'Not currently scheduled'
62
- end
63
- end
64
-
65
- def schedule_interval_every(every)
66
- every = [*every]
67
- s = 'every: ' << every.first
68
-
69
- return s unless every.length > 1
70
-
71
- s << ' ('
72
- meta = every.last.map do |key, value|
73
- "#{key.to_s.gsub(/_/, ' ')} #{value}"
74
- end
75
- s << meta.join(', ') << ')'
76
- end
77
-
78
- def schedule_class(config)
79
- if config['class'].nil? && !config['custom_job_class'].nil?
80
- config['custom_job_class']
81
- else
82
- config['class']
83
- end
84
- end
85
-
86
- def scheduled_in_this_env?(name)
87
- return true if Resque.schedule[name]['rails_env'].nil?
88
- Resque.schedule[name]['rails_env'] == Resque::Scheduler.env
89
- end
90
- end
91
-
92
- get "/schedule" do
93
- Resque.reload_schedule! if Resque::Scheduler.dynamic
94
- # Is there a better way to specify alternate template locations with sinatra?
95
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/scheduler.erb'))
96
- end
97
-
98
- post "/schedule/requeue" do
99
- @job_name = params['job_name'] || params[:job_name]
100
- config = Resque.schedule[@job_name]
101
- @parameters = config['parameters'] || config[:parameters]
102
- if @parameters
103
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/requeue-params.erb'))
104
- else
105
- Resque::Scheduler.enqueue_from_config(config)
106
- redirect u("/overview")
107
- end
108
- end
109
-
110
- post "/schedule/requeue_with_params" do
111
- job_name = params['job_name'] || params[:job_name]
112
- config = Resque.schedule[job_name]
113
- # Build args hash from post data (removing the job name)
114
- submitted_args = params.reject {|key, value| key == 'job_name' || key == :job_name}
115
-
116
- # Merge constructed args hash with existing args hash for
117
- # the job, if it exists
118
- config_args = config['args'] || config[:args] || {}
119
- config_args = config_args.merge(submitted_args)
120
-
121
- # Insert the args hash into config and queue the resque job
122
- config = config.merge('args' => config_args)
123
- Resque::Scheduler.enqueue_from_config(config)
124
- redirect u("/overview")
125
- end
126
-
127
- get "/delayed" do
128
- # Is there a better way to specify alternate template locations with sinatra?
129
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed.erb'))
130
- end
131
-
132
- get "/delayed/jobs/:klass" do
133
- begin
134
- klass = ResqueScheduler::Util::constantize(params[:klass])
135
- @args = JSON.load(URI.decode(params[:args]))
136
- @timestamps = Resque.scheduled_at(klass, *@args)
137
- rescue => err
138
- @timestamps = []
139
- end
140
-
141
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed_schedules.erb'))
142
- end
143
-
144
- post "/delayed/search" do
145
- # Is there a better way to specify alternate template locations with sinatra?
146
- @jobs = find_job(params[:search])
147
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/search.erb'))
148
- end
149
-
150
- get "/delayed/:timestamp" do
151
- # Is there a better way to specify alternate template locations with sinatra?
152
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed_timestamp.erb'))
153
- end
154
-
155
- post "/delayed/queue_now" do
156
- timestamp = params['timestamp']
157
- Resque::Scheduler.enqueue_delayed_items_for_timestamp(timestamp.to_i) if timestamp.to_i > 0
158
- redirect u("/overview")
159
- end
160
-
161
- post "/delayed/cancel_now" do
162
- klass = ResqueScheduler::Util.constantize params['klass']
163
- timestamp = params['timestamp']
164
- args = Resque.decode params['args']
165
- Resque.remove_delayed_job_from_timestamp(timestamp, klass, *args)
166
- redirect u("/delayed")
167
- end
168
-
169
- post "/delayed/clear" do
170
- Resque.reset_delayed_queue
171
- redirect u('delayed')
172
- end
173
- end
174
- end
175
- end
176
- end
177
-
178
- Resque::Server.tabs << 'Schedule'
179
- Resque::Server.tabs << 'Delayed'
180
-
181
- Resque::Server.class_eval do
182
- include ResqueScheduler::Server
183
- end
@@ -1,34 +0,0 @@
1
- module ResqueScheduler
2
- class Util
3
- # In order to upgrade to resque(1.25) which has deprecated following methods ,
4
- # we just added these usefull helpers back to use in Resque Scheduler.
5
- # refer to: https://github.com/resque/resque-scheduler/pull/273
6
-
7
- def self.constantize(camel_cased_word)
8
- camel_cased_word = camel_cased_word.to_s
9
-
10
- if camel_cased_word.include?('-')
11
- camel_cased_word = classify(camel_cased_word)
12
- end
13
-
14
- names = camel_cased_word.split('::')
15
- names.shift if names.empty? || names.first.empty?
16
-
17
- constant = Object
18
- names.each do |name|
19
- args = Module.method(:const_get).arity != 1 ? [false] : []
20
-
21
- if constant.const_defined?(name, *args)
22
- constant = constant.const_get(name)
23
- else
24
- constant = constant.const_missing(name)
25
- end
26
- end
27
- constant
28
- end
29
-
30
- def self.classify(dashed_word)
31
- dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
32
- end
33
- end
34
- end