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
data/.travis.yml DELETED
@@ -1,21 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- env:
6
- global:
7
- - RESQUE_SCHEDULER_DISABLE_TEST_REDIS_SERVER=1
8
- services:
9
- - redis-server
10
- notifications:
11
- email:
12
- recipients: daniel.buch+resque-scheduler@gmail.com
13
- deploy:
14
- provider: rubygems
15
- api_key:
16
- secure: dM97lL/jTu14mEHD2Ih/vQfZ8cWJWb+DQ8UKZjkD2JfYtvwEAlkPG9RrrDkSHb5qkYxG2VAhjyx/0MZJ0TsVL6wrLMC9gcJb3yomw2Sch2Noj68tEndiSUHnxrB9gKzbhHbjLQXDqYf4Hco9/PHHQp4piFPJhLzNZRehIuTJPcA=
17
- gem: resque-scheduler
18
- on:
19
- tags: true
20
- repo: resque/resque-scheduler
21
- rvm: 2.0.0
data/HISTORY.md DELETED
@@ -1,226 +0,0 @@
1
- # Resque Scheduler History / ChangeLog / Release Notes
2
-
3
- ## 2.5.1 (2014-02-09)
4
- * Make signal handling (really) Ruby 2 compatible
5
-
6
- ## 2.5.0 (2014-02-09)
7
- * Use `logger.error` when logging errors from `handle_errors`
8
- * Added search feature to the Delayed tab in Resque Web
9
- * Fix confusion with redis version requirements in `README.md`
10
-
11
- ## 2.4.0 (2014-01-14)
12
-
13
- * Including optional env name in procline
14
- * Fixing an explosion regarding `every` in the views
15
- * Bumping the copyright year
16
- * Corrected doc for syntax of class and every keys
17
- * Adding a standalone executable
18
- * **POSSIBLE BREAKING CHANGE**: Dropping support for ree
19
- * Add support for persistence of dynamic schedules
20
- * Fix unsafe shutdown in Ruby 2
21
- * Adding `.configure` convenience method for block-style configuration
22
- * Add `.remove_delayed_selection` method to remove based on result of a block
23
- * Add support for viewing all schedules for a job in web UI
24
- * Use resque redis namespace in the master lock key
25
- * Including optional app name in procline
26
- * Various test improvements, :bug: fixes, and documentation updates!
27
-
28
- ## 2.3.1 (2013-11-20)
29
-
30
- * Correcting `require_paths` in gemspec
31
-
32
- ## 2.3.0 (2013-11-07)
33
-
34
- * Fix re-introduced `ThreadError` on Ruby 2
35
- * **BREAKING CHANGE**: Added `RESQUE_SCHEDULER_INTERVAL` in place of `INTERVAL`
36
- * Use `Float()` instead of `Integer()` to calculate poll sleep amount
37
- * Upgraded dependence of Resque to support 1.25
38
- * Add rufus scheduler `every` notice to README
39
- * Use `Resque.validate` instead of custom `.validate_job!`
40
- * Specify MIT license in gemspec
41
-
42
- ## 2.2.0 (2013-10-13)
43
-
44
- * Locking rufus-scheduler dependency to `~> 2.0`
45
- * Updated redis dependency to `>= 3.0.0`
46
- * Add support for parameterized resque jobs.
47
- * Allowing prefix for `master_lock_key`.
48
- * Add `Resque.clean_schedules` method, which is useful when setting up the
49
- scheduler for the first time.
50
- * Bug fixes related to first time schedule retrieval and missing schedules.
51
-
52
- ## 2.1.0 (2013-09-20)
53
-
54
- * Locking to resque < 1.25.0 (for now)
55
- * Ensuring `Resque.schedule=` sets rather than appends
56
- * Process daemonization fixes including stdio redirection and redis client
57
- reconnection
58
- * Add `#scheduled_at` which returns an array of timestamps at which the
59
- specified job is scheduled
60
- * Syncing stdout/stderr
61
- * Add `#enqueue_delayed` for enqueueing specific delayed jobs immediately
62
- * Show server local time in resque-web
63
- * Enqueue immediately if job is being enqueued in the past
64
- * Using a logger instead of `#puts`, configurable via `LOGFILE`, `VERBOSE`, and
65
- `MUTE` environmental variables, as well as being settable via
66
- `Resque::Scheduler#logger`
67
- * Fixing scheduler template when arrays are passed to rufus-scheduler
68
- * Add support for configuring `Resque::Scheduler.poll_sleep_amount` via the
69
- `INTERVAL` environmental variable.
70
- * Fixed shutdown in ruby 2.0.0
71
- * Removed dependency on `Resque::Helpers`
72
-
73
- ## 2.0.1 (2013-03-20)
74
-
75
- * Adding locking to support master failover
76
- * Allow custom job classes to be used in `Resque.enqueue_at`
77
- * More efficient `#remove_delayed` implementation
78
- * Allowing `#enqueue_at` to call `#scheduled` when `Resque.inline` is `true`
79
-
80
- ## 2.0.0 (2012-05-04)
81
-
82
- * Add support for Resque.inline configuration (carlosantoniodasilva)
83
- * Fixing possible job loss race condition around deleting delayed queues
84
- and enqueuing a job 0 seconds in the future.
85
-
86
- ### 2.0.0.h (2012-03-19)
87
-
88
- * Adding plugin support with hooks (andreas)
89
-
90
- ### 2.0.0.f (2011-11-03)
91
-
92
- * TODO: address race condition with delayed jobs (using redis transactions)
93
- * Support `ENV['BACKGROUND']` flag for daemonizing (bernerdschaefer)
94
- * Added support for `before_schedule` and `after_schedule` hooks (yaauie)
95
- * Added `remove_delayed_job_from_timestamp` to remove delayed jobs from
96
- a given timestamp.
97
-
98
- ### 2.0.0.e (2011-09-16)
99
-
100
- * Adding `enqueue_at_with_queue`/`enqueue_in_with_queue` support (niralisse)
101
- * Adding `Resque::Scheduler.poll_sleep_amount` to allow for configuring
102
- the sleep time b/w delayed queue polls.
103
- * Add a "Clear Delayed Jobs" button to the Delayed Jobs page (john-griffin)
104
- * Fixed pagination issue on the Delayed tab
105
-
106
- ### 2.0.0.d (2011-04-04)
107
-
108
- * porting bug fixes from v1.9-stable
109
-
110
- ### 2.0.0.c
111
-
112
- * Rake task drop a pid file (sreeix)
113
-
114
- ### 2.0.0.b
115
-
116
- * Bug fixes
117
-
118
- ### 2.0.0.a
119
-
120
- * Dynamic schedule support (brianjlandau, davidyang)
121
- * Now depends on redis >=1.3
122
-
123
- ## 1.9.11 (2013-11-20)
124
-
125
- * Fixed behavior of `#validate_job!` via `#enqueue_at_with_queue` #286
126
- * Correcting `require_paths` in gemspec #288
127
-
128
- ## 1.9.10 (2013-09-19)
129
-
130
- * Backported `#enqueue_at_with_queue`
131
- * Locking to resque < 1.25.0
132
- * Mocha setup compatibility
133
- * Ruby 1.8 compatibility in scheduler tab when schedule keys are symbols
134
-
135
- ## 1.9.9 (2011-03-29)
136
-
137
- * Compatibility with resque 1.15.0
138
-
139
- ## 1.9.8 (???)
140
-
141
- * Validates delayed jobs prior to insertion into the delayed queue (bogdan)
142
- * Rescue exceptions that occur during queuing and log them (dgrijalva)
143
-
144
- ## 1.9.7 (2010-11-09)
145
-
146
- * Support for rufus-scheduler "every" syntax (fallwith)
147
- * Ability to pass a Time to `handle_delayed_items` for testing/staging (rcarver)
148
-
149
- ## 1.9.6 (2010-10-08)
150
-
151
- * Support for custom job classes (like resque-status) (mattetti)
152
-
153
- ## 1.9.5 (2010-09-09)
154
-
155
- * Updated scheduler rake task to allow for an alternate setup task
156
- to avoid loading the entire stack. (chewbranca)
157
- * Fixed sig issue on win32 (#25)
158
-
159
- ## 1.9.4 (2010-07-29)
160
-
161
- * Adding ability to remove jobs from delayed queue (joshsz)
162
- * Fixing issue #23 (removing .present? reference)
163
-
164
- ## 1.9.3 (2010-07-07)
165
-
166
- * Bug fix (#19)
167
-
168
- ## 1.9.2 (2010-06-16)
169
-
170
- * Fixing issue with redis gem 2.0.1 and redis server 1.2.6 (dbackeus)
171
-
172
- ## 1.9.1 (2010-06-04)
173
-
174
- * Fixing issue with redis server 1.2.6 and redis gem 2.0.1
175
-
176
- ## 1.9.0 (2010-06-04)
177
-
178
- * Adding redis 2.0 support (bpo)
179
-
180
- ## 1.8.2 (2010-06-04)
181
-
182
- * Adding queue now functionality to delayed timestamps (daviddoan)
183
-
184
- ## 1.8.1 (2010-05-19)
185
-
186
- * Adding `rails_env` for scheduled jobs to support scoping jobs by
187
- `RAILS_ENV` (gravis).
188
- * Fixing ruby 1.8.6 compatibility issue.
189
- * Adding gemspec for bundler support.
190
-
191
- ## 1.8.0 (2010-04-14)
192
-
193
- * Moving version to match corresponding resque version
194
- * Sorting schedule on Scheduler tab
195
- * Adding tests for resque-web (gravis)
196
-
197
- ## 1.0.5 (2010-03-01)
198
-
199
- * Fixed support for overriding queue from schedule config.
200
- * Removed resque-web dependency on loading the job classes for "Queue Now",
201
- provided "queue" is specified in the schedule.
202
- * The queue is now stored with the job and arguments in the delayed queue so
203
- there is no longer a need for the scheduler to load job classes to introspect
204
- the queue.
205
-
206
- ## 1.0.4 (2010-02-26)
207
-
208
- * Added support for specifying the queue to put the job onto. This allows for
209
- you to have one job that can go onto multiple queues and be able to schedule
210
- jobs without having to load the job classes.
211
-
212
- ## 1.0.3 (2010-02-11)
213
-
214
- * Added support for scheduled jobs with empty crons. This is helpful to have
215
- jobs that you don't want on a schedule, but do want to be able to queue by
216
- clicking a button.
217
-
218
- ## 1.0.2 (2010-02-?)
219
-
220
- * Change Delayed Job tab to display job details if only 1 job exists
221
- for a given timestamp
222
-
223
- ## 1.0.1 (2010-01-?)
224
-
225
- * Bugfix: delayed jobs close together resulted in a 5 second sleep
226
-
data/ROADMAP.md DELETED
@@ -1,10 +0,0 @@
1
- TODO for v3.0.0
2
- ===============
3
-
4
- - Clean up all RuboCop offences, breaking the API if necessary
5
- - Get to at least 95% test coverage
6
-
7
- TODO for v3.1.0
8
- ===============
9
-
10
- - Compatibility with Resque 2
data/bin/resque-scheduler DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # vim:fileencoding=utf-8
3
-
4
- require 'resque_scheduler'
5
- ResqueScheduler::Cli.run!
data/examples/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- # vim:fileencoding=utf-8
2
- require 'resque_scheduler/tasks'
@@ -1,37 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- require 'json'
4
- require 'yaml'
5
- require 'resque'
6
-
7
- redis_env_var = ENV['REDIS_PROVIDER'] || 'REDIS_URL'
8
- Resque.redis = ENV[redis_env_var] || 'localhost:6379'
9
-
10
- require 'resque_scheduler'
11
- require 'resque_scheduler/server'
12
-
13
- schedule_yml = ENV['RESQUE_SCHEDULE_YML']
14
- if schedule_yml
15
- if File.exist?(schedule_yml)
16
- Resque.schedule = YAML.load_file(schedule_yml)
17
- else
18
- Resque.schedule = YAML.load(schedule_yml)
19
- end
20
- end
21
-
22
- schedule_json = ENV['RESQUE_SCHEDULE_JSON']
23
- if schedule_json
24
- if File.exist?(schedule_json)
25
- Resque.schedule = JSON.parse(File.read(schedule_json))
26
- else
27
- Resque.schedule = JSON.parse(schedule_json)
28
- end
29
- end
30
-
31
- class Putter
32
- @queue = 'putting'
33
-
34
- def self.perform(*args)
35
- args.each { |arg| puts arg }
36
- end
37
- end
@@ -1,28 +0,0 @@
1
- Dynamic Scheduling Example
2
- ==========================
3
-
4
- Possible workaround for
5
- https://github.com/resque/resque-scheduler/issues/269
6
-
7
- This folder contains just the relevant files you would have to put into
8
- a rails application.
9
-
10
- The problem we want to fix is that when resque-scheduler is restarted,
11
- any dynamically added jobs are wiped. To fix it, we will run a
12
- statically scheduled job that dynamically reschedules any missing
13
- dynamic schedules.
14
-
15
- This workaround uses both a dynamic schedule (every time a user is
16
- created, a schedule is dynamically added to send him a daily email) and
17
- a static schedule (a job runs hourly, starting 10 seconds after starting
18
- resque-scheduler, to check that there is a scheduled job to send an
19
- email for every user; missing schedules are added).
20
-
21
- This way even though a resque-scheduler restart wipes all dynamic
22
- schedules, they are recreated by the `fix_schedules` job that runs in
23
- the static schedule. Even if dynamic schedules were lost for any reason
24
- (data loss in redis clusters, whatever), they will be recreated hourly.
25
-
26
- This workaround requires that enough information is saved in the
27
- database to recreate all dynamic schedules. In this case we create one
28
- dynamically scheduled job for every user in the database.
@@ -1,54 +0,0 @@
1
- # vim:fileencoding=utf-8
2
- #
3
- # Background job to fix the schedule for email sending. Any missing
4
- # schedule will be added to resque-schedule.
5
- #
6
- # Recent resque-scheduler versions wipe all dynamic schedules when
7
- # restarting. This means all dynamic schedules, which are added via the
8
- # API, are wiped on each application redeployment. A workaround for this
9
- # sometimes undesirable behavior is to make this job part of a static
10
- # schedule (see config/initializers/resque.rb and
11
- # config/static_schedule.yml). This job will be scheduled to run every
12
- # hour even after restarting resque-schedule, and will add back the
13
- # dynamic schedules that were wiped on restart. It also serves as
14
- # safeguard against schedules getting lost for any reason.
15
- #
16
- # For more detail about this unfortunate behavior of resque-scheduler see:
17
- #
18
- # https://github.com/resque/resque-scheduler/issues/269
19
- #
20
- # The perform method of this class will be invoked from a Resque worker.
21
-
22
- class FixSchedulesJob
23
- @queue = :send_emails
24
-
25
- # Fix email sending schedules. Any user which does not have scheduled
26
- # sending of emails will be detected, and the missing scheduled job
27
- # will be added to resque-schedule.
28
- #
29
- # This method is intended to be invoked from Resque, which means it is
30
- # performed in the background.
31
- def self.perform
32
- users_unscheduled = []
33
-
34
- User.all.each do |user|
35
- # get schedule for the user
36
- schedule = Resque.get_schedule("send_email_#{user.id}")
37
- # if a user has no schedule, add it to the array
38
- if schedule.nil?
39
- users_unscheduled << user
40
- end
41
- end
42
-
43
- if users_unscheduled.length > 0
44
- users_unscheduled.each do |user|
45
- name = "send_email_#{user.id}"
46
- config = {}
47
- config[:class] = 'SendEmailJob'
48
- config[:args] = user.id
49
- config[:every] = '1d'
50
- Resque.set_schedule(name, config)
51
- end
52
- end
53
- end
54
- end
@@ -1,9 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- class SendEmailJob
4
- @queue = :send_emails
5
-
6
- def self.perform(user_id)
7
- # ... do whatever you have to do to send an email to the user
8
- end
9
- end
@@ -1,16 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- class User < ActiveRecord::Base
4
- after_create :schedule_send_email
5
-
6
- private
7
-
8
- def schedule_send_email
9
- name = "send_email_#{id}"
10
- config = {}
11
- config[:class] = 'SendEmailJob'
12
- config[:args] = id
13
- config[:every] = '1d'
14
- Resque.set_schedule(name, config)
15
- end
16
- end
@@ -1,4 +0,0 @@
1
- ---
2
- development: localhost:6379
3
- test: localhost:6379:1
4
- production: localhost:6379 # or wherever your redis-server is in production
@@ -1,7 +0,0 @@
1
- ---
2
- FixSchedulesJob:
3
- description: 'Add any missing email sending schedules'
4
- queue: send_emails
5
- every:
6
- - '1h'
7
- - :first_in: '10s'
@@ -1,48 +0,0 @@
1
- # vim:fileencoding=utf-8
2
-
3
- require 'resque/tasks'
4
- require 'resque_scheduler/tasks'
5
- require 'yaml'
6
-
7
- namespace :resque do
8
- task :setup do
9
- require 'resque'
10
- require 'resque_scheduler'
11
-
12
- rails_root = ENV['RAILS_ROOT'] || File.expand_path('../../../', __FILE__)
13
- rails_env = ENV['RAILS_ENV'] || 'development'
14
-
15
- # In resque-only servers we must require each job class individually,
16
- # because we're not running the full Rails app
17
- require "#{rails_root}/app/jobs/send_email_job"
18
- require "#{rails_root}/app/jobs/fix_schedules_job"
19
-
20
- resque_config = YAML.load_file(
21
- File.join(rails_root.to_s, 'config', 'resque.yml')
22
- )
23
- Resque.redis = resque_config[rails_env]
24
-
25
- # If you want to be able to dynamically change the schedule,
26
- # uncomment this line. A dynamic schedule can be updated via the
27
- # Resque::Scheduler.set_schedule (and remove_schedule) methods.
28
- # When dynamic is set to true, the scheduler process looks for
29
- # schedule changes and applies them on the fly.
30
- # Note: This feature is only available in >=2.0.0.
31
- Resque::Scheduler.dynamic = true
32
-
33
- # Load static schedule (only in background servers).
34
- # The schedule doesn't need to be stored in a YAML, it just needs to
35
- # be a hash. YAML is usually the easiest.
36
- Resque.schedule = YAML.load_file(
37
- File.join(rails_root.to_s, 'config', 'static_schedule.yml')
38
- )
39
-
40
- Resque.before_fork do |job|
41
- # Reconnect to the DB before running each job. Otherwise we get errors if
42
- # the DB is restarted after starting Resque.
43
- # Absolutely necessary on Heroku, otherwise we get a "PG::Error: SSL
44
- # SYSCALL error: EOF detected" exception
45
- ActiveRecord::Base.establish_connection
46
- end
47
- end
48
- end
@@ -1,3 +0,0 @@
1
- #!/bin/bash
2
- cd "$(dirname $0)"
3
- exec resque-web --foreground --no-launch config/initializers/resque-web.rb
@@ -1,91 +0,0 @@
1
- # ### Locking the scheduler process
2
- #
3
- # There are two places in resque-scheduler that need to be synchonized
4
- # in order to be able to run redundant scheduler processes while ensuring jobs don't
5
- # get queued multiple times when the master process changes.
6
- #
7
- # 1) Processing the delayed queues (jobs that are created from enqueue_at/enqueue_in, etc)
8
- # 2) Processing the scheduled (cron-like) jobs from rufus-scheduler
9
- #
10
- # Protecting the delayed queues (#1) is relatively easy. A simple SETNX in
11
- # redis would suffice. However, protecting the scheduled jobs is trickier
12
- # because the clocks on machines could be slightly off or actual firing times
13
- # could vary slightly due to load. If scheduler A's clock is slightly ahead
14
- # of scheduler B's clock (since they are on different machines), when
15
- # scheduler A dies, we need to ensure that scheduler B doesn't queue jobs
16
- # that A already queued before it's death. (This all assumes that it is
17
- # better to miss a few scheduled jobs than it is to run them multiple times
18
- # for the same iteration.)
19
- #
20
- # To avoid queuing multiple jobs in the case of master fail-over, the master
21
- # should remain the master as long as it can rather than a simple SETNX which
22
- # would result in the master roll being passed around frequently.
23
- #
24
- # Locking Scheme:
25
- # Each resque-scheduler process attempts to get the master lock via SETNX.
26
- # Once obtained, it sets the expiration for 3 minutes (configurable). The
27
- # master process continually updates the timeout on the lock key to be 3
28
- # minutes in the future in it's loop(s) (see `run`) and when jobs come out of
29
- # rufus-scheduler (see `load_schedule_job`). That ensures that a minimum of
30
- # 3 minutes must pass since the last queuing operation before a new master is
31
- # chosen. If, for whatever reason, the master fails to update the expiration
32
- # for 3 minutes, the key expires and the lock is up for grabs. If
33
- # miraculously the original master comes back to life, it will realize it is
34
- # no longer the master and stop processing jobs.
35
- #
36
- # The clocks on the scheduler machines can then be up to 3 minutes off from
37
- # each other without the risk of queueing the same scheduled job twice during
38
- # a master change. The catch is, in the event of a master change, no
39
- # scheduled jobs will be queued during those 3 minutes. So, there is a trade
40
- # off: the higher the timeout, the less likely scheduled jobs will be fired
41
- # twice but greater chances of missing scheduled jobs. The lower the timeout,
42
- # less likely jobs will be missed, greater the chances of jobs firing twice. If
43
- # you don't care about jobs firing twice or are certain your machines' clocks
44
- # are well in sync, a lower timeout is preferable. One thing to keep in mind:
45
- # this only effects *scheduled* jobs - delayed jobs will never be lost or
46
- # skipped since eventually a master will come online and it will process
47
- # everything that is ready (no matter how old it is). Scheduled jobs work
48
- # like cron - if you stop cron, no jobs fire while it's stopped and it doesn't
49
- # fire jobs that were missed when it starts up again.
50
-
51
- require 'resque/scheduler/lock'
52
-
53
- module Resque
54
- module SchedulerLocking
55
- def master_lock
56
- @master_lock ||= build_master_lock
57
- end
58
-
59
- def supports_lua?
60
- redis_master_version >= 2.5
61
- end
62
-
63
- def is_master?
64
- master_lock.acquire! || master_lock.locked?
65
- end
66
-
67
- def release_master_lock!
68
- master_lock.release!
69
- end
70
-
71
- private
72
-
73
- def build_master_lock
74
- if supports_lua?
75
- Resque::Scheduler::Lock::Resilient.new(master_lock_key)
76
- else
77
- Resque::Scheduler::Lock::Basic.new(master_lock_key)
78
- end
79
- end
80
-
81
- def master_lock_key
82
- lock_prefix = ENV['RESQUE_SCHEDULER_MASTER_LOCK_PREFIX'] || ''
83
- lock_prefix += ':' if lock_prefix != ''
84
- "#{Resque.redis.namespace}:#{lock_prefix}resque_scheduler_master_lock"
85
- end
86
-
87
- def redis_master_version
88
- Resque.redis.info['redis_version'].to_f
89
- end
90
- end
91
- end