inst-jobs 2.0.0 → 3.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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/db/migrate/20101216224513_create_delayed_jobs.rb +9 -7
  3. data/db/migrate/20110531144916_cleanup_delayed_jobs_indexes.rb +8 -13
  4. data/db/migrate/20110610213249_optimize_delayed_jobs.rb +8 -8
  5. data/db/migrate/20110831210257_add_delayed_jobs_next_in_strand.rb +25 -25
  6. data/db/migrate/20120510004759_delayed_jobs_delete_trigger_lock_for_update.rb +4 -8
  7. data/db/migrate/20120531150712_drop_psql_jobs_pop_fn.rb +1 -3
  8. data/db/migrate/20120607164022_delayed_jobs_use_advisory_locks.rb +11 -15
  9. data/db/migrate/20120607181141_index_jobs_on_locked_by.rb +1 -1
  10. data/db/migrate/20120608191051_add_jobs_run_at_index.rb +2 -2
  11. data/db/migrate/20120927184213_change_delayed_jobs_handler_to_text.rb +1 -1
  12. data/db/migrate/20140505215510_copy_failed_jobs_original_id.rb +2 -3
  13. data/db/migrate/20150807133223_add_max_concurrent_to_jobs.rb +9 -13
  14. data/db/migrate/20151210162949_improve_max_concurrent.rb +4 -8
  15. data/db/migrate/20161206323555_add_back_default_string_limits_jobs.rb +3 -2
  16. data/db/migrate/20181217155351_speed_up_max_concurrent_triggers.rb +13 -17
  17. data/db/migrate/20200330230722_add_id_to_get_delayed_jobs_index.rb +8 -8
  18. data/db/migrate/20200824222232_speed_up_max_concurrent_delete_trigger.rb +72 -77
  19. data/db/migrate/20200825011002_add_strand_order_override.rb +93 -97
  20. data/db/migrate/20210809145804_add_n_strand_index.rb +12 -0
  21. data/db/migrate/20210812210128_add_singleton_column.rb +200 -0
  22. data/db/migrate/20210917232626_add_delete_conflicting_singletons_before_unlock_trigger.rb +27 -0
  23. data/db/migrate/20210928174754_fix_singleton_condition_in_before_insert.rb +56 -0
  24. data/db/migrate/20210929204903_update_conflicting_singleton_function_to_use_index.rb +27 -0
  25. data/exe/inst_jobs +3 -2
  26. data/lib/delayed/backend/active_record.rb +211 -168
  27. data/lib/delayed/backend/base.rb +110 -72
  28. data/lib/delayed/batch.rb +11 -9
  29. data/lib/delayed/cli.rb +98 -84
  30. data/lib/delayed/core_ext/kernel.rb +4 -2
  31. data/lib/delayed/daemon.rb +70 -74
  32. data/lib/delayed/job_tracking.rb +26 -25
  33. data/lib/delayed/lifecycle.rb +27 -23
  34. data/lib/delayed/log_tailer.rb +17 -17
  35. data/lib/delayed/logging.rb +13 -16
  36. data/lib/delayed/message_sending.rb +43 -52
  37. data/lib/delayed/performable_method.rb +6 -8
  38. data/lib/delayed/periodic.rb +72 -68
  39. data/lib/delayed/plugin.rb +2 -4
  40. data/lib/delayed/pool.rb +205 -168
  41. data/lib/delayed/server/helpers.rb +6 -6
  42. data/lib/delayed/server.rb +51 -54
  43. data/lib/delayed/settings.rb +94 -81
  44. data/lib/delayed/testing.rb +21 -22
  45. data/lib/delayed/version.rb +1 -1
  46. data/lib/delayed/work_queue/in_process.rb +21 -17
  47. data/lib/delayed/work_queue/parent_process/client.rb +55 -53
  48. data/lib/delayed/work_queue/parent_process/server.rb +245 -207
  49. data/lib/delayed/work_queue/parent_process.rb +52 -53
  50. data/lib/delayed/worker/consul_health_check.rb +32 -33
  51. data/lib/delayed/worker/health_check.rb +34 -26
  52. data/lib/delayed/worker/null_health_check.rb +3 -1
  53. data/lib/delayed/worker/process_helper.rb +8 -9
  54. data/lib/delayed/worker.rb +272 -241
  55. data/lib/delayed/yaml_extensions.rb +12 -10
  56. data/lib/delayed_job.rb +37 -37
  57. data/lib/inst-jobs.rb +1 -1
  58. data/spec/active_record_job_spec.rb +143 -139
  59. data/spec/delayed/cli_spec.rb +7 -7
  60. data/spec/delayed/daemon_spec.rb +10 -9
  61. data/spec/delayed/message_sending_spec.rb +16 -9
  62. data/spec/delayed/periodic_spec.rb +14 -21
  63. data/spec/delayed/server_spec.rb +38 -38
  64. data/spec/delayed/settings_spec.rb +26 -25
  65. data/spec/delayed/work_queue/in_process_spec.rb +7 -8
  66. data/spec/delayed/work_queue/parent_process/client_spec.rb +17 -12
  67. data/spec/delayed/work_queue/parent_process/server_spec.rb +117 -41
  68. data/spec/delayed/work_queue/parent_process_spec.rb +21 -23
  69. data/spec/delayed/worker/consul_health_check_spec.rb +37 -50
  70. data/spec/delayed/worker/health_check_spec.rb +60 -52
  71. data/spec/delayed/worker_spec.rb +44 -21
  72. data/spec/sample_jobs.rb +45 -15
  73. data/spec/shared/delayed_batch.rb +74 -67
  74. data/spec/shared/delayed_method.rb +143 -102
  75. data/spec/shared/performable_method.rb +39 -38
  76. data/spec/shared/shared_backend.rb +550 -437
  77. data/spec/shared/testing.rb +14 -14
  78. data/spec/shared/worker.rb +156 -148
  79. data/spec/shared_jobs_specs.rb +13 -13
  80. data/spec/spec_helper.rb +53 -55
  81. metadata +148 -82
  82. data/lib/delayed/backend/redis/bulk_update.lua +0 -50
  83. data/lib/delayed/backend/redis/destroy_job.lua +0 -2
  84. data/lib/delayed/backend/redis/enqueue.lua +0 -29
  85. data/lib/delayed/backend/redis/fail_job.lua +0 -5
  86. data/lib/delayed/backend/redis/find_available.lua +0 -3
  87. data/lib/delayed/backend/redis/functions.rb +0 -59
  88. data/lib/delayed/backend/redis/get_and_lock_next_available.lua +0 -17
  89. data/lib/delayed/backend/redis/includes/jobs_common.lua +0 -203
  90. data/lib/delayed/backend/redis/job.rb +0 -535
  91. data/lib/delayed/backend/redis/set_running.lua +0 -5
  92. data/lib/delayed/backend/redis/tickle_strand.lua +0 -2
  93. data/spec/gemfiles/42.gemfile +0 -7
  94. data/spec/gemfiles/50.gemfile +0 -7
  95. data/spec/gemfiles/51.gemfile +0 -7
  96. data/spec/gemfiles/52.gemfile +0 -7
  97. data/spec/gemfiles/60.gemfile +0 -7
  98. data/spec/redis_job_spec.rb +0 -148
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
3
+ require "spec_helper"
4
4
 
5
5
  RSpec.describe Delayed::Worker::HealthCheck do
6
6
  let(:klass) { Class.new(Delayed::Worker::HealthCheck) { self.type_name = :test } }
@@ -10,116 +10,124 @@ RSpec.describe Delayed::Worker::HealthCheck do
10
10
  end
11
11
 
12
12
  after do
13
- Delayed::Worker::HealthCheck.subclasses.delete(klass)
13
+ described_class.subclasses.delete(klass)
14
14
  end
15
15
 
16
16
  it "must maintain a list of its subclasses" do
17
17
  klass
18
- expect(Delayed::Worker::HealthCheck.subclasses).to include klass
18
+ expect(described_class.subclasses).to include klass
19
19
  end
20
20
 
21
- describe '.build(type:, config: {})' do
22
- it 'must select the concrete class to use by the type_name in the subclass' do
23
- check = Delayed::Worker::HealthCheck.build(type: 'test', worker_name: 'foobar')
21
+ describe ".build(type:, config: {})" do
22
+ it "must select the concrete class to use by the type_name in the subclass" do
23
+ check = described_class.build(type: "test", worker_name: "foobar")
24
24
  expect(check).to be_a(klass)
25
25
  end
26
26
 
27
27
  it "must raise ArgumentError when the specified type doesn't exist" do
28
- expect {
29
- Delayed::Worker::HealthCheck.build(type: 'nope', config: {worker_name: 'foobar'})
30
- }.to raise_error ArgumentError
28
+ expect do
29
+ described_class.build(type: "nope", config: { worker_name: "foobar" })
30
+ end.to raise_error ArgumentError
31
31
  end
32
32
 
33
- it 'must initiaize the specified class using the supplied config' do
34
- config = {foo: 'bar'}.with_indifferent_access
35
- check = Delayed::Worker::HealthCheck.build(type: 'test', worker_name: 'foobar', config: config)
33
+ it "must initiaize the specified class using the supplied config" do
34
+ config = { foo: "bar" }.with_indifferent_access
35
+ check = described_class.build(type: "test", worker_name: "foobar", config: config)
36
36
  expect(check.config).to eq config
37
37
  end
38
38
  end
39
39
 
40
- describe '.reschedule_abandoned_jobs' do
41
- let(:klass) { Class.new(Delayed::Worker::HealthCheck) {
42
- self.type_name = :fake
43
- class << self
44
- attr_accessor :live_workers
40
+ describe ".reschedule_abandoned_jobs" do
41
+ let(:klass) do
42
+ Class.new(Delayed::Worker::HealthCheck) do
43
+ self.type_name = :fake
44
+ class << self
45
+ attr_accessor :live_workers
46
+ end
47
+
48
+ def live_workers
49
+ self.class.live_workers
50
+ end
45
51
  end
52
+ end
46
53
 
47
- def live_workers
48
- self.class.live_workers
49
- end
50
- } }
51
-
52
- let(:initial_run_at) { Time.zone.now }
54
+ let(:initial_run_at) { 10.minutes.ago }
53
55
 
54
56
  before do
55
- klass.live_workers = %w{alive}
57
+ klass.live_workers = %w[alive]
56
58
  Delayed.select_backend(Delayed::Backend::ActiveRecord::Job)
57
59
 
58
60
  2.times { Delayed::Job.enqueue(SimpleJob.new, run_at: initial_run_at, max_attempts: 4) }
59
61
  @alive_job = Delayed::Job.first
60
62
  @alive_job.update!({
61
- locked_by: 'alive',
62
- locked_at: initial_run_at
63
- })
63
+ locked_by: "alive",
64
+ locked_at: initial_run_at
65
+ })
64
66
  @dead_job = Delayed::Job.last
65
67
  @dead_job.update!({
66
- locked_by: 'dead',
67
- locked_at: initial_run_at
68
- })
68
+ locked_by: "dead",
69
+ locked_at: initial_run_at
70
+ })
69
71
  Delayed::Settings.worker_health_check_type = :fake
70
72
  Delayed::Settings.worker_health_check_config = {}
71
73
  end
72
74
 
73
75
  after do
74
- Delayed::Worker::HealthCheck.subclasses.delete(klass)
76
+ described_class.subclasses.delete(klass)
75
77
  Delayed::Settings.worker_health_check_type = :none
76
78
  Delayed::Settings.worker_health_check_config = {}
77
79
  end
78
80
 
79
- it 'must leave jobs locked by live workers alone' do
80
- Delayed::Worker::HealthCheck.reschedule_abandoned_jobs
81
+ it "must leave jobs locked by live workers alone" do
82
+ described_class.reschedule_abandoned_jobs
81
83
  @alive_job.reload
82
84
  expect(@alive_job.run_at.to_i).to eq initial_run_at.to_i
83
85
  expect(@alive_job.locked_at.to_i).to eq initial_run_at.to_i
84
- expect(@alive_job.locked_by).to eq 'alive'
86
+ expect(@alive_job.locked_by).to eq "alive"
85
87
  end
86
88
 
87
- it 'must reschedule jobs locked by dead workers' do
88
- Delayed::Worker::HealthCheck.reschedule_abandoned_jobs
89
+ it "must reschedule jobs locked by dead workers" do
90
+ described_class.reschedule_abandoned_jobs
89
91
  @dead_job.reload
90
92
  expect(@dead_job.run_at).to be > initial_run_at
91
93
  expect(@dead_job.locked_at).to be_nil
92
94
  expect(@dead_job.locked_by).to be_nil
93
95
  end
94
96
 
95
- it 'ignores jobs that are re-locked after fetching from db' do
96
- Delayed::Job.where(id: @dead_job).update_all(locked_by: 'someone_else')
97
- allow(Delayed::Job).to receive(:running_jobs).and_return([@dead_job])
98
- Delayed::Worker::HealthCheck.reschedule_abandoned_jobs
97
+ it "ignores jobs that are re-locked after fetching from db" do
98
+ Delayed::Job.where(id: @dead_job).update_all(locked_by: "someone_else")
99
+ # we need to return @dead_job itself, which doesn't match the database
100
+ jobs_scope = double
101
+ allow(jobs_scope).to receive(:where).and_return(jobs_scope)
102
+ allow(jobs_scope).to receive(:not).and_return(jobs_scope)
103
+ allow(jobs_scope).to receive(:limit).and_return(jobs_scope)
104
+ allow(jobs_scope).to receive(:to_a).and_return([@dead_job], [])
105
+ allow(Delayed::Job).to receive(:running_jobs).and_return(jobs_scope)
106
+ described_class.reschedule_abandoned_jobs
99
107
  @dead_job.reload
100
- expect(@dead_job.locked_by).to eq 'someone_else'
108
+ expect(@dead_job.locked_by).to eq "someone_else"
101
109
  end
102
110
 
103
- it 'ignores jobs that are prefetched' do
104
- Delayed::Job.where(id: @dead_job).update_all(locked_by: 'prefetch:some_node')
105
- allow(Delayed::Job).to receive(:running_jobs).and_return([@dead_job])
106
- Delayed::Worker::HealthCheck.reschedule_abandoned_jobs
111
+ it "ignores jobs that are prefetched" do
112
+ Delayed::Job.where(id: @dead_job).update_all(locked_by: "prefetch:some_node")
113
+ allow(Delayed::Job).to receive(:running_jobs).and_return(Delayed::Job.where(id: @dead_job.id))
114
+ described_class.reschedule_abandoned_jobs
107
115
  @dead_job.reload
108
- expect(@dead_job.locked_by).to eq 'prefetch:some_node'
116
+ expect(@dead_job.locked_by).to eq "prefetch:some_node"
109
117
  end
110
118
 
111
119
  it "bails immediately if advisory lock already taken" do
112
- allow(Delayed::Worker::HealthCheck).to receive(:attempt_advisory_lock).and_return(false)
113
- Delayed::Worker::HealthCheck.reschedule_abandoned_jobs
120
+ allow(Delayed::Job).to receive(:attempt_advisory_lock).and_return(false)
121
+ described_class.reschedule_abandoned_jobs
114
122
  @dead_job.reload
115
123
  expect(@dead_job.run_at.to_i).to eq(initial_run_at.to_i)
116
- expect(@dead_job.locked_at).to_not be_nil
117
- expect(@dead_job.locked_by).to_not be_nil
124
+ expect(@dead_job.locked_at).not_to be_nil
125
+ expect(@dead_job.locked_by).not_to be_nil
118
126
  end
119
127
  end
120
128
 
121
- describe '#initialize' do
122
- it 'must raise ArgumentError when the worker name is not supplied' do
129
+ describe "#initialize" do
130
+ it "must raise ArgumentError when the worker name is not supplied" do
123
131
  expect { klass.new }.to raise_error ArgumentError
124
132
  end
125
133
  end
@@ -3,45 +3,68 @@
3
3
  require_relative "../spec_helper"
4
4
 
5
5
  describe Delayed::Worker do
6
- let(:worker_config) { {
7
- queue: "test", min_priority: 1, max_priority: 2, stuff: "stuff",
8
- }.freeze }
9
6
  subject { described_class.new(worker_config.dup) }
10
7
 
11
- after { Delayed::Worker.lifecycle.reset! }
8
+ let(:worker_config) do
9
+ {
10
+ queue: "test", min_priority: 1, max_priority: 2, stuff: "stuff"
11
+ }.freeze
12
+ end
13
+ let(:job_attrs) do
14
+ {
15
+ id: 42, name: "testjob", full_name: "testfullname", :last_error= => nil,
16
+ attempts: 1, reschedule: nil, :expired? => false,
17
+ payload_object: {}, priority: 25
18
+ }.freeze
19
+ end
20
+
21
+ after { described_class.lifecycle.reset! }
12
22
 
13
23
  describe "#perform" do
14
24
  it "fires off an error callback when a job raises an exception" do
15
25
  fired = false
16
- Delayed::Worker.lifecycle.before(:error) {|worker, exception| fired = true}
17
- job = double(:last_error= => nil, attempts: 1, reschedule: nil)
18
- subject.perform(job)
26
+ described_class.lifecycle.before(:error) { |_worker, _exception| fired = true }
27
+ job = double(job_attrs)
28
+ output_count = subject.perform(job)
19
29
  expect(fired).to be_truthy
30
+ expect(output_count).to eq(1)
31
+ end
32
+
33
+ it "uses the retry callback for a retriable exception" do
34
+ error_fired = retry_fired = false
35
+ described_class.lifecycle.before(:error) { |_worker, _exception| error_fired = true }
36
+ described_class.lifecycle.before(:retry) { |_worker, _exception| retry_fired = true }
37
+ job = Delayed::Job.new(payload_object: {}, priority: 25, strand: "test_jobs", max_attempts: 3)
38
+ expect(job).to receive(:invoke_job) do
39
+ raise Delayed::RetriableError, "that's all this job does"
40
+ end
41
+ output_count = subject.perform(job)
42
+ expect(error_fired).to be_falsey
43
+ expect(retry_fired).to be_truthy
44
+ expect(output_count).to eq(1)
20
45
  end
21
46
 
22
47
  it "reloads" do
23
- fakeApplication = double('Rails.application',
24
- config: double('Rails.application.config',
25
- cache_classes: false,
26
- reload_classes_only_on_change: false
27
- ),
28
- reloader: double()
29
- )
48
+ fake_application = double("Rails.application",
49
+ config: double("Rails.application.config",
50
+ cache_classes: false,
51
+ reload_classes_only_on_change: false),
52
+ reloader: double)
30
53
 
31
- allow(Rails).to receive(:application).and_return(fakeApplication)
54
+ allow(Rails).to receive(:application).and_return(fake_application)
32
55
  if Rails::VERSION::MAJOR >= 5
33
56
  expect(Rails.application.reloader).to receive(:reload!).once
34
57
  else
35
58
  expect(ActionDispatch::Reloader).to receive(:prepare!).once
36
59
  expect(ActionDispatch::Reloader).to receive(:cleanup!).once
37
60
  end
38
- job = double(:last_error= => nil, attempts: 0, reschedule: nil, expired?: false)
61
+ job = double(job_attrs)
39
62
  subject.perform(job)
40
63
  end
41
64
  end
42
65
 
43
66
  describe "#log_job" do
44
- around(:each) do |block|
67
+ around do |block|
45
68
  prev_logger = Delayed::Settings.job_detailed_log_format
46
69
  block.call
47
70
  Delayed::Settings.job_detailed_log_format = prev_logger
@@ -53,11 +76,11 @@ describe Delayed::Worker do
53
76
  short_log_format = subject.log_job(job, :short)
54
77
  expect(short_log_format).to eq("RSpec::Mocks::Double")
55
78
  long_format = subject.log_job(job, :long)
56
- expect(long_format).to eq("RSpec::Mocks::Double {\"priority\":25,\"attempts\":0,\"created_at\":null,\"tag\":\"RSpec::Mocks::Double#perform\",\"max_attempts\":null,\"strand\":\"test_jobs\",\"source\":null}")
79
+ expect(long_format).to eq("RSpec::Mocks::Double {\"priority\":25,\"attempts\":0,\"created_at\":null,\"tag\":\"RSpec::Mocks::Double#perform\",\"max_attempts\":null,\"strand\":\"test_jobs\",\"source\":null}") # rubocop:disable Layout/LineLength
57
80
  end
58
81
 
59
82
  it "logging format can be changed with settings" do
60
- Delayed::Settings.job_detailed_log_format = ->(job){ "override format #{job.strand}"}
83
+ Delayed::Settings.job_detailed_log_format = ->(job) { "override format #{job.strand}" }
61
84
  payload = double(perform: nil)
62
85
  job = Delayed::Job.new(payload_object: payload, priority: 25, strand: "test_jobs")
63
86
  short_log_format = subject.log_job(job, :short)
@@ -69,8 +92,8 @@ describe Delayed::Worker do
69
92
 
70
93
  describe "#run" do
71
94
  it "passes extra config options through to the WorkQueue" do
72
- expect(subject.work_queue).to receive(:get_and_lock_next_available).
73
- with(subject.name, worker_config).and_return(nil)
95
+ expect(subject.work_queue).to receive(:get_and_lock_next_available)
96
+ .with(subject.name, worker_config).and_return(nil)
74
97
  subject.run
75
98
  end
76
99
  end
data/spec/sample_jobs.rb CHANGED
@@ -1,49 +1,79 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class SimpleJob
4
- cattr_accessor :runs; self.runs = 0
5
- def perform; @@runs += 1; end
4
+ class << self
5
+ attr_accessor :runs
6
+ end
7
+
8
+ self.runs = 0
9
+
10
+ def perform
11
+ self.class.runs += 1
12
+ end
6
13
  end
7
14
 
8
15
  class ErrorJob
9
- cattr_accessor :runs; self.runs = 0
10
- def perform; raise 'did not work'; end
16
+ class << self
17
+ attr_accessor :runs, :last_error, :failure_runs, :permanent_failure_runs
18
+ end
11
19
 
12
- cattr_accessor :last_error; self.last_error = nil
20
+ self.runs = 0
21
+ def perform
22
+ raise "did not work"
23
+ end
13
24
 
14
- cattr_accessor :failure_runs; self.failure_runs = 0
15
- def on_failure(error); @@last_error = error; @@failure_runs += 1; end
25
+ self.last_error = nil
26
+ self.failure_runs = 0
27
+ def on_failure(error)
28
+ self.class.last_error = error
29
+ self.class.failure_runs += 1
30
+ end
16
31
 
17
- cattr_accessor :permanent_failure_runs; self.permanent_failure_runs = 0
18
- def on_permanent_failure(error); @@last_error = error; @@permanent_failure_runs += 1; end
32
+ self.permanent_failure_runs = 0
33
+ def on_permanent_failure(error)
34
+ self.class.last_error = error
35
+ self.class.permanent_failure_runs += 1
36
+ end
19
37
  end
20
38
 
21
39
  class UnlockJob
22
40
  attr_accessor :times_to_unlock
41
+
23
42
  def initialize(times_to_unlock)
24
43
  @times_to_unlock = times_to_unlock
25
44
  end
26
45
 
27
- def perform; raise SystemExit, 'raising to trigger on_failure'; end
46
+ def perform
47
+ raise SystemExit, "raising to trigger on_failure"
48
+ end
28
49
 
29
- def on_failure(error)
50
+ def on_failure(_error)
30
51
  times_to_unlock -= 1
31
52
  :unlock if times_to_unlock <= 0
32
53
  end
33
54
  end
34
55
 
35
56
  class LongRunningJob
36
- def perform; sleep 250; end
57
+ def perform
58
+ sleep 250
59
+ end
37
60
  end
38
61
 
39
62
  module M
40
63
  class ModuleJob
41
- cattr_accessor :runs; self.runs = 0
42
- def perform; @@runs += 1; end
64
+ class << self
65
+ attr_accessor :runs
66
+ end
67
+
68
+ cattr_accessor :runs
69
+ self.runs = 0
70
+ def perform
71
+ self.class.runs += 1
72
+ end
43
73
  end
44
74
  end
45
75
 
46
76
  class DeserializeErrorJob < SimpleJob; end
47
- Psych.add_domain_type("ruby/object", "DeserializeErrorJob") do |_type, val|
77
+ Psych.add_domain_type("ruby/object", "DeserializeErrorJob") do |_type, _val|
48
78
  raise "error deserializing"
49
79
  end
@@ -1,98 +1,105 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- shared_examples_for 'Delayed::Batch' do
3
+ shared_examples_for "Delayed::Batch" do
4
4
  context "batching" do
5
- it "should batch up all deferrable delayed methods" do
5
+ it "batches up all deferrable delayed methods" do
6
6
  later = 1.hour.from_now
7
- Delayed::Batch.serial_batch {
8
- "string".delay(ignore_transaction: true).size.should be true
9
- "string".delay(run_at: later, ignore_transaction: true).reverse.should be_truthy # won't be batched, it'll get its own job
10
- "string".delay(ignore_transaction: true).gsub(/./, "!").should be_truthy
11
- }
7
+ Delayed::Batch.serial_batch do
8
+ expect("string".delay(ignore_transaction: true).size).to be true
9
+ # won't be batched, it'll get its own job
10
+ expect("string".delay(run_at: later, ignore_transaction: true).reverse).to be_truthy
11
+ expect("string".delay(ignore_transaction: true).gsub(/./, "!")).to be_truthy
12
+ end
12
13
  batch_jobs = Delayed::Job.find_available(5)
13
14
  regular_jobs = Delayed::Job.list_jobs(:future, 5)
14
- regular_jobs.size.should == 1
15
- regular_jobs.first.batch?.should == false
16
- batch_jobs.size.should == 1
15
+ expect(regular_jobs.size).to eq(1)
16
+ expect(regular_jobs.first.batch?).to eq(false)
17
+ expect(batch_jobs.size).to eq(1)
17
18
  batch_job = batch_jobs.first
18
- batch_job.batch?.should == true
19
- batch_job.payload_object.mode.should == :serial
20
- batch_job.payload_object.jobs.map { |j| [j.payload_object.object, j.payload_object.method, j.payload_object.args] }.should == [
21
- ["string", :size, []],
22
- ["string", :gsub, [/./, "!"]]
23
- ]
19
+ expect(batch_job.batch?).to eq(true)
20
+ expect(batch_job.payload_object.mode).to eq(:serial)
21
+ expect(batch_job.payload_object.jobs.map do |j|
22
+ [j.payload_object.object, j.payload_object.method, j.payload_object.args]
23
+ end).to eq([
24
+ [
25
+ "string", :size, []
26
+ ],
27
+ [
28
+ "string", :gsub, [
29
+ /./, "!"
30
+ ]
31
+ ]
32
+ ])
24
33
  end
25
34
 
26
- it "should not let you invoke it directly" do
27
- later = 1.hour.from_now
28
- Delayed::Batch.serial_batch {
29
- "string".delay(ignore_transaction: true).size.should be true
30
- "string".delay(ignore_transaction: true).gsub(/./, "!").should be true
31
- }
32
- Delayed::Job.jobs_count(:current).should == 1
35
+ it "does not let you invoke it directly" do
36
+ Delayed::Batch.serial_batch do
37
+ expect("string".delay(ignore_transaction: true).size).to be true
38
+ expect("string".delay(ignore_transaction: true).gsub(/./, "!")).to be true
39
+ end
40
+ expect(Delayed::Job.jobs_count(:current)).to eq(1)
33
41
  job = Delayed::Job.find_available(1).first
34
- expect{ job.invoke_job }.to raise_error(RuntimeError)
42
+ expect { job.invoke_job }.to raise_error(RuntimeError)
35
43
  end
36
44
 
37
- it "should create valid jobs" do
38
- Delayed::Batch.serial_batch {
39
- "string".delay(ignore_transaction: true).size.should be true
40
- "string".delay(ignore_transaction: true).gsub(/./, "!").should be true
41
- }
42
- Delayed::Job.jobs_count(:current).should == 1
45
+ it "creates valid jobs" do
46
+ Delayed::Batch.serial_batch do
47
+ expect("string".delay(ignore_transaction: true).size).to be true
48
+ expect("string".delay(ignore_transaction: true).gsub(/./, "!")).to be true
49
+ end
50
+ expect(Delayed::Job.jobs_count(:current)).to eq(1)
43
51
 
44
52
  batch_job = Delayed::Job.find_available(1).first
45
- batch_job.batch?.should == true
53
+ expect(batch_job.batch?).to eq(true)
46
54
  jobs = batch_job.payload_object.jobs
47
- jobs.size.should == 2
48
- jobs[0].should be_new_record
49
- jobs[0].payload_object.class.should == Delayed::PerformableMethod
50
- jobs[0].payload_object.method.should == :size
51
- jobs[0].payload_object.args.should == []
52
- jobs[0].payload_object.perform.should == 6
53
- jobs[1].should be_new_record
54
- jobs[1].payload_object.class.should == Delayed::PerformableMethod
55
- jobs[1].payload_object.method.should == :gsub
56
- jobs[1].payload_object.args.should == [/./, "!"]
57
- jobs[1].payload_object.perform.should == "!!!!!!"
55
+ expect(jobs.size).to eq(2)
56
+ expect(jobs[0]).to be_new_record
57
+ expect(jobs[0].payload_object.class).to eq(Delayed::PerformableMethod)
58
+ expect(jobs[0].payload_object.method).to eq(:size)
59
+ expect(jobs[0].payload_object.args).to eq([])
60
+ expect(jobs[0].payload_object.perform).to eq(6)
61
+ expect(jobs[1]).to be_new_record
62
+ expect(jobs[1].payload_object.class).to eq(Delayed::PerformableMethod)
63
+ expect(jobs[1].payload_object.method).to eq(:gsub)
64
+ expect(jobs[1].payload_object.args).to eq([/./, "!"])
65
+ expect(jobs[1].payload_object.perform).to eq("!!!!!!")
58
66
  end
59
67
 
60
- it "should create a different batch for each priority" do
61
- later = 1.hour.from_now
62
- Delayed::Batch.serial_batch {
63
- "string".delay(priority: Delayed::LOW_PRIORITY, ignore_transaction: true).size.should be true
64
- "string".delay(ignore_transaction: true).gsub(/./, "!").should be true
65
- }
66
- Delayed::Job.jobs_count(:current).should == 2
68
+ it "creates a different batch for each priority" do
69
+ Delayed::Batch.serial_batch do
70
+ expect("string".delay(priority: Delayed::LOW_PRIORITY, ignore_transaction: true).size).to be true
71
+ expect("string".delay(ignore_transaction: true).gsub(/./, "!")).to be true
72
+ end
73
+ expect(Delayed::Job.jobs_count(:current)).to eq(2)
67
74
  end
68
75
 
69
- it "should use the given priority for all, if specified" do
70
- Delayed::Batch.serial_batch(:priority => 11) {
71
- "string".delay(priority: 20, ignore_transaction: true).size.should be true
72
- "string".delay(priority: 15, ignore_transaction: true).gsub(/./, "!").should be true
73
- }
74
- Delayed::Job.jobs_count(:current).should == 1
75
- Delayed::Job.find_available(1).first.priority.should == 11
76
+ it "uses the given priority for all, if specified" do
77
+ Delayed::Batch.serial_batch(priority: 11) do
78
+ expect("string".delay(priority: 20, ignore_transaction: true).size).to be true
79
+ expect("string".delay(priority: 15, ignore_transaction: true).gsub(/./, "!")).to be true
80
+ end
81
+ expect(Delayed::Job.jobs_count(:current)).to eq(1)
82
+ expect(Delayed::Job.find_available(1).first.priority).to eq(11)
76
83
  end
77
84
 
78
- it "should just create the job, if there's only one in the batch" do
79
- Delayed::Batch.serial_batch(:priority => 11) {
80
- "string".delay(ignore_transaction: true).size.should be true
81
- }
82
- Delayed::Job.jobs_count(:current).should == 1
83
- Delayed::Job.find_available(1).first.tag.should == "String#size"
84
- Delayed::Job.find_available(1).first.priority.should == 11
85
+ it "justs create the job, if there's only one in the batch" do
86
+ Delayed::Batch.serial_batch(priority: 11) do
87
+ expect("string".delay(ignore_transaction: true).size).to be true
88
+ end
89
+ expect(Delayed::Job.jobs_count(:current)).to eq(1)
90
+ expect(Delayed::Job.find_available(1).first.tag).to eq("String#size")
91
+ expect(Delayed::Job.find_available(1).first.priority).to eq(11)
85
92
  end
86
93
 
87
- it "should list a job only once when the same call is made multiple times" do
88
- Delayed::Batch.serial_batch(:priority => 11) {
94
+ it "lists a job only once when the same call is made multiple times" do
95
+ Delayed::Batch.serial_batch(priority: 11) do
89
96
  "string".delay(ignore_transaction: true).size
90
97
  "string".delay(ignore_transaction: true).gsub(/./, "!")
91
98
  "string".delay(ignore_transaction: true).size
92
- }
99
+ end
93
100
  batch_job = Delayed::Job.find_available(1).first
94
101
  jobs = batch_job.payload_object.jobs
95
- jobs.size.should == 2
102
+ expect(jobs.size).to eq(2)
96
103
  end
97
104
  end
98
105
  end