workhorse 0.3.9 → 0.4.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: 4bcaee406798a7da933fd0a48c7e556447a589a6be42f73d259902ae3a69b339
4
- data.tar.gz: 2201639a37267a0980b873d0398222fbd0da0ac0a672244180a37b7e0b5265aa
3
+ metadata.gz: 713d9c117c06a2e448ec9b963b216df9e46741f81f7e5e80c62f4b3648b1d29b
4
+ data.tar.gz: f625a84d3173812546d200feac847cbd16dcd800ee6d2f188e44302c79078211
5
5
  SHA512:
6
- metadata.gz: 4f7813c2d5945df4a9a4757b2dd3ac2f270da19888b5d47cff50a3e5321bef65ded194d0775b30719754b20018c4050dc46ee0f602f445871661edf400a4afda
7
- data.tar.gz: db1da7961291a3eefd8c066fe9b36e8e0d4ce0fcb673968229b364b79e5fbe37e06574f506f1a1024487dbb1d73d667b92a14726d9adcce8791962a850505d8a
6
+ metadata.gz: 25818f59461494d50b08a00f863c81d48521998462643134454fac6b8ba7c6c7d2358213342373c01b4dbc5ef1dd4d02bdd561e1a1c9c5294d840095e1e5b931
7
+ data.tar.gz: 833e6132e223e85060253f678109dad5624802ad163c990caae78fc8a6de44820318505c36bb5709e2f7fd591f4ce7ed71aa2ed2487b6de43d3cec17eeb085da
data/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Workhorse Change log
2
2
 
3
- ## Unreleased
3
+ ## 0.4.0 - 2019-05-15
4
+
5
+ * Added instruments for clearing DbJob data. (PR #17)
6
+
7
+ * Added instant repolling feature. (#PR18)
4
8
 
5
9
  ## 0.3.9 – 2019-03-09
6
10
 
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # Workhorse
5
5
 
6
- Multi-threaded job backend with database queuing for ruby.
6
+ Multi-threaded job backend with database queuing for ruby. Battle-tested and ready for production-use.
7
7
 
8
8
  ## Introduction
9
9
 
@@ -240,6 +240,23 @@ Workhorse::Daemon::ShellHandler.run count: 5 do
240
240
  end
241
241
  ```
242
242
 
243
+ ### Instant repolling
244
+
245
+ Per default, each worker only polls in the given interval. This means that if
246
+ you schedule, for example, 50 jobs at once and have a polling interval of 1
247
+ minute with a queue size of 1, the poller would tackle the first job and then
248
+ wait for a whole minute until the next poll. This would mean that these 50 jobs
249
+ would take at least 50 minutes to be executed, even if they only take a few
250
+ seconds each.
251
+
252
+ This is where *instant repolling* comes into play: Using the worker option
253
+ `instant_repolling`, you can force the poller to automatically re-poll the
254
+ database whenever a job has been performed. It then goes back to the usual
255
+ polling interval.
256
+
257
+ This setting is recommended for all setups and may eventually be enabled by
258
+ default.
259
+
243
260
  ## Exception handling
244
261
 
245
262
  Per default, exceptions occurring in a worker thread will only be visible in the
@@ -257,6 +274,77 @@ Workhorse.setup do |config|
257
274
  end
258
275
  ```
259
276
 
277
+ ## Handling database jobs
278
+
279
+ Jobs stored in the database can be accessed via the ActiveRecord model
280
+ {Workhorse::DbJob}. This is the model representing a specific job database entry
281
+ and is not to be confused with the actual job class you're enqueueing.
282
+
283
+ ### Obtaining database jobs
284
+
285
+ DbJobs are returned to you when enqueuing new jobs:
286
+
287
+ ```ruby
288
+ db_job = Workhorse.enqueue(MyJob.new)
289
+ ```
290
+
291
+ You can also obtain a job via its ID that you either get from a returned job
292
+ (see example above) or else by manually querying the database table:
293
+
294
+ ```ruby
295
+ db_job = Workhorse::DbJob.find(42)
296
+ ```
297
+
298
+ Note that database job objects reflect the job at the point in time when the
299
+ database job object has been instantiated. To make sure you're looking at the
300
+ latest job info, use the in-place `reload` method:
301
+
302
+ ```ruby
303
+ db_job.reload
304
+ ```
305
+
306
+ You can also retrieve a list of jobs in a specific state using one of the
307
+ following methods:
308
+
309
+ ```ruby
310
+ DbJob.waiting
311
+ DbJob.locked
312
+ DbJob.started
313
+ DbJob.succeeded
314
+ DbJob.failed
315
+ ```
316
+
317
+ ### Resetting jobs
318
+
319
+ Jobs in a state other than `waiting` are either being processed or else already
320
+ in a final state such as `succeeded` and won't be performed again. Workhorse
321
+ provides an API method for resetting jobs in the following cases:
322
+
323
+ * A job has succeeded or failed (states `succeeded` and `failed`) and needs to
324
+ re-run. In these cases, perform a non-forced reset:
325
+
326
+ ```ruby
327
+ db_job.reset!
328
+ ```
329
+
330
+ This is always safe to do, even with workers running.
331
+
332
+ * A job is stuck in state `locked` or `started` and the corresponding worker
333
+ (check the database field `locked_by`) is not running anymore, i.e. due to a
334
+ database connection loss or an unexpected worker crash. In these cases, the
335
+ job will never be processed, and, if the job is in a queue, the entire queue is
336
+ considered to be locked and no further jobs will be processed in this queue.
337
+
338
+ In these cases, make sure the worker is stopped and perform a forced reset:
339
+
340
+ ```ruby
341
+ db_job.reset!(true)
342
+ ```
343
+
344
+ Performing a reset will reset the job state to `waiting` and it will be
345
+ processed again. All meta fields will be reset as well. See inline documentation
346
+ of `Workhorse::DbJob#reset!` for more details.
347
+
260
348
  ## Frequently asked questions
261
349
 
262
350
  Please consult the [FAQ](FAQ.md).
data/Rakefile CHANGED
@@ -11,11 +11,12 @@ task :gemspec do
11
11
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
12
12
  spec.require_paths = ['lib']
13
13
 
14
- spec.add_development_dependency 'bundler', '~> 1.3'
14
+ spec.add_development_dependency 'bundler'
15
15
  spec.add_development_dependency 'rake'
16
16
  spec.add_development_dependency 'rubocop', '0.51.0'
17
17
  spec.add_development_dependency 'minitest'
18
18
  spec.add_development_dependency 'mysql2'
19
+ spec.add_development_dependency 'colorize'
19
20
  spec.add_development_dependency 'benchmark-ips'
20
21
  spec.add_dependency 'activesupport'
21
22
  spec.add_dependency 'activerecord'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.9
1
+ 0.4.0
@@ -12,6 +12,58 @@ module Workhorse
12
12
 
13
13
  self.table_name = 'jobs'
14
14
 
15
+ def self.waiting
16
+ where(state: STATE_WAITING)
17
+ end
18
+
19
+ def self.locked
20
+ where(state: STATE_LOCKED)
21
+ end
22
+
23
+ def self.started
24
+ where(state: STATE_STARTED)
25
+ end
26
+
27
+ def self.succeeded
28
+ where(state: STATE_SUCCEEDED)
29
+ end
30
+
31
+ def self.failed
32
+ where(state: STATE_FAILED)
33
+ end
34
+
35
+ # Resets job to state "waiting" and clears all meta fields
36
+ # set by workhorse in course of processing this job.
37
+ #
38
+ # This is only allowed if the job is in a final state ("succeeded" or
39
+ # "failed"), as only those jobs are safe to modify; workhorse will not touch
40
+ # these jobs. To reset a job without checking the state it is in, set
41
+ # "force" to true. Prior to doing so, ensure that the job is not still being
42
+ # processed by a worker. If possible, shut down all workers before
43
+ # performing a forced reset.
44
+ #
45
+ # After the job is reset, it will be performed again. If you reset a job
46
+ # that has already been performed ("succeeded") or partially performed
47
+ # ("failed"), make sure the actions performed in the job are repeatable or
48
+ # have been rolled back. E.g. if the job already wrote something to an
49
+ # external API, it may cause inconsistencies if the job is performed again.
50
+ def reset!(force = false)
51
+ unless force
52
+ assert_state! STATE_SUCCEEDED, STATE_FAILED
53
+ end
54
+
55
+ self.state = STATE_WAITING
56
+ self.locked_at = nil
57
+ self.locked_by = nil
58
+ self.started_at = nil
59
+ self.succeeded_at = nil
60
+ self.failed_at = nil
61
+ self.last_error = nil
62
+
63
+ save!
64
+ end
65
+
66
+ # @private Only to be used by workhorse
15
67
  def mark_locked!(worker_id)
16
68
  if changed?
17
69
  fail "Dirty jobs can't be locked."
@@ -27,6 +79,7 @@ module Workhorse
27
79
  save!
28
80
  end
29
81
 
82
+ # @private Only to be used by workhorse
30
83
  def mark_started!
31
84
  assert_state! STATE_LOCKED
32
85
 
@@ -35,6 +88,7 @@ module Workhorse
35
88
  save!
36
89
  end
37
90
 
91
+ # @private Only to be used by workhorse
38
92
  def mark_failed!(exception)
39
93
  assert_state! STATE_LOCKED, STATE_STARTED
40
94
 
@@ -44,6 +98,7 @@ module Workhorse
44
98
  save!
45
99
  end
46
100
 
101
+ # @private Only to be used by workhorse
47
102
  def mark_succeeded!
48
103
  assert_state! STATE_STARTED
49
104
 
@@ -57,15 +112,5 @@ module Workhorse
57
112
  fail "Job #{id} is not in state #{states.inspect} but in state #{state.inspect}."
58
113
  end
59
114
  end
60
-
61
- def assert_locked_by!(worker_id)
62
- assert_state! STATE_WAITING
63
-
64
- if locked_by.nil?
65
- fail "Job #{id} is not locked by any worker."
66
- elsif locked_by != worker_id
67
- fail "Job #{id} is locked by another worker (#{locked_by})."
68
- end
69
- end
70
115
  end
71
116
  end
@@ -8,6 +8,7 @@ module Workhorse
8
8
  @running = false
9
9
  @table = Workhorse::DbJob.arel_table
10
10
  @is_oracle = ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
11
+ @instant_repoll = Concurrent::AtomicBoolean.new(false)
11
12
  end
12
13
 
13
14
  def running?
@@ -41,18 +42,27 @@ module Workhorse
41
42
  @thread.join
42
43
  end
43
44
 
45
+ # Call this to interrupt current sleep and perform the next poll as soon as
46
+ # possible, then resume in the normal polling interval.
47
+ def instant_repoll!
48
+ worker.log 'Aborting next sleep to perform instant repoll', :debug
49
+ @instant_repoll.make_true
50
+ end
51
+
44
52
  private
45
53
 
46
54
  def sleep
47
55
  remaining = worker.polling_interval
48
56
 
49
- while running? && remaining > 0
57
+ while running? && remaining > 0 && @instant_repoll.false?
50
58
  Kernel.sleep 0.1
51
59
  remaining -= 0.1
52
60
  end
53
61
  end
54
62
 
55
63
  def poll
64
+ @instant_repoll.make_false
65
+
56
66
  Workhorse.tx_callback.call do
57
67
  # As we are the only thread posting into the worker pool, it is safe to
58
68
  # get the number of idle threads without mutex synchronization. The
@@ -150,7 +160,7 @@ module Workhorse
150
160
 
151
161
  select = select.lock
152
162
 
153
- return Workhorse::DbJob.find_by_sql(select.to_sql)
163
+ return Workhorse::DbJob.find_by_sql(select.to_sql).to_a
154
164
  end
155
165
 
156
166
  # Returns a fresh Arel select manager containing the id of all waiting jobs,
@@ -14,6 +14,11 @@ module Workhorse
14
14
  )
15
15
  @mutex = Mutex.new
16
16
  @active_threads = Concurrent::AtomicFixnum.new(0)
17
+ @on_idle = nil
18
+ end
19
+
20
+ def on_idle(&block)
21
+ @on_idle = block
17
22
  end
18
23
 
19
24
  # Posts a new work unit to the pool.
@@ -32,6 +37,7 @@ module Workhorse
32
37
  yield
33
38
  ensure
34
39
  active_threads.decrement
40
+ @on_idle&.call
35
41
  end
36
42
  end
37
43
  end
@@ -35,10 +35,12 @@ module Workhorse
35
35
  # worker properly on INT and TERM signals.
36
36
  # @param quiet [Boolean] If this is set to `false`, the worker will also log
37
37
  # to STDOUT.
38
+ # @param instant_repolling [Boolean] If this is set to `true`, the worker
39
+ # immediately re-polls for new jobs when a job execution has finished.
38
40
  # @param logger [Logger] An optional logger the worker will append to. This
39
41
  # can be any instance of ruby's `Logger` but is commonly set to
40
42
  # `Rails.logger`.
41
- def initialize(queues: [], pool_size: nil, polling_interval: 300, auto_terminate: true, quiet: true, logger: nil)
43
+ def initialize(queues: [], pool_size: nil, polling_interval: 300, auto_terminate: true, quiet: true, instant_repolling: false, logger: nil)
42
44
  @queues = queues
43
45
  @pool_size = pool_size || queues.size + 1
44
46
  @polling_interval = polling_interval
@@ -55,6 +57,10 @@ module Workhorse
55
57
  fail 'Polling interval must be a multiple of 0.1.'
56
58
  end
57
59
 
60
+ if instant_repolling
61
+ @pool.on_idle { @poller.instant_repoll! }
62
+ end
63
+
58
64
  check_rails_env if defined?(Rails)
59
65
  end
60
66
 
@@ -11,6 +11,14 @@ class WorkhorseTest < ActiveSupport::TestCase
11
11
 
12
12
  protected
13
13
 
14
+ def capture_log(level: :debug)
15
+ io = StringIO.new
16
+ logger = Logger.new(io, level: level)
17
+ yield logger
18
+ io.close
19
+ return io.string
20
+ end
21
+
14
22
  def work(time = 2, options = {})
15
23
  options[:pool_size] ||= 5
16
24
  options[:polling_interval] ||= 1
@@ -0,0 +1,58 @@
1
+ require 'test_helper'
2
+
3
+ class Workhorse::DbJobTest < WorkhorseTest
4
+ def test_reset_succeeded
5
+ job = Workhorse.enqueue(BasicJob.new(sleep_time: 0))
6
+ work 0.5
7
+ job.reload
8
+ assert_equal 'succeeded', job.state
9
+
10
+ job.reset!
11
+
12
+ assert_clean job
13
+ end
14
+
15
+ def test_reset_failed
16
+ job = Workhorse.enqueue FailingTestJob
17
+ work 0.5
18
+ job.reload
19
+ assert_equal 'failed', job.state
20
+
21
+ job.reset!
22
+
23
+ assert_clean job
24
+ end
25
+
26
+ def test_reset_locked_unforced
27
+ job = Workhorse.enqueue(BasicJob.new(sleep_time: 0))
28
+ job.mark_locked!(42)
29
+
30
+ err = assert_raises do
31
+ job.reset!
32
+ end
33
+ assert_equal %(Job #{job.id} is not in state [:succeeded, :failed] but in state "locked".), err.message
34
+ end
35
+
36
+ def test_forced_reset
37
+ job = Workhorse.enqueue(BasicJob.new(sleep_time: 0))
38
+ job.mark_locked!(42)
39
+
40
+ assert_nothing_raised do
41
+ job.reset!(true)
42
+ end
43
+
44
+ assert_clean job
45
+ end
46
+
47
+ private
48
+
49
+ def assert_clean(job)
50
+ assert_equal 'waiting', job.state
51
+ assert_nil job.locked_by
52
+ assert_nil job.locked_at
53
+ assert_nil job.started_at
54
+ assert_nil job.failed_at
55
+ assert_nil job.succeeded_at
56
+ assert_nil job.last_error
57
+ end
58
+ end
@@ -67,4 +67,42 @@ class Workhorse::PollerTest < WorkhorseTest
67
67
 
68
68
  assert_equal [nil], w.poller.send(:valid_queues)
69
69
  end
70
+
71
+ def test_with_instant_repolling
72
+ 3.times do
73
+ Workhorse.enqueue BasicJob.new(sleep_time: 0)
74
+ end
75
+
76
+ assert_equal 3, Workhorse::DbJob.where(state: :waiting).count
77
+
78
+ log = capture_log do |logger|
79
+ work 2, instant_repolling: true, polling_interval: 5, pool_size: 1, logger: logger
80
+ end
81
+
82
+ assert_repolling_logged 3, log
83
+ assert_equal 3, Workhorse::DbJob.where(state: :succeeded).count
84
+ end
85
+
86
+ def test_without_instant_repolling
87
+ 3.times do
88
+ Workhorse.enqueue BasicJob.new(sleep_time: 0)
89
+ end
90
+
91
+ log = capture_log do |logger|
92
+ work 0.5, instant_repolling: false, polling_interval: 5, pool_size: 1, logger: logger
93
+ end
94
+
95
+ assert_repolling_logged 0, log
96
+ assert_equal 1, Workhorse::DbJob.where(state: :succeeded).count
97
+ end
98
+
99
+ private
100
+
101
+ def setup
102
+ Workhorse::DbJob.delete_all
103
+ end
104
+
105
+ def assert_repolling_logged(count, log)
106
+ assert_equal count, log.scan(/Aborting next sleep to perform instant repoll/m).size
107
+ end
70
108
  end
@@ -19,6 +19,28 @@ class Workhorse::PoolTest < WorkhorseTest
19
19
  end
20
20
  end
21
21
 
22
+ def test_on_idle
23
+ on_idle_calls = Concurrent::AtomicFixnum.new
24
+
25
+ with_pool 2 do |p|
26
+ p.on_idle { on_idle_calls.increment }
27
+
28
+ assert_equal 0, on_idle_calls.value
29
+
30
+ p.post { sleep 0.2 }
31
+ p.post { sleep 0.4 }
32
+
33
+ sleep 0.1
34
+ assert_equal 0, on_idle_calls.value
35
+
36
+ sleep 0.2
37
+ assert_equal 1, on_idle_calls.value
38
+
39
+ sleep 0.1
40
+ assert_equal 2, on_idle_calls.value
41
+ end
42
+ end
43
+
22
44
  def test_overflow
23
45
  with_pool 5 do |p|
24
46
  5.times { p.post { sleep 0.2 } }
@@ -5,10 +5,10 @@ class Workhorse::WorkerTest < WorkhorseTest
5
5
  with_worker(pool_size: 5, polling_interval: 0.2) do |w|
6
6
  assert_equal 5, w.idle
7
7
 
8
- sleep 0.1
8
+ sleep 0.05
9
9
  Workhorse.enqueue BasicJob.new(sleep_time: 0.2)
10
10
 
11
- sleep 0.2
11
+ sleep 0.25
12
12
  assert_equal 4, w.idle
13
13
 
14
14
  sleep 0.2
data/workhorse.gemspec CHANGED
@@ -1,39 +1,41 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: workhorse 0.3.9 ruby lib
2
+ # stub: workhorse 0.4.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "workhorse".freeze
6
- s.version = "0.3.9"
6
+ s.version = "0.4.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
10
10
  s.authors = ["Sitrox".freeze]
11
- s.date = "2019-04-09"
12
- s.files = [".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, ".travis.yml".freeze, "CHANGELOG.md".freeze, "FAQ.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "RUBY_VERSION".freeze, "Rakefile".freeze, "VERSION".freeze, "bin/rubocop".freeze, "lib/generators/workhorse/install_generator.rb".freeze, "lib/generators/workhorse/templates/bin/workhorse.rb".freeze, "lib/generators/workhorse/templates/config/initializers/workhorse.rb".freeze, "lib/generators/workhorse/templates/create_table_jobs.rb".freeze, "lib/workhorse.rb".freeze, "lib/workhorse/daemon.rb".freeze, "lib/workhorse/daemon/shell_handler.rb".freeze, "lib/workhorse/db_job.rb".freeze, "lib/workhorse/enqueuer.rb".freeze, "lib/workhorse/jobs/cleanup_succeeded_jobs.rb".freeze, "lib/workhorse/jobs/run_rails_op.rb".freeze, "lib/workhorse/performer.rb".freeze, "lib/workhorse/poller.rb".freeze, "lib/workhorse/pool.rb".freeze, "lib/workhorse/worker.rb".freeze, "test/lib/db_schema.rb".freeze, "test/lib/jobs.rb".freeze, "test/lib/test_helper.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/worker_test.rb".freeze, "workhorse.gemspec".freeze]
11
+ s.date = "2019-05-15"
12
+ s.files = [".gitignore".freeze, ".releaser_config".freeze, ".rubocop.yml".freeze, ".travis.yml".freeze, "CHANGELOG.md".freeze, "FAQ.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "RUBY_VERSION".freeze, "Rakefile".freeze, "VERSION".freeze, "bin/rubocop".freeze, "lib/generators/workhorse/install_generator.rb".freeze, "lib/generators/workhorse/templates/bin/workhorse.rb".freeze, "lib/generators/workhorse/templates/config/initializers/workhorse.rb".freeze, "lib/generators/workhorse/templates/create_table_jobs.rb".freeze, "lib/workhorse.rb".freeze, "lib/workhorse/daemon.rb".freeze, "lib/workhorse/daemon/shell_handler.rb".freeze, "lib/workhorse/db_job.rb".freeze, "lib/workhorse/enqueuer.rb".freeze, "lib/workhorse/jobs/cleanup_succeeded_jobs.rb".freeze, "lib/workhorse/jobs/run_rails_op.rb".freeze, "lib/workhorse/performer.rb".freeze, "lib/workhorse/poller.rb".freeze, "lib/workhorse/pool.rb".freeze, "lib/workhorse/worker.rb".freeze, "test/lib/db_schema.rb".freeze, "test/lib/jobs.rb".freeze, "test/lib/test_helper.rb".freeze, "test/workhorse/db_job_test.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/worker_test.rb".freeze, "workhorse.gemspec".freeze]
13
13
  s.rubygems_version = "3.0.3".freeze
14
14
  s.summary = "Multi-threaded job backend with database queuing for ruby.".freeze
15
- s.test_files = ["test/lib/db_schema.rb".freeze, "test/lib/jobs.rb".freeze, "test/lib/test_helper.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/worker_test.rb".freeze]
15
+ s.test_files = ["test/lib/db_schema.rb".freeze, "test/lib/jobs.rb".freeze, "test/lib/test_helper.rb".freeze, "test/workhorse/db_job_test.rb".freeze, "test/workhorse/enqueuer_test.rb".freeze, "test/workhorse/performer_test.rb".freeze, "test/workhorse/poller_test.rb".freeze, "test/workhorse/pool_test.rb".freeze, "test/workhorse/worker_test.rb".freeze]
16
16
 
17
17
  if s.respond_to? :specification_version then
18
18
  s.specification_version = 4
19
19
 
20
20
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
21
- s.add_development_dependency(%q<bundler>.freeze, ["~> 1.3"])
21
+ s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
22
22
  s.add_development_dependency(%q<rake>.freeze, [">= 0"])
23
23
  s.add_development_dependency(%q<rubocop>.freeze, ["= 0.51.0"])
24
24
  s.add_development_dependency(%q<minitest>.freeze, [">= 0"])
25
25
  s.add_development_dependency(%q<mysql2>.freeze, [">= 0"])
26
+ s.add_development_dependency(%q<colorize>.freeze, [">= 0"])
26
27
  s.add_development_dependency(%q<benchmark-ips>.freeze, [">= 0"])
27
28
  s.add_runtime_dependency(%q<activesupport>.freeze, [">= 0"])
28
29
  s.add_runtime_dependency(%q<activerecord>.freeze, [">= 0"])
29
30
  s.add_runtime_dependency(%q<schemacop>.freeze, ["~> 2.0"])
30
31
  s.add_runtime_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
31
32
  else
32
- s.add_dependency(%q<bundler>.freeze, ["~> 1.3"])
33
+ s.add_dependency(%q<bundler>.freeze, [">= 0"])
33
34
  s.add_dependency(%q<rake>.freeze, [">= 0"])
34
35
  s.add_dependency(%q<rubocop>.freeze, ["= 0.51.0"])
35
36
  s.add_dependency(%q<minitest>.freeze, [">= 0"])
36
37
  s.add_dependency(%q<mysql2>.freeze, [">= 0"])
38
+ s.add_dependency(%q<colorize>.freeze, [">= 0"])
37
39
  s.add_dependency(%q<benchmark-ips>.freeze, [">= 0"])
38
40
  s.add_dependency(%q<activesupport>.freeze, [">= 0"])
39
41
  s.add_dependency(%q<activerecord>.freeze, [">= 0"])
@@ -41,11 +43,12 @@ Gem::Specification.new do |s|
41
43
  s.add_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
42
44
  end
43
45
  else
44
- s.add_dependency(%q<bundler>.freeze, ["~> 1.3"])
46
+ s.add_dependency(%q<bundler>.freeze, [">= 0"])
45
47
  s.add_dependency(%q<rake>.freeze, [">= 0"])
46
48
  s.add_dependency(%q<rubocop>.freeze, ["= 0.51.0"])
47
49
  s.add_dependency(%q<minitest>.freeze, [">= 0"])
48
50
  s.add_dependency(%q<mysql2>.freeze, [">= 0"])
51
+ s.add_dependency(%q<colorize>.freeze, [">= 0"])
49
52
  s.add_dependency(%q<benchmark-ips>.freeze, [">= 0"])
50
53
  s.add_dependency(%q<activesupport>.freeze, [">= 0"])
51
54
  s.add_dependency(%q<activerecord>.freeze, [">= 0"])
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: workhorse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.9
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sitrox
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-09 00:00:00.000000000 Z
11
+ date: 2019-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: colorize
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: benchmark-ips
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -187,6 +201,7 @@ files:
187
201
  - test/lib/db_schema.rb
188
202
  - test/lib/jobs.rb
189
203
  - test/lib/test_helper.rb
204
+ - test/workhorse/db_job_test.rb
190
205
  - test/workhorse/enqueuer_test.rb
191
206
  - test/workhorse/performer_test.rb
192
207
  - test/workhorse/poller_test.rb
@@ -219,6 +234,7 @@ test_files:
219
234
  - test/lib/db_schema.rb
220
235
  - test/lib/jobs.rb
221
236
  - test/lib/test_helper.rb
237
+ - test/workhorse/db_job_test.rb
222
238
  - test/workhorse/enqueuer_test.rb
223
239
  - test/workhorse/performer_test.rb
224
240
  - test/workhorse/poller_test.rb