good_job 0.6.0 → 0.9.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: c20439024a46084c22e46bb42e38589687c2ab2cea47d90705abc4fa042ad449
4
- data.tar.gz: e5ea8e7fa607a10fd936c62623a39d32b891ef629636e0b04fb3d36aa7ab1f3f
3
+ metadata.gz: bddc60fa297293af01b1334d4adf51981bfb8b314ba4577a7b3bff1d7385bd5a
4
+ data.tar.gz: 3533f21ffee4dba606558fa0fadd1f37a943aa285b4f6bebed4bb3b89045b61e
5
5
  SHA512:
6
- metadata.gz: 4af34ce400c409a61892c9d35693f69e023b5dbd3d54cacfd6d9b044aaf481c616e3fdb9da53470f79919830a916a08f67550ce807eda1c59a43fa1da4092f07
7
- data.tar.gz: 81304be6bc9e6925b16fa030d2031377bd90bac894b4c8792cfe5e02ff81677bf04d476b1a3435e951a804e6d284e4d8aa7452c6090969f841dbd6e59a9c301c
6
+ metadata.gz: 74c0d3df2df08e78d06baa8982401d633af536277231a17d87687ff3c7ba1553d4123f7c1ce49ae23094bd781db5ee23eb9584eeba94aae49fb8cc83da0c68e0
7
+ data.tar.gz: 7df20f03621cb66b864a2c2950e4a11f60199cd4954ad5a622a1631254fa04bab1243ee376567600a2aed294a8c14fa9802fd1024855dc7d473a7ff8f7166515
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.9.0](https://github.com/bensheldon/good_job/tree/v0.9.0) (2020-07-20)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v0.6.0...v0.9.0)
6
+
7
+ **Closed issues:**
8
+
9
+ - Always store a default priority \(0\) and scheduled\_at\(Time.current\) [\#30](https://github.com/bensheldon/good_job/issues/30)
10
+ - Add a job timeout configuration to time out jobs that have run too long [\#19](https://github.com/bensheldon/good_job/issues/19)
11
+
12
+ **Merged pull requests:**
13
+
14
+ - Allow preservation of finished job records [\#46](https://github.com/bensheldon/good_job/pull/46) ([bensheldon](https://github.com/bensheldon))
15
+ - Run Github Action tests on PRs from forks [\#44](https://github.com/bensheldon/good_job/pull/44) ([bensheldon](https://github.com/bensheldon))
16
+ - Fix Rubygems homepage URL [\#43](https://github.com/bensheldon/good_job/pull/43) ([joshmn](https://github.com/joshmn))
17
+ - Move where\(scheduled\_at: Time.current\) into dynamic part of GoodJob::Job::Performer [\#42](https://github.com/bensheldon/good_job/pull/42) ([bensheldon](https://github.com/bensheldon))
18
+ - Replace Adapter inline boolean kwarg with execution\_mode instead [\#41](https://github.com/bensheldon/good_job/pull/41) ([bensheldon](https://github.com/bensheldon))
19
+ - Add more examples to Readme [\#39](https://github.com/bensheldon/good_job/pull/39) ([bensheldon](https://github.com/bensheldon))
20
+ - Add additional Rubocops and lint [\#38](https://github.com/bensheldon/good_job/pull/38) ([bensheldon](https://github.com/bensheldon))
21
+ - Always store a default queue\_name, priority and scheduled\_at; index by queue\_name and scheduled\_at [\#37](https://github.com/bensheldon/good_job/pull/37) ([bensheldon](https://github.com/bensheldon))
22
+
3
23
  ## [v0.6.0](https://github.com/bensheldon/good_job/tree/v0.6.0) (2020-07-15)
4
24
 
5
25
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v0.5.0...v0.6.0)
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # GoodJob
2
2
 
3
- GoodJob is a multithreaded, Postgres-based ActiveJob backend for Ruby on Rails.
3
+ GoodJob is a multithreaded, Postgres-based, ActiveJob backend for Ruby on Rails.
4
4
 
5
- Inspired by [Delayed::Job](https://github.com/collectiveidea/delayed_job) and [Que](https://github.com/que-rb/que), GoodJob’s design principles are:
5
+ **Inspired by [Delayed::Job](https://github.com/collectiveidea/delayed_job) and [Que](https://github.com/que-rb/que), GoodJob is designed for maximum compatibility with Ruby on Rails, ActiveJob, and Postgres to be simple and performant for most workloads.**
6
6
 
7
- - Stand on the shoulders of ActiveJob. For example, [exception](https://edgeguides.rubyonrails.org/active_job_basics.html#exceptions) and [retry](https://edgeguides.rubyonrails.org/active_job_basics.html#retrying-or-discarding-failed-jobs) behavior.
8
- - Stand on the shoulders of Ruby on Rails. For example, ActiveRecord ORM, connection pools, and [multithreaded support](https://guides.rubyonrails.org/threading_and_code_execution.html) with [Concurrent-Ruby](https://github.com/ruby-concurrency/concurrent-ruby).
9
- - Stand on the shoulders of Postgres. For example, Advisory Locks.
10
- - Convention over simplicity over performance.
7
+ - **Designed for ActiveJob.** Complete support for [async, queues, delays, priorities, timeouts, and retries](https://edgeguides.rubyonrails.org/active_job_basics.html) with near-zero configuration.
8
+ - **Built for Rails.** Fully adopts Ruby on Rails [threading and code execution guidelines](https://guides.rubyonrails.org/threading_and_code_execution.html) with [Concurrent::Ruby](https://github.com/ruby-concurrency/concurrent-ruby).
9
+ - **Backed by Postgres.** Relies upon Postgres integrity and session-level Advisory Locks to provide run-once safety and stay within the limits of `schema.rb`.
10
+ - **For most workloads.** Targets full-stack teams, economy-minded solo developers, and applications that enqueue less than 1-million jobs/day.
11
11
 
12
12
  ## Installation
13
13
 
@@ -43,6 +43,12 @@ $ bundle install
43
43
  t.integer :priority
44
44
  t.jsonb :serialized_params
45
45
  t.timestamp :scheduled_at
46
+ t.timestamp :performed_at
47
+ t.timestamp :finished_at
48
+ t.text :error
49
+
50
+ add_index :good_jobs, :scheduled_at, where: "(finished_at IS NULL)"
51
+ add_index :good_jobs, [:queue_name, :scheduled_at], where: "(finished_at IS NULL)"
46
52
  end
47
53
  end
48
54
  end
@@ -64,13 +70,18 @@ $ bundle install
64
70
 
65
71
  ```ruby
66
72
  # config/environments/development.rb
67
- config.active_job.queue_adapter = GoodJob::Adapter.new(inline: true)
73
+ config.active_job.queue_adapter = GoodJob::Adapter.new(execution_mode: :inline)
68
74
 
69
75
  # config/environments/test.rb
70
- config.active_job.queue_adapter = GoodJob::Adapter.new(inline: true)
76
+ config.active_job.queue_adapter = GoodJob::Adapter.new(execution_mode: :inline)
71
77
 
72
78
  # config/environments/production.rb
73
- config.active_job.queue_adapter = GoodJob::Adapter.new
79
+ config.active_job.queue_adapter = GoodJob::Adapter.new(execution_mode: :external)
80
+ ```
81
+
82
+ 1. Queue your job 🎉:
83
+ ```ruby
84
+ YourJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later
74
85
  ```
75
86
 
76
87
  1. In production, the scheduler is designed to run in its own process:
@@ -90,6 +101,25 @@ $ bundle install
90
101
  # [--queues=queue1,queue2] # Queues to work from. Separate multiple queues with commas (default: *)
91
102
  # [--poll-interval=N] # Interval between polls for available jobs in seconds (default: 1)
92
103
  ```
104
+
105
+ ### Taking advantage of ActiveJob
106
+
107
+ ActiveJob has a rich set of built-in functionality for timeouts, error handling, and retrying. For example:
108
+
109
+ ```ruby
110
+ class ApplicationJob < ActiveJob::Base
111
+ # Retry errors an infinite number of times with exponential back-off
112
+ retry_on StandardError, wait: :exponentially_longer, attempts: Float::INFINITY
113
+
114
+ # Timeout jobs after 10 minutes
115
+ JobTimeoutError = Class.new(StandardError)
116
+ around_perform do |_job, block|
117
+ Timeout.timeout(10.minutes, JobTimeoutError) do
118
+ block.call
119
+ end
120
+ end
121
+ end
122
+ ```
93
123
 
94
124
  ### Configuring Job Execution Threads
95
125
 
@@ -102,6 +132,55 @@ GoodJob executes enqueued jobs using threads. There is a lot than can be said ab
102
132
  3. `$ RAILS_MAX_THREADS=4 bundle exec good_job`
103
133
  4. Implicitly via Rails's database connection pool size (`ActiveRecord::Base.connection_pool.size`)
104
134
 
135
+ ### Migrating to GoodJob from a different ActiveJob backend
136
+
137
+ If your application is already using an ActiveJob backend, you will need to install GoodJob to enqueue and perform newly created jobs _and_ finish performing pre-existing jobs on the previous backend.
138
+
139
+ 1. Enqueue newly created jobs on GoodJob either entirely by setting `ActiveJob::Base.queue_adapter = :good_job` or progressively via individual job classes:
140
+
141
+ ```ruby
142
+ # jobs/specific_job.rb
143
+ class SpecificJob < ApplicationJob
144
+ self.queue_adapter = :good_job
145
+ # ...
146
+ end
147
+ ```
148
+
149
+ 1. Continue running executors for both backends. For example, on Heroku it's possible to run [two processes](https://help.heroku.com/CTFS2TJK/how-do-i-run-multiple-processes-on-a-dyno) within the same dyno:
150
+ ```procfile
151
+ # Procfile
152
+ # ...
153
+ worker: bundle exec que ./config/environment.rb & bundle exec good_job & wait -n
154
+ ```
155
+
156
+ 1. Once you are confident that no unperformed jobs remain in the previous ActiveJob backend, code and configuration for that backend can be completely removed.
157
+
158
+ ### Monitoring and preserving worked jobs
159
+
160
+ GoodJob is fully instrumented with [`ActiveSupport::Notifications`](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#introduction-to-instrumentation).
161
+
162
+ By default, GoodJob will delete job records after they are run, regardless of whether they succeed or not (raising a kind of `StandardError`), unless they are interrupted (raising a kind of `Exception`).
163
+
164
+ To preserve job records for later inspection, set an initializer:
165
+
166
+ ```ruby
167
+ # config/initializers/good_job.rb
168
+ GoodJob.preserve_job_records = true
169
+ ```
170
+
171
+ It is also necessary to delete these preserved jobs from the database after a certain time period:
172
+
173
+ - For example, in a Rake task:
174
+
175
+ ```ruby
176
+ # GoodJob::Job.finished(1.day.ago).delete_all
177
+ ```
178
+ - For example, using the `good_job` command-line utility:
179
+
180
+ ```bash
181
+ $ bundle exec good_job cleanup_preserved_jobs --before-seconds-ago=86400
182
+ ```
183
+
105
184
  ## Development
106
185
 
107
186
  To run tests:
@@ -1,12 +1,18 @@
1
1
  module ActiveJob
2
2
  module QueueAdapters
3
3
  class GoodJobAdapter < GoodJob::Adapter
4
- def initialize
5
- if Rails.env.development? || Rails.env.test?
6
- super(inline: true)
7
- else
8
- super(inline: false)
9
- end
4
+ def initialize(execution_mode: nil)
5
+ execution_mode = if execution_mode
6
+ execution_mode
7
+ elsif ENV['GOOD_JOB_EXECUTION_MODE'].present?
8
+ ENV['GOOD_JOB_EXECUTION_MODE'].to_sym
9
+ elsif Rails.env.development? || Rails.env.test?
10
+ :inline
11
+ else
12
+ :external
13
+ end
14
+
15
+ super(execution_mode: execution_mode)
10
16
  end
11
17
  end
12
18
  end
@@ -4,13 +4,15 @@ require 'good_job/railtie'
4
4
  require 'good_job/logging'
5
5
  require 'good_job/lockable'
6
6
  require 'good_job/job'
7
- require "good_job/scheduler"
7
+ require 'good_job/scheduler'
8
8
  require 'good_job/adapter'
9
9
  require 'good_job/pg_locks'
10
+ require 'good_job/performer'
10
11
 
11
12
  require 'active_job/queue_adapters/good_job_adapter'
12
13
 
13
14
  module GoodJob
15
+ mattr_accessor :preserve_job_records, default: false
14
16
  include Logging
15
17
 
16
18
  ActiveSupport.run_load_hooks(:good_job, self)
@@ -1,7 +1,18 @@
1
1
  module GoodJob
2
2
  class Adapter
3
- def initialize(inline: false)
4
- @inline = inline
3
+ EXECUTION_MODES = [:inline, :external].freeze # TODO: async
4
+
5
+ def initialize(execution_mode: nil, inline: false)
6
+ if inline
7
+ ActiveSupport::Deprecation.warn('GoodJob::Adapter#new(inline: true) is deprecated; use GoodJob::Adapter.new(execution_mode: :inline) instead')
8
+ @execution_mode = :inline
9
+ elsif execution_mode
10
+ raise ArgumentError, "execution_mode: must be one of #{EXECUTION_MODES.join(', ')}." unless EXECUTION_MODES.include?(execution_mode)
11
+
12
+ @execution_mode = execution_mode
13
+ else
14
+ @execution_mode = :external
15
+ end
5
16
  end
6
17
 
7
18
  def enqueue(active_job)
@@ -11,11 +22,11 @@ module GoodJob
11
22
  def enqueue_at(active_job, timestamp)
12
23
  good_job = GoodJob::Job.enqueue(
13
24
  active_job,
14
- scheduled_at: timestamp ? Time.at(timestamp) : nil,
15
- create_with_advisory_lock: inline?
25
+ scheduled_at: timestamp ? Time.zone.at(timestamp) : nil,
26
+ create_with_advisory_lock: execute_inline?
16
27
  )
17
28
 
18
- if inline?
29
+ if execute_inline?
19
30
  begin
20
31
  good_job.perform
21
32
  ensure
@@ -30,8 +41,17 @@ module GoodJob
30
41
  nil
31
42
  end
32
43
 
44
+ def execute_inline?
45
+ @execution_mode == :inline
46
+ end
47
+
33
48
  def inline?
34
- @inline
49
+ ActiveSupport::Deprecation.warn('GoodJob::Adapter::inline? is deprecated; use GoodJob::Adapter::execute_inline? instead')
50
+ execute_inline?
51
+ end
52
+
53
+ def execute_externally?
54
+ @execution_mode == :external
35
55
  end
36
56
  end
37
57
  end
@@ -36,11 +36,16 @@ module GoodJob
36
36
  ENV['GOOD_JOB_POLL_INTERVAL']
37
37
  ).to_i
38
38
 
39
- job_query = GoodJob::Job.all
39
+ job_query = GoodJob::Job.all.priority_ordered
40
40
  queue_names_without_all = queue_names.reject { |q| q == '*' }
41
41
  job_query = job_query.where(queue_name: queue_names_without_all) unless queue_names_without_all.size.zero?
42
42
 
43
- job_performer = job_query.only_scheduled.priority_ordered.to_performer
43
+ performer_method = if GoodJob.preserve_job_records
44
+ :perform_with_advisory_lock_and_preserve_job_records
45
+ else
46
+ :perform_with_advisory_lock_and_destroy_job_records
47
+ end
48
+ job_performer = GoodJob::Performer.new(job_query, performer_method)
44
49
 
45
50
  $stdout.puts "GoodJob worker starting with max_threads=#{max_threads} on queues=#{queue_names.join(',')}"
46
51
 
@@ -68,6 +73,19 @@ module GoodJob
68
73
  $stdout.puts "GoodJob's jobs finished, exiting..."
69
74
  end
70
75
 
76
+ desc :cleanup_preserved_jobs, "Delete preserved job records"
77
+ method_option :before_seconds_ago,
78
+ type: :numeric,
79
+ default: 24 * 60 * 60,
80
+ desc: "Delete records finished more than this many seconds ago"
81
+ def cleanup_preserved_jobs
82
+ require RAILS_ENVIRONMENT_RB
83
+
84
+ timestamp = Time.current - options[:before_seconds_ago]
85
+ result = GoodJob::Job.finished(timestamp).delete_all
86
+ $stdout.puts "Deleted #{result} preserved #{'job'.pluralize(result)} finished before #{timestamp}."
87
+ end
88
+
71
89
  default_task :start
72
90
  end
73
91
  end
@@ -2,39 +2,56 @@ module GoodJob
2
2
  class Job < ActiveRecord::Base
3
3
  include Lockable
4
4
 
5
- self.table_name = 'good_jobs'
5
+ PreviouslyPerformedError = Class.new(StandardError)
6
6
 
7
- scope :only_scheduled, -> { where("scheduled_at < ?", Time.current).or(where(scheduled_at: nil)) }
8
- scope :priority_ordered, -> { order(priority: :desc) }
9
- scope :to_performer, -> { Performer.new(self) }
7
+ DEFAULT_QUEUE_NAME = 'default'.freeze
8
+ DEFAULT_PRIORITY = 0
10
9
 
11
- class Performer
12
- def initialize(query)
13
- @query = query
14
- end
10
+ self.table_name = 'good_jobs'.freeze
15
11
 
16
- def next
17
- good_job = nil
12
+ scope :unfinished, (lambda do
13
+ if column_names.include?('finished_at')
14
+ where(finished_at: nil)
15
+ else
16
+ ActiveSupport::Deprecation.warn('GoodJob expects a good_jobs.finished_at column to exist. Please see the GoodJob README.md for migration instructions.')
17
+ nil
18
+ end
19
+ end)
20
+ scope :only_scheduled, -> { where(arel_table['scheduled_at'].lteq(Time.current)).or(where(scheduled_at: nil)) }
21
+ scope :priority_ordered, -> { order(priority: :desc) }
22
+ scope :finished, ->(timestamp = nil) { timestamp ? where(arel_table['finished_at'].lteq(timestamp)) : where.not(finished_at: nil) }
18
23
 
19
- @query.limit(1).with_advisory_lock do |good_jobs|
20
- good_job = good_jobs.first
21
- break unless good_job
24
+ def self.perform_with_advisory_lock(destroy_after: !GoodJob.preserve_job_records)
25
+ good_job = nil
26
+ result = nil
27
+ error = nil
22
28
 
23
- good_job.perform
24
- end
29
+ unfinished.only_scheduled.limit(1).with_advisory_lock do |good_jobs|
30
+ good_job = good_jobs.first
31
+ break unless good_job
25
32
 
26
- good_job
33
+ result, error = good_job.perform(destroy_after: destroy_after)
27
34
  end
35
+
36
+ [good_job, result, error] if good_job
37
+ end
38
+
39
+ def self.perform_with_advisory_lock_and_preserve_job_records
40
+ perform_with_advisory_lock(destroy_after: false)
41
+ end
42
+
43
+ def self.perform_with_advisory_lock_and_destroy_job_records
44
+ perform_with_advisory_lock(destroy_after: true)
28
45
  end
29
46
 
30
47
  def self.enqueue(active_job, scheduled_at: nil, create_with_advisory_lock: false)
31
48
  good_job = nil
32
49
  ActiveSupport::Notifications.instrument("enqueue_job.good_job", { active_job: active_job, scheduled_at: scheduled_at, create_with_advisory_lock: create_with_advisory_lock }) do |instrument_payload|
33
50
  good_job = GoodJob::Job.new(
34
- queue_name: active_job.queue_name,
35
- priority: active_job.priority,
51
+ queue_name: active_job.queue_name.presence || DEFAULT_QUEUE_NAME,
52
+ priority: active_job.priority || DEFAULT_PRIORITY,
36
53
  serialized_params: active_job.serialize,
37
- scheduled_at: scheduled_at,
54
+ scheduled_at: scheduled_at || Time.current,
38
55
  create_with_advisory_lock: create_with_advisory_lock
39
56
  )
40
57
 
@@ -47,16 +64,43 @@ module GoodJob
47
64
  good_job
48
65
  end
49
66
 
50
- def perform
67
+ def perform(destroy_after: true)
68
+ raise PreviouslyPerformedError, 'Cannot perform a job that has already been performed' if finished_at
69
+
70
+ result = nil
71
+ error = nil
72
+
51
73
  ActiveSupport::Notifications.instrument("before_perform_job.good_job", { good_job: self })
74
+ self.performed_at = Time.current
75
+ save! unless destroy_after
76
+
52
77
  ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self }) do
53
78
  params = serialized_params.merge(
54
79
  "provider_job_id" => id
55
80
  )
56
- ActiveJob::Base.execute(params)
81
+ begin
82
+ result = ActiveJob::Base.execute(params)
83
+ rescue StandardError => e
84
+ error = e
85
+ end
86
+ end
87
+
88
+ if error.nil? && result.is_a?(Exception)
89
+ error = result
90
+ result = nil
91
+ end
57
92
 
93
+ error_message = "#{error.class}: #{error.message}" if error
94
+ self.error = error_message
95
+ self.finished_at = Time.current
96
+
97
+ if destroy_after
58
98
  destroy!
99
+ else
100
+ save!
59
101
  end
102
+
103
+ [result, error]
60
104
  end
61
105
  end
62
106
  end
@@ -37,14 +37,17 @@ module GoodJob
37
37
  scope :owns_advisory_locked, -> { joins_advisory_locks.where('"pg_locks"."pid" = pg_backend_pid()') }
38
38
 
39
39
  attr_accessor :create_with_advisory_lock
40
+
40
41
  after_create -> { advisory_lock }, if: :create_with_advisory_lock
41
42
  end
42
43
 
43
44
  class_methods do
44
- def with_advisory_lock(&block)
45
+ def with_advisory_lock
46
+ raise ArgumentError, "Must provide a block" unless block_given?
47
+
45
48
  records = advisory_lock.to_a
46
49
  begin
47
- block.call(records)
50
+ yield(records)
48
51
  ensure
49
52
  records.each(&:advisory_unlock)
50
53
  end
@@ -73,6 +76,8 @@ module GoodJob
73
76
  end
74
77
 
75
78
  def with_advisory_lock
79
+ raise ArgumentError, "Must provide a block" unless block_given?
80
+
76
81
  advisory_lock!
77
82
  yield
78
83
  ensure
@@ -0,0 +1,12 @@
1
+ module GoodJob
2
+ class Performer
3
+ def initialize(target, method_name)
4
+ @target = target
5
+ @method_name = method_name
6
+ end
7
+
8
+ def next
9
+ @target.public_send(@method_name)
10
+ end
11
+ end
12
+ end
@@ -59,21 +59,21 @@ module GoodJob
59
59
 
60
60
  def create_thread
61
61
  future = Concurrent::Future.new(args: [@performer], executor: @pool) do |performer|
62
- result = nil
63
- Rails.application.executor.wrap { result = performer.next }
64
- result
62
+ output = nil
63
+ Rails.application.executor.wrap { output = performer.next }
64
+ output
65
65
  end
66
66
  future.add_observer(self, :task_observer)
67
67
  future.execute
68
68
  end
69
69
 
70
- def timer_observer(time, executed_task, error)
71
- ActiveSupport::Notifications.instrument("finished_timer_task.good_job", { result: executed_task, error: error, time: time })
70
+ def timer_observer(time, executed_task, thread_error)
71
+ ActiveSupport::Notifications.instrument("finished_timer_task.good_job", { result: executed_task, error: thread_error, time: time })
72
72
  end
73
73
 
74
- def task_observer(time, result, error)
75
- ActiveSupport::Notifications.instrument("finished_job_task.good_job", { result: result, error: error, time: time })
76
- create_thread if result
74
+ def task_observer(time, output, thread_error)
75
+ ActiveSupport::Notifications.instrument("finished_job_task.good_job", { result: output, error: thread_error, time: time })
76
+ create_thread if output
77
77
  end
78
78
  end
79
79
  end
@@ -1,3 +1,3 @@
1
1
  module GoodJob
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.9.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-15 00:00:00.000000000 Z
11
+ date: 2020-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -178,6 +178,34 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rubocop-performance
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rubocop-rails
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
181
209
  - !ruby/object:Gem::Dependency
182
210
  name: rubocop-rspec
183
211
  requirement: !ruby/object:Gem::Requirement
@@ -214,18 +242,19 @@ files:
214
242
  - lib/good_job/job.rb
215
243
  - lib/good_job/lockable.rb
216
244
  - lib/good_job/logging.rb
245
+ - lib/good_job/performer.rb
217
246
  - lib/good_job/pg_locks.rb
218
247
  - lib/good_job/railtie.rb
219
248
  - lib/good_job/scheduler.rb
220
249
  - lib/good_job/version.rb
221
- homepage: https://github.com/benheldon/good_job
250
+ homepage: https://github.com/bensheldon/good_job
222
251
  licenses:
223
252
  - MIT
224
253
  metadata:
225
254
  bug_tracker_uri: https://github.com/bensheldon/good_job/issues
226
255
  changelog_uri: https://github.com/bensheldon/good_job/blob/master/CHANGELOG.md
227
256
  documentation_uri: https://rdoc.info/github/bensheldon/good_job
228
- homepage_uri: https://github.com/benheldon/good_job
257
+ homepage_uri: https://github.com/bensheldon/good_job
229
258
  source_code_uri: https://github.com/bensheldon/good_job
230
259
  post_install_message:
231
260
  rdoc_options: