que-scheduler 1.2.0 → 2.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 +28 -11
- data/lib/que/scheduler/audit.rb +24 -0
- data/lib/que/scheduler/db.rb +21 -0
- data/lib/que/scheduler/defined_job.rb +28 -25
- data/lib/que/scheduler/enqueueing_calculator.rb +14 -16
- data/lib/que/scheduler/migrations/1/down.sql +1 -0
- data/lib/que/scheduler/migrations/1/up.sql +1 -0
- data/lib/que/scheduler/migrations/2/down.sql +1 -0
- data/lib/que/scheduler/migrations/2/up.sql +9 -0
- data/lib/que/scheduler/migrations.rb +52 -0
- data/lib/que/scheduler/scheduler_job.rb +22 -52
- data/lib/que/scheduler/scheduler_job_args.rb +4 -4
- data/lib/que/scheduler/version.rb +1 -1
- data/lib/que/scheduler.rb +3 -1
- metadata +33 -36
- data/lib/que/scheduler/adapters/orm.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b29b7a589afd759e434332a022ab0af875e23f09fc05494877bcea0cab51b00a
|
4
|
+
data.tar.gz: ab33b4eac4c33b8372be44176036c171c5cd5d0248a741b81d3895edafae383a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf3a481eae1cfa91ab4743bdc591fbbd7632563dcd16c34353d4663abf99b38e08fedcdb899339f6f6994f7e84d265dc909972ab741c599a91c430455871d71a
|
7
|
+
data.tar.gz: 55b7a0c8a91f8f08129489e0e349524540df8811e4220e70612dbaeec48cf0cdadd6e3622cd1346ca5a0d50562836de7d9027a5f023453835a976f9b9d280c6d
|
data/README.md
CHANGED
@@ -26,7 +26,11 @@ files, but with additional features.
|
|
26
26
|
1. Add a migration to start the job scheduler.
|
27
27
|
|
28
28
|
```ruby
|
29
|
-
|
29
|
+
class CreateQueSchedulerSchema < ActiveRecord::Migration
|
30
|
+
def change
|
31
|
+
Que::Scheduler::Migrations.migrate!(version: 2)
|
32
|
+
end
|
33
|
+
end
|
30
34
|
```
|
31
35
|
|
32
36
|
## Schedule configuration
|
@@ -95,13 +99,18 @@ DailyBatchReport:
|
|
95
99
|
|
96
100
|
A job can have a `schedule_type` assigned to it. Valid values are:
|
97
101
|
|
98
|
-
1. `default` - This job will be scheduled
|
99
|
-
go by during an extended period of downtime
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
102
|
+
1. `default` - This job will be scheduled in a manner closer to resque-scheduler. If multiple cron
|
103
|
+
times go by during an extended period of downtime (eg a long maintenance window) then only one job
|
104
|
+
will be enqueued when the system starts back up. Multiple missed events are coalesced. This mimics
|
105
|
+
the way resque-scheduler would perform if it were taken down for some time.
|
106
|
+
1. `every_event` - Every cron match will result in a job being scheduled. If multiple cron times go
|
107
|
+
by during an extended period of downtime, then a job will be scheduled for every one missed on
|
108
|
+
startup. This `schedule_type` should be used for daily batch jobs that need to know which day
|
109
|
+
they are running a batch for. The job will always be scheduled with an ISO8601 string of the cron
|
110
|
+
that matched as the first argument.
|
111
|
+
|
112
|
+
This feature ensures that jobs which *must run* for a certain cron match will always eventually
|
113
|
+
execute, even after a total system crash, or even a DB backup restore.
|
105
114
|
|
106
115
|
## Environment Variables
|
107
116
|
|
@@ -109,6 +118,11 @@ You can configure some aspects of the gem with environment variables.
|
|
109
118
|
|
110
119
|
* `QUE_SCHEDULER_CONFIG_LOCATION` - The location of the schedule configuration (default `config/que_schedule.yml`)
|
111
120
|
|
121
|
+
## Scheduler Audit
|
122
|
+
|
123
|
+
An audit table _que_scheduler_audit_ is written to by the scheduler to keep a history of what jobs
|
124
|
+
were enqueued when. It is created by the included migration tasks.
|
125
|
+
|
112
126
|
## HA Redundancy and DB restores
|
113
127
|
|
114
128
|
Because of the way que-scheduler works, it requires no additional processes. It is, itself, a Que job.
|
@@ -135,9 +149,12 @@ que-scheduler is a job that reads a config file, enqueues any jobs it determines
|
|
135
149
|
then reschedules itself. The flow is as follows:
|
136
150
|
|
137
151
|
1. The que-scheduler job runs for the very first time.
|
138
|
-
1. que-scheduler loads the schedule config file. It will not schedule any other jobs, except itself,
|
139
|
-
|
140
|
-
1.
|
152
|
+
1. que-scheduler loads the schedule config file. It will not schedule any other jobs, except itself,
|
153
|
+
as it has never run before.
|
154
|
+
1. Some time later it runs again. It knows what jobs it should be monitoring, and notices that some
|
155
|
+
have are due. It enqueues those jobs and then itself. Repeat.
|
156
|
+
1. After a deploy that changes the config, the job notices any new jobs to schedule, and knows which
|
157
|
+
ones to forget. It does not need to be re-enqueued or restarted.
|
141
158
|
|
142
159
|
## Inspiration
|
143
160
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Que
|
4
|
+
module Scheduler
|
5
|
+
module Audit
|
6
|
+
TABLE_NAME = 'que_scheduler_audit'
|
7
|
+
INSERT_AUDIT = %{
|
8
|
+
INSERT INTO #{TABLE_NAME} (scheduler_job_id, jobs_enqueued, executed_at, next_run_at)
|
9
|
+
VALUES ($1::integer, $2::jsonb, $3::timestamptz, $4::timestamptz)
|
10
|
+
RETURNING *
|
11
|
+
}
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def append(job_id, executed_at, result, next_run_at)
|
15
|
+
json = result.missed_jobs.map { |j| j.to_h.merge(job_class: j.job_class.to_s) }.to_json
|
16
|
+
inserted = ::Que.execute(
|
17
|
+
INSERT_AUDIT, [job_id, json, executed_at, next_run_at]
|
18
|
+
)
|
19
|
+
raise "Cannot save audit row #{job_id} #{executed_at}" if inserted.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Que
|
4
|
+
module Scheduler
|
5
|
+
module Db
|
6
|
+
SCHEDULER_COUNT_SQL =
|
7
|
+
"SELECT COUNT(*) FROM que_jobs WHERE job_class = '#{Que::Scheduler::SchedulerJob.name}'"
|
8
|
+
NOW_SQL = 'SELECT now()'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def count_schedulers
|
12
|
+
Que.execute(SCHEDULER_COUNT_SQL).first.values.first.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def now
|
16
|
+
Que.execute(NOW_SQL).first.values.first
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -8,6 +8,7 @@ module Que
|
|
8
8
|
class DefinedJob < Hashie::Dash
|
9
9
|
QUE_SCHEDULER_CONFIG_LOCATION =
|
10
10
|
ENV.fetch('QUE_SCHEDULER_CONFIG_LOCATION', 'config/que_schedule.yml')
|
11
|
+
ERROR_MESSAGE_SUFFIX = "in que-scheduler config #{QUE_SCHEDULER_CONFIG_LOCATION}".freeze
|
11
12
|
|
12
13
|
include Hashie::Extensions::Dash::PropertyTranslation
|
13
14
|
|
@@ -16,17 +17,14 @@ module Que
|
|
16
17
|
SCHEDULE_TYPE_EVERY_EVENT = :every_event
|
17
18
|
].freeze
|
18
19
|
|
19
|
-
def self.err_field(f, v)
|
20
|
-
suffix = "in que-scheduler config #{QUE_SCHEDULER_CONFIG_LOCATION}"
|
21
|
-
raise "Invalid #{f} '#{v}' #{suffix}"
|
22
|
-
end
|
23
|
-
|
24
20
|
property :name, required: true
|
25
21
|
property :job_class, required: true, transform_with: lambda { |v|
|
26
22
|
job_class = Object.const_get(v)
|
27
23
|
job_class < Que::Job ? job_class : err_field(:job_class, v)
|
28
24
|
}
|
29
|
-
property :cron, transform_with:
|
25
|
+
property :cron, required: true, transform_with: lambda { |v|
|
26
|
+
Fugit::Cron.parse(v) || err_field(:cron, v)
|
27
|
+
}
|
30
28
|
property :queue, transform_with: ->(v) { v.is_a?(String) ? v : err_field(:queue, v) }
|
31
29
|
property :priority, transform_with: ->(v) { v.is_a?(Integer) ? v : err_field(:priority, v) }
|
32
30
|
property :args
|
@@ -52,7 +50,7 @@ module Que
|
|
52
50
|
last_time = next_run
|
53
51
|
end
|
54
52
|
|
55
|
-
|
53
|
+
generate_to_enqueue_list(missed_times)
|
56
54
|
end
|
57
55
|
|
58
56
|
class << self
|
@@ -66,37 +64,42 @@ module Que
|
|
66
64
|
args: v['args'],
|
67
65
|
priority: v['priority'],
|
68
66
|
cron: v['cron'],
|
69
|
-
schedule_type: v['schedule_type']
|
67
|
+
schedule_type: v['schedule_type'],
|
70
68
|
}.compact
|
71
69
|
).freeze
|
72
70
|
end
|
73
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def err_field(field, value)
|
76
|
+
raise "Invalid #{field} '#{value}' #{ERROR_MESSAGE_SUFFIX}"
|
77
|
+
end
|
74
78
|
end
|
75
79
|
|
76
80
|
private
|
77
81
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
+
class ToEnqueue < Hashie::Dash
|
83
|
+
property :args, required: true, default: []
|
84
|
+
property :queue
|
85
|
+
property :priority
|
86
|
+
property :job_class, required: true
|
87
|
+
end
|
82
88
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
priority: priority
|
88
|
-
}.compact
|
89
|
+
def generate_to_enqueue_list(missed_times)
|
90
|
+
[].tap do |jobs_for_class|
|
91
|
+
unless missed_times.empty?
|
92
|
+
options = to_h.slice(:args, :queue, :priority, :job_class).compact
|
89
93
|
|
90
|
-
|
91
|
-
|
92
|
-
|
94
|
+
if schedule_type == DefinedJob::SCHEDULE_TYPE_EVERY_EVENT
|
95
|
+
missed_times.each do |time_missed|
|
96
|
+
jobs_for_class << ToEnqueue.new(options.merge(args: [time_missed] + (args || [])))
|
97
|
+
end
|
98
|
+
else
|
99
|
+
jobs_for_class << ToEnqueue.new(options)
|
93
100
|
end
|
94
|
-
else
|
95
|
-
jobs_for_class << options
|
96
101
|
end
|
97
|
-
options.freeze
|
98
102
|
end
|
99
|
-
jobs_for_class
|
100
103
|
end
|
101
104
|
end
|
102
105
|
end
|
@@ -2,34 +2,32 @@ require 'fugit'
|
|
2
2
|
|
3
3
|
module Que
|
4
4
|
module Scheduler
|
5
|
-
|
6
|
-
Result
|
5
|
+
module EnqueueingCalculator
|
6
|
+
class Result < Hashie::Dash
|
7
|
+
property :missed_jobs, required: true
|
8
|
+
property :job_dictionary, required: true
|
9
|
+
end
|
7
10
|
|
8
11
|
class << self
|
9
12
|
def parse(scheduler_config, scheduler_job_args)
|
10
|
-
|
11
|
-
schedule_dictionary = []
|
13
|
+
job_dictionary = []
|
12
14
|
|
13
|
-
# For each scheduled item
|
14
|
-
#
|
15
|
-
# If
|
16
|
-
|
17
|
-
scheduler_config.each do |desc|
|
15
|
+
# For each scheduled item:
|
16
|
+
# 1) If never seen before, schedule nothing, but add to known list
|
17
|
+
# 2) If seen before, calculate what (if anything) needs to be enqueued.
|
18
|
+
missed_jobs = scheduler_config.map do |desc|
|
18
19
|
job_name = desc.name
|
19
|
-
|
20
|
+
job_dictionary << job_name
|
20
21
|
|
21
22
|
# If we have never seen this job before, we don't want to scheduled any jobs for it.
|
22
23
|
# But we have added it to the dictionary, so it will be used to enqueue jobs next time.
|
23
24
|
next unless scheduler_job_args.job_dictionary.include?(job_name)
|
24
25
|
|
25
26
|
# This has been seen before. We should check if we have missed any executions.
|
26
|
-
|
27
|
-
|
28
|
-
)
|
29
|
-
missed_jobs[desc.job_class] = missed unless missed.empty?
|
30
|
-
end
|
27
|
+
desc.calculate_missed_runs(scheduler_job_args.last_run_time, scheduler_job_args.as_time)
|
28
|
+
end.flatten.compact
|
31
29
|
|
32
|
-
Result.new(missed_jobs,
|
30
|
+
Result.new(missed_jobs: missed_jobs, job_dictionary: job_dictionary)
|
33
31
|
end
|
34
32
|
end
|
35
33
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
DELETE FROM que_jobs WHERE job_class = 'Que::Scheduler::SchedulerJob';
|
@@ -0,0 +1 @@
|
|
1
|
+
-- Do nothing. This is handled by an "enqueue" call.
|
@@ -0,0 +1 @@
|
|
1
|
+
DROP TABLE que_scheduler_audit;
|
@@ -0,0 +1,9 @@
|
|
1
|
+
CREATE TABLE que_scheduler_audit (
|
2
|
+
scheduler_job_id integer NOT NULL,
|
3
|
+
executed_at timestamptz NOT NULL,
|
4
|
+
next_run_at timestamptz NOT NULL,
|
5
|
+
jobs_enqueued jsonb NOT NULL
|
6
|
+
);
|
7
|
+
|
8
|
+
CREATE UNIQUE INDEX index_que_scheduler_audit_on_scheduler_job_id ON que_scheduler_audit USING btree (scheduler_job_id);
|
9
|
+
CREATE INDEX index_que_scheduler_audit_on_jobs_enqueued ON que_scheduler_audit USING btree (jobs_enqueued);
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Que
|
4
|
+
module Scheduler
|
5
|
+
module Migrations
|
6
|
+
AUDIT_TABLE_NAME = Que::Scheduler::Audit::TABLE_NAME
|
7
|
+
TABLE_COMMENT = %(
|
8
|
+
SELECT description FROM pg_class
|
9
|
+
LEFT JOIN pg_description ON pg_description.objoid = pg_class.oid
|
10
|
+
WHERE relname = '#{AUDIT_TABLE_NAME}'
|
11
|
+
)
|
12
|
+
MAX_VERSION = Dir.glob("#{__dir__}/migrations/*").map { |d| File.basename(d) }.map(&:to_i).max
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def migrate!(version:)
|
16
|
+
::Que.transaction do
|
17
|
+
current = db_version
|
18
|
+
if current < version
|
19
|
+
migrate_up(current, version)
|
20
|
+
elsif current > version
|
21
|
+
migrate_down(current, version)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def db_version
|
27
|
+
return 0 if Que::Scheduler::Db.count_schedulers.zero?
|
28
|
+
return 1 unless ActiveRecord::Base.connection.table_exists?(AUDIT_TABLE_NAME)
|
29
|
+
Que.execute(TABLE_COMMENT).first[:description].to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def migrate_up(current, version)
|
35
|
+
Que::Scheduler::SchedulerJob.enqueue if current.zero? # Version 1 does not use SQL
|
36
|
+
execute_step((current += 1), :up) until current == version
|
37
|
+
end
|
38
|
+
|
39
|
+
def migrate_down(current, version)
|
40
|
+
current += 1
|
41
|
+
execute_step((current -= 1), :down) until current == version + 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute_step(number, direction)
|
45
|
+
Que.execute(IO.read("#{__dir__}/migrations/#{number}/#{direction}.sql"))
|
46
|
+
return unless number >= (direction == :up ? 2 : 3)
|
47
|
+
Que.execute "COMMENT ON TABLE que_scheduler_audit IS '#{number}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -4,6 +4,8 @@ require_relative 'defined_job'
|
|
4
4
|
require_relative 'enqueueing_calculator'
|
5
5
|
require_relative 'scheduler_job_args'
|
6
6
|
|
7
|
+
# The main job that runs every minute, determining what needs to be enqueued, enqueues the required
|
8
|
+
# jobs, then re-enqueues itself.
|
7
9
|
module Que
|
8
10
|
module Scheduler
|
9
11
|
class SchedulerJob < Que::Job
|
@@ -13,83 +15,51 @@ module Que
|
|
13
15
|
@priority = 0
|
14
16
|
|
15
17
|
def run(options = nil)
|
16
|
-
::Que
|
18
|
+
::Que.transaction do
|
17
19
|
assert_one_scheduler_job
|
18
20
|
scheduler_job_args = SchedulerJobArgs.build(options)
|
19
21
|
logs = ["que-scheduler last ran at #{scheduler_job_args.last_run_time}."]
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# rescheduled with the same last run at.
|
25
|
-
if scheduler_job_args.as_time < scheduler_job_args.last_run_time
|
26
|
-
handle_db_clock_change_backwards(scheduler_job_args, logs)
|
27
|
-
else
|
28
|
-
# Otherwise, run as normal
|
29
|
-
handle_normal_call(scheduler_job_args, logs)
|
30
|
-
end
|
23
|
+
result = EnqueueingCalculator.parse(DefinedJob.defined_jobs, scheduler_job_args)
|
24
|
+
enqueue_required_jobs(result, logs)
|
25
|
+
enqueue_self_again(scheduler_job_args, scheduler_job_args.as_time, result)
|
31
26
|
|
32
27
|
# Only now we're sure nothing errored, log the results
|
33
|
-
logs.each { |str| Que.log(message: str) }
|
28
|
+
logs.each { |str| ::Que.log(message: str) }
|
34
29
|
destroy
|
35
30
|
end
|
36
31
|
end
|
37
32
|
|
38
33
|
def enqueue_required_jobs(result, logs)
|
39
|
-
result.missed_jobs.each do |
|
40
|
-
|
41
|
-
|
34
|
+
result.missed_jobs.each do |to_enqueue|
|
35
|
+
job_class = to_enqueue.job_class
|
36
|
+
args = to_enqueue.args
|
37
|
+
remaining_hash = to_enqueue.except(:job_class, :args)
|
38
|
+
if args.is_a?(Hash)
|
39
|
+
job_class.enqueue(args.merge(remaining_hash))
|
40
|
+
else
|
41
|
+
job_class.enqueue(*args, remaining_hash)
|
42
42
|
end
|
43
|
+
logs << "que-scheduler enqueueing #{job_class} with: #{to_enqueue}"
|
43
44
|
end
|
44
|
-
result.schedule_dictionary
|
45
45
|
end
|
46
46
|
|
47
47
|
private
|
48
48
|
|
49
49
|
def assert_one_scheduler_job
|
50
|
-
schedulers =
|
50
|
+
schedulers = Que::Scheduler::Db.count_schedulers
|
51
51
|
return if schedulers == 1
|
52
52
|
raise "Only one #{self.class.name} should be enqueued. #{schedulers} were found."
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
56
|
-
|
57
|
-
# each containing params to enqueue.
|
58
|
-
result = EnqueueingCalculator.parse(DefinedJob.defined_jobs, scheduler_job_args)
|
59
|
-
new_job_dictionary = enqueue_required_jobs(result, logs)
|
60
|
-
enqueue_self_again(
|
61
|
-
scheduler_job_args.as_time,
|
62
|
-
scheduler_job_args.as_time,
|
63
|
-
new_job_dictionary
|
64
|
-
)
|
65
|
-
end
|
66
|
-
|
67
|
-
def enqueue_new_job(job_class, options, logs)
|
68
|
-
logs << "que-scheduler enqueueing #{job_class} with options: #{options}"
|
69
|
-
args = options.delete(:args)
|
70
|
-
if args.is_a?(Hash)
|
71
|
-
job_class.enqueue(args.merge(options))
|
72
|
-
else
|
73
|
-
job_class.enqueue(*args, options)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def handle_db_clock_change_backwards(scheduler_job_args, logs)
|
78
|
-
logs << 'que-scheduler detected the DB time is further back than the last run. ' \
|
79
|
-
'Rescheduling self again without enqueueing jobs to wait for the clock to catch up.'
|
80
|
-
enqueue_self_again(
|
81
|
-
scheduler_job_args.last_run_time,
|
82
|
-
scheduler_job_args.as_time,
|
83
|
-
scheduler_job_args.job_dictionary
|
84
|
-
)
|
85
|
-
end
|
86
|
-
|
87
|
-
def enqueue_self_again(last_full_execution, this_run_time, new_job_dictionary)
|
55
|
+
def enqueue_self_again(scheduler_job_args, last_full_execution, result)
|
56
|
+
next_run_at = scheduler_job_args.as_time.beginning_of_minute + SCHEDULER_FREQUENCY
|
88
57
|
SchedulerJob.enqueue(
|
89
58
|
last_run_time: last_full_execution.iso8601,
|
90
|
-
job_dictionary:
|
91
|
-
run_at:
|
59
|
+
job_dictionary: result.job_dictionary,
|
60
|
+
run_at: next_run_at
|
92
61
|
)
|
62
|
+
Audit.append(attrs[:job_id], scheduler_job_args.as_time, result, next_run_at)
|
93
63
|
end
|
94
64
|
end
|
95
65
|
end
|
@@ -11,19 +11,19 @@ module Que
|
|
11
11
|
property :as_time, required: true
|
12
12
|
|
13
13
|
def self.build(options)
|
14
|
-
now =
|
14
|
+
now = Que::Scheduler::Db.now
|
15
15
|
parsed =
|
16
16
|
if options.nil?
|
17
|
-
# First ever run, there is nothing to do but reschedule self.
|
17
|
+
# First ever run, there is nothing to do but reschedule self to run on the next minute.
|
18
18
|
{
|
19
19
|
last_run_time: now,
|
20
|
-
job_dictionary: []
|
20
|
+
job_dictionary: [],
|
21
21
|
}
|
22
22
|
else
|
23
23
|
options = options.symbolize_keys
|
24
24
|
{
|
25
25
|
last_run_time: Time.zone.parse(options.fetch(:last_run_time)),
|
26
|
-
job_dictionary: options.fetch(:job_dictionary)
|
26
|
+
job_dictionary: options.fetch(:job_dictionary),
|
27
27
|
}
|
28
28
|
end
|
29
29
|
SchedulerJobArgs.new(parsed.merge(as_time: now))
|
data/lib/que/scheduler.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'que/scheduler/version'
|
2
2
|
require 'que/scheduler/scheduler_job'
|
3
|
-
require 'que/scheduler/
|
3
|
+
require 'que/scheduler/db'
|
4
|
+
require 'que/scheduler/audit'
|
5
|
+
require 'que/scheduler/migrations'
|
4
6
|
# rubocop:disable Style/PreferredHashMethods
|
5
7
|
require 'que/scheduler/engine' if Gem.loaded_specs.has_key?('railties')
|
6
8
|
# rubocop:enable Style/PreferredHashMethods
|
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: 2.0.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: 2018-
|
11
|
+
date: 2018-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: pg
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
@@ -81,35 +81,35 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.10'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: que
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
90
|
-
type: :
|
89
|
+
version: '0.10'
|
90
|
+
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '0.10'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: activerecord
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
103
|
+
version: '3.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
110
|
+
version: '3.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: bundler
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: combustion
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: coveralls
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -151,7 +151,7 @@ dependencies:
|
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: pry-byebug
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - ">="
|
@@ -224,30 +224,16 @@ dependencies:
|
|
224
224
|
name: rubocop
|
225
225
|
requirement: !ruby/object:Gem::Requirement
|
226
226
|
requirements:
|
227
|
-
- - "
|
228
|
-
- !ruby/object:Gem::Version
|
229
|
-
version: '0'
|
230
|
-
type: :development
|
231
|
-
prerelease: false
|
232
|
-
version_requirements: !ruby/object:Gem::Requirement
|
233
|
-
requirements:
|
234
|
-
- - ">="
|
235
|
-
- !ruby/object:Gem::Version
|
236
|
-
version: '0'
|
237
|
-
- !ruby/object:Gem::Dependency
|
238
|
-
name: sequel
|
239
|
-
requirement: !ruby/object:Gem::Requirement
|
240
|
-
requirements:
|
241
|
-
- - ">="
|
227
|
+
- - "~>"
|
242
228
|
- !ruby/object:Gem::Version
|
243
|
-
version:
|
229
|
+
version: 0.54.0
|
244
230
|
type: :development
|
245
231
|
prerelease: false
|
246
232
|
version_requirements: !ruby/object:Gem::Requirement
|
247
233
|
requirements:
|
248
|
-
- - "
|
234
|
+
- - "~>"
|
249
235
|
- !ruby/object:Gem::Version
|
250
|
-
version:
|
236
|
+
version: 0.54.0
|
251
237
|
- !ruby/object:Gem::Dependency
|
252
238
|
name: sqlite3
|
253
239
|
requirement: !ruby/object:Gem::Requirement
|
@@ -299,17 +285,28 @@ extra_rdoc_files: []
|
|
299
285
|
files:
|
300
286
|
- README.md
|
301
287
|
- lib/que/scheduler.rb
|
302
|
-
- lib/que/scheduler/
|
288
|
+
- lib/que/scheduler/audit.rb
|
289
|
+
- lib/que/scheduler/db.rb
|
303
290
|
- lib/que/scheduler/defined_job.rb
|
304
291
|
- lib/que/scheduler/engine.rb
|
305
292
|
- lib/que/scheduler/enqueueing_calculator.rb
|
293
|
+
- lib/que/scheduler/migrations.rb
|
294
|
+
- lib/que/scheduler/migrations/1/down.sql
|
295
|
+
- lib/que/scheduler/migrations/1/up.sql
|
296
|
+
- lib/que/scheduler/migrations/2/down.sql
|
297
|
+
- lib/que/scheduler/migrations/2/up.sql
|
306
298
|
- lib/que/scheduler/scheduler_job.rb
|
307
299
|
- lib/que/scheduler/scheduler_job_args.rb
|
308
300
|
- lib/que/scheduler/version.rb
|
309
301
|
homepage: https://github.com/hlascelles/que-scheduler
|
310
302
|
licenses:
|
311
303
|
- MIT
|
312
|
-
metadata:
|
304
|
+
metadata:
|
305
|
+
homepage_uri: https://github.com/hlascelles/que-scheduler
|
306
|
+
documentation_uri: https://github.com/hlascelles/que-scheduler
|
307
|
+
changelog_uri: https://github.com/hlascelles/que-scheduler/blob/master/CHANGELOG.md
|
308
|
+
source_code_uri: https://github.com/hlascelles/que-scheduler/
|
309
|
+
bug_tracker_uri: https://github.com/hlascelles/que-scheduler/issues
|
313
310
|
post_install_message:
|
314
311
|
rdoc_options: []
|
315
312
|
require_paths:
|
@@ -1,74 +0,0 @@
|
|
1
|
-
module Que
|
2
|
-
module Scheduler
|
3
|
-
module Adapters
|
4
|
-
module Orm
|
5
|
-
class AdapterBase
|
6
|
-
SCHEDULER_COUNT_SQL =
|
7
|
-
'SELECT COUNT(*) FROM que_jobs WHERE job_class = ' \
|
8
|
-
"'#{Que::Scheduler::SchedulerJob.name}'".freeze
|
9
|
-
NOW_SQL = 'SELECT now()'.freeze
|
10
|
-
|
11
|
-
def transaction
|
12
|
-
transaction_base.transaction do
|
13
|
-
yield
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def count_schedulers
|
18
|
-
dml(SCHEDULER_COUNT_SQL).first.values.first.to_i
|
19
|
-
end
|
20
|
-
|
21
|
-
def now
|
22
|
-
Time.zone.parse(dml(NOW_SQL).first.values.first)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class ActiveRecordAdapter < AdapterBase
|
27
|
-
def transaction_base
|
28
|
-
::ActiveRecord::Base
|
29
|
-
end
|
30
|
-
|
31
|
-
def dml(sql)
|
32
|
-
::ActiveRecord::Base.connection.execute(sql)
|
33
|
-
end
|
34
|
-
|
35
|
-
def ddl(sql)
|
36
|
-
dml(sql)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class SequelAdapter < AdapterBase
|
41
|
-
def transaction_base
|
42
|
-
::DB
|
43
|
-
end
|
44
|
-
|
45
|
-
def dml(sql)
|
46
|
-
::DB.fetch(sql)
|
47
|
-
end
|
48
|
-
|
49
|
-
def ddl(sql)
|
50
|
-
::DB.run(sql)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class << self
|
55
|
-
# The exposed mutator method here allows any future orm adapters to be tested.
|
56
|
-
attr_writer :instance
|
57
|
-
|
58
|
-
def instance
|
59
|
-
# rubocop:disable Style/PreferredHashMethods
|
60
|
-
@instance ||=
|
61
|
-
if Gem.loaded_specs.has_key?('activerecord')
|
62
|
-
ActiveRecordAdapter.new
|
63
|
-
elsif Gem.loaded_specs.has_key?('sequel')
|
64
|
-
SequelAdapter.new
|
65
|
-
else
|
66
|
-
raise 'No known ORM adapter is available for que-scheduler'
|
67
|
-
end
|
68
|
-
# rubocop:enable Style/PreferredHashMethods
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|