canvas_sync 0.21.1 → 0.22.0.beta1
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 +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 }
|