que-scheduler 3.4.3 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +72 -31
- data/lib/que/scheduler.rb +2 -1
- data/lib/que/scheduler/configuration.rb +34 -0
- data/lib/que/scheduler/db.rb +3 -1
- data/lib/que/scheduler/defined_job.rb +2 -2
- data/lib/que/scheduler/jobs/que_scheduler_audit_clear_down_job.rb +2 -2
- data/lib/que/scheduler/migrations.rb +5 -0
- data/lib/que/scheduler/migrations/6/down.sql +7 -0
- data/lib/que/scheduler/migrations/6/up.sql +26 -0
- data/lib/que/scheduler/schedule.rb +25 -17
- data/lib/que/scheduler/scheduler_job.rb +6 -5
- data/lib/que/scheduler/scheduler_job_args.rb +2 -1
- data/lib/que/scheduler/state_checks.rb +1 -17
- data/lib/que/scheduler/time_zone.rb +61 -0
- data/lib/que/scheduler/to_enqueue.rb +12 -8
- data/lib/que/scheduler/version.rb +1 -1
- data/lib/que/scheduler/version_support.rb +5 -1
- metadata +27 -10
- data/lib/que/scheduler/config.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a75db11d923c140204bd92360916e4cacaac5531fc25470616a4d558dde22dd3
|
4
|
+
data.tar.gz: ba08caa1a8bf8a87532da3fa5cbfc8b720784e88354ab74b240c5d724c46fb07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19ad5f4284dd03b6129d970e87cdd1652f8e992a5c141c032fc4935beaea2bbf15f35253c5c5f6c93e3cf6b32875beb6138112d43e8e5e123235c32fa9593984
|
7
|
+
data.tar.gz: f8618b848befcccf03387fe2e891f201790c1dc8b736406a62bd3d63990ac1c8f47737966103713d843a627ec39d2ff23128570dd6b03cd28682bc4613638fcf
|
data/README.md
CHANGED
@@ -28,7 +28,7 @@ resque-scheduler files, but with additional features.
|
|
28
28
|
```ruby
|
29
29
|
class CreateQueSchedulerSchema < ActiveRecord::Migration
|
30
30
|
def change
|
31
|
-
Que::Scheduler::Migrations.migrate!(version:
|
31
|
+
Que::Scheduler::Migrations.migrate!(version: 6)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
```
|
@@ -36,8 +36,8 @@ resque-scheduler files, but with additional features.
|
|
36
36
|
## Schedule configuration
|
37
37
|
|
38
38
|
The schedule file should be placed here: `config/que_schedule.yml`. Alternatively if you
|
39
|
-
wish to generate the configuration dynamically, you can set it directly
|
40
|
-
|
39
|
+
wish to generate the configuration dynamically, you can set it directly using an initializer
|
40
|
+
(see "Gem configuration" below).
|
41
41
|
|
42
42
|
The file is a list of que job classes with arguments and a schedule frequency (in crontab
|
43
43
|
syntax). The format is similar to the resque-scheduler format, though priorities must be supplied as
|
@@ -135,19 +135,42 @@ A job can have a `schedule_type` assigned to it. Valid values are:
|
|
135
135
|
|
136
136
|
## Gem configuration
|
137
137
|
|
138
|
-
You can configure some aspects of the gem with
|
138
|
+
You can configure some aspects of the gem with a config block (eg in a Rails initializer).
|
139
|
+
The default is given below. You can omit any configuration sections you are not intending to change.
|
140
|
+
It is quite likely you won't have to create this config at all.
|
139
141
|
|
140
142
|
```ruby
|
141
143
|
Que::Scheduler.configure do |config|
|
142
144
|
# The location of the schedule yaml file.
|
143
|
-
config.schedule_location = ENV.fetch(
|
145
|
+
config.schedule_location = ENV.fetch("QUE_SCHEDULER_CONFIG_LOCATION", "config/que_schedule.yml")
|
146
|
+
|
147
|
+
# The schedule as a hash. You can use this if you want to build the schedule yourself at runtime.
|
148
|
+
# This will override the above value if provided.
|
149
|
+
config.schedule = {
|
150
|
+
SpecifiedByHashTestJob: {
|
151
|
+
cron: "02 11 * * *"
|
152
|
+
}
|
153
|
+
}
|
144
154
|
|
145
|
-
#
|
146
|
-
# However
|
155
|
+
# The transaction block adapter. By default, que-scheduler uses the one supplied by que.
|
156
|
+
# However if, for example, you rely on listeners to ActiveRecord's exact `transaction` method, or
|
147
157
|
# Sequel's DB.after_commit helper, then you can supply it here.
|
148
158
|
config.transaction_adapter = ::Que.method(:transaction)
|
149
|
-
end
|
150
159
|
|
160
|
+
# Which queue name the que-scheduler job should self-schedule on. Typically this is the default
|
161
|
+
# queue of que, which has a different name in Que 0.x ("") and 1.x ("default").
|
162
|
+
# It *must* be the "highest throughput" queue - do not work the scheduler on a "long
|
163
|
+
# running jobs" queue. It is very unlikely you will want to change this.
|
164
|
+
config.que_scheduler_queue = ENV.fetch("QUE_SCHEDULER_QUEUE", "" or "default")
|
165
|
+
|
166
|
+
# If que-scheduler is being used with Rails, then it will inherit the time zone from that
|
167
|
+
# framework, and you can leave the value as nil as shown below. However, if you are not using
|
168
|
+
# Rails, you may need to set the time zone here. If que-scheduler cannot determine the time zone
|
169
|
+
# it will yield an error prompting you for action.
|
170
|
+
# If you need to set a value, use the string representation:
|
171
|
+
# eg: config.time_zone = "Europe/London"
|
172
|
+
config.time_zone = nil
|
173
|
+
end
|
151
174
|
```
|
152
175
|
|
153
176
|
## Scheduler Audit
|
@@ -159,9 +182,29 @@ migration tasks.
|
|
159
182
|
Additionally, there is the audit table `que_scheduler_audit_enqueued`. This logs every job that
|
160
183
|
the scheduler enqueues.
|
161
184
|
|
185
|
+
que-scheduler comes with the `QueSchedulerAuditClearDownJob` job built in that you can optionally
|
186
|
+
schedule to clear down audit rows if you don't need to retain them indefinitely. You should add this
|
187
|
+
to your own scheduler config yaml.
|
188
|
+
|
189
|
+
For example:
|
190
|
+
|
191
|
+
```yaml
|
192
|
+
# This will clear down the oldest que-scheduler audit rows. Since que-scheduler
|
193
|
+
# runs approximately every minute, 129600 is 90 days.
|
194
|
+
Que::Scheduler::Jobs::QueSchedulerAuditClearDownJob:
|
195
|
+
cron: "0 0 * * *"
|
196
|
+
args:
|
197
|
+
retain_row_count: 129600
|
198
|
+
```
|
199
|
+
|
200
|
+
## Required migrations
|
201
|
+
|
162
202
|
When there is a major version (breaking) change, a migration should be run in. The version of the
|
163
|
-
migration proceeds at a faster rate than the version of the gem.
|
164
|
-
|
203
|
+
latest migration proceeds at a faster rate than the version of the gem. eg If the gem is on version
|
204
|
+
3 then the migrations may be on version 6).
|
205
|
+
|
206
|
+
To run in all the migrations required up to a number, just migrate to that number with one line, and
|
207
|
+
it will perform all the intermediary steps.
|
165
208
|
|
166
209
|
ie, This will perform all migrations necessary up to the latest version, skipping any already
|
167
210
|
performed.
|
@@ -169,7 +212,7 @@ performed.
|
|
169
212
|
```ruby
|
170
213
|
class CreateQueSchedulerSchema < ActiveRecord::Migration
|
171
214
|
def change
|
172
|
-
Que::Scheduler::Migrations.migrate!(version:
|
215
|
+
Que::Scheduler::Migrations.migrate!(version: 6)
|
173
216
|
end
|
174
217
|
end
|
175
218
|
```
|
@@ -183,22 +226,15 @@ The changes in past migrations were:
|
|
183
226
|
| 3 | Added the audit table `que_scheduler_audit_enqueued`. |
|
184
227
|
| 4 | Updated the the audit tables to use bigints |
|
185
228
|
| 5 | Dropped an unnecessary index |
|
229
|
+
| 6 | Enforced single scheduler job at the trigger level |
|
186
230
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
to your own scheduler config yaml.
|
231
|
+
The changes to the DB ([DDL](https://en.wikipedia.org/wiki/Data_definition_language)) are all
|
232
|
+
captured in the structure.sql so will be re-run in correctly if squashed - except for the actual
|
233
|
+
scheduling of the job itself (as that is [DML](https://en.wikipedia.org/wiki/Data_manipulation_language)).
|
234
|
+
If you squash your migrations make sure this is added as the final line:
|
192
235
|
|
193
|
-
|
194
|
-
|
195
|
-
```yaml
|
196
|
-
# This will clear down the oldest que-scheduler audit rows. Since que-scheduler
|
197
|
-
# runs approximately every minute, 129600 is 90 days.
|
198
|
-
Que::Scheduler::Jobs::QueSchedulerAuditClearDownJob:
|
199
|
-
cron: "0 0 * * *"
|
200
|
-
args:
|
201
|
-
retain_row_count: 129600
|
236
|
+
```ruby
|
237
|
+
Que::Scheduler::Migrations.reenqueue_scheduler_if_missing
|
202
238
|
```
|
203
239
|
|
204
240
|
## HA Redundancy and DB restores
|
@@ -214,8 +250,8 @@ in a coherent state with the rest of your database.
|
|
214
250
|
## Concurrent scheduler detection
|
215
251
|
|
216
252
|
No matter how many tasks you have defined in your schedule, you will only ever need one que-scheduler
|
217
|
-
job enqueued. que-scheduler knows this, and
|
218
|
-
|
253
|
+
job enqueued. que-scheduler knows this, and there are DB constraints in place to ensure there is
|
254
|
+
only ever exactly one scheduler job.
|
219
255
|
|
220
256
|
It also follows que job design [best practices](https://github.com/chanks/que/blob/master/docs/writing_reliable_jobs.md),
|
221
257
|
using ACID guarantees, to ensure that it will never run multiple times. If the scheduler crashes for any reason,
|
@@ -267,9 +303,13 @@ The scheduler will then continue to retry indefinitely.
|
|
267
303
|
que-scheduler uses [semantic versioning](https://semver.org/), so major version changes will usually
|
268
304
|
require additional actions to be taken upgrading from one major version to another.
|
269
305
|
|
306
|
+
## Changelog
|
307
|
+
|
308
|
+
A full changelog can be found here: [CHANGELOG.md](https://github.com/hlascelles/que-scheduler/blob/master/CHANGELOG.md)
|
309
|
+
|
270
310
|
## System requirements
|
271
311
|
|
272
|
-
Your [postgres](https://www.postgresql.org/) database must be at least version 9.
|
312
|
+
Your [postgres](https://www.postgresql.org/) database must be at least version 9.5.0.
|
273
313
|
|
274
314
|
## Inspiration
|
275
315
|
|
@@ -277,9 +317,10 @@ This gem was inspired by the makers of the excellent [Que](https://github.com/ch
|
|
277
317
|
|
278
318
|
## Contributors
|
279
319
|
|
320
|
+
* @bnauta
|
321
|
+
* @bjeanes
|
322
|
+
* @JackDanger
|
280
323
|
* @jish
|
281
324
|
* @joehorsnell
|
282
|
-
* @bnauta
|
283
|
-
* @papodaca
|
284
325
|
* @krzyzak
|
285
|
-
* @
|
326
|
+
* @papodaca
|
data/lib/que/scheduler.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "que/scheduler/version"
|
2
2
|
require "que/scheduler/version_support"
|
3
|
-
require "que/scheduler/
|
3
|
+
require "que/scheduler/configuration"
|
4
|
+
require "que/scheduler/time_zone"
|
4
5
|
require "que/scheduler/scheduler_job"
|
5
6
|
require "que/scheduler/db"
|
6
7
|
require "que/scheduler/audit"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "que"
|
2
|
+
require_relative "version_support"
|
3
|
+
|
4
|
+
module Que
|
5
|
+
module Scheduler
|
6
|
+
class Configuration
|
7
|
+
attr_accessor :schedule_location, :schedule, :transaction_adapter, :que_scheduler_queue,
|
8
|
+
:time_zone
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :configuration
|
13
|
+
|
14
|
+
def configure
|
15
|
+
self.configuration ||= Configuration.new
|
16
|
+
yield(configuration)
|
17
|
+
end
|
18
|
+
|
19
|
+
def apply_defaults
|
20
|
+
configure do |config|
|
21
|
+
config.schedule_location =
|
22
|
+
ENV.fetch("QUE_SCHEDULER_CONFIG_LOCATION", "config/que_schedule.yml")
|
23
|
+
config.transaction_adapter = ::Que.method(:transaction)
|
24
|
+
config.que_scheduler_queue =
|
25
|
+
ENV.fetch("QUE_SCHEDULER_QUEUE", Que::Scheduler::VersionSupport.default_scheduler_queue)
|
26
|
+
config.schedule = nil
|
27
|
+
config.time_zone = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Que::Scheduler.apply_defaults
|
data/lib/que/scheduler/db.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "configuration"
|
4
4
|
|
5
5
|
module Que
|
6
6
|
module Scheduler
|
@@ -18,9 +18,11 @@ module Que
|
|
18
18
|
Que::Scheduler::VersionSupport.execute(NOW_SQL).first.values.first
|
19
19
|
end
|
20
20
|
|
21
|
+
# rubocop:disable Style/ExplicitBlockArgument
|
21
22
|
def transaction
|
22
23
|
Que::Scheduler.configuration.transaction_adapter.call { yield }
|
23
24
|
end
|
25
|
+
# rubocop:enable Style/ExplicitBlockArgument
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -86,10 +86,10 @@ module Que
|
|
86
86
|
# queue name is only supported for a subrange of ActiveJob versions. Print this out as a
|
87
87
|
# warning.
|
88
88
|
if queue &&
|
89
|
-
Que::Scheduler::ToEnqueue.
|
89
|
+
Que::Scheduler::ToEnqueue.active_job_loaded? &&
|
90
90
|
job_class < ::ActiveJob::Base &&
|
91
91
|
Que::Scheduler::ToEnqueue.active_job_version < Gem::Version.create("6.0.3")
|
92
|
-
puts
|
92
|
+
puts <<~ERR
|
93
93
|
WARNING from que-scheduler....
|
94
94
|
Between versions 4.2.3 and 6.0.2 (inclusive) Rails did not support setting queue names
|
95
95
|
on que jobs with ActiveJob, so que-scheduler cannot support it.
|
@@ -8,7 +8,7 @@ module Que
|
|
8
8
|
class QueSchedulerAuditClearDownJob < Que::Job
|
9
9
|
class << self
|
10
10
|
def build_sql(table_name)
|
11
|
-
|
11
|
+
<<~SQL
|
12
12
|
WITH deleted AS (
|
13
13
|
DELETE FROM #{table_name}
|
14
14
|
WHERE scheduler_job_id <= (
|
@@ -28,7 +28,7 @@ module Que
|
|
28
28
|
Que::Scheduler::VersionSupport.set_priority(self, 100)
|
29
29
|
|
30
30
|
def run(options)
|
31
|
-
retain_row_count = options.fetch(:retain_row_count)
|
31
|
+
retain_row_count = options.symbolize_keys.fetch(:retain_row_count)
|
32
32
|
Que::Scheduler::Db.transaction do
|
33
33
|
# This may delete zero or more than `retain_row_count` depending on if anything was
|
34
34
|
# scheduled in each of the past schedule runs
|
@@ -41,6 +41,11 @@ module Que
|
|
41
41
|
result.any?
|
42
42
|
end
|
43
43
|
|
44
|
+
# This method is only intended for use in squashed migrations
|
45
|
+
def reenqueue_scheduler_if_missing
|
46
|
+
Que::Scheduler::SchedulerJob.enqueue if Que::Scheduler::Db.count_schedulers.zero?
|
47
|
+
end
|
48
|
+
|
44
49
|
private
|
45
50
|
|
46
51
|
def migrate_up(current, version)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
-- Ensure there is no more than one scheduler
|
2
|
+
CREATE UNIQUE INDEX que_scheduler_job_in_que_jobs_unique_index ON que_jobs(job_class)
|
3
|
+
WHERE job_class = 'Que::Scheduler::SchedulerJob';
|
4
|
+
|
5
|
+
-- Ensure there is at least one scheduler
|
6
|
+
CREATE OR REPLACE FUNCTION que_scheduler_check_job_exists() RETURNS bool AS $$
|
7
|
+
SELECT EXISTS(SELECT * FROM que_jobs WHERE job_class = 'Que::Scheduler::SchedulerJob');
|
8
|
+
$$ LANGUAGE SQL;
|
9
|
+
|
10
|
+
CREATE OR REPLACE FUNCTION que_scheduler_prevent_job_deletion() RETURNS TRIGGER AS
|
11
|
+
$BODY$
|
12
|
+
DECLARE
|
13
|
+
BEGIN
|
14
|
+
IF OLD.job_class = 'Que::Scheduler::SchedulerJob' THEN
|
15
|
+
IF NOT que_scheduler_check_job_exists() THEN
|
16
|
+
raise exception 'Deletion of que_scheduler job % prevented. Deleting the que_scheduler job is almost certainly a mistake.', OLD.job_id;
|
17
|
+
END IF;
|
18
|
+
END IF;
|
19
|
+
RETURN OLD;
|
20
|
+
END;
|
21
|
+
$BODY$
|
22
|
+
LANGUAGE 'plpgsql';
|
23
|
+
|
24
|
+
CREATE CONSTRAINT TRIGGER que_scheduler_prevent_job_deletion_trigger AFTER UPDATE OR DELETE ON que_jobs
|
25
|
+
DEFERRABLE INITIALLY DEFERRED
|
26
|
+
FOR EACH ROW EXECUTE PROCEDURE que_scheduler_prevent_job_deletion();
|
@@ -1,21 +1,31 @@
|
|
1
|
+
require_relative "configuration"
|
1
2
|
require_relative "defined_job"
|
2
3
|
|
3
4
|
module Que
|
4
5
|
module Scheduler
|
5
6
|
class Schedule
|
6
7
|
class << self
|
8
|
+
# The main method for determining the schedule. It has to evaluate the schedule as late as
|
9
|
+
# possible (ie just as it is about to be used) as we cannot guarantee we are in a Rails
|
10
|
+
# app with initializers. In a future release this may change to "fast fail" in Rails by
|
11
|
+
# checking the config up front.
|
7
12
|
def schedule
|
8
13
|
@schedule ||=
|
9
14
|
begin
|
10
|
-
|
11
|
-
|
15
|
+
configuration = Que::Scheduler.configuration
|
16
|
+
if !configuration.schedule.nil?
|
17
|
+
# If an explicit schedule as a hash has been defined, use that.
|
18
|
+
from_hash(configuration.schedule)
|
19
|
+
elsif File.exist?(configuration.schedule_location)
|
20
|
+
# If the schedule is defined as a file location, then load it and return it.
|
21
|
+
from_file(configuration.schedule_location)
|
22
|
+
else
|
23
|
+
raise "No que-scheduler config set, or file found " \
|
24
|
+
"at #{configuration.schedule_location}"
|
25
|
+
end
|
12
26
|
end
|
13
27
|
end
|
14
28
|
|
15
|
-
def schedule=(schedule_config)
|
16
|
-
@schedule = schedule_config.nil? ? nil : from_yaml(schedule_config)
|
17
|
-
end
|
18
|
-
|
19
29
|
def from_file(location)
|
20
30
|
from_yaml(IO.read(location))
|
21
31
|
end
|
@@ -27,20 +37,19 @@ module Que
|
|
27
37
|
|
28
38
|
def from_hash(config_hash)
|
29
39
|
config_hash.map do |name, defined_job_hash|
|
30
|
-
|
40
|
+
name_str = name.to_s
|
41
|
+
[name_str, hash_item_to_defined_job(name_str, defined_job_hash)]
|
31
42
|
end.to_h
|
32
43
|
end
|
33
44
|
|
34
|
-
def hash_item_to_defined_job(name,
|
45
|
+
def hash_item_to_defined_job(name, defined_job_hash_in)
|
46
|
+
defined_job_hash = defined_job_hash_in.stringify_keys
|
35
47
|
# Que stores arguments as a json array. If the args we have to provide are already an
|
36
48
|
# array we can can simply pass them through. If it is a single non-nil value, then we make
|
37
49
|
# an array with one item which is that value (this includes if it is a hash). It could
|
38
50
|
# also be a single nil value.
|
39
51
|
args_array =
|
40
|
-
if
|
41
|
-
# No args were requested
|
42
|
-
[]
|
43
|
-
else
|
52
|
+
if defined_job_hash.key?("args")
|
44
53
|
args = defined_job_hash["args"]
|
45
54
|
if args.is_a?(Array)
|
46
55
|
# An array of args was requested
|
@@ -50,11 +59,14 @@ module Que
|
|
50
59
|
# be enqueued as an array of 1 item
|
51
60
|
[args]
|
52
61
|
end
|
62
|
+
else
|
63
|
+
# No args were requested
|
64
|
+
[]
|
53
65
|
end
|
54
66
|
|
55
67
|
Que::Scheduler::DefinedJob.create(
|
56
68
|
name: name,
|
57
|
-
job_class: defined_job_hash["class"] || name,
|
69
|
+
job_class: defined_job_hash["class"]&.to_s || name,
|
58
70
|
queue: defined_job_hash["queue"],
|
59
71
|
args_array: args_array,
|
60
72
|
priority: defined_job_hash["priority"],
|
@@ -69,10 +81,6 @@ module Que
|
|
69
81
|
def schedule
|
70
82
|
Schedule.schedule
|
71
83
|
end
|
72
|
-
|
73
|
-
def schedule=(value)
|
74
|
-
Schedule.schedule = value
|
75
|
-
end
|
76
84
|
end
|
77
85
|
end
|
78
86
|
end
|
@@ -16,8 +16,8 @@ module Que
|
|
16
16
|
class SchedulerJob < Que::Job
|
17
17
|
SCHEDULER_FREQUENCY = 60
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
VersionSupport.set_priority(self, 0)
|
20
|
+
VersionSupport.apply_retry_semantics(self)
|
21
21
|
|
22
22
|
def run(options = nil)
|
23
23
|
Que::Scheduler::Db.transaction do
|
@@ -27,12 +27,13 @@ module Que
|
|
27
27
|
logs = ["que-scheduler last ran at #{scheduler_job_args.last_run_time}."]
|
28
28
|
result = EnqueueingCalculator.parse(Scheduler.schedule.values, scheduler_job_args)
|
29
29
|
enqueued_jobs = enqueue_required_jobs(result, logs)
|
30
|
+
# Remove this job and schedule self again
|
31
|
+
destroy
|
30
32
|
enqueue_self_again(
|
31
33
|
scheduler_job_args, scheduler_job_args.as_time, result.job_dictionary, enqueued_jobs
|
32
34
|
)
|
33
35
|
# Only now we're sure nothing errored, log the results
|
34
36
|
logs.each { |str| ::Que.log(event: "que-scheduler".to_sym, message: str) }
|
35
|
-
destroy
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -59,7 +60,7 @@ module Que
|
|
59
60
|
|
60
61
|
def enqueue_self_again(scheduler_job_args, last_full_execution, job_dictionary, enqueued_jobs)
|
61
62
|
# Log last run...
|
62
|
-
job_id =
|
63
|
+
job_id = VersionSupport.job_attributes(self).fetch(:job_id)
|
63
64
|
Audit.append(job_id, scheduler_job_args.as_time, enqueued_jobs)
|
64
65
|
|
65
66
|
# And rerun...
|
@@ -72,7 +73,7 @@ module Que
|
|
72
73
|
)
|
73
74
|
|
74
75
|
# rubocop:disable Style/GuardClause This reads better as a conditional
|
75
|
-
unless
|
76
|
+
unless enqueued_job && VersionSupport.job_attributes(enqueued_job).fetch(:job_id)
|
76
77
|
raise "SchedulerJob could not self-schedule. Has `.enqueue` been monkey patched?"
|
77
78
|
end
|
78
79
|
# rubocop:enable Style/GuardClause
|
@@ -22,7 +22,8 @@ module Que
|
|
22
22
|
else
|
23
23
|
options = options.symbolize_keys
|
24
24
|
{
|
25
|
-
last_run_time:
|
25
|
+
last_run_time:
|
26
|
+
Que::Scheduler::TimeZone.time_zone.parse(options.fetch(:last_run_time)),
|
26
27
|
job_dictionary: options.fetch(:job_dictionary),
|
27
28
|
}
|
28
29
|
end
|
@@ -8,7 +8,6 @@ module Que
|
|
8
8
|
class << self
|
9
9
|
def check
|
10
10
|
assert_db_migrated
|
11
|
-
assert_one_scheduler_job
|
12
11
|
end
|
13
12
|
|
14
13
|
private
|
@@ -20,7 +19,7 @@ module Que
|
|
20
19
|
sync_err =
|
21
20
|
if Que::Scheduler::VersionSupport.running_synchronously? && db_version.zero?
|
22
21
|
code = Que::Scheduler::VersionSupport.running_synchronously_code?
|
23
|
-
|
22
|
+
<<~ERR_SYNC
|
24
23
|
You currently have Que to run in synchronous mode using
|
25
24
|
#{code}, so it is most likely this error
|
26
25
|
has happened during an initial migration. You should disable synchronous mode and
|
@@ -60,21 +59,6 @@ module Que
|
|
60
59
|
synchronously. This will fail as que-scheduler needs the above tables to work.
|
61
60
|
ERR
|
62
61
|
end
|
63
|
-
|
64
|
-
def assert_one_scheduler_job
|
65
|
-
schedulers = Que::Scheduler::Db.count_schedulers
|
66
|
-
return if schedulers == 1
|
67
|
-
|
68
|
-
raise(<<-ERR)
|
69
|
-
Only one #{Que::Scheduler::SchedulerJob.name} should be enqueued. #{schedulers} were found.
|
70
|
-
|
71
|
-
que-scheduler works by running a self-enqueueing version of itself that determines which
|
72
|
-
jobs should be enqueued based on the provided config. If two or more que-schedulers were
|
73
|
-
to run at once, then duplicate jobs would occur.
|
74
|
-
|
75
|
-
To resolve this problem, please remove any duplicate scheduler jobs from the que_jobs table.
|
76
|
-
ERR
|
77
|
-
end
|
78
62
|
end
|
79
63
|
end
|
80
64
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Que
|
2
|
+
module Scheduler
|
3
|
+
module TimeZone
|
4
|
+
BOTH_CONFIG_AND_TIME_DOT_ZONE_SET = <<~ERR.freeze
|
5
|
+
The que-scheduler config for time_zone has been set to a non-nil value, but
|
6
|
+
it appears to also have been set on Time.zone (possibly by Rails). Both of these
|
7
|
+
cannot be non-nil.
|
8
|
+
You should remove the time_zone config from the que-scheduler config block.
|
9
|
+
ERR
|
10
|
+
|
11
|
+
TIME_ZONE_COULD_NOT_BE_DETERMINED = <<~ERR.freeze
|
12
|
+
It appears Time.zone is nil. This prevents proper functioning of que-scheduler.
|
13
|
+
|
14
|
+
Resolving this issue depends on your application setup.
|
15
|
+
|
16
|
+
1) If you are using Rails, set the standard time_zone config
|
17
|
+
eg:
|
18
|
+
```
|
19
|
+
# In application.rb
|
20
|
+
config.time_zone = "Europe/London"
|
21
|
+
```
|
22
|
+
|
23
|
+
2) If you are not using Rails, set your time zone in the que-scheduler config:
|
24
|
+
eg:
|
25
|
+
```
|
26
|
+
Que::Scheduler.configure do |config|
|
27
|
+
config.time_zone = "Europe/London"
|
28
|
+
end
|
29
|
+
```
|
30
|
+
ERR
|
31
|
+
|
32
|
+
TIME_ZONE_CONFIG_IS_NOT_VALID = <<~ERR.freeze
|
33
|
+
The que-scheduler config for time_zone has been set to a non-nil value, but that value
|
34
|
+
does not yield a real time zone when passed to ActiveSupport::TimeZone.new
|
35
|
+
ERR
|
36
|
+
|
37
|
+
class << self
|
38
|
+
def time_zone
|
39
|
+
@time_zone ||=
|
40
|
+
begin
|
41
|
+
time_dot_zone = Time.zone
|
42
|
+
if time_dot_zone.present?
|
43
|
+
if Que::Scheduler.configuration.time_zone.present?
|
44
|
+
raise BOTH_CONFIG_AND_TIME_DOT_ZONE_SET
|
45
|
+
end
|
46
|
+
|
47
|
+
time_dot_zone
|
48
|
+
elsif Que::Scheduler.configuration.time_zone
|
49
|
+
new_tz = ActiveSupport::TimeZone.new(Que::Scheduler.configuration.time_zone)
|
50
|
+
raise TIME_ZONE_CONFIG_IS_NOT_VALID unless new_tz
|
51
|
+
|
52
|
+
new_tz
|
53
|
+
else
|
54
|
+
raise TIME_ZONE_COULD_NOT_BE_DETERMINED
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -22,14 +22,12 @@ module Que
|
|
22
22
|
type_from_job_class(job_class).present?
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
|
25
|
+
def active_job_loaded?
|
26
|
+
!!active_job_version
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
30
|
-
|
31
|
-
# https://github.com/rails/rails/pull/20056/files
|
32
|
-
active_job_version && active_job_version > Gem::Version.create("5")
|
29
|
+
def active_job_version
|
30
|
+
Gem.loaded_specs["activejob"]&.version
|
33
31
|
end
|
34
32
|
|
35
33
|
def active_job_version_supports_queues?
|
@@ -56,7 +54,7 @@ module Que
|
|
56
54
|
hash = {
|
57
55
|
::Que::Job => QueJobType,
|
58
56
|
}
|
59
|
-
hash[::ActiveJob::Base] = ActiveJobType if ToEnqueue.
|
57
|
+
hash[::ActiveJob::Base] = ActiveJobType if ToEnqueue.active_job_loaded?
|
60
58
|
hash
|
61
59
|
end
|
62
60
|
end
|
@@ -105,7 +103,13 @@ module Que
|
|
105
103
|
scheduled_at =
|
106
104
|
begin
|
107
105
|
scheduled_at_float = data[:scheduled_at]
|
108
|
-
|
106
|
+
# rubocop:disable Style/EmptyElse
|
107
|
+
if scheduled_at_float
|
108
|
+
Que::Scheduler::TimeZone.time_zone.at(scheduled_at_float)
|
109
|
+
else
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
# rubocop:enable Style/EmptyElse
|
109
113
|
end
|
110
114
|
|
111
115
|
# Rails didn't support queues for ActiveJob for a while
|
@@ -61,7 +61,11 @@ module Que
|
|
61
61
|
|
62
62
|
def zero_major?
|
63
63
|
# This is the only way to handle beta releases too
|
64
|
-
@zero_major ||=
|
64
|
+
@zero_major ||= que_version.split(".").first.to_i.zero?
|
65
|
+
end
|
66
|
+
|
67
|
+
def que_version
|
68
|
+
@que_version ||= Gem.loaded_specs["que"].version.to_s
|
65
69
|
end
|
66
70
|
|
67
71
|
private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: que-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harry Lascelles
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: fugit
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,14 +90,14 @@ dependencies:
|
|
90
90
|
requirements:
|
91
91
|
- - ">="
|
92
92
|
- !ruby/object:Gem::Version
|
93
|
-
version: '
|
93
|
+
version: '5.0'
|
94
94
|
type: :development
|
95
95
|
prerelease: false
|
96
96
|
version_requirements: !ruby/object:Gem::Requirement
|
97
97
|
requirements:
|
98
98
|
- - ">="
|
99
99
|
- !ruby/object:Gem::Version
|
100
|
-
version: '
|
100
|
+
version: '5.0'
|
101
101
|
- !ruby/object:Gem::Dependency
|
102
102
|
name: appraisal
|
103
103
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,6 +112,20 @@ dependencies:
|
|
112
112
|
- - ">="
|
113
113
|
- !ruby/object:Gem::Version
|
114
114
|
version: '0'
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: climate_control
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
type: :development
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
115
129
|
- !ruby/object:Gem::Dependency
|
116
130
|
name: combustion
|
117
131
|
requirement: !ruby/object:Gem::Requirement
|
@@ -244,14 +258,14 @@ dependencies:
|
|
244
258
|
requirements:
|
245
259
|
- - '='
|
246
260
|
- !ruby/object:Gem::Version
|
247
|
-
version:
|
261
|
+
version: 1.5.2
|
248
262
|
type: :development
|
249
263
|
prerelease: false
|
250
264
|
version_requirements: !ruby/object:Gem::Requirement
|
251
265
|
requirements:
|
252
266
|
- - '='
|
253
267
|
- !ruby/object:Gem::Version
|
254
|
-
version:
|
268
|
+
version: 1.5.2
|
255
269
|
- !ruby/object:Gem::Dependency
|
256
270
|
name: rubocop-rspec
|
257
271
|
requirement: !ruby/object:Gem::Requirement
|
@@ -319,7 +333,7 @@ files:
|
|
319
333
|
- lib/que-scheduler.rb
|
320
334
|
- lib/que/scheduler.rb
|
321
335
|
- lib/que/scheduler/audit.rb
|
322
|
-
- lib/que/scheduler/
|
336
|
+
- lib/que/scheduler/configuration.rb
|
323
337
|
- lib/que/scheduler/db.rb
|
324
338
|
- lib/que/scheduler/defined_job.rb
|
325
339
|
- lib/que/scheduler/enqueueing_calculator.rb
|
@@ -335,10 +349,13 @@ files:
|
|
335
349
|
- lib/que/scheduler/migrations/4/up.sql
|
336
350
|
- lib/que/scheduler/migrations/5/down.sql
|
337
351
|
- lib/que/scheduler/migrations/5/up.sql
|
352
|
+
- lib/que/scheduler/migrations/6/down.sql
|
353
|
+
- lib/que/scheduler/migrations/6/up.sql
|
338
354
|
- lib/que/scheduler/schedule.rb
|
339
355
|
- lib/que/scheduler/scheduler_job.rb
|
340
356
|
- lib/que/scheduler/scheduler_job_args.rb
|
341
357
|
- lib/que/scheduler/state_checks.rb
|
358
|
+
- lib/que/scheduler/time_zone.rb
|
342
359
|
- lib/que/scheduler/to_enqueue.rb
|
343
360
|
- lib/que/scheduler/version.rb
|
344
361
|
- lib/que/scheduler/version_support.rb
|
@@ -359,7 +376,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
359
376
|
requirements:
|
360
377
|
- - ">="
|
361
378
|
- !ruby/object:Gem::Version
|
362
|
-
version: '
|
379
|
+
version: '2.5'
|
363
380
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
364
381
|
requirements:
|
365
382
|
- - ">="
|
data/lib/que/scheduler/config.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require "que"
|
2
|
-
require_relative "version_support"
|
3
|
-
|
4
|
-
module Que
|
5
|
-
module Scheduler
|
6
|
-
class << self
|
7
|
-
attr_accessor :configuration
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.configure
|
11
|
-
self.configuration ||= Configuration.new
|
12
|
-
yield(configuration)
|
13
|
-
end
|
14
|
-
|
15
|
-
class Configuration
|
16
|
-
attr_accessor :schedule_location
|
17
|
-
attr_accessor :transaction_adapter
|
18
|
-
attr_accessor :que_scheduler_queue
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
Que::Scheduler.configure do |config|
|
24
|
-
config.schedule_location = ENV.fetch("QUE_SCHEDULER_CONFIG_LOCATION", "config/que_schedule.yml")
|
25
|
-
config.transaction_adapter = ::Que.method(:transaction)
|
26
|
-
config.que_scheduler_queue = Que::Scheduler::VersionSupport.default_scheduler_queue
|
27
|
-
end
|