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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ba02202915db229bcbd1c2e859f29299803507dbf61bd460008400abca3611c
4
- data.tar.gz: 6293d7b6e6766d0e75fe65df01850f356b44b030c8b8ae539eb4c71059d9d15d
3
+ metadata.gz: a75db11d923c140204bd92360916e4cacaac5531fc25470616a4d558dde22dd3
4
+ data.tar.gz: ba08caa1a8bf8a87532da3fa5cbfc8b720784e88354ab74b240c5d724c46fb07
5
5
  SHA512:
6
- metadata.gz: d8d18550867d7d54cfb5c03aaa68876d423876a870b972cde0fbc4e8399fb616d327ef0410ebbc8e3d9021a34d73a960774eabb808f2b33a526e11ea2fb579fe
7
- data.tar.gz: bfc0e4be1f64d2b070dacb0863cc710ce13cf58cb30924fcf9f301d2e535be4ef9530740949bdb5abb90c0173924e71f7f151e4622405caf8c14a9d7d6f0469d
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: 5)
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 with
40
- `Que::Scheduler.schedule = some_hash`.
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 an initializer. The default is given below.
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('QUE_SCHEDULER_CONFIG_LOCATION', 'config/que_schedule.yml')
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
- # Specify a transaction block adapter. By default, que-scheduler uses the one supplied by que.
146
- # However, if, for example you rely on listeners to ActiveRecord's exact `transaction` method, or
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. To run in all the migrations required
164
- up to a number, just migrate to that number with one line, and it will perform all the intermediary steps.
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: 5)
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
- ## Built in optional job for audit clear down
188
-
189
- que-scheduler comes with the `QueSchedulerAuditClearDownJob` job built in that you can optionally
190
- schedule to clear down audit rows if you don't need to retain them indefinitely. You should add this
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
- For example:
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 it will check before performing any operations that
218
- there is only one of itself present.
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.4.0.
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
- * @JackDanger
326
+ * @papodaca
@@ -1,6 +1,7 @@
1
1
  require "que/scheduler/version"
2
2
  require "que/scheduler/version_support"
3
- require "que/scheduler/config"
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "config"
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.active_job_sufficient_version? &&
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 <<-ERR
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
- <<-SQL
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,7 @@
1
+ DROP TRIGGER que_scheduler_prevent_job_deletion_trigger ON que_jobs;
2
+
3
+ DROP FUNCTION que_scheduler_prevent_job_deletion();
4
+
5
+ DROP FUNCTION que_scheduler_check_job_exists();
6
+
7
+ DROP INDEX que_scheduler_job_in_que_jobs_unique_index;
@@ -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
- location = Que::Scheduler.configuration.schedule_location
11
- from_file(location)
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
- [name, hash_item_to_defined_job(name, defined_job_hash)]
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, defined_job_hash)
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 !defined_job_hash.key?("args")
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
- Que::Scheduler::VersionSupport.set_priority(self, 0)
20
- Que::Scheduler::VersionSupport.apply_retry_semantics(self)
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 = Que::Scheduler::VersionSupport.job_attributes(self).fetch(: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 Que::Scheduler::VersionSupport.job_attributes(enqueued_job).fetch(:job_id)
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: Time.zone.parse(options.fetch(: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
- <<-ERR_SYNC
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 active_job_version
26
- Gem.loaded_specs["activejob"]&.version
25
+ def active_job_loaded?
26
+ !!active_job_version
27
27
  end
28
28
 
29
- def active_job_sufficient_version?
30
- # ActiveJob 4.x does not support job_ids correctly
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.active_job_sufficient_version?
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
- scheduled_at_float ? Time.zone.at(scheduled_at_float) : nil
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
@@ -1,5 +1,5 @@
1
1
  module Que
2
2
  module Scheduler
3
- VERSION = "3.4.3".freeze
3
+ VERSION = "4.1.0".freeze
4
4
  end
5
5
  end
@@ -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 ||= Gem.loaded_specs["que"].version.to_s.split(".").first.to_i.zero?
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: 3.4.3
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: 2020-10-28 00:00:00.000000000 Z
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: '4.0'
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: '4.0'
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: '4.0'
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: '4.0'
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: 0.84.0
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: 0.84.0
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/config.rb
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: '0'
379
+ version: '2.5'
363
380
  required_rubygems_version: !ruby/object:Gem::Requirement
364
381
  requirements:
365
382
  - - ">="
@@ -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