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 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