queue_classic_plus 1.0.0.alpha2 → 4.0.0.alpha8
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 +5 -5
- data/.circleci/config.yml +67 -0
- data/.github/dependabot.yml +8 -0
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/README.md +21 -11
- data/lib/queue_classic_plus/base.rb +43 -8
- data/lib/queue_classic_plus/datadog.rb +11 -0
- data/lib/queue_classic_plus/inheritable_attr.rb +3 -1
- data/lib/queue_classic_plus/metrics.rb +1 -1
- data/lib/queue_classic_plus/new_relic.rb +18 -19
- data/lib/queue_classic_plus/queue_classic/queue.rb +46 -3
- data/lib/queue_classic_plus/tasks/work.rake +16 -2
- data/lib/queue_classic_plus/version.rb +1 -1
- data/lib/queue_classic_plus/worker.rb +67 -25
- data/lib/queue_classic_plus.rb +3 -3
- data/queue_classic_plus.gemspec +8 -2
- data/spec/base_spec.rb +59 -17
- data/spec/datadog_spec.rb +18 -0
- data/spec/helpers.rb +1 -1
- data/spec/new_relic_spec.rb +26 -0
- data/spec/queue_classic/queue_spec.rb +43 -0
- data/spec/sample_jobs.rb +31 -4
- data/spec/spec_helper.rb +5 -0
- data/spec/worker_spec.rb +124 -30
- metadata +46 -11
- data/.travis.yml +0 -8
data/spec/base_spec.rb
CHANGED
@@ -12,7 +12,19 @@ describe QueueClassicPlus::Base do
|
|
12
12
|
it "does not allow multiple enqueues" do
|
13
13
|
subject.do
|
14
14
|
subject.do
|
15
|
-
subject.
|
15
|
+
expect(subject).to have_queue_size_of(1)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "checks for an existing job using the same serializing as job enqueuing" do
|
19
|
+
# simulate a case where obj#to_json and JSON.dump(obj) do not match
|
20
|
+
require 'active_support/core_ext/date_time'
|
21
|
+
require 'active_support/json'
|
22
|
+
ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
|
23
|
+
|
24
|
+
date = DateTime.new(2020, 11, 3)
|
25
|
+
subject.do(date)
|
26
|
+
subject.do(date)
|
27
|
+
expect(subject).to have_queue_size_of(1)
|
16
28
|
end
|
17
29
|
|
18
30
|
it "does allow multiple enqueues if something got locked for too long" do
|
@@ -20,7 +32,7 @@ describe QueueClassicPlus::Base do
|
|
20
32
|
one_day_ago = Time.now - 60*60*24
|
21
33
|
execute "UPDATE queue_classic_jobs SET locked_at = '#{one_day_ago}' WHERE q_name = 'test'"
|
22
34
|
subject.do
|
23
|
-
subject.
|
35
|
+
expect(subject).to have_queue_size_of(2)
|
24
36
|
end
|
25
37
|
end
|
26
38
|
|
@@ -39,13 +51,15 @@ describe QueueClassicPlus::Base do
|
|
39
51
|
end
|
40
52
|
|
41
53
|
it "calls perform in a transaction" do
|
42
|
-
QueueClassicPlus::Base.
|
54
|
+
expect(QueueClassicPlus::Base).to receive(:transaction).and_call_original
|
55
|
+
|
43
56
|
subject._perform
|
44
57
|
end
|
45
58
|
|
46
59
|
it "measures the time" do
|
47
|
-
QueueClassicPlus::Metrics.
|
48
|
-
|
60
|
+
expect(QueueClassicPlus::Metrics).to receive(:timing).with("qu_perform_time", {source: "funky.name"}).and_call_original
|
61
|
+
|
62
|
+
subject._perform
|
49
63
|
end
|
50
64
|
end
|
51
65
|
|
@@ -61,7 +75,8 @@ describe QueueClassicPlus::Base do
|
|
61
75
|
end
|
62
76
|
|
63
77
|
it "calls perform outside of a transaction" do
|
64
|
-
QueueClassicPlus::Base.
|
78
|
+
expect(QueueClassicPlus::Base).to_not receive(:transaction)
|
79
|
+
|
65
80
|
subject._perform
|
66
81
|
end
|
67
82
|
end
|
@@ -79,15 +94,15 @@ describe QueueClassicPlus::Base do
|
|
79
94
|
end
|
80
95
|
|
81
96
|
it "retries on specified exception" do
|
82
|
-
subject.retries_on?(SomeException.new).
|
97
|
+
expect(subject.retries_on?(SomeException.new)).to be(true)
|
83
98
|
end
|
84
99
|
|
85
100
|
it "does not retry on unspecified exceptions" do
|
86
|
-
subject.retries_on?(RuntimeError).
|
101
|
+
expect(subject.retries_on?(RuntimeError)).to be(false)
|
87
102
|
end
|
88
103
|
|
89
104
|
it "sets max retries" do
|
90
|
-
subject.max_retries.
|
105
|
+
expect(subject.max_retries).to eq(5)
|
91
106
|
end
|
92
107
|
end
|
93
108
|
|
@@ -104,16 +119,16 @@ describe QueueClassicPlus::Base do
|
|
104
119
|
end
|
105
120
|
|
106
121
|
it "retries on all specified exceptions" do
|
107
|
-
subject.retries_on?(SomeException.new).
|
108
|
-
subject.retries_on?(SomeOtherException.new).
|
122
|
+
expect(subject.retries_on?(SomeException.new)).to be(true)
|
123
|
+
expect(subject.retries_on?(SomeOtherException.new)).to be(true)
|
109
124
|
end
|
110
125
|
|
111
126
|
it "does not retry on unspecified exceptions" do
|
112
|
-
subject.retries_on?(RuntimeError).
|
127
|
+
expect(subject.retries_on?(RuntimeError)).to be(false)
|
113
128
|
end
|
114
129
|
|
115
130
|
it "sets max retries" do
|
116
|
-
subject.max_retries.
|
131
|
+
expect(subject.max_retries).to eq(5)
|
117
132
|
end
|
118
133
|
end
|
119
134
|
|
@@ -133,22 +148,49 @@ describe QueueClassicPlus::Base do
|
|
133
148
|
end
|
134
149
|
|
135
150
|
it "retries on a subclass of a specified exception" do
|
136
|
-
subject.retries_on?(ServiceReallyUnavailable.new).
|
151
|
+
expect(subject.retries_on?(ServiceReallyUnavailable.new)).to be(true)
|
137
152
|
end
|
138
153
|
|
139
154
|
it "does not retry on unspecified exceptions" do
|
140
|
-
subject.retries_on?(RuntimeError).
|
155
|
+
expect(subject.retries_on?(RuntimeError)).to be(false)
|
141
156
|
end
|
142
157
|
|
143
158
|
it "sets max retries" do
|
144
|
-
subject.max_retries.
|
159
|
+
expect(subject.max_retries).to eq(5)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "with Rails defined" do
|
164
|
+
require 'active_job/arguments'
|
165
|
+
|
166
|
+
before { stub_const('Rails', true) }
|
167
|
+
|
168
|
+
subject do
|
169
|
+
Class.new(QueueClassicPlus::Base) do
|
170
|
+
@queue = :test
|
171
|
+
|
172
|
+
def self.perform(foo, bar)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it "serializes parameters when enqueuing a job" do
|
178
|
+
expect(ActiveJob::Arguments).to receive(:serialize).with([42, true])
|
179
|
+
|
180
|
+
subject.do(42, true)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "deserializes parameters when performing an enqueued job" do
|
184
|
+
expect(ActiveJob::Arguments).to receive(:deserialize).with([42, true]) { [42, true] }
|
185
|
+
|
186
|
+
subject._perform(42, true)
|
145
187
|
end
|
146
188
|
end
|
147
189
|
end
|
148
190
|
|
149
191
|
describe ".librato_key" do
|
150
192
|
it "removes unsupported caracter from the classname" do
|
151
|
-
Jobs::Tests::TestJob.librato_key.
|
193
|
+
expect(Jobs::Tests::TestJob.librato_key).to eq('jobs.tests.test_job')
|
152
194
|
end
|
153
195
|
end
|
154
196
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
describe 'requiring queue_classic_plus/new_relic' do
|
2
|
+
class FunkyName < QueueClassicPlus::Base
|
3
|
+
@queue = :test
|
4
|
+
|
5
|
+
def self.perform
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { FunkyName._perform }
|
10
|
+
|
11
|
+
it 'adds Datadog profiling support' do
|
12
|
+
require 'queue_classic_plus/datadog'
|
13
|
+
expect(Datadog.tracer).to receive(:trace).with(
|
14
|
+
'qc.job', service_name: 'qc.job', resource: 'FunkyName#perform'
|
15
|
+
)
|
16
|
+
subject
|
17
|
+
end
|
18
|
+
end
|
data/spec/helpers.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
describe 'requiring queue_classic_plus/new_relic' do
|
2
|
+
subject do
|
3
|
+
Class.new(QueueClassicPlus::Base) do
|
4
|
+
@queue = :test
|
5
|
+
|
6
|
+
def self.perform
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.name
|
10
|
+
'Funky::Name'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'adds NewRelic profiling support' do
|
16
|
+
expect(subject).to receive(:perform_action_with_newrelic_trace).once.with({
|
17
|
+
name: 'perform',
|
18
|
+
class_name: 'Funky::Name',
|
19
|
+
category: 'OtherTransaction/QueueClassicPlus',
|
20
|
+
})
|
21
|
+
|
22
|
+
subject._perform
|
23
|
+
require 'queue_classic_plus/new_relic'
|
24
|
+
subject._perform
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
describe QC do
|
5
|
+
describe ".lock" do
|
6
|
+
context "with a connection from ActiveRecord that casts return types" do
|
7
|
+
before do
|
8
|
+
@old_conn_adapter = QC.default_conn_adapter
|
9
|
+
@activerecord_conn = ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
|
10
|
+
QC.default_conn_adapter = QC::ConnAdapter.new(
|
11
|
+
connection: ActiveRecord::Base.connection.raw_connection
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
@activerecord_conn.disconnect!
|
17
|
+
QC.default_conn_adapter = @old_conn_adapter
|
18
|
+
end
|
19
|
+
|
20
|
+
it "locks the job with remaining_retries" do
|
21
|
+
QC.enqueue_retry_in(1, "puts", 5, 2)
|
22
|
+
sleep 1
|
23
|
+
job = QC.lock
|
24
|
+
|
25
|
+
expect(job[:q_name]).to eq("default")
|
26
|
+
expect(job[:method]).to eq("puts")
|
27
|
+
expect(job[:args][0]).to be(2)
|
28
|
+
expect(job[:remaining_retries]).to eq("5")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "locks the job with remaining_retries" do
|
33
|
+
QC.enqueue_retry_in(1, "puts", 5, 2)
|
34
|
+
sleep 1
|
35
|
+
job = QC.lock
|
36
|
+
|
37
|
+
expect(job[:q_name]).to eq("default")
|
38
|
+
expect(job[:method]).to eq("puts")
|
39
|
+
expect(job[:args][0]).to be(2)
|
40
|
+
expect(job[:remaining_retries]).to eq("5")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/sample_jobs.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
1
3
|
class SomeException < RuntimeError
|
2
4
|
end
|
3
5
|
|
@@ -12,7 +14,7 @@ module Jobs
|
|
12
14
|
@queue = :low
|
13
15
|
retry! on: SomeException, max: 5
|
14
16
|
|
15
|
-
def self.perform
|
17
|
+
def self.perform(should_raise)
|
16
18
|
raise SomeException if should_raise
|
17
19
|
end
|
18
20
|
end
|
@@ -21,10 +23,11 @@ module Jobs
|
|
21
23
|
class TestJobNoRetry < QueueClassicPlus::Base
|
22
24
|
class Custom < RuntimeError
|
23
25
|
end
|
26
|
+
disable_retries!
|
24
27
|
|
25
28
|
@queue = :low
|
26
29
|
|
27
|
-
def self.perform
|
30
|
+
def self.perform(should_raise)
|
28
31
|
raise Custom if should_raise
|
29
32
|
end
|
30
33
|
end
|
@@ -32,12 +35,36 @@ module Jobs
|
|
32
35
|
|
33
36
|
class TestJob < QueueClassicPlus::Base
|
34
37
|
@queue = :low
|
35
|
-
retry! on: SomeException, max:
|
38
|
+
retry! on: SomeException, max: 1
|
36
39
|
|
37
|
-
def self.perform
|
40
|
+
def self.perform(should_raise)
|
38
41
|
raise SomeException if should_raise
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
45
|
+
class Exception < RuntimeError
|
46
|
+
attr_reader :original_exception
|
47
|
+
|
48
|
+
def initialize(e)
|
49
|
+
@original_exception = e
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class ConnectionReapedTestJob < QueueClassicPlus::Base
|
54
|
+
@queue = :low
|
55
|
+
retry! on: Exception, max: 5
|
56
|
+
|
57
|
+
def self.perform
|
58
|
+
raise Exception.new(PG::UnableToSend.new)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class UniqueViolationTestJob < QueueClassicPlus::Base
|
63
|
+
@queue = :low
|
64
|
+
|
65
|
+
def self.perform
|
66
|
+
raise Exception.new(PG::UniqueViolation.new)
|
67
|
+
end
|
68
|
+
end
|
42
69
|
end
|
43
70
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,8 +4,11 @@ require 'timecop'
|
|
4
4
|
require 'queue_classic_matchers'
|
5
5
|
require_relative './sample_jobs'
|
6
6
|
require_relative './helpers'
|
7
|
+
require 'byebug'
|
7
8
|
require 'pry'
|
9
|
+
require 'ddtrace'
|
8
10
|
|
11
|
+
ENV["QC_RAILS_DATABASE"] ||= "false" # test on QC::ConnAdapter by default
|
9
12
|
ENV["DATABASE_URL"] ||= "postgres:///queue_classic_plus_test"
|
10
13
|
|
11
14
|
RSpec.configure do |config|
|
@@ -20,5 +23,7 @@ RSpec.configure do |config|
|
|
20
23
|
|
21
24
|
config.before(:each) do
|
22
25
|
QC.default_conn_adapter.execute "TRUNCATE queue_classic_jobs;"
|
26
|
+
# Reset the default (memoized) queue instance between specs
|
27
|
+
QC.default_queue = nil
|
23
28
|
end
|
24
29
|
end
|
data/spec/worker_spec.rb
CHANGED
@@ -9,22 +9,22 @@ describe QueueClassicPlus::CustomWorker do
|
|
9
9
|
|
10
10
|
it "record failures in the failed queue" do
|
11
11
|
queue.enqueue("Kerklfadsjflaksj", 1, 2, 3)
|
12
|
-
failed_queue.count.
|
12
|
+
expect(failed_queue.count).to eq(0)
|
13
13
|
worker.work
|
14
|
-
failed_queue.count.
|
14
|
+
expect(failed_queue.count).to eq(1)
|
15
15
|
job = failed_queue.lock
|
16
|
-
job[:method].
|
17
|
-
job[:args].
|
16
|
+
expect(job[:method]).to eq("Kerklfadsjflaksj")
|
17
|
+
expect(job[:args]).to eq([1, 2, 3])
|
18
18
|
full_job = find_job(job[:id])
|
19
19
|
|
20
|
-
full_job['last_error'].
|
20
|
+
expect(full_job['last_error']).to_not be_nil
|
21
21
|
end
|
22
22
|
|
23
23
|
it "records normal errors" do
|
24
24
|
queue.enqueue("Jobs::Tests::TestJobNoRetry.perform", true)
|
25
|
-
failed_queue.count.
|
25
|
+
expect(failed_queue.count).to eq(0)
|
26
26
|
worker.work
|
27
|
-
failed_queue.count.
|
27
|
+
expect(failed_queue.count).to eq(1)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -42,47 +42,141 @@ describe QueueClassicPlus::CustomWorker do
|
|
42
42
|
job_type.enqueue_perform(true)
|
43
43
|
end.to change_queue_size_of(job_type).by(1)
|
44
44
|
|
45
|
-
Jobs::Tests::LockedTestJob.
|
46
|
-
failed_queue.count.
|
47
|
-
QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries'].
|
45
|
+
expect(Jobs::Tests::LockedTestJob).to have_queue_size_of(1)
|
46
|
+
expect(failed_queue.count).to eq(0)
|
47
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to be_nil
|
48
|
+
|
49
|
+
expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.retry', source: nil )
|
48
50
|
|
49
51
|
Timecop.freeze do
|
50
52
|
worker.work
|
51
53
|
|
52
|
-
failed_queue.count.
|
53
|
-
QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries'].
|
54
|
-
Jobs::Tests::LockedTestJob.
|
54
|
+
expect(failed_queue.count).to eq(0) # not enqueued on Failed
|
55
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to eq "4"
|
56
|
+
expect(Jobs::Tests::LockedTestJob).to have_scheduled(true).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
|
55
57
|
end
|
56
58
|
|
57
59
|
Timecop.freeze(Time.now + (described_class::BACKOFF_WIDTH * 2)) do
|
58
60
|
# the job should be re-enqueued with a decremented retry count
|
59
61
|
jobs = QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true])
|
60
|
-
jobs.size.
|
62
|
+
expect(jobs.size).to eq(1)
|
61
63
|
job = jobs.first
|
62
|
-
job['remaining_retries'].to_i.
|
63
|
-
job['locked_by'].
|
64
|
-
job['locked_at'].
|
64
|
+
expect(job['remaining_retries'].to_i).to eq(job_type.max_retries - 1)
|
65
|
+
expect(job['locked_by']).to be_nil
|
66
|
+
expect(job['locked_at']).to be_nil
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
job_type.enqueue_perform(true)
|
72
|
-
end.to change_queue_size_of(job_type).by(1)
|
70
|
+
context 'when Rails is defined' do
|
71
|
+
require 'active_job'
|
72
|
+
require 'active_job/arguments'
|
73
73
|
|
74
|
-
|
75
|
-
|
76
|
-
|
74
|
+
before { stub_const('Rails', Struct.new(:logger).new(Logger.new(STDOUT))) }
|
75
|
+
|
76
|
+
it 'retries' do
|
77
|
+
expect do
|
78
|
+
job_type.enqueue_perform(:foo)
|
79
|
+
end.to change_queue_size_of(job_type).by(1)
|
80
|
+
|
81
|
+
expect(Jobs::Tests::LockedTestJob).to have_queue_size_of(1)
|
82
|
+
expect(failed_queue.count).to eq(0)
|
83
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [:foo]).first['remaining_retries']).to be_nil
|
84
|
+
|
85
|
+
expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.retry', source: nil).twice
|
86
|
+
|
87
|
+
Timecop.freeze do
|
88
|
+
worker.work
|
89
|
+
|
90
|
+
expect(failed_queue.count).to eq(0) # not enqueued on Failed
|
91
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [:foo]).first['remaining_retries']).to eq "4"
|
92
|
+
expect(Jobs::Tests::LockedTestJob).to have_scheduled(:foo).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
|
93
|
+
end
|
94
|
+
|
95
|
+
Timecop.freeze(Time.now + (described_class::BACKOFF_WIDTH * 2)) do
|
96
|
+
# the job should be re-enqueued with a decremented retry count
|
97
|
+
jobs = QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [:foo])
|
98
|
+
expect(jobs.size).to eq(1)
|
99
|
+
job = jobs.first
|
100
|
+
expect(job['remaining_retries'].to_i).to eq(job_type.max_retries - 1)
|
101
|
+
expect(job['locked_by']).to be_nil
|
102
|
+
expect(job['locked_at']).to be_nil
|
103
|
+
end
|
77
104
|
|
78
|
-
Timecop.freeze do
|
79
105
|
worker.work
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'when PG connection reaped during a job' do
|
110
|
+
before { Jobs::Tests::ConnectionReapedTestJob.enqueue_perform }
|
80
111
|
|
81
|
-
|
82
|
-
|
83
|
-
|
112
|
+
it 'retries' do
|
113
|
+
expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.force_retry', source: nil )
|
114
|
+
Timecop.freeze do
|
115
|
+
worker.work
|
116
|
+
expect(failed_queue.count).to eq 0
|
117
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::ConnectionReapedTestJob._perform', []).first['remaining_retries']).to eq "4"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'ensures to rollback' do
|
122
|
+
allow(QC.default_conn_adapter).to receive(:execute).and_call_original
|
123
|
+
expect(QC.default_conn_adapter).to receive(:execute).with('ROLLBACK')
|
124
|
+
Timecop.freeze do
|
125
|
+
worker.work
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with a custom exception having max: 1 retry' do
|
131
|
+
before { Jobs::Tests::TestJob.enqueue_perform(true) }
|
132
|
+
|
133
|
+
it 'retries' do
|
134
|
+
expect(QueueClassicPlus::Metrics).to receive(:increment).with('qc.retry', source: nil )
|
135
|
+
Timecop.freeze do
|
136
|
+
worker.work
|
137
|
+
expect(failed_queue.count).to eq 0
|
138
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::TestJob._perform', [true]).first['remaining_retries']).to eq "0"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'with non-connection based PG jobs' do
|
144
|
+
before { Jobs::Tests::UniqueViolationTestJob.enqueue_perform }
|
145
|
+
|
146
|
+
it 'sends the job to the failed jobs queue' do
|
147
|
+
Timecop.freeze do
|
148
|
+
worker.work
|
149
|
+
end
|
150
|
+
expect(failed_queue.count).to eq 1
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'when retries have been exhausted' do
|
155
|
+
before do
|
156
|
+
job_type.max_retries = 0
|
157
|
+
end
|
158
|
+
|
159
|
+
after do
|
160
|
+
job_type.max_retries = 5
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'enqueues in the failed queue' do
|
164
|
+
expect do
|
165
|
+
job_type.enqueue_perform(true)
|
166
|
+
end.to change_queue_size_of(job_type).by(1)
|
167
|
+
|
168
|
+
expect(Jobs::Tests::LockedTestJob).to have_queue_size_of(1)
|
169
|
+
expect(failed_queue.count).to eq(0)
|
170
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('low', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to be_nil
|
171
|
+
|
172
|
+
Timecop.freeze do
|
173
|
+
worker.work
|
174
|
+
|
175
|
+
expect(QueueClassicMatchers::QueueClassicRspec.find_by_args('failed_jobs', 'Jobs::Tests::LockedTestJob._perform', [true]).first['remaining_retries']).to be_nil
|
176
|
+
expect(failed_queue.count).to eq(1) # not enqueued on Failed
|
177
|
+
expect(Jobs::Tests::LockedTestJob).to_not have_scheduled(true).at(Time.now + described_class::BACKOFF_WIDTH) # should have scheduled a retry for later
|
178
|
+
end
|
84
179
|
end
|
85
180
|
end
|
86
181
|
end
|
87
182
|
end
|
88
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: queue_classic_plus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0.alpha8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Mathieu
|
@@ -10,36 +10,36 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2021-10-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: queue_classic
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - '='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 4.0.0.pre.alpha1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - '='
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version:
|
28
|
+
version: 4.0.0.pre.alpha1
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: bundler
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
33
|
- - "~>"
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version: '
|
35
|
+
version: '2.0'
|
36
36
|
type: :development
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
40
|
- - "~>"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '
|
42
|
+
version: '2.0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: rake
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -54,6 +54,34 @@ dependencies:
|
|
54
54
|
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '0'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: activerecord
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '6.0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '6.0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: activejob
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
57
85
|
description: ''
|
58
86
|
email:
|
59
87
|
- simon.math@gmail.com
|
@@ -63,10 +91,11 @@ executables: []
|
|
63
91
|
extensions: []
|
64
92
|
extra_rdoc_files: []
|
65
93
|
files:
|
94
|
+
- ".circleci/config.yml"
|
95
|
+
- ".github/dependabot.yml"
|
66
96
|
- ".gitignore"
|
67
97
|
- ".rspec"
|
68
98
|
- ".rvmrc"
|
69
|
-
- ".travis.yml"
|
70
99
|
- Gemfile
|
71
100
|
- Guardfile
|
72
101
|
- LICENSE.txt
|
@@ -74,6 +103,7 @@ files:
|
|
74
103
|
- Rakefile
|
75
104
|
- lib/queue_classic_plus.rb
|
76
105
|
- lib/queue_classic_plus/base.rb
|
106
|
+
- lib/queue_classic_plus/datadog.rb
|
77
107
|
- lib/queue_classic_plus/inflector.rb
|
78
108
|
- lib/queue_classic_plus/inheritable_attr.rb
|
79
109
|
- lib/queue_classic_plus/metrics.rb
|
@@ -89,8 +119,11 @@ files:
|
|
89
119
|
- lib/rails/generators/qc_plus_job/templates/job_spec.rb.erb
|
90
120
|
- queue_classic_plus.gemspec
|
91
121
|
- spec/base_spec.rb
|
122
|
+
- spec/datadog_spec.rb
|
92
123
|
- spec/helpers.rb
|
93
124
|
- spec/inflector_spec.rb
|
125
|
+
- spec/new_relic_spec.rb
|
126
|
+
- spec/queue_classic/queue_spec.rb
|
94
127
|
- spec/sample_jobs.rb
|
95
128
|
- spec/spec_helper.rb
|
96
129
|
- spec/update_metrics_spec.rb
|
@@ -114,15 +147,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
147
|
- !ruby/object:Gem::Version
|
115
148
|
version: 1.3.1
|
116
149
|
requirements: []
|
117
|
-
|
118
|
-
rubygems_version: 2.2.2
|
150
|
+
rubygems_version: 3.1.6
|
119
151
|
signing_key:
|
120
152
|
specification_version: 4
|
121
153
|
summary: Useful extras for Queue Classic
|
122
154
|
test_files:
|
123
155
|
- spec/base_spec.rb
|
156
|
+
- spec/datadog_spec.rb
|
124
157
|
- spec/helpers.rb
|
125
158
|
- spec/inflector_spec.rb
|
159
|
+
- spec/new_relic_spec.rb
|
160
|
+
- spec/queue_classic/queue_spec.rb
|
126
161
|
- spec/sample_jobs.rb
|
127
162
|
- spec/spec_helper.rb
|
128
163
|
- spec/update_metrics_spec.rb
|