inst-jobs 3.1.2 → 3.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,134 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe Delayed::Worker::HealthCheck do
6
- let(:klass) { Class.new(Delayed::Worker::HealthCheck) { self.type_name = :test } }
7
-
8
- before do
9
- klass # Gotta make sure the class has been defined before we try to use it
10
- end
11
-
12
- after do
13
- described_class.subclasses.delete(klass)
14
- end
15
-
16
- it "must maintain a list of its subclasses" do
17
- klass
18
- expect(described_class.subclasses).to include klass
19
- end
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 = described_class.build(type: "test", worker_name: "foobar")
24
- expect(check).to be_a(klass)
25
- end
26
-
27
- it "must raise ArgumentError when the specified type doesn't exist" do
28
- expect do
29
- described_class.build(type: "nope", config: { worker_name: "foobar" })
30
- end.to raise_error ArgumentError
31
- end
32
-
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
- expect(check.config).to eq config
37
- end
38
- end
39
-
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
51
- end
52
- end
53
-
54
- let(:initial_run_at) { 10.minutes.ago }
55
-
56
- before do
57
- klass.live_workers = %w[alive]
58
- Delayed.select_backend(Delayed::Backend::ActiveRecord::Job)
59
-
60
- 2.times { Delayed::Job.enqueue(SimpleJob.new, run_at: initial_run_at, max_attempts: 4) }
61
- @alive_job = Delayed::Job.first
62
- @alive_job.update!({
63
- locked_by: "alive",
64
- locked_at: initial_run_at
65
- })
66
- @dead_job = Delayed::Job.last
67
- @dead_job.update!({
68
- locked_by: "dead",
69
- locked_at: initial_run_at
70
- })
71
- Delayed::Settings.worker_health_check_type = :fake
72
- Delayed::Settings.worker_health_check_config = {}
73
- end
74
-
75
- after do
76
- described_class.subclasses.delete(klass)
77
- Delayed::Settings.worker_health_check_type = :none
78
- Delayed::Settings.worker_health_check_config = {}
79
- end
80
-
81
- it "must leave jobs locked by live workers alone" do
82
- described_class.reschedule_abandoned_jobs
83
- @alive_job.reload
84
- expect(@alive_job.run_at.to_i).to eq initial_run_at.to_i
85
- expect(@alive_job.locked_at.to_i).to eq initial_run_at.to_i
86
- expect(@alive_job.locked_by).to eq "alive"
87
- end
88
-
89
- it "must reschedule jobs locked by dead workers" do
90
- described_class.reschedule_abandoned_jobs
91
- @dead_job.reload
92
- expect(@dead_job.run_at).to be > initial_run_at
93
- expect(@dead_job.locked_at).to be_nil
94
- expect(@dead_job.locked_by).to be_nil
95
- end
96
-
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
107
- @dead_job.reload
108
- expect(@dead_job.locked_by).to eq "someone_else"
109
- end
110
-
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
115
- @dead_job.reload
116
- expect(@dead_job.locked_by).to eq "prefetch:some_node"
117
- end
118
-
119
- it "bails immediately if advisory lock already taken" do
120
- allow(Delayed::Job).to receive(:attempt_advisory_lock).and_return(false)
121
- described_class.reschedule_abandoned_jobs
122
- @dead_job.reload
123
- expect(@dead_job.run_at.to_i).to eq(initial_run_at.to_i)
124
- expect(@dead_job.locked_at).not_to be_nil
125
- expect(@dead_job.locked_by).not_to be_nil
126
- end
127
- end
128
-
129
- describe "#initialize" do
130
- it "must raise ArgumentError when the worker name is not supplied" do
131
- expect { klass.new }.to raise_error ArgumentError
132
- end
133
- end
134
- end
@@ -1,106 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../spec_helper"
4
-
5
- describe Delayed::Worker do
6
- subject { described_class.new(worker_config.dup) }
7
-
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! }
22
-
23
- describe "#perform" do
24
- it "fires off an error callback when a job raises an exception" do
25
- fired = false
26
- described_class.lifecycle.before(:error) { |_worker, _exception| fired = true }
27
- job = double(job_attrs)
28
- output_count = subject.perform(job)
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)
45
- end
46
-
47
- it "reloads Rails classes (never more than once)" do
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)
53
-
54
- allow(Rails).to receive(:application).and_return(fake_application)
55
- if Rails::VERSION::MAJOR >= 5
56
- expect(Rails.application.reloader).to receive(:reload!).once
57
- else
58
- expect(ActionDispatch::Reloader).to receive(:prepare!).once
59
- expect(ActionDispatch::Reloader).to receive(:cleanup!).once
60
- end
61
- job = double(job_attrs)
62
-
63
- # Create extra workers to make sure we don't reload multiple times
64
- described_class.new(worker_config.dup)
65
- described_class.new(worker_config.dup)
66
-
67
- subject.perform(job)
68
- end
69
- end
70
-
71
- describe "#log_job" do
72
- around do |block|
73
- prev_logger = Delayed::Settings.job_detailed_log_format
74
- block.call
75
- Delayed::Settings.job_detailed_log_format = prev_logger
76
- end
77
-
78
- it "has a reasonable default format" do
79
- payload = double(perform: nil)
80
- job = Delayed::Job.new(payload_object: payload, priority: 25, strand: "test_jobs")
81
- short_log_format = subject.log_job(job, :short)
82
- expect(short_log_format).to eq("RSpec::Mocks::Double")
83
- long_format = subject.log_job(job, :long)
84
- 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,\"singleton\":null}") # rubocop:disable Layout/LineLength
85
- end
86
-
87
- it "logging format can be changed with settings" do
88
- Delayed::Settings.job_detailed_log_format = ->(job) { "override format detailed #{job.strand}" }
89
- Delayed::Settings.job_short_log_format = ->(_job) { "override format short" }
90
- payload = double(perform: nil)
91
- job = Delayed::Job.new(payload_object: payload, priority: 25, strand: "test_jobs")
92
- short_log_format = subject.log_job(job, :short)
93
- expect(short_log_format).to eq("RSpec::Mocks::Double override format short")
94
- long_format = subject.log_job(job, :long)
95
- expect(long_format).to eq("RSpec::Mocks::Double override format detailed test_jobs")
96
- end
97
- end
98
-
99
- describe "#run" do
100
- it "passes extra config options through to the WorkQueue" do
101
- expect(subject.work_queue).to receive(:get_and_lock_next_available)
102
- .with(subject.name, worker_config).and_return(nil)
103
- subject.run
104
- end
105
- end
106
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class AddStoryTable < ActiveRecord::Migration[4.2]
4
- def change
5
- create_table :stories do |table|
6
- table.string :text
7
- end
8
- end
9
- end
data/spec/sample_jobs.rb DELETED
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class SimpleJob
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
13
- end
14
-
15
- class ErrorJob
16
- class << self
17
- attr_accessor :runs, :last_error, :failure_runs, :permanent_failure_runs
18
- end
19
-
20
- self.runs = 0
21
- def perform
22
- raise "did not work"
23
- end
24
-
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
31
-
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
37
- end
38
-
39
- class UnlockJob
40
- attr_accessor :times_to_unlock
41
-
42
- def initialize(times_to_unlock)
43
- @times_to_unlock = times_to_unlock
44
- end
45
-
46
- def perform
47
- raise SystemExit, "raising to trigger on_failure"
48
- end
49
-
50
- def on_failure(_error)
51
- times_to_unlock -= 1
52
- :unlock if times_to_unlock <= 0
53
- end
54
- end
55
-
56
- class LongRunningJob
57
- def perform
58
- sleep 250
59
- end
60
- end
61
-
62
- module M
63
- class ModuleJob
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
73
- end
74
- end
75
-
76
- class DeserializeErrorJob < SimpleJob; end
77
- Psych.add_domain_type("ruby/object", "DeserializeErrorJob") do |_type, _val|
78
- raise "error deserializing"
79
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- shared_examples_for "Delayed::Batch" do
4
- context "batching" do
5
- it "batches up all deferrable delayed methods" do
6
- later = 1.hour.from_now
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
13
- batch_jobs = Delayed::Job.find_available(5)
14
- regular_jobs = Delayed::Job.list_jobs(:future, 5)
15
- expect(regular_jobs.size).to eq(1)
16
- expect(regular_jobs.first.batch?).to be(false)
17
- expect(batch_jobs.size).to eq(1)
18
- batch_job = batch_jobs.first
19
- expect(batch_job.batch?).to be(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
- ])
33
- end
34
-
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)
41
- job = Delayed::Job.find_available(1).first
42
- expect { job.invoke_job }.to raise_error(RuntimeError)
43
- end
44
-
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)
51
-
52
- batch_job = Delayed::Job.find_available(1).first
53
- expect(batch_job.batch?).to be(true)
54
- jobs = batch_job.payload_object.jobs
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("!!!!!!")
66
- end
67
-
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)
74
- end
75
-
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)
83
- end
84
-
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)
92
- end
93
-
94
- it "lists a job only once when the same call is made multiple times" do
95
- Delayed::Batch.serial_batch(priority: 11) do
96
- "string".delay(ignore_transaction: true).size
97
- "string".delay(ignore_transaction: true).gsub(/./, "!")
98
- "string".delay(ignore_transaction: true).size
99
- end
100
- batch_job = Delayed::Job.find_available(1).first
101
- jobs = batch_job.payload_object.jobs
102
- expect(jobs.size).to eq(2)
103
- end
104
- end
105
- end