joblin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +1 -0
- data/app/models/joblin/background_task/api_access.rb +148 -0
- data/app/models/joblin/background_task/attachments.rb +47 -0
- data/app/models/joblin/background_task/executor.rb +63 -0
- data/app/models/joblin/background_task/options.rb +75 -0
- data/app/models/joblin/background_task/retention_policy.rb +28 -0
- data/app/models/joblin/background_task.rb +72 -0
- data/app/models/joblin/concerns/job_working_dirs.rb +21 -0
- data/db/migrate/20250903184852_create_background_tasks.rb +12 -0
- data/joblin.gemspec +35 -0
- data/lib/joblin/batching/batch.rb +537 -0
- data/lib/joblin/batching/callback.rb +135 -0
- data/lib/joblin/batching/chain_builder.rb +247 -0
- data/lib/joblin/batching/compat/active_job.rb +108 -0
- data/lib/joblin/batching/compat/sidekiq/web/batches_assets/css/styles.less +182 -0
- data/lib/joblin/batching/compat/sidekiq/web/batches_assets/js/batch_tree.js +108 -0
- data/lib/joblin/batching/compat/sidekiq/web/batches_assets/js/util.js +2 -0
- data/lib/joblin/batching/compat/sidekiq/web/helpers.rb +41 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/_batch_tree.erb +6 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/_batches_table.erb +44 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/_common.erb +13 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/_jobs_table.erb +21 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/_pagination.erb +26 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/batch.erb +81 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/batches.erb +23 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/pool.erb +137 -0
- data/lib/joblin/batching/compat/sidekiq/web/views/pools.erb +47 -0
- data/lib/joblin/batching/compat/sidekiq/web.rb +218 -0
- data/lib/joblin/batching/compat/sidekiq.rb +149 -0
- data/lib/joblin/batching/compat.rb +20 -0
- data/lib/joblin/batching/context_hash.rb +157 -0
- data/lib/joblin/batching/hier_batch_ids.lua +25 -0
- data/lib/joblin/batching/jobs/base_job.rb +7 -0
- data/lib/joblin/batching/jobs/concurrent_batch_job.rb +20 -0
- data/lib/joblin/batching/jobs/managed_batch_job.rb +175 -0
- data/lib/joblin/batching/jobs/serial_batch_job.rb +20 -0
- data/lib/joblin/batching/pool.rb +254 -0
- data/lib/joblin/batching/pool_refill.lua +47 -0
- data/lib/joblin/batching/schedule_callback.lua +14 -0
- data/lib/joblin/batching/status.rb +89 -0
- data/lib/joblin/engine.rb +15 -0
- data/lib/joblin/lazy_access.rb +72 -0
- data/lib/joblin/uniqueness/compat/active_job.rb +75 -0
- data/lib/joblin/uniqueness/compat/sidekiq.rb +135 -0
- data/lib/joblin/uniqueness/compat.rb +20 -0
- data/lib/joblin/uniqueness/configuration.rb +25 -0
- data/lib/joblin/uniqueness/job_uniqueness.rb +49 -0
- data/lib/joblin/uniqueness/lock_context.rb +199 -0
- data/lib/joblin/uniqueness/locksmith.rb +92 -0
- data/lib/joblin/uniqueness/on_conflict/base.rb +32 -0
- data/lib/joblin/uniqueness/on_conflict/log.rb +13 -0
- data/lib/joblin/uniqueness/on_conflict/null_strategy.rb +9 -0
- data/lib/joblin/uniqueness/on_conflict/raise.rb +11 -0
- data/lib/joblin/uniqueness/on_conflict/reject.rb +21 -0
- data/lib/joblin/uniqueness/on_conflict/reschedule.rb +20 -0
- data/lib/joblin/uniqueness/on_conflict.rb +62 -0
- data/lib/joblin/uniqueness/strategy/base.rb +107 -0
- data/lib/joblin/uniqueness/strategy/until_and_while_executing.rb +35 -0
- data/lib/joblin/uniqueness/strategy/until_executed.rb +20 -0
- data/lib/joblin/uniqueness/strategy/until_executing.rb +20 -0
- data/lib/joblin/uniqueness/strategy/until_expired.rb +16 -0
- data/lib/joblin/uniqueness/strategy/while_executing.rb +26 -0
- data/lib/joblin/uniqueness/strategy.rb +27 -0
- data/lib/joblin/uniqueness/unique_job_common.rb +79 -0
- data/lib/joblin/version.rb +3 -0
- data/lib/joblin.rb +37 -0
- data/spec/batching/batch_spec.rb +493 -0
- data/spec/batching/callback_spec.rb +38 -0
- data/spec/batching/compat/active_job_spec.rb +107 -0
- data/spec/batching/compat/sidekiq_spec.rb +127 -0
- data/spec/batching/context_hash_spec.rb +54 -0
- data/spec/batching/flow_spec.rb +82 -0
- data/spec/batching/integration/fail_then_succeed.rb +42 -0
- data/spec/batching/integration/integration.rb +57 -0
- data/spec/batching/integration/nested.rb +88 -0
- data/spec/batching/integration/simple.rb +47 -0
- data/spec/batching/integration/workflow.rb +134 -0
- data/spec/batching/integration_helper.rb +50 -0
- data/spec/batching/pool_spec.rb +161 -0
- data/spec/batching/status_spec.rb +76 -0
- data/spec/batching/support/base_job.rb +19 -0
- data/spec/batching/support/sample_callback.rb +2 -0
- data/spec/internal/config/database.yml +5 -0
- data/spec/internal/config/routes.rb +5 -0
- data/spec/internal/config/storage.yml +3 -0
- data/spec/internal/db/combustion_test.sqlite +0 -0
- data/spec/internal/db/schema.rb +6 -0
- data/spec/internal/log/test.log +48200 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/models/background_task_spec.rb +41 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/uniqueness/compat/active_job_spec.rb +49 -0
- data/spec/uniqueness/compat/sidekiq_spec.rb +68 -0
- data/spec/uniqueness/lock_context_spec.rb +106 -0
- data/spec/uniqueness/on_conflict/log_spec.rb +11 -0
- data/spec/uniqueness/on_conflict/raise_spec.rb +10 -0
- data/spec/uniqueness/on_conflict/reschedule_spec.rb +63 -0
- data/spec/uniqueness/on_conflict_spec.rb +16 -0
- data/spec/uniqueness/spec_helper.rb +19 -0
- data/spec/uniqueness/strategy/base_spec.rb +100 -0
- data/spec/uniqueness/strategy/until_and_while_executing_spec.rb +48 -0
- data/spec/uniqueness/strategy/until_executed_spec.rb +23 -0
- data/spec/uniqueness/strategy/until_executing_spec.rb +23 -0
- data/spec/uniqueness/strategy/until_expired_spec.rb +23 -0
- data/spec/uniqueness/strategy/while_executing_spec.rb +33 -0
- data/spec/uniqueness/support/lock_strategy.rb +28 -0
- data/spec/uniqueness/support/on_conflict.rb +24 -0
- data/spec/uniqueness/support/test_worker.rb +19 -0
- data/spec/uniqueness/unique_job_common_spec.rb +45 -0
- metadata +308 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Joblin::Batching::Compat::Sidekiq do
|
|
4
|
+
describe Joblin::Batching::Compat::Sidekiq::ServerMiddleware do
|
|
5
|
+
context 'when without batch' do
|
|
6
|
+
it 'just yields' do
|
|
7
|
+
yielded = false
|
|
8
|
+
expect(Joblin::Batching::Batch).not_to receive(:process_successful_job)
|
|
9
|
+
expect(Joblin::Batching::Batch).not_to receive(:process_failed_job)
|
|
10
|
+
subject.call(nil, {}, nil) { yielded = true }
|
|
11
|
+
expect(yielded).to be_truthy
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context 'when in batch' do
|
|
16
|
+
let(:bid) { 'SAMPLEBID' }
|
|
17
|
+
let(:jid) { 'SAMPLEJID' }
|
|
18
|
+
|
|
19
|
+
context 'when successful' do
|
|
20
|
+
it 'yields' do
|
|
21
|
+
yielded = false
|
|
22
|
+
subject.call(nil, { 'bid' => bid, 'jid' => jid }, nil) { yielded = true }
|
|
23
|
+
expect(yielded).to be_truthy
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'calls process_successful_job' do
|
|
27
|
+
expect(Joblin::Batching::Batch).to receive(:process_successful_job).with(bid, nil)
|
|
28
|
+
subject.call(nil, { 'bid' => bid }, nil) {}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context 'when failed' do
|
|
33
|
+
it 'calls process_failed_job and reraises exception' do
|
|
34
|
+
reraised = false
|
|
35
|
+
expect(Joblin::Batching::Batch).to receive(:process_failed_job)
|
|
36
|
+
begin
|
|
37
|
+
subject.call(nil, { 'bid' => bid }, nil) { raise 'ERR' }
|
|
38
|
+
rescue
|
|
39
|
+
reraised = true
|
|
40
|
+
end
|
|
41
|
+
expect(reraised).to be_truthy
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe Joblin::Batching::Compat::Sidekiq::ClientMiddleware do
|
|
48
|
+
context 'when without batch' do
|
|
49
|
+
it 'just yields' do
|
|
50
|
+
yielded = false
|
|
51
|
+
expect(Joblin::Batching::Batch).not_to receive(:append_jobs)
|
|
52
|
+
subject.call(nil, {}, nil) { yielded = true }
|
|
53
|
+
expect(yielded).to be_truthy
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'when in batch' do
|
|
58
|
+
let(:bid) { 'SAMPLEBID' }
|
|
59
|
+
let(:jid) { 'SAMPLEJID' }
|
|
60
|
+
before { Thread.current[Joblin::Batching::CURRENT_BATCH_THREAD_KEY] = Joblin::Batching::Batch.new(bid) }
|
|
61
|
+
after { Thread.current[Joblin::Batching::CURRENT_BATCH_THREAD_KEY] = nil }
|
|
62
|
+
|
|
63
|
+
it 'yields' do
|
|
64
|
+
yielded = false
|
|
65
|
+
subject.call(nil, { 'jid' => jid }, nil) { yielded = true }
|
|
66
|
+
expect(yielded).to be_truthy
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'increments job queue' do
|
|
70
|
+
# expect(Joblin::Batching::Batch).to receive(:append_jobs).with(bid)
|
|
71
|
+
# subject.call(nil, { 'jid' => jid }, nil) {}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'assigns bid to msg' do
|
|
75
|
+
msg = { 'jid' => jid }
|
|
76
|
+
subject.call(nil, msg, nil) {}
|
|
77
|
+
expect(msg['bid']).to eq(bid)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
RSpec.describe Joblin::Batching::Compat::Sidekiq do
|
|
84
|
+
let(:config) { defined?(Sidekiq::Config) ? double(Sidekiq::Config) : class_double(Sidekiq) }
|
|
85
|
+
let(:client_middleware) { double(Sidekiq::Middleware::Chain) }
|
|
86
|
+
|
|
87
|
+
context 'client' do
|
|
88
|
+
it 'adds client middleware' do
|
|
89
|
+
allow(Sidekiq).to receive(:configure_client).and_yield(config)
|
|
90
|
+
expect(config).to receive(:client_middleware).and_yield(client_middleware)
|
|
91
|
+
expect(client_middleware).to receive(:add).with(Joblin::Batching::Compat::Sidekiq::ClientMiddleware)
|
|
92
|
+
Joblin::Batching::Compat::Sidekiq.instance_variable_set(:@already_configured, false)
|
|
93
|
+
Joblin::Batching::Compat::Sidekiq.configure
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'server' do
|
|
98
|
+
let(:server_middleware) { double(Sidekiq::Middleware::Chain) }
|
|
99
|
+
let(:death_handlers) { double(Array) }
|
|
100
|
+
|
|
101
|
+
it 'adds client and server middleware' do
|
|
102
|
+
allow(Sidekiq).to receive(:configure_server).and_yield(config)
|
|
103
|
+
expect(config).to receive(:client_middleware).and_yield(client_middleware)
|
|
104
|
+
expect(config).to receive(:server_middleware).and_yield(server_middleware)
|
|
105
|
+
expect(config).to receive(:death_handlers).and_return(death_handlers)
|
|
106
|
+
expect(client_middleware).to receive(:add).with(Joblin::Batching::Compat::Sidekiq::ClientMiddleware)
|
|
107
|
+
expect(server_middleware).to receive(:add).with(Joblin::Batching::Compat::Sidekiq::ServerMiddleware)
|
|
108
|
+
expect(death_handlers).to receive(:<<)
|
|
109
|
+
Joblin::Batching::Compat::Sidekiq.instance_variable_set(:@already_configured, false)
|
|
110
|
+
Joblin::Batching::Compat::Sidekiq.configure
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context 'worker' do
|
|
115
|
+
it 'defines method bid' do
|
|
116
|
+
expect(Sidekiq::Worker.instance_methods).to include(:bid)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it 'defines method batch' do
|
|
120
|
+
expect(Sidekiq::Worker.instance_methods).to include(:batch)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'defines method valid_within_batch?' do
|
|
124
|
+
expect(Sidekiq::Worker.instance_methods).to include(:valid_within_batch?)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Joblin::Batching::ContextHash do
|
|
4
|
+
|
|
5
|
+
class ContextedJob < BatchTestJobBase
|
|
6
|
+
def perform
|
|
7
|
+
was_performed(batch_context.flatten)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def was_performed(*args); end
|
|
11
|
+
|
|
12
|
+
def self.callback_perform(*args)
|
|
13
|
+
perform_later
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "works with first-level jobs" do
|
|
18
|
+
ActiveJob::Base.queue_adapter = :sidekiq
|
|
19
|
+
b = Joblin::Batching::Batch.new
|
|
20
|
+
b.context = { hello: 'world' }
|
|
21
|
+
expect_any_instance_of(ContextedJob).to receive(:was_performed).with({ 'hello' => 'world' })
|
|
22
|
+
b.jobs do
|
|
23
|
+
ContextedJob.perform_later
|
|
24
|
+
end
|
|
25
|
+
Sidekiq::Worker.drain_all
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "works with nested-batch jobs" do
|
|
29
|
+
ActiveJob::Base.queue_adapter = :sidekiq
|
|
30
|
+
b = Joblin::Batching::Batch.new
|
|
31
|
+
b.context = { hello: 'world', foo: 'bar' }
|
|
32
|
+
expect_any_instance_of(ContextedJob).to receive(:was_performed).with({ 'hello' => 'world', 'foo' => 'baz', 'some' => 'other' })
|
|
33
|
+
b.jobs do
|
|
34
|
+
b2 = Joblin::Batching::Batch.new
|
|
35
|
+
b2.context = { some: 'other', foo: 'baz' }
|
|
36
|
+
b2.jobs do
|
|
37
|
+
ContextedJob.perform_later
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
Sidekiq::Worker.drain_all
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'works with a callback batch' do
|
|
44
|
+
ActiveJob::Base.queue_adapter = :sidekiq
|
|
45
|
+
b = Joblin::Batching::Batch.new
|
|
46
|
+
b.context = { hello: 'world' }
|
|
47
|
+
b.on(:success, "ContextedJob.callback_perform")
|
|
48
|
+
expect_any_instance_of(ContextedJob).to receive(:was_performed).with({ 'hello' => 'world' })
|
|
49
|
+
b.jobs do
|
|
50
|
+
BatchTestJobBase.perform_later
|
|
51
|
+
end
|
|
52
|
+
Sidekiq::Worker.drain_all
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class WorkerA < BatchTestJobBase
|
|
4
|
+
def perform
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class WorkerB < BatchTestJobBase
|
|
9
|
+
def perform
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class WorkerC < BatchTestJobBase
|
|
14
|
+
def perform
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
RSpec.describe 'Batch flow' do
|
|
19
|
+
context 'when handling a batch' do
|
|
20
|
+
let(:batch) { Joblin::Batching::Batch.new }
|
|
21
|
+
before { batch.on(:complete, SampleCallback, :id => 42) }
|
|
22
|
+
before { batch.description = 'describing the batch' }
|
|
23
|
+
let(:status) { Joblin::Batching::Batch::Status.new(batch.bid) }
|
|
24
|
+
let(:queue) { Sidekiq::Queue.new }
|
|
25
|
+
|
|
26
|
+
it 'correctly initializes' do
|
|
27
|
+
expect(batch.bid).not_to be_nil
|
|
28
|
+
expect(batch.description).to eq('describing the batch')
|
|
29
|
+
|
|
30
|
+
batch.jobs do
|
|
31
|
+
3.times do
|
|
32
|
+
BatchTestWorker.perform_async
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
expect(status.pending).to eq(3)
|
|
37
|
+
expect(status.failures).to eq(0)
|
|
38
|
+
expect(status.complete?).to be false
|
|
39
|
+
expect(status.created_at).not_to be_nil
|
|
40
|
+
expect(status.bid).to eq(batch.bid)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'when handling a nested batch' do
|
|
45
|
+
let(:batchA) { Joblin::Batching::Batch.new }
|
|
46
|
+
let(:batchB) { Joblin::Batching::Batch.new }
|
|
47
|
+
let(:batchC) { Joblin::Batching::Batch.new(batchA.bid) }
|
|
48
|
+
let(:batchD) { Joblin::Batching::Batch.new }
|
|
49
|
+
let(:jids) { [] }
|
|
50
|
+
let(:parent) { batchA.bid }
|
|
51
|
+
let(:children) { [] }
|
|
52
|
+
|
|
53
|
+
it 'handles a basic nested batch' do
|
|
54
|
+
batchA.jobs do
|
|
55
|
+
jids << WorkerA.perform_async
|
|
56
|
+
batchB.jobs do
|
|
57
|
+
jids << WorkerB.perform_async
|
|
58
|
+
end
|
|
59
|
+
jids << WorkerA.perform_async
|
|
60
|
+
children << batchB.bid
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
batchC.jobs do
|
|
64
|
+
batchD.jobs do
|
|
65
|
+
jids << WorkerC.perform_async
|
|
66
|
+
end
|
|
67
|
+
children << batchD.bid
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
expect(jids.size).to eq(4)
|
|
71
|
+
expect(Joblin::Batching::Batch::Status.new(parent).child_count).to eq(2)
|
|
72
|
+
children.each do |kid|
|
|
73
|
+
status = Joblin::Batching::Batch::Status.new(kid)
|
|
74
|
+
expect(status.child_count).to eq(0)
|
|
75
|
+
expect(status.pending).to eq(1)
|
|
76
|
+
expect(status.parent_bid).to eq(parent)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require_relative '../integration_helper'
|
|
2
|
+
|
|
3
|
+
# Workflow when a Job fails, retries, and then succeeds
|
|
4
|
+
|
|
5
|
+
class Worker1
|
|
6
|
+
include Sidekiq::Worker
|
|
7
|
+
sidekiq_options retry: 5
|
|
8
|
+
|
|
9
|
+
@@failed = false
|
|
10
|
+
|
|
11
|
+
def perform
|
|
12
|
+
Sidekiq.logger.info "Work 1"
|
|
13
|
+
|
|
14
|
+
unless @@failed
|
|
15
|
+
@@failed = true
|
|
16
|
+
raise "One Failure"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class MyCallback
|
|
22
|
+
def on_success(status, options)
|
|
23
|
+
Sidekiq.logger.info "Overall Success #{options} #{status.data}"
|
|
24
|
+
end
|
|
25
|
+
alias_method :multi, :on_success
|
|
26
|
+
|
|
27
|
+
def on_complete(status, options)
|
|
28
|
+
Sidekiq.logger.info "Overall Complete #{options} #{status.data}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
overall = Joblin::Batching::Batch.new
|
|
33
|
+
overall.on(:success, MyCallback, to: 'success@gmail.com')
|
|
34
|
+
overall.on(:complete, MyCallback, to: 'success@gmail.com')
|
|
35
|
+
overall.jobs do
|
|
36
|
+
Worker1.perform_async
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts "Overall bid #{overall.bid}"
|
|
40
|
+
|
|
41
|
+
output, keys = process_tests
|
|
42
|
+
overall_tests(output, keys, file: __FILE__)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'integration_helper'
|
|
2
|
+
|
|
3
|
+
# Simple test of adding jobs to the current batch
|
|
4
|
+
# Batches:
|
|
5
|
+
# - Overall (TestWoker) + Another worker
|
|
6
|
+
|
|
7
|
+
class AnotherWorker
|
|
8
|
+
include Sidekiq::Worker
|
|
9
|
+
|
|
10
|
+
def perform
|
|
11
|
+
Sidekiq.logger.info "Another Worker"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class TestWorker
|
|
16
|
+
include Sidekiq::Worker
|
|
17
|
+
|
|
18
|
+
def perform
|
|
19
|
+
Sidekiq.logger.info "Test Worker"
|
|
20
|
+
if bid
|
|
21
|
+
batch.jobs do
|
|
22
|
+
AnotherWorker.perform_async
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class MyCallback
|
|
29
|
+
def on_success(status, options)
|
|
30
|
+
Sidekiq.logger.info "Success #{options} #{status.data}"
|
|
31
|
+
end
|
|
32
|
+
alias_method :multi, :on_success
|
|
33
|
+
|
|
34
|
+
def on_complete(status, options)
|
|
35
|
+
Sidekiq.logger.info "Complete #{options} #{status.data}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
batch = Joblin::Batching::Batch.new
|
|
40
|
+
batch.description = 'Test batch'
|
|
41
|
+
batch.callback_queue = :default
|
|
42
|
+
batch.on(:success, 'MyCallback#on_success', to: 'success@gmail.com')
|
|
43
|
+
batch.on(:success, 'MyCallback#multi', to: 'success@gmail.com')
|
|
44
|
+
batch.on(:complete, MyCallback, to: 'complete@gmail.com')
|
|
45
|
+
|
|
46
|
+
batch.jobs do
|
|
47
|
+
10.times do
|
|
48
|
+
TestWorker.perform_async
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
puts Joblin::Batching::Batch::Status.new(batch.bid).data
|
|
52
|
+
|
|
53
|
+
dump_redis_keys
|
|
54
|
+
|
|
55
|
+
Sidekiq::Worker.drain_all
|
|
56
|
+
|
|
57
|
+
dump_redis_keys
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'integration_helper'
|
|
2
|
+
|
|
3
|
+
# Tests deep nesting of batches
|
|
4
|
+
# Batches:
|
|
5
|
+
# - Overall (Worker 1)
|
|
6
|
+
# - Worker 2
|
|
7
|
+
# - Worker 3
|
|
8
|
+
# - Worker 4
|
|
9
|
+
|
|
10
|
+
class Worker1
|
|
11
|
+
include Sidekiq::Worker
|
|
12
|
+
|
|
13
|
+
def perform
|
|
14
|
+
Sidekiq.logger.info "Work1"
|
|
15
|
+
batch = Joblin::Batching::Batch.new
|
|
16
|
+
batch.on(:success, Worker2)
|
|
17
|
+
batch.jobs do
|
|
18
|
+
Worker2.perform_async
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Worker2
|
|
24
|
+
include Sidekiq::Worker
|
|
25
|
+
|
|
26
|
+
def perform
|
|
27
|
+
Sidekiq.logger.info "Work2"
|
|
28
|
+
batch = Joblin::Batching::Batch.new
|
|
29
|
+
batch.on(:success, Worker3)
|
|
30
|
+
batch.jobs do
|
|
31
|
+
Worker3.perform_async
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def on_success status, opts
|
|
36
|
+
Sidekiq.logger.info "Worker 2 Success"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Worker3
|
|
41
|
+
include Sidekiq::Worker
|
|
42
|
+
|
|
43
|
+
def perform
|
|
44
|
+
Sidekiq.logger.info "Work3"
|
|
45
|
+
batch = Joblin::Batching::Batch.new
|
|
46
|
+
batch.on(:success, Worker4)
|
|
47
|
+
batch.jobs do
|
|
48
|
+
Worker4.perform_async
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def on_success status, opts
|
|
53
|
+
Sidekiq.logger.info "Worker 3 Success"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Worker4
|
|
58
|
+
include Sidekiq::Worker
|
|
59
|
+
|
|
60
|
+
def perform
|
|
61
|
+
Sidekiq.logger.info "Work4"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def on_success status, opts
|
|
65
|
+
Sidekiq.logger.info "Worker 4 Success"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class SomeClass
|
|
71
|
+
def on_complete(status, options)
|
|
72
|
+
Sidekiq.logger.info "Overall Complete #{options} #{status.data}"
|
|
73
|
+
end
|
|
74
|
+
def on_success(status, options)
|
|
75
|
+
Sidekiq.logger.info "Overall Success #{options} #{status.data}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
batch = Joblin::Batching::Batch.new
|
|
79
|
+
batch.on(:success, SomeClass, 'uid' => 3)
|
|
80
|
+
batch.on(:complete, SomeClass, 'uid' => 3)
|
|
81
|
+
batch.jobs do
|
|
82
|
+
Worker1.perform_async
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
puts "Overall bid #{batch.bid}"
|
|
86
|
+
|
|
87
|
+
output, keys = process_tests
|
|
88
|
+
overall_tests output, keys
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'integration_helper'
|
|
2
|
+
|
|
3
|
+
# Simple nested batch without callbacks
|
|
4
|
+
# Batches:
|
|
5
|
+
# - Overall (Worker1)
|
|
6
|
+
# - Worker2
|
|
7
|
+
|
|
8
|
+
class Worker1
|
|
9
|
+
include Sidekiq::Worker
|
|
10
|
+
|
|
11
|
+
def perform
|
|
12
|
+
Sidekiq.logger.info "Work1"
|
|
13
|
+
batch = Joblin::Batching::Batch.new
|
|
14
|
+
batch.jobs do
|
|
15
|
+
Worker2.perform_async
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Worker2
|
|
21
|
+
include Sidekiq::Worker
|
|
22
|
+
|
|
23
|
+
def perform
|
|
24
|
+
Sidekiq.logger.info "Work2"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class SomeClass
|
|
29
|
+
def on_complete(status, options)
|
|
30
|
+
Sidekiq.logger.info "Overall Complete #{options} #{status.data}"
|
|
31
|
+
end
|
|
32
|
+
def on_success(status, options)
|
|
33
|
+
Sidekiq.logger.info "Overall Success #{options} #{status.data}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
batch = Joblin::Batching::Batch.new
|
|
38
|
+
batch.on(:success, SomeClass)
|
|
39
|
+
batch.on(:complete, SomeClass)
|
|
40
|
+
batch.jobs do
|
|
41
|
+
Worker1.perform_async
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
puts "Overall bid #{batch.bid}"
|
|
45
|
+
|
|
46
|
+
output, keys = process_tests
|
|
47
|
+
overall_tests output, keys
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
require 'integration_helper'
|
|
2
|
+
|
|
3
|
+
# Complex workflow with sequential and nested
|
|
4
|
+
# Also test sub batches without callbacks
|
|
5
|
+
# Batches:
|
|
6
|
+
# - Overall
|
|
7
|
+
# - Worker1
|
|
8
|
+
# - Worker3
|
|
9
|
+
# - Worker2 + Worker3
|
|
10
|
+
# - Worker1
|
|
11
|
+
# - Worker3
|
|
12
|
+
# - Worker4
|
|
13
|
+
# - Worker5
|
|
14
|
+
|
|
15
|
+
class Callbacks
|
|
16
|
+
def worker1 status, opts
|
|
17
|
+
Sidekiq.logger.info "Success 1 #{status.data}"
|
|
18
|
+
|
|
19
|
+
overall = Joblin::Batching::Batch.new status.parent_bid
|
|
20
|
+
overall.jobs do
|
|
21
|
+
batch = Joblin::Batching::Batch.new
|
|
22
|
+
batch.on(:success, "Callbacks#worker2")
|
|
23
|
+
batch.jobs do
|
|
24
|
+
Worker2.perform_async
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def worker2 status, opts
|
|
30
|
+
Sidekiq.logger.info "Success 2 #{status.data}"
|
|
31
|
+
overall = Joblin::Batching::Batch.new status.parent_bid
|
|
32
|
+
overall.jobs do
|
|
33
|
+
batch = Joblin::Batching::Batch.new
|
|
34
|
+
batch.on(:success, "Callbacks#worker4")
|
|
35
|
+
batch.jobs do
|
|
36
|
+
Worker4.perform_async
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def worker4 status, opts
|
|
43
|
+
Sidekiq.logger.info "Success 4 #{status.data}"
|
|
44
|
+
overall = Joblin::Batching::Batch.new status.parent_bid
|
|
45
|
+
overall.jobs do
|
|
46
|
+
batch = Joblin::Batching::Batch.new
|
|
47
|
+
batch.on(:success, "Callbacks#worker5")
|
|
48
|
+
batch.jobs do
|
|
49
|
+
Worker5.perform_async
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def worker5 status, opts
|
|
55
|
+
Sidekiq.logger.info "Success 5 #{status.data}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class Worker1
|
|
60
|
+
include Sidekiq::Worker
|
|
61
|
+
|
|
62
|
+
def perform
|
|
63
|
+
Sidekiq.logger.info "Work 1"
|
|
64
|
+
batch = Joblin::Batching::Batch.new
|
|
65
|
+
batch.jobs do
|
|
66
|
+
Worker3.perform_async
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
class Worker2
|
|
72
|
+
include Sidekiq::Worker
|
|
73
|
+
|
|
74
|
+
def perform
|
|
75
|
+
Sidekiq.logger.info "Work 2"
|
|
76
|
+
if bid
|
|
77
|
+
batch.jobs do
|
|
78
|
+
Worker3.perform_async
|
|
79
|
+
end
|
|
80
|
+
newb = Joblin::Batching::Batch.new
|
|
81
|
+
newb.jobs do
|
|
82
|
+
Worker1.perform_async
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Worker3
|
|
89
|
+
include Sidekiq::Worker
|
|
90
|
+
def perform
|
|
91
|
+
Sidekiq.logger.info "Work 3"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class Worker4
|
|
96
|
+
include Sidekiq::Worker
|
|
97
|
+
def perform
|
|
98
|
+
Sidekiq.logger.info "Work 4"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class Worker5
|
|
103
|
+
include Sidekiq::Worker
|
|
104
|
+
def perform
|
|
105
|
+
Sidekiq.logger.info "Work 5"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class MyCallback
|
|
110
|
+
def on_success(status, options)
|
|
111
|
+
Sidekiq.logger.info "Overall Success #{options} #{status.data}"
|
|
112
|
+
end
|
|
113
|
+
alias_method :multi, :on_success
|
|
114
|
+
|
|
115
|
+
def on_complete(status, options)
|
|
116
|
+
Sidekiq.logger.info "Overall Complete #{options} #{status.data}"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
overall = Joblin::Batching::Batch.new
|
|
121
|
+
overall.on(:success, MyCallback, to: 'success@gmail.com')
|
|
122
|
+
overall.on(:complete, MyCallback, to: 'success@gmail.com')
|
|
123
|
+
overall.jobs do
|
|
124
|
+
batch1 = Joblin::Batching::Batch.new
|
|
125
|
+
batch1.on(:success, "Callbacks#worker1")
|
|
126
|
+
batch1.jobs do
|
|
127
|
+
Worker1.perform_async
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
puts "Overall bid #{overall.bid}"
|
|
132
|
+
|
|
133
|
+
output, keys = process_tests
|
|
134
|
+
overall_tests output, keys
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'sidekiq/testing'
|
|
3
|
+
|
|
4
|
+
Sidekiq::Testing.server_middleware do |chain|
|
|
5
|
+
chain.add Joblin::Batching::Compat::Sidekiq::ServerMiddleware
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
Joblin.redis { |r| r.flushdb }
|
|
9
|
+
|
|
10
|
+
def redis_keys
|
|
11
|
+
Joblin.redis { |r| r.keys('BID-*') }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def dump_redis_keys
|
|
15
|
+
puts redis_keys.inspect
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def process_tests
|
|
19
|
+
out_buf = StringIO.new
|
|
20
|
+
Sidekiq.logger = Logger.new out_buf
|
|
21
|
+
|
|
22
|
+
# Sidekiq.logger.level = :info
|
|
23
|
+
|
|
24
|
+
Sidekiq::Worker.drain_all
|
|
25
|
+
|
|
26
|
+
output = out_buf.string
|
|
27
|
+
keys = redis_keys
|
|
28
|
+
puts out_buf.string
|
|
29
|
+
|
|
30
|
+
[output, keys]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def overall_tests(output, keys, file: nil)
|
|
34
|
+
test_name = "Batch Integration Test"
|
|
35
|
+
test_name = File.basename(file, ".*") if file
|
|
36
|
+
|
|
37
|
+
Rspec.describe test_name do
|
|
38
|
+
it "runs overall complete callback" do
|
|
39
|
+
expect(output).to include "Overall Complete"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "runs overall success callback" do
|
|
43
|
+
expect(output).to include "Overall Success"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "cleans redis keys" do
|
|
47
|
+
expect(keys).to eq([])
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|