que-scheduler 7.1.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/que/scheduler/audit.rb +12 -0
- data/lib/que/scheduler/configuration.rb +29 -6
- data/lib/que/scheduler/db.rb +20 -6
- data/lib/que/scheduler/db_support.rb +28 -10
- data/lib/que/scheduler/defined_job.rb +20 -24
- data/lib/que/scheduler/enqueueing_calculator.rb +11 -0
- data/lib/que/scheduler/jobs/que_scheduler_audit_clear_down_job.rb +10 -1
- data/lib/que/scheduler/migrations.rb +14 -1
- data/lib/que/scheduler/schedule.rb +22 -2
- data/lib/que/scheduler/scheduler_job.rb +29 -1
- data/lib/que/scheduler/scheduler_job_args.rb +6 -0
- data/lib/que/scheduler/state_checks.rb +7 -0
- data/lib/que/scheduler/time_zone.rb +7 -0
- data/lib/que/scheduler/to_enqueue.rb +39 -36
- data/lib/que/scheduler/types.rb +18 -0
- data/lib/que/scheduler/version.rb +3 -1
- data/lib/que/scheduler.rb +2 -0
- data/lib/que-scheduler.rb +2 -0
- metadata +21 -51
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77e19a08d2c15c1f6c40f0c3b924f5f72316fcf9827e3f99bcfff7a0df759b71
|
|
4
|
+
data.tar.gz: 7be4dbc6187cbbe530540bc64e2f4051f12cb0fc23ca4f69275dfd70d966e828
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be271ad6ca3d25ddf106352a44933cb1400381c006d1e7c15b1467093adec05f78e57beaf2eb51cba197ccf30b800561d74eb27d7dee367eb9af2106197c129c
|
|
7
|
+
data.tar.gz: abedfcada9b8fc855542844b2c5325a3d518523176d4070e8e912036934d6118d65e7627cf0c29cada59ca68c3f35eb9705872525a4d349ff7cdd460ba343084
|
data/README.md
CHANGED
|
@@ -5,7 +5,6 @@ que-scheduler
|
|
|
5
5
|
[](https://github.com/hlascelles/que-scheduler/actions)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://coveralls.io/github/hlascelles/que-scheduler?branch=master)
|
|
8
|
-
[](https://codeclimate.com/github/hlascelles/que-scheduler/maintainability)
|
|
9
8
|
|
|
10
9
|
## Description
|
|
11
10
|
|
|
@@ -317,9 +316,10 @@ A full changelog can be found here: [CHANGELOG.md](https://github.com/hlascelles
|
|
|
317
316
|
|
|
318
317
|
## System requirements
|
|
319
318
|
|
|
320
|
-
Your [postgres](https://www.postgresql.org/) database must be at least version
|
|
319
|
+
Your [postgres](https://www.postgresql.org/) database must be at least version 14. que-scheduler is tested against version 17.
|
|
321
320
|
|
|
322
|
-
The latest version of que-scheduler supports Ruby
|
|
321
|
+
The latest version of que-scheduler supports Ruby 4.0 and above, and Rails 8.0 and above.
|
|
322
|
+
que-scheduler versions below 8.0.0 work with Ruby 3.1 and Rails 6.0.
|
|
323
323
|
que-scheduler versions below 4.4.0 work with Ruby 2.7.
|
|
324
324
|
que-scheduler versions below 4.2.3 work with Ruby 2.5 and Ruby 2.6.
|
|
325
325
|
|
data/lib/que/scheduler/audit.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
# typed: true
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
4
|
+
require "sorbet-runtime"
|
|
3
5
|
require_relative "to_enqueue"
|
|
6
|
+
require_relative "types"
|
|
4
7
|
|
|
5
8
|
module Que
|
|
6
9
|
module Scheduler
|
|
@@ -23,6 +26,15 @@ module Que
|
|
|
23
26
|
}.freeze
|
|
24
27
|
|
|
25
28
|
class << self
|
|
29
|
+
extend T::Sig
|
|
30
|
+
|
|
31
|
+
sig do
|
|
32
|
+
params(
|
|
33
|
+
scheduler_job_id: Integer,
|
|
34
|
+
executed_at: Types::TIME,
|
|
35
|
+
enqueued_jobs: T::Array[Que::Scheduler::EnqueuedJobType]
|
|
36
|
+
).void
|
|
37
|
+
end
|
|
26
38
|
def append(scheduler_job_id, executed_at, enqueued_jobs)
|
|
27
39
|
::Que::Scheduler::DbSupport.execute(INSERT_AUDIT, [scheduler_job_id, executed_at])
|
|
28
40
|
enqueued_jobs.each do |j|
|
|
@@ -1,23 +1,46 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "que"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
require_relative "db_support"
|
|
6
|
+
require_relative "types"
|
|
3
7
|
|
|
4
8
|
module Que
|
|
5
9
|
module Scheduler
|
|
6
10
|
# :reek:Attribute
|
|
7
11
|
class Configuration
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
sig { returns(T.any(String, Pathname)) }
|
|
15
|
+
attr_accessor :schedule_location
|
|
16
|
+
|
|
17
|
+
sig { returns(T.nilable(Types::SCHEDULE_CONFIG)) }
|
|
18
|
+
attr_accessor :schedule
|
|
19
|
+
|
|
20
|
+
sig { returns(T.any(Method, Proc)) }
|
|
21
|
+
attr_accessor :transaction_adapter
|
|
22
|
+
|
|
23
|
+
sig { returns(String) }
|
|
24
|
+
attr_accessor :que_scheduler_queue
|
|
25
|
+
|
|
26
|
+
sig { returns(T.nilable(String)) }
|
|
27
|
+
attr_accessor :time_zone
|
|
10
28
|
end
|
|
11
29
|
|
|
12
30
|
class << self
|
|
13
|
-
|
|
14
|
-
|
|
31
|
+
extend T::Sig
|
|
32
|
+
|
|
33
|
+
sig { returns(Configuration) }
|
|
34
|
+
def configuration
|
|
35
|
+
@configuration ||= Configuration.new
|
|
36
|
+
end
|
|
15
37
|
|
|
16
|
-
|
|
17
|
-
|
|
38
|
+
sig { params(blk: T.proc.params(config: Configuration).void).void }
|
|
39
|
+
def configure(&blk)
|
|
18
40
|
yield(configuration)
|
|
19
41
|
end
|
|
20
42
|
|
|
43
|
+
sig { void }
|
|
21
44
|
def apply_defaults
|
|
22
45
|
configure do |config|
|
|
23
46
|
config.schedule_location =
|
data/lib/que/scheduler/db.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
# typed: true
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
4
|
+
require "sorbet-runtime"
|
|
3
5
|
require_relative "configuration"
|
|
4
6
|
|
|
5
7
|
module Que
|
|
@@ -10,19 +12,31 @@ module Que
|
|
|
10
12
|
NOW_SQL = "SELECT now()"
|
|
11
13
|
|
|
12
14
|
class << self
|
|
15
|
+
extend T::Sig
|
|
16
|
+
|
|
17
|
+
sig { returns(Integer) }
|
|
13
18
|
def count_schedulers
|
|
14
|
-
|
|
19
|
+
T.cast(first_value(SCHEDULER_COUNT_SQL), Integer)
|
|
15
20
|
end
|
|
16
21
|
|
|
22
|
+
sig { returns(Time) }
|
|
17
23
|
def now
|
|
18
|
-
|
|
24
|
+
T.cast(first_value(NOW_SQL), Time)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { params(sql: String).returns(T.anything) }
|
|
28
|
+
private def first_value(sql)
|
|
29
|
+
Que::Scheduler::DbSupport.execute(sql).fetch(0).values.fetch(0)
|
|
19
30
|
end
|
|
20
31
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
sig do
|
|
33
|
+
type_parameters(:U)
|
|
34
|
+
.params(blk: T.proc.returns(T.type_parameter(:U)))
|
|
35
|
+
.returns(T.type_parameter(:U))
|
|
36
|
+
end
|
|
37
|
+
def transaction(&blk)
|
|
38
|
+
Que::Scheduler.configuration.transaction_adapter.call(&blk)
|
|
24
39
|
end
|
|
25
|
-
# rubocop:enable Style/ExplicitBlockArgument
|
|
26
40
|
end
|
|
27
41
|
end
|
|
28
42
|
end
|
|
@@ -1,39 +1,57 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "que"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
|
|
3
6
|
module Que
|
|
4
7
|
module Scheduler
|
|
5
8
|
module DbSupport
|
|
6
9
|
class << self
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig { params(enqueued_job: Que::Job).returns(T::Hash[Symbol, T.anything]) }
|
|
7
13
|
def job_attributes(enqueued_job)
|
|
8
14
|
enqueued_job.que_attrs.to_h.transform_keys(&:to_sym).tap do |hash|
|
|
9
15
|
hash[:job_id] = hash.delete(:id)
|
|
10
16
|
end
|
|
11
17
|
end
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
sig { params(enqueued_job: Que::Job).returns(Integer) }
|
|
20
|
+
def job_id(enqueued_job) = T.cast(job_attributes(enqueued_job).fetch(:job_id), Integer)
|
|
21
|
+
|
|
22
|
+
sig do
|
|
23
|
+
params(str: String, args: T::Array[T.anything])
|
|
24
|
+
.returns(T::Array[T::Hash[Symbol, T.anything]])
|
|
25
|
+
end
|
|
15
26
|
def execute(str, args = [])
|
|
16
|
-
|
|
27
|
+
T.cast(Que.execute(str, args), T::Array[T::Hash[Symbol, T.anything]])
|
|
17
28
|
end
|
|
18
29
|
|
|
30
|
+
sig do
|
|
31
|
+
params(
|
|
32
|
+
clazz: T.class_of(Que::Job),
|
|
33
|
+
job_options: T::Hash[Symbol, T.anything],
|
|
34
|
+
job_args: T.anything
|
|
35
|
+
).returns(T.nilable(T.any(Que::Job, FalseClass)))
|
|
36
|
+
end
|
|
19
37
|
def enqueue_a_job(clazz, job_options = {}, job_args = [])
|
|
20
|
-
|
|
38
|
+
case job_args
|
|
39
|
+
when Hash
|
|
21
40
|
clazz.enqueue(job_args, job_options: job_options)
|
|
22
41
|
else
|
|
23
|
-
|
|
42
|
+
# Sorbet cannot type a splat of a value of unknown size
|
|
43
|
+
T.unsafe(clazz).enqueue(*job_args, job_options: job_options)
|
|
24
44
|
end
|
|
25
45
|
end
|
|
26
46
|
|
|
47
|
+
sig { returns(String) }
|
|
27
48
|
def que_version
|
|
28
49
|
@que_version ||= que_version_object.to_s
|
|
29
50
|
end
|
|
30
51
|
|
|
52
|
+
sig { returns(Gem::Version) }
|
|
31
53
|
private def que_version_object
|
|
32
|
-
@que_version_object ||= Gem.loaded_specs["que"].version
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private def normalise_array_of_hashes(array)
|
|
36
|
-
array.map { |row| row.to_h.transform_keys(&:to_sym) }
|
|
54
|
+
@que_version_object ||= T.must(Gem.loaded_specs["que"]).version
|
|
37
55
|
end
|
|
38
56
|
end
|
|
39
57
|
end
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "hashie"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
require "fugit"
|
|
6
|
+
require_relative "types"
|
|
3
7
|
|
|
4
8
|
# This is the definition of one scheduleable job in the que-scheduler config yml file.
|
|
5
9
|
module Que
|
|
6
10
|
module Scheduler
|
|
7
11
|
class DefinedJob < Hashie::Dash
|
|
8
12
|
include Hashie::Extensions::Dash::PropertyTranslation
|
|
13
|
+
extend T::Sig
|
|
9
14
|
|
|
10
15
|
DEFINED_JOB_TYPES = [
|
|
11
16
|
DEFINED_JOB_TYPE_DEFAULT = :default,
|
|
@@ -21,6 +26,9 @@ module Que
|
|
|
21
26
|
property :schedule_type, default: DEFINED_JOB_TYPE_DEFAULT
|
|
22
27
|
|
|
23
28
|
class << self
|
|
29
|
+
extend T::Sig
|
|
30
|
+
|
|
31
|
+
sig { params(options: T::Hash[Symbol, T.anything]).returns(DefinedJob) }
|
|
24
32
|
def create(options)
|
|
25
33
|
defined_job = new(options.compact)
|
|
26
34
|
defined_job.freeze.tap { |dj| dj.validate(options) }
|
|
@@ -29,6 +37,7 @@ module Que
|
|
|
29
37
|
|
|
30
38
|
# Given a "last time", return the next Time the event will occur, or nil if it
|
|
31
39
|
# is after "to".
|
|
40
|
+
sig { params(from: Types::TIME, to: Types::TIME).returns(T.nilable(ActiveSupport::TimeWithZone)) }
|
|
32
41
|
def next_run_time(from, to)
|
|
33
42
|
next_time = cron.next_time(from)
|
|
34
43
|
next_run = next_time.to_local_time.in_time_zone(next_time.zone)
|
|
@@ -37,9 +46,10 @@ module Que
|
|
|
37
46
|
|
|
38
47
|
# Given the last scheduler run time, and this run time, return all
|
|
39
48
|
# the instances that should be enqueued for the job class.
|
|
49
|
+
sig { params(last_run_time: Types::TIME, as_time: Types::TIME).returns(T::Array[ToEnqueue]) }
|
|
40
50
|
def calculate_missed_runs(last_run_time, as_time)
|
|
41
|
-
missed_times = []
|
|
42
|
-
last_time = last_run_time
|
|
51
|
+
missed_times = T.let([], T::Array[ActiveSupport::TimeWithZone])
|
|
52
|
+
last_time = T.let(last_run_time, Types::TIME)
|
|
43
53
|
while (next_run = next_run_time(last_time, as_time))
|
|
44
54
|
missed_times << next_run
|
|
45
55
|
last_time = next_run
|
|
@@ -48,6 +58,7 @@ module Que
|
|
|
48
58
|
generate_to_enqueue_list(missed_times)
|
|
49
59
|
end
|
|
50
60
|
|
|
61
|
+
sig { params(options: T::Hash[Symbol, T.anything]).void }
|
|
51
62
|
def validate(options)
|
|
52
63
|
validate_fields_presence(options)
|
|
53
64
|
validate_fields_types(options)
|
|
@@ -55,6 +66,7 @@ module Que
|
|
|
55
66
|
end
|
|
56
67
|
|
|
57
68
|
# rubocop:disable Style/GuardClause -- This reads better as a conditional
|
|
69
|
+
sig { params(options: T::Hash[Symbol, T.anything]).void }
|
|
58
70
|
private def validate_fields_types(options)
|
|
59
71
|
unless queue.nil? || queue.is_a?(String)
|
|
60
72
|
err_field(:queue, options, "queue must be a string")
|
|
@@ -68,6 +80,7 @@ module Que
|
|
|
68
80
|
end
|
|
69
81
|
# rubocop:enable Style/GuardClause
|
|
70
82
|
|
|
83
|
+
sig { params(options: T::Hash[Symbol, T.anything]).void }
|
|
71
84
|
private def validate_fields_presence(options)
|
|
72
85
|
err_field(:name, options, "name must be present") if name.nil?
|
|
73
86
|
err_field(:job_class, options, "job_class must be present") if job_class.nil?
|
|
@@ -75,33 +88,15 @@ module Que
|
|
|
75
88
|
err_field(:cron, options, "cron must be present") if cron.nil?
|
|
76
89
|
end
|
|
77
90
|
|
|
91
|
+
sig { params(options: T::Hash[Symbol, T.anything]).void }
|
|
78
92
|
private def validate_job_class_related(options)
|
|
79
93
|
# Only support known job engines
|
|
80
|
-
|
|
81
|
-
err_field(:job_class, options, "Job #{job_class} was not a supported job type")
|
|
82
|
-
end
|
|
94
|
+
return if Que::Scheduler::ToEnqueue.valid_job_class?(job_class)
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
# warning.
|
|
86
|
-
if queue &&
|
|
87
|
-
Que::Scheduler::ToEnqueue.active_job_defined? &&
|
|
88
|
-
job_class < ::ActiveJob::Base &&
|
|
89
|
-
Que::Scheduler::ToEnqueue.active_job_version < Gem::Version.create("6.0.3")
|
|
90
|
-
puts <<~ERR
|
|
91
|
-
WARNING from que-scheduler....
|
|
92
|
-
Between versions 4.2.3 and 6.0.2 (inclusive) Rails did not support setting queue names
|
|
93
|
-
on que jobs with ActiveJob, so que-scheduler cannot support it.
|
|
94
|
-
See removed in Rails 4.2.3
|
|
95
|
-
https://github.com/rails/rails/pull/19498
|
|
96
|
-
And readded in Rails 6.0.3
|
|
97
|
-
https://github.com/rails/rails/pull/38635
|
|
98
|
-
|
|
99
|
-
Please remove all "queue" keys from ActiveJobs defined in the que-scheduler.yml config.
|
|
100
|
-
Specifically #{queue} for job #{name}.
|
|
101
|
-
ERR
|
|
102
|
-
end
|
|
96
|
+
err_field(:job_class, options, "Job #{job_class} was not a supported job type")
|
|
103
97
|
end
|
|
104
98
|
|
|
99
|
+
sig { params(field: Symbol, options: T::Hash[Symbol, T.anything], reason: String).returns(T.noreturn) }
|
|
105
100
|
private def err_field(field, options, reason = "")
|
|
106
101
|
schedule = Que::Scheduler.configuration.schedule_location
|
|
107
102
|
value = options[field]
|
|
@@ -109,6 +104,7 @@ module Que
|
|
|
109
104
|
"#{reason}"
|
|
110
105
|
end
|
|
111
106
|
|
|
107
|
+
sig { params(missed_times: T::Array[ActiveSupport::TimeWithZone]).returns(T::Array[ToEnqueue]) }
|
|
112
108
|
private def generate_to_enqueue_list(missed_times)
|
|
113
109
|
return [] if missed_times.empty?
|
|
114
110
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "fugit"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
|
|
3
6
|
module Que
|
|
4
7
|
module Scheduler
|
|
@@ -9,6 +12,14 @@ module Que
|
|
|
9
12
|
end
|
|
10
13
|
|
|
11
14
|
class << self
|
|
15
|
+
extend T::Sig
|
|
16
|
+
|
|
17
|
+
sig do
|
|
18
|
+
params(
|
|
19
|
+
scheduler_config: T::Array[DefinedJob],
|
|
20
|
+
scheduler_job_args: SchedulerJobArgs
|
|
21
|
+
).returns(Result)
|
|
22
|
+
end
|
|
12
23
|
def parse(scheduler_config, scheduler_job_args)
|
|
13
24
|
job_dictionary = []
|
|
14
25
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "que"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
|
|
3
6
|
# This job can optionally be scheduled to clear down the que-scheduler audit log if it
|
|
4
7
|
# isn't required in the long term.
|
|
@@ -6,7 +9,12 @@ module Que
|
|
|
6
9
|
module Scheduler
|
|
7
10
|
module Jobs
|
|
8
11
|
class QueSchedulerAuditClearDownJob < Que::Job
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
9
14
|
class << self
|
|
15
|
+
extend T::Sig
|
|
16
|
+
|
|
17
|
+
sig { params(table_name: String).returns(String) }
|
|
10
18
|
def build_sql(table_name)
|
|
11
19
|
<<~SQL
|
|
12
20
|
WITH deleted AS (
|
|
@@ -28,6 +36,7 @@ module Que
|
|
|
28
36
|
self.priority = 100
|
|
29
37
|
self.queue = -> { Que::Scheduler.configuration.que_scheduler_queue }
|
|
30
38
|
|
|
39
|
+
sig { params(options: T::Hash[T.any(String, Symbol), T.anything]).void }
|
|
31
40
|
def run(options)
|
|
32
41
|
retain_row_count = options.symbolize_keys.fetch(:retain_row_count)
|
|
33
42
|
Que::Scheduler::Db.transaction do
|
|
@@ -36,7 +45,7 @@ module Que
|
|
|
36
45
|
Que::Scheduler::DbSupport.execute(DELETE_AUDIT_ENQUEUED_SQL, [retain_row_count])
|
|
37
46
|
# This will delete all but `retain_row_count` oldest rows
|
|
38
47
|
count = Que::Scheduler::DbSupport.execute(DELETE_AUDIT_SQL, [retain_row_count])
|
|
39
|
-
log = "#{self.class} cleared down #{count.
|
|
48
|
+
log = "#{self.class} cleared down #{count.fetch(0).fetch(:count)} rows"
|
|
40
49
|
::Que.log(event: :"que-scheduler", message: log)
|
|
41
50
|
end
|
|
42
51
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
# typed: true
|
|
1
2
|
# frozen_string_literal: true
|
|
2
3
|
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
3
6
|
module Que
|
|
4
7
|
module Scheduler
|
|
5
8
|
module Migrations
|
|
@@ -12,6 +15,9 @@ module Que
|
|
|
12
15
|
MAX_VERSION = Dir.glob("#{__dir__}/migrations/*").map { |d| File.basename(d) }.map(&:to_i).max
|
|
13
16
|
|
|
14
17
|
class << self
|
|
18
|
+
extend T::Sig
|
|
19
|
+
|
|
20
|
+
sig { params(version: Integer).void }
|
|
15
21
|
def migrate!(version:)
|
|
16
22
|
# Like que, Do not migrate test DBs.
|
|
17
23
|
return if defined?(Que::Testing)
|
|
@@ -27,9 +33,11 @@ module Que
|
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
# rubocop:disable MagicNumbers/NoReturn
|
|
36
|
+
sig { returns(Integer) }
|
|
30
37
|
def db_version
|
|
31
38
|
if audit_table_exists?
|
|
32
|
-
|
|
39
|
+
comment = Que::Scheduler::DbSupport.execute(TABLE_COMMENT).fetch(0).fetch(:description)
|
|
40
|
+
return T.cast(comment, T.nilable(String)).to_i
|
|
33
41
|
end
|
|
34
42
|
|
|
35
43
|
# At this point we used to be able to tell if it was 0 or 1 by the presence of the
|
|
@@ -38,6 +46,7 @@ module Que
|
|
|
38
46
|
end
|
|
39
47
|
# rubocop:enable MagicNumbers/NoReturn
|
|
40
48
|
|
|
49
|
+
sig { returns(T::Boolean) }
|
|
41
50
|
def audit_table_exists?
|
|
42
51
|
result = Que::Scheduler::DbSupport.execute(<<-SQL)
|
|
43
52
|
SELECT * FROM information_schema.tables WHERE table_name = '#{AUDIT_TABLE_NAME}';
|
|
@@ -47,6 +56,7 @@ module Que
|
|
|
47
56
|
|
|
48
57
|
# This method must be used during initial installation of que-scheduler and if the
|
|
49
58
|
# project migrations are squashed.
|
|
59
|
+
sig { void }
|
|
50
60
|
def reenqueue_scheduler_if_missing
|
|
51
61
|
raise <<~MSG unless Que::Migrations.db_version >= 6
|
|
52
62
|
Cannot (re)enqueue the que-scheduler worker unless the Que migrations have been run to at least version 6.
|
|
@@ -64,15 +74,18 @@ module Que
|
|
|
64
74
|
Que::Scheduler::DbSupport.enqueue_a_job(Que::Scheduler::SchedulerJob)
|
|
65
75
|
end
|
|
66
76
|
|
|
77
|
+
sig { params(current: Integer, version: Integer).void }
|
|
67
78
|
private def migrate_up(current, version)
|
|
68
79
|
execute_step(current += 1, :up) until current == version
|
|
69
80
|
end
|
|
70
81
|
|
|
82
|
+
sig { params(current: Integer, version: Integer).void }
|
|
71
83
|
private def migrate_down(current, version)
|
|
72
84
|
current += 1
|
|
73
85
|
execute_step(current -= 1, :down) until current == version + 1
|
|
74
86
|
end
|
|
75
87
|
|
|
88
|
+
sig { params(number: Integer, direction: Symbol).void }
|
|
76
89
|
private def execute_step(number, direction)
|
|
77
90
|
sql = File.read("#{__dir__}/migrations/#{number}/#{direction}.sql")
|
|
78
91
|
Que::Scheduler::DbSupport.execute(sql)
|
|
@@ -1,21 +1,29 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
require "sorbet-runtime"
|
|
1
4
|
require_relative "configuration"
|
|
2
5
|
require_relative "defined_job"
|
|
6
|
+
require_relative "types"
|
|
3
7
|
|
|
4
8
|
module Que
|
|
5
9
|
module Scheduler
|
|
6
10
|
class Schedule
|
|
7
11
|
class << self
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
8
14
|
# The main method for determining the schedule. It has to evaluate the schedule as late as
|
|
9
15
|
# possible (ie just as it is about to be used) as we cannot guarantee we are in a Rails
|
|
10
16
|
# app with initializers. In a future release this may change to "fast fail" in Rails by
|
|
11
17
|
# checking the config up front.
|
|
18
|
+
sig { returns(T::Hash[String, DefinedJob]) }
|
|
12
19
|
def schedule
|
|
13
20
|
@schedule ||=
|
|
14
21
|
begin
|
|
15
22
|
configuration = Que::Scheduler.configuration
|
|
16
|
-
|
|
23
|
+
explicit_schedule = configuration.schedule
|
|
24
|
+
if !explicit_schedule.nil?
|
|
17
25
|
# If an explicit schedule as a hash has been defined, use that.
|
|
18
|
-
from_hash(
|
|
26
|
+
from_hash(explicit_schedule)
|
|
19
27
|
elsif File.exist?(configuration.schedule_location)
|
|
20
28
|
# If the schedule is defined as a file location, then load it and return it.
|
|
21
29
|
from_file(configuration.schedule_location)
|
|
@@ -26,21 +34,30 @@ module Que
|
|
|
26
34
|
end
|
|
27
35
|
end
|
|
28
36
|
|
|
37
|
+
sig { params(location: T.any(String, Pathname)).returns(T::Hash[String, DefinedJob]) }
|
|
29
38
|
def from_file(location)
|
|
30
39
|
from_yaml(File.read(location))
|
|
31
40
|
end
|
|
32
41
|
|
|
42
|
+
sig { params(config: String).returns(T::Hash[String, DefinedJob]) }
|
|
33
43
|
def from_yaml(config)
|
|
34
44
|
config_hash = YAML.safe_load(config)
|
|
35
45
|
from_hash(config_hash)
|
|
36
46
|
end
|
|
37
47
|
|
|
48
|
+
sig { params(config_hash: Types::SCHEDULE_CONFIG).returns(T::Hash[String, DefinedJob]) }
|
|
38
49
|
def from_hash(config_hash)
|
|
39
50
|
config_hash
|
|
40
51
|
.map { |name, defined_job_hash| hash_item_to_defined_job(name.to_s, defined_job_hash) }
|
|
41
52
|
.index_by(&:name)
|
|
42
53
|
end
|
|
43
54
|
|
|
55
|
+
sig do
|
|
56
|
+
params(
|
|
57
|
+
name: String,
|
|
58
|
+
defined_job_hash_in: T::Hash[T.any(String, Symbol), T.anything]
|
|
59
|
+
).returns(DefinedJob)
|
|
60
|
+
end
|
|
44
61
|
def hash_item_to_defined_job(name, defined_job_hash_in)
|
|
45
62
|
defined_job_hash = defined_job_hash_in.stringify_keys
|
|
46
63
|
# Que stores arguments as a json array. If the args we have to provide are already an
|
|
@@ -77,6 +94,9 @@ module Que
|
|
|
77
94
|
end
|
|
78
95
|
|
|
79
96
|
class << self
|
|
97
|
+
extend T::Sig
|
|
98
|
+
|
|
99
|
+
sig { returns(T::Hash[String, DefinedJob]) }
|
|
80
100
|
def schedule
|
|
81
101
|
Schedule.schedule
|
|
82
102
|
end
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "que"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
require "active_support"
|
|
3
6
|
require "active_support/core_ext/time"
|
|
4
7
|
|
|
@@ -8,12 +11,15 @@ require_relative "scheduler_job_args"
|
|
|
8
11
|
require_relative "state_checks"
|
|
9
12
|
require_relative "to_enqueue"
|
|
10
13
|
require_relative "db_support"
|
|
14
|
+
require_relative "types"
|
|
11
15
|
|
|
12
16
|
# The main job that runs every minute, determining what needs to be enqueued, enqueues the required
|
|
13
17
|
# jobs, then re-enqueues itself.
|
|
14
18
|
module Que
|
|
15
19
|
module Scheduler
|
|
16
20
|
class SchedulerJob < Que::Job
|
|
21
|
+
extend T::Sig
|
|
22
|
+
|
|
17
23
|
SCHEDULER_FREQUENCY = 60
|
|
18
24
|
RETRY_PROC = proc { |count|
|
|
19
25
|
# Maximum one hour, otherwise use the default backoff
|
|
@@ -24,6 +30,7 @@ module Que
|
|
|
24
30
|
self.maximum_retry_count = 1 << 128 # Heat death of universe
|
|
25
31
|
self.retry_interval = RETRY_PROC
|
|
26
32
|
|
|
33
|
+
sig { params(options: T.nilable(T::Hash[T.any(String, Symbol), T.anything])).void }
|
|
27
34
|
def run(options = nil)
|
|
28
35
|
Que::Scheduler::Db.transaction do
|
|
29
36
|
Que::Scheduler::StateChecks.check
|
|
@@ -42,6 +49,12 @@ module Que
|
|
|
42
49
|
end
|
|
43
50
|
end
|
|
44
51
|
|
|
52
|
+
sig do
|
|
53
|
+
params(
|
|
54
|
+
calculator_result: EnqueueingCalculator::Result,
|
|
55
|
+
logs: T::Array[String]
|
|
56
|
+
).returns(T::Array[EnqueuedJobType])
|
|
57
|
+
end
|
|
45
58
|
def enqueue_required_jobs(calculator_result, logs)
|
|
46
59
|
calculator_result.missed_jobs.filter_map do |to_enqueue|
|
|
47
60
|
to_enqueue.enqueue.tap do |enqueued_job|
|
|
@@ -50,6 +63,13 @@ module Que
|
|
|
50
63
|
end
|
|
51
64
|
end
|
|
52
65
|
|
|
66
|
+
sig do
|
|
67
|
+
params(
|
|
68
|
+
to_enqueue: ToEnqueue,
|
|
69
|
+
enqueued_job: T.nilable(EnqueuedJobType),
|
|
70
|
+
logs: T::Array[String]
|
|
71
|
+
).void
|
|
72
|
+
end
|
|
53
73
|
private def check_enqueued_job(to_enqueue, enqueued_job, logs)
|
|
54
74
|
logs << if enqueued_job.present?
|
|
55
75
|
"que-scheduler enqueueing #{enqueued_job.job_class} " \
|
|
@@ -61,10 +81,18 @@ module Que
|
|
|
61
81
|
end
|
|
62
82
|
end
|
|
63
83
|
|
|
84
|
+
sig do
|
|
85
|
+
params(
|
|
86
|
+
scheduler_job_args: SchedulerJobArgs,
|
|
87
|
+
last_full_execution: Types::TIME,
|
|
88
|
+
job_dictionary: T::Array[String],
|
|
89
|
+
enqueued_jobs: T::Array[EnqueuedJobType]
|
|
90
|
+
).void
|
|
91
|
+
end
|
|
64
92
|
private def enqueue_self_again(scheduler_job_args, last_full_execution, job_dictionary,
|
|
65
93
|
enqueued_jobs)
|
|
66
94
|
# Log last run...
|
|
67
|
-
job_id = DbSupport.
|
|
95
|
+
job_id = DbSupport.job_id(self)
|
|
68
96
|
Audit.append(job_id, scheduler_job_args.as_time, enqueued_jobs)
|
|
69
97
|
|
|
70
98
|
# And rerun...
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "hashie"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
require "active_support"
|
|
3
6
|
require "active_support/time_with_zone"
|
|
4
7
|
|
|
@@ -10,6 +13,9 @@ module Que
|
|
|
10
13
|
property :job_dictionary, required: true
|
|
11
14
|
property :as_time, required: true
|
|
12
15
|
|
|
16
|
+
extend T::Sig
|
|
17
|
+
|
|
18
|
+
sig { params(options: T.nilable(T::Hash[T.any(String, Symbol), T.anything])).returns(SchedulerJobArgs) }
|
|
13
19
|
def self.build(options)
|
|
14
20
|
now = Que::Scheduler::Db.now
|
|
15
21
|
parsed =
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
require "sorbet-runtime"
|
|
1
4
|
require_relative "audit"
|
|
2
5
|
require_relative "db"
|
|
3
6
|
require_relative "migrations"
|
|
@@ -6,11 +9,15 @@ module Que
|
|
|
6
9
|
module Scheduler
|
|
7
10
|
module StateChecks
|
|
8
11
|
class << self
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
sig { void }
|
|
9
15
|
def check
|
|
10
16
|
assert_db_migrated
|
|
11
17
|
end
|
|
12
18
|
|
|
13
19
|
# rubocop:disable Metrics/MethodLength
|
|
20
|
+
sig { void }
|
|
14
21
|
private def assert_db_migrated
|
|
15
22
|
db_version = Que::Scheduler::Migrations.db_version
|
|
16
23
|
return if db_version == Que::Scheduler::Migrations::MAX_VERSION
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
require "sorbet-runtime"
|
|
4
|
+
|
|
1
5
|
module Que
|
|
2
6
|
module Scheduler
|
|
3
7
|
module TimeZone
|
|
@@ -35,6 +39,9 @@ module Que
|
|
|
35
39
|
ERR
|
|
36
40
|
|
|
37
41
|
class << self
|
|
42
|
+
extend T::Sig
|
|
43
|
+
|
|
44
|
+
sig { returns(ActiveSupport::TimeZone) }
|
|
38
45
|
def time_zone
|
|
39
46
|
@time_zone ||=
|
|
40
47
|
begin
|
|
@@ -1,44 +1,48 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
1
3
|
require "que"
|
|
4
|
+
require "sorbet-runtime"
|
|
2
5
|
|
|
3
6
|
# This module uses polymorphic dispatch to centralise the differences between supporting Que::Job
|
|
4
7
|
# and other job systems.
|
|
5
8
|
module Que
|
|
6
9
|
module Scheduler
|
|
7
10
|
class ToEnqueue < Hashie::Dash
|
|
11
|
+
extend T::Sig
|
|
12
|
+
extend T::Helpers
|
|
13
|
+
|
|
14
|
+
abstract!
|
|
15
|
+
|
|
8
16
|
property :args, required: true, default: []
|
|
9
17
|
property :queue
|
|
10
18
|
property :priority
|
|
11
19
|
property :run_at, required: true
|
|
12
20
|
property :job_class, required: true
|
|
13
21
|
|
|
22
|
+
sig { abstract.returns(T.nilable(EnqueuedJobType)) }
|
|
23
|
+
def enqueue; end
|
|
24
|
+
|
|
14
25
|
class << self
|
|
26
|
+
extend T::Sig
|
|
27
|
+
|
|
28
|
+
sig { params(options: T::Hash[Symbol, T.anything]).returns(ToEnqueue) }
|
|
15
29
|
def create(options)
|
|
16
|
-
type_from_job_class(options.fetch(:job_class)).new(
|
|
30
|
+
T.must(type_from_job_class(T.cast(options.fetch(:job_class), Class))).new(
|
|
17
31
|
options.merge(run_at: options.fetch(:run_at) { Que::Scheduler::Db.now })
|
|
18
32
|
)
|
|
19
33
|
end
|
|
20
34
|
|
|
35
|
+
sig { params(job_class: Class).returns(T::Boolean) }
|
|
21
36
|
def valid_job_class?(job_class)
|
|
22
37
|
type_from_job_class(job_class).present?
|
|
23
38
|
end
|
|
24
39
|
|
|
40
|
+
sig { returns(T::Boolean) }
|
|
25
41
|
def active_job_defined?
|
|
26
42
|
Object.const_defined?(:ActiveJob)
|
|
27
43
|
end
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
Gem.loaded_specs["activejob"]&.version
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def active_job_version_supports_queues?
|
|
34
|
-
# Supporting queue name in ActiveJob was removed in Rails 4.2.3
|
|
35
|
-
# https://github.com/rails/rails/pull/19498
|
|
36
|
-
# and readded in Rails 6.0.3
|
|
37
|
-
# https://github.com/rails/rails/pull/38635
|
|
38
|
-
ToEnqueue.active_job_version && ToEnqueue.active_job_version >=
|
|
39
|
-
Gem::Version.create("6.0.3")
|
|
40
|
-
end
|
|
41
|
-
|
|
45
|
+
sig { params(job_class: Class).returns(T.nilable(T.class_of(ToEnqueue))) }
|
|
42
46
|
private def type_from_job_class(job_class)
|
|
43
47
|
types.each do |type, implementation|
|
|
44
48
|
return implementation if job_class < type
|
|
@@ -46,6 +50,7 @@ module Que
|
|
|
46
50
|
nil
|
|
47
51
|
end
|
|
48
52
|
|
|
53
|
+
sig { returns(T::Hash[Class, T.class_of(ToEnqueue)]) }
|
|
49
54
|
private def types
|
|
50
55
|
@types ||=
|
|
51
56
|
begin
|
|
@@ -61,15 +66,16 @@ module Que
|
|
|
61
66
|
|
|
62
67
|
# For jobs of type Que::Job
|
|
63
68
|
class QueJobType < ToEnqueue
|
|
69
|
+
sig { override.returns(T.nilable(EnqueuedJobType)) }
|
|
64
70
|
def enqueue
|
|
65
71
|
job_settings = to_h.slice(:queue, :priority, :run_at).compact
|
|
66
72
|
job = Que::Scheduler::DbSupport.enqueue_a_job(
|
|
67
|
-
job_class,
|
|
73
|
+
T.cast(job_class, T.class_of(Que::Job)),
|
|
68
74
|
job_settings,
|
|
69
75
|
args
|
|
70
76
|
)
|
|
71
77
|
|
|
72
|
-
return nil if job.nil? || !job
|
|
78
|
+
return nil if job.nil? || !job
|
|
73
79
|
|
|
74
80
|
# Now read the just inserted job back out of the DB to get the actual values that will
|
|
75
81
|
# be used when the job is worked.
|
|
@@ -82,15 +88,17 @@ module Que
|
|
|
82
88
|
|
|
83
89
|
# For jobs of type ActiveJob
|
|
84
90
|
class ActiveJobType < ToEnqueue
|
|
91
|
+
sig { override.returns(T.nilable(EnqueuedJobType)) }
|
|
85
92
|
def enqueue
|
|
86
93
|
job = enqueue_active_job
|
|
87
94
|
|
|
88
|
-
return nil if job.nil? || !job
|
|
95
|
+
return nil if job.nil? || !job
|
|
89
96
|
|
|
90
97
|
enqueued_values = calculate_enqueued_values(job)
|
|
91
98
|
EnqueuedJobType.new(enqueued_values)
|
|
92
99
|
end
|
|
93
100
|
|
|
101
|
+
sig { params(job: ::ActiveJob::Base).returns(T::Hash[Symbol, T.anything]) }
|
|
94
102
|
def calculate_enqueued_values(job)
|
|
95
103
|
# Now read the just inserted job back out of the DB to get the actual values that will
|
|
96
104
|
# be used when the job is worked.
|
|
@@ -98,9 +106,6 @@ module Que
|
|
|
98
106
|
|
|
99
107
|
scheduled_at = self.class.extract_scheduled_at(data[:scheduled_at])
|
|
100
108
|
|
|
101
|
-
# Rails didn't support queues for ActiveJob for a while
|
|
102
|
-
used_queue = data[:queue_name] if ToEnqueue.active_job_version_supports_queues?
|
|
103
|
-
|
|
104
109
|
# We can't get the priority out of the DB, as the returned `job` doesn't give us access
|
|
105
110
|
# to the underlying ActiveJob that was scheduled. We have no option but to assume
|
|
106
111
|
# it was what we told it to use. If no priority was specified, we must assume it was
|
|
@@ -109,7 +114,7 @@ module Que
|
|
|
109
114
|
|
|
110
115
|
{
|
|
111
116
|
args: data.fetch(:arguments),
|
|
112
|
-
queue:
|
|
117
|
+
queue: data[:queue_name],
|
|
113
118
|
priority: assume_used_priority,
|
|
114
119
|
run_at: scheduled_at,
|
|
115
120
|
job_class: job_class.to_s,
|
|
@@ -117,6 +122,7 @@ module Que
|
|
|
117
122
|
}
|
|
118
123
|
end
|
|
119
124
|
|
|
125
|
+
sig { returns(T.nilable(T.any(::ActiveJob::Base, FalseClass))) }
|
|
120
126
|
def enqueue_active_job
|
|
121
127
|
job_settings = {
|
|
122
128
|
priority: priority,
|
|
@@ -124,27 +130,24 @@ module Que
|
|
|
124
130
|
queue: queue || Que::DEFAULT_QUEUE,
|
|
125
131
|
}.compact
|
|
126
132
|
|
|
127
|
-
job_class_set = job_class.set(**job_settings)
|
|
128
|
-
|
|
129
|
-
|
|
133
|
+
job_class_set = T.cast(job_class, T.class_of(::ActiveJob::Base)).set(**job_settings)
|
|
134
|
+
job_args = args
|
|
135
|
+
case job_args
|
|
136
|
+
when Hash
|
|
137
|
+
job_class_set.perform_later(**job_args)
|
|
130
138
|
else
|
|
131
|
-
|
|
139
|
+
# Sorbet cannot type a splat of a value of unknown size
|
|
140
|
+
T.unsafe(job_class_set).perform_later(*job_args)
|
|
132
141
|
end
|
|
133
142
|
end
|
|
134
143
|
|
|
135
144
|
class << self
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
extend T::Sig
|
|
146
|
+
|
|
147
|
+
# ActiveJob serialises scheduled_at as an ISO 8601 string; we want a Time
|
|
148
|
+
sig { params(scheduled_at: T.nilable(String)).returns(T.nilable(ActiveSupport::TimeWithZone)) }
|
|
138
149
|
def extract_scheduled_at(scheduled_at)
|
|
139
|
-
|
|
140
|
-
if scheduled_at.is_a?(Float)
|
|
141
|
-
Que::Scheduler::TimeZone.time_zone.at(scheduled_at)
|
|
142
|
-
elsif scheduled_at.is_a?(String)
|
|
143
|
-
Que::Scheduler::TimeZone.time_zone.parse(scheduled_at)
|
|
144
|
-
else
|
|
145
|
-
nil
|
|
146
|
-
end
|
|
147
|
-
# rubocop:enable Style/EmptyElse
|
|
150
|
+
scheduled_at && Que::Scheduler::TimeZone.time_zone.parse(scheduled_at)
|
|
148
151
|
end
|
|
149
152
|
end
|
|
150
153
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
require "sorbet-runtime"
|
|
4
|
+
require "active_support"
|
|
5
|
+
require "active_support/time_with_zone"
|
|
6
|
+
|
|
7
|
+
module Que
|
|
8
|
+
module Scheduler
|
|
9
|
+
module Types
|
|
10
|
+
# Times come from both Postgres (Time) and the configured zone (TimeWithZone)
|
|
11
|
+
TIME = T.type_alias { T.any(Time, ActiveSupport::TimeWithZone) }
|
|
12
|
+
# A schedule as given in config: job name to its options
|
|
13
|
+
SCHEDULE_CONFIG = T.type_alias do
|
|
14
|
+
T::Hash[T.any(String, Symbol), T::Hash[T.any(String, Symbol), T.anything]]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/que/scheduler.rb
CHANGED
data/lib/que-scheduler.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: que-scheduler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 8.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Harry Lascelles
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: activesupport
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '8.0'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '8.0'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
27
|
name: fugit
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -85,41 +84,41 @@ dependencies:
|
|
|
85
84
|
- !ruby/object:Gem::Version
|
|
86
85
|
version: '3.0'
|
|
87
86
|
- !ruby/object:Gem::Dependency
|
|
88
|
-
name:
|
|
87
|
+
name: sorbet-runtime
|
|
89
88
|
requirement: !ruby/object:Gem::Requirement
|
|
90
89
|
requirements:
|
|
91
90
|
- - ">="
|
|
92
91
|
- !ruby/object:Gem::Version
|
|
93
|
-
version: '
|
|
94
|
-
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '9.0'
|
|
97
|
-
type: :development
|
|
92
|
+
version: '0.5'
|
|
93
|
+
type: :runtime
|
|
98
94
|
prerelease: false
|
|
99
95
|
version_requirements: !ruby/object:Gem::Requirement
|
|
100
96
|
requirements:
|
|
101
97
|
- - ">="
|
|
102
98
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
104
|
-
- - "<"
|
|
105
|
-
- !ruby/object:Gem::Version
|
|
106
|
-
version: '9.0'
|
|
99
|
+
version: '0.5'
|
|
107
100
|
- !ruby/object:Gem::Dependency
|
|
108
|
-
name:
|
|
101
|
+
name: activerecord
|
|
109
102
|
requirement: !ruby/object:Gem::Requirement
|
|
110
103
|
requirements:
|
|
111
104
|
- - ">="
|
|
112
105
|
- !ruby/object:Gem::Version
|
|
113
|
-
version: '0'
|
|
106
|
+
version: '8.0'
|
|
107
|
+
- - "<"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '9.0'
|
|
114
110
|
type: :development
|
|
115
111
|
prerelease: false
|
|
116
112
|
version_requirements: !ruby/object:Gem::Requirement
|
|
117
113
|
requirements:
|
|
118
114
|
- - ">="
|
|
119
115
|
- !ruby/object:Gem::Version
|
|
120
|
-
version: '0'
|
|
116
|
+
version: '8.0'
|
|
117
|
+
- - "<"
|
|
118
|
+
- !ruby/object:Gem::Version
|
|
119
|
+
version: '9.0'
|
|
121
120
|
- !ruby/object:Gem::Dependency
|
|
122
|
-
name:
|
|
121
|
+
name: appraisal
|
|
123
122
|
requirement: !ruby/object:Gem::Requirement
|
|
124
123
|
requirements:
|
|
125
124
|
- - ">="
|
|
@@ -174,20 +173,6 @@ dependencies:
|
|
|
174
173
|
- - ">="
|
|
175
174
|
- !ruby/object:Gem::Version
|
|
176
175
|
version: '0'
|
|
177
|
-
- !ruby/object:Gem::Dependency
|
|
178
|
-
name: database_cleaner
|
|
179
|
-
requirement: !ruby/object:Gem::Requirement
|
|
180
|
-
requirements:
|
|
181
|
-
- - ">="
|
|
182
|
-
- !ruby/object:Gem::Version
|
|
183
|
-
version: '0'
|
|
184
|
-
type: :development
|
|
185
|
-
prerelease: false
|
|
186
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
187
|
-
requirements:
|
|
188
|
-
- - ">="
|
|
189
|
-
- !ruby/object:Gem::Version
|
|
190
|
-
version: '0'
|
|
191
176
|
- !ruby/object:Gem::Dependency
|
|
192
177
|
name: dememoize
|
|
193
178
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -216,20 +201,6 @@ dependencies:
|
|
|
216
201
|
- - ">="
|
|
217
202
|
- !ruby/object:Gem::Version
|
|
218
203
|
version: '0'
|
|
219
|
-
- !ruby/object:Gem::Dependency
|
|
220
|
-
name: mutex_m
|
|
221
|
-
requirement: !ruby/object:Gem::Requirement
|
|
222
|
-
requirements:
|
|
223
|
-
- - ">="
|
|
224
|
-
- !ruby/object:Gem::Version
|
|
225
|
-
version: '0'
|
|
226
|
-
type: :development
|
|
227
|
-
prerelease: false
|
|
228
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
229
|
-
requirements:
|
|
230
|
-
- - ">="
|
|
231
|
-
- !ruby/object:Gem::Version
|
|
232
|
-
version: '0'
|
|
233
204
|
- !ruby/object:Gem::Dependency
|
|
234
205
|
name: pg
|
|
235
206
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -452,6 +423,7 @@ files:
|
|
|
452
423
|
- lib/que/scheduler/state_checks.rb
|
|
453
424
|
- lib/que/scheduler/time_zone.rb
|
|
454
425
|
- lib/que/scheduler/to_enqueue.rb
|
|
426
|
+
- lib/que/scheduler/types.rb
|
|
455
427
|
- lib/que/scheduler/version.rb
|
|
456
428
|
homepage: https://github.com/hlascelles/que-scheduler
|
|
457
429
|
licenses:
|
|
@@ -463,7 +435,6 @@ metadata:
|
|
|
463
435
|
source_code_uri: https://github.com/hlascelles/que-scheduler/
|
|
464
436
|
bug_tracker_uri: https://github.com/hlascelles/que-scheduler/issues
|
|
465
437
|
rubygems_mfa_required: 'true'
|
|
466
|
-
post_install_message:
|
|
467
438
|
rdoc_options: []
|
|
468
439
|
require_paths:
|
|
469
440
|
- lib
|
|
@@ -471,15 +442,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
471
442
|
requirements:
|
|
472
443
|
- - ">="
|
|
473
444
|
- !ruby/object:Gem::Version
|
|
474
|
-
version: '
|
|
445
|
+
version: '4.0'
|
|
475
446
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
476
447
|
requirements:
|
|
477
448
|
- - ">="
|
|
478
449
|
- !ruby/object:Gem::Version
|
|
479
450
|
version: '0'
|
|
480
451
|
requirements: []
|
|
481
|
-
rubygems_version:
|
|
482
|
-
signing_key:
|
|
452
|
+
rubygems_version: 4.0.20
|
|
483
453
|
specification_version: 4
|
|
484
454
|
summary: A cron scheduler for Que
|
|
485
455
|
test_files: []
|