resque-scheduler 2.5.1 → 4.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +12 -0
  3. data/.github/funding.yml +4 -0
  4. data/.github/workflows/codeql-analysis.yml +59 -0
  5. data/.github/workflows/rubocop.yml +27 -0
  6. data/.github/workflows/ruby.yml +81 -0
  7. data/AUTHORS.md +25 -0
  8. data/CHANGELOG.md +539 -0
  9. data/CODE_OF_CONDUCT.md +74 -0
  10. data/Gemfile +26 -2
  11. data/README.md +291 -70
  12. data/Rakefile +8 -19
  13. data/exe/resque-scheduler +5 -0
  14. data/lib/resque/scheduler/cli.rb +147 -0
  15. data/lib/resque/scheduler/configuration.rb +102 -0
  16. data/lib/resque/scheduler/delaying_extensions.rb +371 -0
  17. data/lib/resque/scheduler/env.rb +85 -0
  18. data/lib/resque/scheduler/extension.rb +13 -0
  19. data/lib/resque/scheduler/failure_handler.rb +11 -0
  20. data/lib/resque/scheduler/lock/base.rb +13 -4
  21. data/lib/resque/scheduler/lock/basic.rb +4 -5
  22. data/lib/resque/scheduler/lock/resilient.rb +52 -43
  23. data/lib/resque/scheduler/lock.rb +2 -1
  24. data/lib/resque/scheduler/locking.rb +104 -0
  25. data/lib/resque/scheduler/logger_builder.rb +83 -0
  26. data/lib/resque/scheduler/plugin.rb +31 -0
  27. data/lib/resque/scheduler/scheduling_extensions.rb +142 -0
  28. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed.erb +21 -12
  29. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed_schedules.erb +1 -1
  30. data/lib/{resque_scheduler → resque/scheduler}/server/views/delayed_timestamp.erb +1 -1
  31. data/lib/resque/scheduler/server/views/scheduler.erb +58 -0
  32. data/lib/{resque_scheduler → resque/scheduler}/server/views/search.erb +4 -7
  33. data/lib/{resque_scheduler → resque/scheduler}/server/views/search_form.erb +1 -5
  34. data/lib/resque/scheduler/server.rb +268 -0
  35. data/lib/resque/scheduler/signal_handling.rb +40 -0
  36. data/lib/{resque_scheduler → resque/scheduler}/tasks.rb +3 -6
  37. data/lib/resque/scheduler/util.rb +39 -0
  38. data/lib/resque/scheduler/version.rb +7 -0
  39. data/lib/resque/scheduler.rb +271 -199
  40. data/lib/resque-scheduler.rb +3 -1
  41. data/resque-scheduler.gemspec +53 -20
  42. metadata +176 -132
  43. data/.gitignore +0 -11
  44. data/.rubocop.yml +0 -129
  45. data/.simplecov +0 -1
  46. data/.travis.yml +0 -21
  47. data/HISTORY.md +0 -226
  48. data/ROADMAP.md +0 -10
  49. data/bin/resque-scheduler +0 -5
  50. data/examples/Rakefile +0 -2
  51. data/examples/config/initializers/resque-web.rb +0 -37
  52. data/examples/dynamic-scheduling/README.md +0 -28
  53. data/examples/dynamic-scheduling/app/jobs/fix_schedules_job.rb +0 -54
  54. data/examples/dynamic-scheduling/app/jobs/send_email_job.rb +0 -9
  55. data/examples/dynamic-scheduling/app/models/user.rb +0 -16
  56. data/examples/dynamic-scheduling/config/resque.yml +0 -4
  57. data/examples/dynamic-scheduling/config/static_schedule.yml +0 -7
  58. data/examples/dynamic-scheduling/lib/tasks/resque.rake +0 -48
  59. data/examples/run-resque-web +0 -3
  60. data/lib/resque/scheduler_locking.rb +0 -91
  61. data/lib/resque_scheduler/cli.rb +0 -160
  62. data/lib/resque_scheduler/logger_builder.rb +0 -70
  63. data/lib/resque_scheduler/plugin.rb +0 -28
  64. data/lib/resque_scheduler/server/views/scheduler.erb +0 -36
  65. data/lib/resque_scheduler/server.rb +0 -182
  66. data/lib/resque_scheduler/util.rb +0 -34
  67. data/lib/resque_scheduler/version.rb +0 -5
  68. data/lib/resque_scheduler.rb +0 -386
  69. data/script/migrate_to_timestamps_set.rb +0 -14
  70. data/tasks/resque_scheduler.rake +0 -2
  71. data/test/cli_test.rb +0 -286
  72. data/test/delayed_queue_test.rb +0 -449
  73. data/test/redis-test.conf +0 -108
  74. data/test/resque-web_test.rb +0 -199
  75. data/test/scheduler_args_test.rb +0 -190
  76. data/test/scheduler_hooks_test.rb +0 -23
  77. data/test/scheduler_locking_test.rb +0 -242
  78. data/test/scheduler_setup_test.rb +0 -95
  79. data/test/scheduler_task_test.rb +0 -35
  80. data/test/scheduler_test.rb +0 -344
  81. data/test/support/redis_instance.rb +0 -134
  82. data/test/test_helper.rb +0 -131
  83. /data/lib/{resque_scheduler → resque/scheduler}/server/views/requeue-params.erb +0 -0
@@ -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,70 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- module ResqueScheduler
4
- # Just builds a logger, with specified verbosity and destination.
5
- # The simplest example:
6
- #
7
- # ResqueScheduler::LoggerBuilder.new.build
8
- class LoggerBuilder
9
- # Initializes new instance of the builder
10
- #
11
- # Pass :opts Hash with
12
- # - :mute if logger needs to be silent for all levels. Default - false
13
- # - :verbose if there is a need in debug messages. Default - false
14
- # - :log_dev to output logs into a desired file. Default - STDOUT
15
- # - :format log format, either 'text' or 'json'. Default - 'text'
16
- #
17
- # Example:
18
- #
19
- # LoggerBuilder.new(
20
- # :mute => false, :verbose => true, :log_dev => 'log/scheduler.log'
21
- # )
22
- def initialize(opts={})
23
- @muted = !!opts[:mute]
24
- @verbose = !!opts[:verbose]
25
- @log_dev = opts[:log_dev] || $stdout
26
- @format = opts[:format] || 'text'
27
- end
28
-
29
- # Returns an instance of Logger
30
- def build
31
- logger = Logger.new(@log_dev)
32
- logger.level = level
33
- logger.formatter = send(:"#{@format}_formatter")
34
- logger
35
- end
36
-
37
- private
38
-
39
- def level
40
- if @verbose && !@muted
41
- Logger::DEBUG
42
- elsif !@muted
43
- Logger::INFO
44
- else
45
- Logger::FATAL
46
- end
47
- end
48
-
49
- def text_formatter
50
- proc do |severity, datetime, progname, msg|
51
- "resque-scheduler: [#{severity}] #{datetime.iso8601}: #{msg}\n"
52
- end
53
- end
54
-
55
- def json_formatter
56
- proc do |severity, datetime, progname, msg|
57
- require 'json'
58
- JSON.dump(
59
- {
60
- :name => 'resque-scheduler',
61
- :progname => progname,
62
- :level => severity,
63
- :timestamp => datetime.iso8601,
64
- :msg => msg
65
- }
66
- ) + "\n"
67
- end
68
- end
69
- end
70
- 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,36 +0,0 @@
1
- <h1>Schedule</h1>
2
-
3
- <p class='intro'>
4
- The list below contains all scheduled jobs. Click &quot;Queue now&quot; to queue
5
- a job immediately.
6
- Server local time: <%= Time.now %>
7
- </p>
8
-
9
- <table>
10
- <tr>
11
- <th></th>
12
- <th>Name</th>
13
- <th>Description</th>
14
- <th>Interval</th>
15
- <th>Class</th>
16
- <th>Queue</th>
17
- <th>Arguments</th>
18
- </tr>
19
- <% Resque.schedule.keys.sort.each do |name| %>
20
- <% config = Resque.schedule[name] %>
21
- <tr>
22
- <td style="padding-top: 12px; padding-bottom: 2px; width: 10px">
23
- <form action="<%= u "/schedule/requeue" %>" method="post" style="margin-left: 0">
24
- <input type="hidden" name="job_name" value="<%= h name %>">
25
- <input type="submit" value="Queue now">
26
- </form>
27
- </td>
28
- <td><%= h name %></td>
29
- <td><%= h config['description'] %></td>
30
- <td style="white-space:nowrap"><%= h schedule_interval(config) %></td>
31
- <td><%= h schedule_class(config) %></td>
32
- <td><%= h config['queue'] || queue_from_class_name(config['class']) %></td>
33
- <td><%= h config['args'].inspect %></td>
34
- </tr>
35
- <% end %>
36
- </table>
@@ -1,182 +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
- s = 'every: '
67
- if every.respond_to?(:first)
68
- s << every.first
69
- else
70
- s << every
71
- end
72
-
73
- return s unless every.respond_to?(:last) && every.length > 1
74
-
75
- s << ' ('
76
- meta = every.last.map do |key, value|
77
- "#{key.to_s.gsub(/_/, ' ')} #{value}"
78
- end
79
- s << meta.join(', ') << ')'
80
- end
81
-
82
- def schedule_class(config)
83
- if config['class'].nil? && !config['custom_job_class'].nil?
84
- config['custom_job_class']
85
- else
86
- config['class']
87
- end
88
- end
89
- end
90
-
91
- get "/schedule" do
92
- Resque.reload_schedule! if Resque::Scheduler.dynamic
93
- # Is there a better way to specify alternate template locations with sinatra?
94
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/scheduler.erb'))
95
- end
96
-
97
- post "/schedule/requeue" do
98
- @job_name = params['job_name'] || params[:job_name]
99
- config = Resque.schedule[@job_name]
100
- @parameters = config['parameters'] || config[:parameters]
101
- if @parameters
102
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/requeue-params.erb'))
103
- else
104
- Resque::Scheduler.enqueue_from_config(config)
105
- redirect u("/overview")
106
- end
107
- end
108
-
109
- post "/schedule/requeue_with_params" do
110
- job_name = params['job_name'] || params[:job_name]
111
- config = Resque.schedule[job_name]
112
- # Build args hash from post data (removing the job name)
113
- submitted_args = params.reject {|key, value| key == 'job_name' || key == :job_name}
114
-
115
- # Merge constructed args hash with existing args hash for
116
- # the job, if it exists
117
- config_args = config['args'] || config[:args] || {}
118
- config_args = config_args.merge(submitted_args)
119
-
120
- # Insert the args hash into config and queue the resque job
121
- config = config.merge('args' => config_args)
122
- Resque::Scheduler.enqueue_from_config(config)
123
- redirect u("/overview")
124
- end
125
-
126
- get "/delayed" do
127
- # Is there a better way to specify alternate template locations with sinatra?
128
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed.erb'))
129
- end
130
-
131
- get "/delayed/jobs/:klass" do
132
- begin
133
- klass = ResqueScheduler::Util::constantize(params[:klass])
134
- @args = JSON.load(URI.decode(params[:args]))
135
- @timestamps = Resque.scheduled_at(klass, *@args)
136
- rescue => err
137
- @timestamps = []
138
- end
139
-
140
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed_schedules.erb'))
141
- end
142
-
143
- post "/delayed/search" do
144
- # Is there a better way to specify alternate template locations with sinatra?
145
- @jobs = find_job(params[:search])
146
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/search.erb'))
147
- end
148
-
149
- get "/delayed/:timestamp" do
150
- # Is there a better way to specify alternate template locations with sinatra?
151
- erb File.read(File.join(File.dirname(__FILE__), 'server/views/delayed_timestamp.erb'))
152
- end
153
-
154
- post "/delayed/queue_now" do
155
- timestamp = params['timestamp']
156
- Resque::Scheduler.enqueue_delayed_items_for_timestamp(timestamp.to_i) if timestamp.to_i > 0
157
- redirect u("/overview")
158
- end
159
-
160
- post "/delayed/cancel_now" do
161
- klass = ResqueScheduler::Util.constantize params['klass']
162
- timestamp = params['timestamp']
163
- args = Resque.decode params['args']
164
- Resque.remove_delayed_job_from_timestamp(timestamp, klass, *args)
165
- redirect u("/delayed")
166
- end
167
-
168
- post "/delayed/clear" do
169
- Resque.reset_delayed_queue
170
- redirect u('delayed')
171
- end
172
- end
173
- end
174
- end
175
- end
176
-
177
- Resque::Server.tabs << 'Schedule'
178
- Resque::Server.tabs << 'Delayed'
179
-
180
- Resque::Server.class_eval do
181
- include ResqueScheduler::Server
182
- 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
@@ -1,5 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- module ResqueScheduler
4
- VERSION = '2.5.1'
5
- end