canvas_sync 0.21.1.beta1 → 0.22.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/canvas_sync/concerns/auto_relations.rb +11 -0
- data/lib/canvas_sync/config.rb +3 -5
- data/lib/canvas_sync/generators/templates/models/rubric.rb +2 -1
- data/lib/canvas_sync/job_batches/batch.rb +432 -402
- data/lib/canvas_sync/job_batches/callback.rb +100 -114
- data/lib/canvas_sync/job_batches/chain_builder.rb +194 -196
- data/lib/canvas_sync/job_batches/{active_job.rb → compat/active_job.rb} +2 -2
- data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/helpers.rb +1 -1
- data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web.rb +3 -3
- data/lib/canvas_sync/job_batches/{sidekiq.rb → compat/sidekiq.rb} +35 -22
- data/lib/canvas_sync/job_batches/compat.rb +20 -0
- data/lib/canvas_sync/job_batches/context_hash.rb +124 -126
- data/lib/canvas_sync/job_batches/jobs/base_job.rb +2 -4
- data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +14 -16
- data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +125 -127
- data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +14 -16
- data/lib/canvas_sync/job_batches/pool.rb +193 -195
- data/lib/canvas_sync/job_batches/redis_model.rb +50 -52
- data/lib/canvas_sync/job_batches/redis_script.rb +129 -131
- data/lib/canvas_sync/job_batches/status.rb +85 -87
- data/lib/canvas_sync/job_uniqueness/compat/active_job.rb +75 -0
- data/lib/canvas_sync/job_uniqueness/compat/sidekiq.rb +135 -0
- data/lib/canvas_sync/job_uniqueness/compat.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/configuration.rb +25 -0
- data/lib/canvas_sync/job_uniqueness/job_uniqueness.rb +47 -0
- data/lib/canvas_sync/job_uniqueness/lock_context.rb +171 -0
- data/lib/canvas_sync/job_uniqueness/locksmith.rb +92 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/base.rb +32 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/log.rb +13 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/null_strategy.rb +9 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/raise.rb +11 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/reject.rb +21 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict/reschedule.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/on_conflict.rb +41 -0
- data/lib/canvas_sync/job_uniqueness/strategy/base.rb +104 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_and_while_executing.rb +35 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_executed.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_executing.rb +20 -0
- data/lib/canvas_sync/job_uniqueness/strategy/until_expired.rb +16 -0
- data/lib/canvas_sync/job_uniqueness/strategy/while_executing.rb +26 -0
- data/lib/canvas_sync/job_uniqueness/strategy.rb +27 -0
- data/lib/canvas_sync/job_uniqueness/unique_job_common.rb +79 -0
- data/lib/canvas_sync/misc_helper.rb +1 -1
- data/lib/canvas_sync/version.rb +1 -1
- data/lib/canvas_sync.rb +4 -3
- data/spec/dummy/app/models/rubric.rb +2 -1
- data/spec/dummy/config/environments/test.rb +1 -1
- data/spec/job_batching/batch_spec.rb +49 -7
- data/spec/job_batching/{active_job_spec.rb → compat/active_job_spec.rb} +2 -2
- data/spec/job_batching/{sidekiq_spec.rb → compat/sidekiq_spec.rb} +14 -12
- data/spec/job_batching/flow_spec.rb +1 -1
- data/spec/job_batching/integration_helper.rb +1 -1
- data/spec/job_batching/status_spec.rb +2 -2
- data/spec/job_uniqueness/compat/active_job_spec.rb +49 -0
- data/spec/job_uniqueness/compat/sidekiq_spec.rb +68 -0
- data/spec/job_uniqueness/lock_context_spec.rb +95 -0
- data/spec/job_uniqueness/on_conflict/log_spec.rb +11 -0
- data/spec/job_uniqueness/on_conflict/raise_spec.rb +10 -0
- data/spec/job_uniqueness/on_conflict/reschedule_spec.rb +24 -0
- data/spec/job_uniqueness/on_conflict_spec.rb +16 -0
- data/spec/job_uniqueness/spec_helper.rb +14 -0
- data/spec/job_uniqueness/strategy/base_spec.rb +100 -0
- data/spec/job_uniqueness/strategy/until_and_while_executing_spec.rb +48 -0
- data/spec/job_uniqueness/strategy/until_executed_spec.rb +23 -0
- data/spec/job_uniqueness/strategy/until_executing_spec.rb +23 -0
- data/spec/job_uniqueness/strategy/until_expired_spec.rb +23 -0
- data/spec/job_uniqueness/strategy/while_executing_spec.rb +33 -0
- data/spec/job_uniqueness/support/lock_strategy.rb +28 -0
- data/spec/job_uniqueness/support/on_conflict.rb +24 -0
- data/spec/job_uniqueness/support/test_worker.rb +19 -0
- data/spec/job_uniqueness/unique_job_common_spec.rb +45 -0
- data/spec/spec_helper.rb +1 -1
- metadata +278 -204
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/css/styles.less +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/js/batch_tree.js +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/js/util.js +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_batch_tree.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_batches_table.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_common.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_jobs_table.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_pagination.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/batch.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/batches.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/pool.erb +0 -0
- /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/pools.erb +0 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::Compat::Sidekiq do
|
4
|
+
let(:config) { defined?(Sidekiq::Config) ? double(Sidekiq::Config) : class_double(Sidekiq) }
|
5
|
+
let(:client_middleware) { double(Sidekiq::Middleware::Chain) }
|
6
|
+
|
7
|
+
context 'client' do
|
8
|
+
it 'adds client middleware' do
|
9
|
+
allow(Sidekiq).to receive(:configure_client).and_yield(config)
|
10
|
+
expect(config).to receive(:client_middleware).and_yield(client_middleware)
|
11
|
+
expect(client_middleware).to receive(:insert_before).with(anything, CanvasSync::JobUniqueness::Compat::Sidekiq::ClientMiddleware)
|
12
|
+
CanvasSync::JobUniqueness::Compat::Sidekiq.configure
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'server' do
|
17
|
+
let(:server_middleware) { double(Sidekiq::Middleware::Chain) }
|
18
|
+
|
19
|
+
it 'adds client and server middleware' do
|
20
|
+
allow(Sidekiq).to receive(:configure_server).and_yield(config)
|
21
|
+
expect(config).to receive(:client_middleware).and_yield(client_middleware)
|
22
|
+
expect(config).to receive(:server_middleware).and_yield(server_middleware)
|
23
|
+
expect(client_middleware).to receive(:insert_before).with(anything, CanvasSync::JobUniqueness::Compat::Sidekiq::ClientMiddleware)
|
24
|
+
expect(server_middleware).to receive(:insert_after).with(anything, CanvasSync::JobUniqueness::Compat::Sidekiq::ServerMiddleware)
|
25
|
+
CanvasSync::JobUniqueness::Compat::Sidekiq.configure
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'Job Extension' do
|
30
|
+
let!(:dummy_worker) do
|
31
|
+
Class.new do
|
32
|
+
include Sidekiq::Worker
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'includes UniqueJobExtension' do
|
37
|
+
expect(dummy_worker < CanvasSync::JobUniqueness::Compat::Sidekiq::WorkerExtension).to be true
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'has the ensure_uniqueness method' do
|
41
|
+
expect(dummy_worker.method(:ensure_uniqueness)).to be_present
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'Job' do
|
46
|
+
include_context 'with TestWorker'
|
47
|
+
|
48
|
+
it 'runs as expected' do
|
49
|
+
strategy = CanvasSync::JobUniqueness::Strategy::UntilExecuted.new(nil)
|
50
|
+
allow_any_instance_of(CanvasSync::JobUniqueness::LockContext).to receive(:lock_strategy) do |lock_context|
|
51
|
+
strategy.instance_variable_set(:@lock_context, lock_context)
|
52
|
+
strategy
|
53
|
+
end
|
54
|
+
allow(CanvasSync::JobUniqueness::Strategy::UntilExecuted).to receive(:new).and_return(strategy)
|
55
|
+
|
56
|
+
expect(strategy).to receive(:on_enqueue).and_call_original
|
57
|
+
expect(strategy).to receive(:on_perform).and_call_original
|
58
|
+
expect(strategy).to receive(:batch_callback).with(:complete, anything).and_call_original
|
59
|
+
expect(strategy).to receive(:batch_callback).with(:success, anything).and_call_original
|
60
|
+
|
61
|
+
expect_any_instance_of(TestWorker).to receive(:perform)
|
62
|
+
|
63
|
+
Sidekiq::Testing.inline! do
|
64
|
+
worker.perform_async
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::LockContext do
|
4
|
+
let!(:worker) do
|
5
|
+
Class.new do
|
6
|
+
include Sidekiq::Worker
|
7
|
+
|
8
|
+
ensure_uniqueness(
|
9
|
+
strategy: :until_executed,
|
10
|
+
)
|
11
|
+
|
12
|
+
def perform; end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:each) { stub_const('TestWorker', worker) }
|
17
|
+
|
18
|
+
let(:lock_context) { described_class.new(context_data) }
|
19
|
+
let(:context_data) { { queue: 'default', job_clazz: 'TestWorker', args: [1,2,3], kwargs: { foo: 'bar' } } }
|
20
|
+
|
21
|
+
describe '#base_key' do
|
22
|
+
let(:scope) { :per_queue }
|
23
|
+
before(:each) do
|
24
|
+
TestWorker.unique_job_options[:scope] = scope
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when scope is a Proc' do
|
28
|
+
let(:scope) { ->(queue:) { "blob" } }
|
29
|
+
|
30
|
+
it 'returns the base key' do
|
31
|
+
expect(lock_context.base_key).to match /^uniquejob:blob:.*/
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when scope is :global' do
|
36
|
+
let(:scope) { :global }
|
37
|
+
|
38
|
+
it 'returns the base key' do
|
39
|
+
expect(lock_context.base_key).to match /^uniquejob:TestWorker:.*/
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when scope is :per_queue' do
|
44
|
+
let(:scope) { :per_queue }
|
45
|
+
|
46
|
+
it 'returns the base key' do
|
47
|
+
expect(lock_context.base_key).to match /^uniquejob:TestWorker:default:.*/
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when scope is not a Proc, :global, or :per_queue' do
|
52
|
+
let(:scope) { 'foo' }
|
53
|
+
|
54
|
+
it 'returns the base key' do
|
55
|
+
expect(lock_context.base_key).to match /^uniquejob:foo:.*/
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#call_conflict_strategy' do
|
61
|
+
before(:each) do
|
62
|
+
worker.unique_job_options[:on_conflict] = :raise
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'invokes the conflict strategy' do
|
66
|
+
expect_any_instance_of(CanvasSync::JobUniqueness::OnConflict::Raise).to receive(:call)
|
67
|
+
lock_context.send(:call_conflict_strategy, :perform)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#conflict_strategy_for' do
|
72
|
+
before(:each) do
|
73
|
+
worker.unique_job_options[:on_conflict] = :raise
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'resolves a united conflict strategy' do
|
77
|
+
expect(lock_context.send(:conflict_strategy_for, :perform)).to be_a CanvasSync::JobUniqueness::OnConflict::Raise
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'resolves enqueue strategy' do
|
81
|
+
worker.unique_job_options[:on_conflict] = { enqueue: :log, perform: :raise }
|
82
|
+
expect(lock_context.send(:conflict_strategy_for, :enqueue)).to be_a CanvasSync::JobUniqueness::OnConflict::Log
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'resolves perform strategy' do
|
86
|
+
worker.unique_job_options[:on_conflict] = { enqueue: :log, perform: :reject }
|
87
|
+
expect(lock_context.send(:conflict_strategy_for, :perform)).to be_a CanvasSync::JobUniqueness::OnConflict::Reject
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'resolves null strategy' do
|
91
|
+
worker.unique_job_options[:on_conflict] = { enqueue: :log }
|
92
|
+
expect(lock_context.send(:conflict_strategy_for, :perform)).to be_a CanvasSync::JobUniqueness::OnConflict::NullStrategy
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::OnConflict::Log do
|
4
|
+
include_context "on_conflict specs"
|
5
|
+
include_examples "OnConflict is compatible with", %i[until_executing while_executing until_executed until_and_while_executing until_expired]
|
6
|
+
|
7
|
+
it "logs" do
|
8
|
+
expect(CanvasSync::JobUniqueness.logger).to receive(:info)
|
9
|
+
on_conflict.call
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::OnConflict::Raise do
|
4
|
+
include_context "on_conflict specs"
|
5
|
+
include_examples "OnConflict is compatible with", %i[until_executing while_executing until_executed until_and_while_executing until_expired]
|
6
|
+
|
7
|
+
it "raises an error" do
|
8
|
+
expect { on_conflict.call }.to raise_error(CanvasSync::JobUniqueness::Conflict)
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::OnConflict::Reschedule do
|
4
|
+
include_context "on_conflict specs"
|
5
|
+
include_examples "OnConflict is compatible with", %i[while_executing]
|
6
|
+
|
7
|
+
it "calls reenqueue" do
|
8
|
+
expect(lock_context).to receive(:reenqueue) do |*args, **kwargs|
|
9
|
+
expect(Thread.current[:unique_jobs_previous_jid]).to be_present
|
10
|
+
end
|
11
|
+
on_conflict.call
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reschedules successfully" do
|
15
|
+
TestWorker.unique_job_options.merge!({ strategy: :until_and_while_executing, on_conflict: { enqueue: :raise, perform: :reschedule }})
|
16
|
+
|
17
|
+
lock_context.handle_lifecycle!(:enqueue) {}
|
18
|
+
lock_context.handle_lifecycle!(:perform) {}
|
19
|
+
|
20
|
+
lock_context2 = CanvasSync::JobUniqueness::Compat::Sidekiq::SidekiqLockContext.new({ jid: 'j2', queue: 'default', job_clazz: 'TestWorker'}, job_instance: {})
|
21
|
+
lock_context2.handle_lifecycle!(:enqueue) {}
|
22
|
+
lock_context2.handle_lifecycle!(:perform) {}
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::OnConflict do
|
4
|
+
|
5
|
+
describe ".validate!" do
|
6
|
+
it "passes a valid pair" do
|
7
|
+
described_class.validate!(:reject, :while_executing)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "raises an error if the strategy is not valid for the given action" do
|
11
|
+
expect do
|
12
|
+
described_class.validate!(:reject, :until_executed)
|
13
|
+
end.to raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
require "canvas_sync/job_uniqueness/job_uniqueness"
|
3
|
+
|
4
|
+
Dir[File.join(File.dirname(__FILE__), "./support/**/*.rb")].sort.each { |f| require f }
|
5
|
+
|
6
|
+
Sidekiq::Testing.server_middleware do |chain|
|
7
|
+
chain.insert_before CanvasSync::JobBatches::Compat::Sidekiq::ServerMiddleware, CanvasSync::JobUniqueness::Compat::Sidekiq::ServerMiddleware
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.before(:each) do
|
12
|
+
CanvasSync.redis.flushdb
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::Strategy::Base do
|
4
|
+
include_context 'with TestWorker'
|
5
|
+
|
6
|
+
let!(:jid) { worker.perform_async }
|
7
|
+
let(:context) { CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker' }) }
|
8
|
+
let(:strategy) { CanvasSync::JobUniqueness::Strategy::UntilExecuted.new(context) }
|
9
|
+
|
10
|
+
describe '#wrap_in_batch' do
|
11
|
+
it 'does not wrap if re-enqueuing' do
|
12
|
+
Thread.current[:unique_jobs_previous_jid] = "j100"
|
13
|
+
expect(CanvasSync::JobBatches::Batch).not_to receive(:new)
|
14
|
+
strategy.send(:wrap_in_batch, &->{ })
|
15
|
+
ensure
|
16
|
+
Thread.current[:unique_jobs_previous_jid] = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when the job fails' do
|
20
|
+
before(:each) do
|
21
|
+
TestWorker.unique_job_options[:strategy] = :until_executed
|
22
|
+
allow_any_instance_of(worker).to receive(:perform) do |w|
|
23
|
+
@bid = w.bid
|
24
|
+
raise 'error'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'unlock_on_failure: :any' do
|
29
|
+
before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :any }
|
30
|
+
|
31
|
+
it 'unlocks on any failure' do
|
32
|
+
expect(strategy).to be_locked
|
33
|
+
expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
|
34
|
+
Sidekiq::Worker.drain_all
|
35
|
+
expect(strategy).not_to be_locked
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'unlock_on_failure: :death' do
|
40
|
+
before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :death }
|
41
|
+
|
42
|
+
it 'does not unlock on any failure' do
|
43
|
+
expect(strategy).to be_locked
|
44
|
+
expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
|
45
|
+
Sidekiq::Worker.drain_all
|
46
|
+
expect(strategy).to be_locked
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'it unlocks on death' do
|
50
|
+
expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
|
51
|
+
Sidekiq::Worker.drain_all
|
52
|
+
expect(strategy).to be_locked
|
53
|
+
CanvasSync::JobBatches::Batch.process_dead_job(@bid, jid)
|
54
|
+
Sidekiq::Worker.drain_all
|
55
|
+
expect(strategy).not_to be_locked
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'unlock_on_failure: :stagnant' do
|
60
|
+
before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :stagnant }
|
61
|
+
|
62
|
+
it 'does not unlock on any failure' do
|
63
|
+
expect(strategy).to be_locked
|
64
|
+
expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
|
65
|
+
Sidekiq::Worker.drain_all
|
66
|
+
expect(strategy).to be_locked
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'it unlocks on stagnation' do
|
70
|
+
expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
|
71
|
+
Sidekiq::Worker.drain_all
|
72
|
+
expect(strategy).to be_locked
|
73
|
+
CanvasSync::JobBatches::Batch.process_dead_job(@bid, jid)
|
74
|
+
Sidekiq::Worker.drain_all
|
75
|
+
expect(strategy).not_to be_locked
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'unlock_on_failure: :expire' do
|
80
|
+
before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :expire }
|
81
|
+
|
82
|
+
it 'does not unlock on any failure' do
|
83
|
+
expect(strategy).to be_locked
|
84
|
+
expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
|
85
|
+
Sidekiq::Worker.drain_all
|
86
|
+
expect(strategy).to be_locked
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'does not unlock on death' do
|
90
|
+
expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
|
91
|
+
Sidekiq::Worker.drain_all
|
92
|
+
expect(strategy).to be_locked
|
93
|
+
CanvasSync::JobBatches::Batch.process_dead_job(@bid, jid)
|
94
|
+
Sidekiq::Worker.drain_all
|
95
|
+
expect(strategy).to be_locked
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilAndWhileExecuting do
|
4
|
+
include_context 'lock strategy specs', strategy: :until_and_while_executing
|
5
|
+
|
6
|
+
include_examples "a lock implementation"
|
7
|
+
|
8
|
+
describe "#on_enqueue" do
|
9
|
+
it "locks the queue" do
|
10
|
+
process_one.on_enqueue {}
|
11
|
+
expect(process_one).to be_locked
|
12
|
+
end
|
13
|
+
|
14
|
+
it "does not lock the runtime lock" do
|
15
|
+
process_one.on_enqueue {}
|
16
|
+
expect(process_one.send(:runtime_lock)).to_not be_locked
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#on_perform" do
|
21
|
+
it "unlocks the queue" do
|
22
|
+
process_one.on_perform do
|
23
|
+
expect(process_one).to_not be_locked
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "locks the runtime lock" do
|
28
|
+
process_one.on_perform do
|
29
|
+
expect(process_one.send(:runtime_lock)).to be_locked
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "unlocks the runtime lock after processing" do
|
34
|
+
process_one.on_perform do
|
35
|
+
process_one.send(:runtime_lock).batch_callback(:success, nil)
|
36
|
+
expect(process_one.send(:runtime_lock)).to_not be_locked
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "prevents other processes from executing" do
|
41
|
+
process_one.on_perform do
|
42
|
+
expect do
|
43
|
+
process_two.on_perform { }
|
44
|
+
end.to raise_error(CanvasSync::JobUniqueness::CouldNotLockError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilExecuted do
|
4
|
+
include_context 'lock strategy specs', strategy: :until_executed
|
5
|
+
|
6
|
+
include_examples "a lock implementation"
|
7
|
+
|
8
|
+
describe "#on_enqueue" do
|
9
|
+
it "locks the queue" do
|
10
|
+
process_one.on_enqueue {}
|
11
|
+
expect(process_one).to be_locked
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#on_perform" do
|
16
|
+
it "unlocks after processing" do
|
17
|
+
process_one.on_enqueue {}
|
18
|
+
process_one.on_perform {}
|
19
|
+
process_one.batch_callback(:success, nil)
|
20
|
+
expect(process_one).to_not be_locked
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilExecuting do
|
4
|
+
include_context 'lock strategy specs', strategy: :until_executing
|
5
|
+
|
6
|
+
include_examples "a lock implementation"
|
7
|
+
|
8
|
+
describe "#on_enqueue" do
|
9
|
+
it "locks the queue" do
|
10
|
+
process_one.on_enqueue {}
|
11
|
+
expect(process_one).to be_locked
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#on_perform" do
|
16
|
+
it "unlocks before processing" do
|
17
|
+
process_one.on_enqueue {}
|
18
|
+
process_one.on_perform do
|
19
|
+
expect(process_one).to_not be_locked
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilExpired do
|
4
|
+
include_context 'lock strategy specs', strategy: :until_expired
|
5
|
+
|
6
|
+
# include_examples "a lock implementation"
|
7
|
+
|
8
|
+
describe "#on_enqueue" do
|
9
|
+
it "locks the queue" do
|
10
|
+
process_one.on_enqueue {}
|
11
|
+
expect(process_one).to be_locked
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#on_perform" do
|
16
|
+
it "does not unlock after processing" do
|
17
|
+
process_one.on_enqueue {}
|
18
|
+
process_one.on_perform {}
|
19
|
+
process_one.batch_callback(:success, nil)
|
20
|
+
expect(process_one).to be_locked
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::Strategy::WhileExecuting do
|
4
|
+
include_context 'lock strategy specs', strategy: :while_executing
|
5
|
+
|
6
|
+
include_examples "a lock implementation"
|
7
|
+
|
8
|
+
describe "#on_enqueue" do
|
9
|
+
it "does not lock jobs" do
|
10
|
+
expect(process_one.on_enqueue {})
|
11
|
+
expect(process_one).not_to be_locked
|
12
|
+
|
13
|
+
expect(process_two.on_enqueue {})
|
14
|
+
expect(process_two).not_to be_locked
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#on_perform" do
|
19
|
+
it "locks the process" do
|
20
|
+
process_one.on_perform do
|
21
|
+
expect(process_one).to be_locked
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "prevents other processes from executing" do
|
26
|
+
process_one.on_perform do
|
27
|
+
expect do
|
28
|
+
process_two.on_perform { }
|
29
|
+
end.to raise_error(CanvasSync::JobUniqueness::CouldNotLockError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.shared_context "lock strategy specs" do |strategy:|
|
4
|
+
include_context 'with TestWorker'
|
5
|
+
|
6
|
+
before(:each) { TestWorker.unique_job_options[:strategy] = strategy }
|
7
|
+
|
8
|
+
let(:context1) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j1', queue: 'default', job_clazz: 'TestWorker'}) }
|
9
|
+
let(:context2) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j2', queue: 'default', job_clazz: 'TestWorker'}) }
|
10
|
+
|
11
|
+
let(:process_one) { described_class.new(context1) }
|
12
|
+
let(:process_two) { described_class.new(context2) }
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.shared_examples "a lock implementation" do
|
16
|
+
it "is unlocked when done" do
|
17
|
+
process_one.on_enqueue {}
|
18
|
+
did_lock = process_one.locked?
|
19
|
+
|
20
|
+
process_one.on_perform {}
|
21
|
+
did_lock ||= process_one.locked?
|
22
|
+
|
23
|
+
expect(did_lock).to be_truthy
|
24
|
+
|
25
|
+
process_one.batch_callback(:success, nil)
|
26
|
+
expect(process_one).not_to be_locked
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
RSpec.shared_context "on_conflict specs" do
|
3
|
+
include_context 'with TestWorker'
|
4
|
+
let(:lock_context) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j1', queue: 'default', job_clazz: 'TestWorker'}) }
|
5
|
+
let(:on_conflict) { described_class.new(lock_context)}
|
6
|
+
end
|
7
|
+
|
8
|
+
RSpec.shared_examples "OnConflict is compatible with" do |lock_strategies|
|
9
|
+
it "is compatible with the expected Lock Strategies" do
|
10
|
+
lock_strategies.each do |lock_strategy|
|
11
|
+
CanvasSync::JobUniqueness::OnConflict.validate!(described_class, lock_strategy)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec.shared_examples "OnConflict is incompatible with" do |lock_strategies|
|
17
|
+
it "is not compatible with the expected Lock Strategies" do
|
18
|
+
lock_strategies.each do |lock_strategy|
|
19
|
+
expect do
|
20
|
+
CanvasSync::JobUniqueness::OnConflict.validate!(described_class, lock_strategy)
|
21
|
+
end.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.shared_context "with TestWorker" do
|
4
|
+
let!(:worker) do
|
5
|
+
Class.new do
|
6
|
+
include Sidekiq::Worker
|
7
|
+
|
8
|
+
ensure_uniqueness(
|
9
|
+
strategy: :until_executed,
|
10
|
+
)
|
11
|
+
|
12
|
+
def perform; end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:each) { stub_const('TestWorker', worker) }
|
17
|
+
|
18
|
+
let(:lock_context) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j1', queue: 'default', job_clazz: 'TestWorker'}) }
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'job_uniqueness/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::JobUniqueness::UniqueJobCommon do
|
4
|
+
describe ".unlock!" do
|
5
|
+
include_context "with TestWorker"
|
6
|
+
|
7
|
+
it "unlocks the lock with a specific JID" do
|
8
|
+
jid = TestWorker.perform_async
|
9
|
+
lctx = CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker'})
|
10
|
+
lstrat = lctx.lock_strategy
|
11
|
+
lsmith = lstrat.send(:locksmith)
|
12
|
+
expect(lsmith.locked?).to be_truthy
|
13
|
+
|
14
|
+
TestWorker.unlock!(jid, queue: 'default')
|
15
|
+
expect(lsmith.locked?).to be_falsey
|
16
|
+
end
|
17
|
+
|
18
|
+
it "unlocks the lock without a specific JID" do
|
19
|
+
jid = TestWorker.perform_async
|
20
|
+
lctx = CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker'})
|
21
|
+
lstrat = lctx.lock_strategy
|
22
|
+
lsmith = lstrat.send(:locksmith)
|
23
|
+
expect(lsmith.locked?).to be_truthy
|
24
|
+
|
25
|
+
TestWorker.unlock!(queue: 'default')
|
26
|
+
expect(lsmith.locked?).to be_falsey
|
27
|
+
end
|
28
|
+
|
29
|
+
it "unlocks the lock matching parameters" do
|
30
|
+
args = [1, 2, 3]
|
31
|
+
jid = TestWorker.perform_async(*args)
|
32
|
+
|
33
|
+
lctx = CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker', args: args})
|
34
|
+
lstrat = lctx.lock_strategy
|
35
|
+
lsmith = lstrat.send(:locksmith)
|
36
|
+
expect(lsmith.locked?).to be_truthy
|
37
|
+
|
38
|
+
TestWorker.unlock!(queue: 'default')
|
39
|
+
expect(lsmith.locked?).to be_truthy
|
40
|
+
|
41
|
+
TestWorker.unlock!(queue: 'default', args: args)
|
42
|
+
expect(lsmith.locked?).to be_falsey
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -19,7 +19,7 @@ require 'with_model'
|
|
19
19
|
require 'sidekiq/testing'
|
20
20
|
Sidekiq::Testing.fake!
|
21
21
|
Sidekiq::Testing.server_middleware do |chain|
|
22
|
-
chain.add CanvasSync::JobBatches::Sidekiq::ServerMiddleware
|
22
|
+
chain.add CanvasSync::JobBatches::Compat::Sidekiq::ServerMiddleware
|
23
23
|
end
|
24
24
|
|
25
25
|
Dir[File.dirname(__FILE__) + "/job_batching/support/**/*.rb"].each {|f| require f }
|