que-scheduler 1.2.0 → 2.0.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: '0099d4a7447334aabd3912520a2c7575deac3a05e8b564e3711dcf913235dfd2'
4
- data.tar.gz: fb3b867db4ac06c74b81d3ef41a0593dbcf90ac30754ab38746b3a9ef41a17ab
3
+ metadata.gz: b29b7a589afd759e434332a022ab0af875e23f09fc05494877bcea0cab51b00a
4
+ data.tar.gz: ab33b4eac4c33b8372be44176036c171c5cd5d0248a741b81d3895edafae383a
5
5
  SHA512:
6
- metadata.gz: b10b18c7ffb66739556743aec5badbc1f3934edcb0db960c3d3202de327c24495320f73667bc22e207e03ea02fc477c4422f32bd0b0e564400af67185efcc1d2
7
- data.tar.gz: 7400225137aa503175f42b26d04d0943f973ffb923e60e92bdf0acbc2a2ac664ef43782f5b61cce6b3b4bb4f9e5c276aa45e7f594aa5bb264ea5fd70188d4a41
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
- Que::Scheduler::SchedulerJob.enqueue
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 when a worker becomes available. If multiple cron times
99
- go by during an extended period of downtime then only one job will be enqueued. This is closer to
100
- how ordinary cron works.
101
- 1. `every_event` - This job will always be scheduled with an ISO8601 time as the first argument.
102
- If multiple cron times go by during an extended period of downtime, then a job will be scheduled
103
- for every one missed. This `schedule_type` should be used for daily batch jobs that need to know
104
- which day they are running a batch for.
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, as it has never run before.
139
- 1. Some time later it runs again. It knows what jobs it should be monitoring, and notices that some have are due. It enqueues those jobs and then itself. Repeat.
140
- 1. After a deploy that changes the config, the job notices any new jobs to schedule, and knows which ones to forget. It does not need to be re-enqueued or restarted.
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: ->(v) { Fugit::Cron.parse(v) || err_field(:cron, v) }
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
- generate_required_jobs_list(missed_times)
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'] || DefinedJob::SCHEDULE_TYPE_DEFAULT
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
- # Given the timestamps of the missed events, generate a list of jobs
79
- # that can be enqueued as an array of arrays of args.
80
- def generate_required_jobs_list(missed_times)
81
- jobs_for_class = []
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
- unless missed_times.empty?
84
- options = {
85
- args: args,
86
- queue: queue,
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
- if schedule_type == DefinedJob::SCHEDULE_TYPE_EVERY_EVENT
91
- missed_times.each do |time_missed|
92
- jobs_for_class << options.merge(args: [time_missed] + (args || []))
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
- class EnqueueingCalculator
6
- Result = Struct.new(:missed_jobs, :schedule_dictionary)
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
- missed_jobs = {}
11
- schedule_dictionary = []
13
+ job_dictionary = []
12
14
 
13
- # For each scheduled item, we need not schedule a job it if it has no history, as it is
14
- # new. Otherwise, check how many times we have missed the job since the last run time.
15
- # If it is "every_event" then we schedule all of them, with the missed time as an arg,
16
- # otherwise just schedule it once.
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
- schedule_dictionary << job_name
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
- missed = desc.calculate_missed_runs(
27
- scheduler_job_args.last_run_time, scheduler_job_args.as_time
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, schedule_dictionary)
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::Scheduler::Adapters::Orm.instance.transaction do
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
- # It's possible the DB time has been changed manaully to an earlier time than it was
22
- # before. Whether this was a small amount of time (eg clock drift correction), or a major
23
- # change like timezone, the business schedule semantics of this are unknowable, so log and
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 |job_class, options_array|
40
- options_array.each do |options|
41
- enqueue_new_job(job_class, options.dup, logs)
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 = ::Que::Scheduler::Adapters::Orm.instance.count_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 handle_normal_call(scheduler_job_args, logs)
56
- # Obtain the hash of missed jobs. Keys are the job classes, and the values are arrays
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: new_job_dictionary,
91
- run_at: this_run_time.beginning_of_minute + SCHEDULER_FREQUENCY
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 = ::Que::Scheduler::Adapters::Orm.instance.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))
@@ -1,5 +1,5 @@
1
1
  module Que
2
2
  module Scheduler
3
- VERSION = '1.2.0'.freeze
3
+ VERSION = '2.0.0'.freeze
4
4
  end
5
5
  end
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/adapters/orm'
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: 1.2.0
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-03-29 00:00:00.000000000 Z
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: que
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: activerecord
84
+ name: que
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '3.0'
90
- type: :development
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: '3.0'
96
+ version: '0.10'
97
97
  - !ruby/object:Gem::Dependency
98
- name: bundler
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: combustion
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: coveralls
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: pry-byebug
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: que-testing
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: '4.0'
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: '4.0'
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/adapters/orm.rb
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