jiggler 0.1.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE +4 -0
- data/README.md +423 -0
- data/bin/jiggler +31 -0
- data/lib/jiggler/cleaner.rb +130 -0
- data/lib/jiggler/cli.rb +263 -0
- data/lib/jiggler/config.rb +165 -0
- data/lib/jiggler/core.rb +22 -0
- data/lib/jiggler/errors.rb +5 -0
- data/lib/jiggler/job.rb +116 -0
- data/lib/jiggler/launcher.rb +69 -0
- data/lib/jiggler/manager.rb +73 -0
- data/lib/jiggler/redis_store.rb +55 -0
- data/lib/jiggler/retrier.rb +122 -0
- data/lib/jiggler/scheduled/enqueuer.rb +78 -0
- data/lib/jiggler/scheduled/poller.rb +97 -0
- data/lib/jiggler/stats/collection.rb +26 -0
- data/lib/jiggler/stats/monitor.rb +103 -0
- data/lib/jiggler/summary.rb +101 -0
- data/lib/jiggler/support/helper.rb +35 -0
- data/lib/jiggler/version.rb +5 -0
- data/lib/jiggler/web/assets/stylesheets/application.css +64 -0
- data/lib/jiggler/web/views/application.erb +329 -0
- data/lib/jiggler/web.rb +80 -0
- data/lib/jiggler/worker.rb +179 -0
- data/lib/jiggler.rb +10 -0
- data/spec/examples.txt +79 -0
- data/spec/fixtures/config/jiggler.yml +4 -0
- data/spec/fixtures/jobs.rb +5 -0
- data/spec/fixtures/my_failed_job.rb +10 -0
- data/spec/fixtures/my_job.rb +9 -0
- data/spec/fixtures/my_job_with_args.rb +18 -0
- data/spec/jiggler/cleaner_spec.rb +171 -0
- data/spec/jiggler/cli_spec.rb +87 -0
- data/spec/jiggler/config_spec.rb +56 -0
- data/spec/jiggler/core_spec.rb +34 -0
- data/spec/jiggler/job_spec.rb +99 -0
- data/spec/jiggler/launcher_spec.rb +66 -0
- data/spec/jiggler/manager_spec.rb +52 -0
- data/spec/jiggler/redis_store_spec.rb +20 -0
- data/spec/jiggler/retrier_spec.rb +55 -0
- data/spec/jiggler/scheduled/enqueuer_spec.rb +81 -0
- data/spec/jiggler/scheduled/poller_spec.rb +40 -0
- data/spec/jiggler/stats/monitor_spec.rb +40 -0
- data/spec/jiggler/summary_spec.rb +168 -0
- data/spec/jiggler/web_spec.rb +37 -0
- data/spec/jiggler/worker_spec.rb +110 -0
- data/spec/spec_helper.rb +54 -0
- metadata +230 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MyJobWithArgs
|
4
|
+
include Jiggler::Job
|
5
|
+
job_options retries: 2
|
6
|
+
|
7
|
+
def perform(str, int, float, bool, array, hash)
|
8
|
+
if !(str.is_a?(String) &&
|
9
|
+
int.is_a?(Integer) &&
|
10
|
+
float.is_a?(Float) &&
|
11
|
+
bool.is_a?(TrueClass) &&
|
12
|
+
array.is_a?(Array) &&
|
13
|
+
hash.is_a?(Hash))
|
14
|
+
|
15
|
+
raise StandardError, 'Args are not the correct type ◉ ︵ ◉'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Jiggler::Cleaner do
|
4
|
+
let(:config) do
|
5
|
+
Jiggler::Config.new(
|
6
|
+
concurrency: 1,
|
7
|
+
timeout: 1,
|
8
|
+
poller_enabled: false
|
9
|
+
)
|
10
|
+
end
|
11
|
+
let(:cleaner) { described_class.new(config) }
|
12
|
+
after(:all) { Jiggler.config.cleaner.prune_all }
|
13
|
+
|
14
|
+
describe '#prune_failures_counter' do
|
15
|
+
it 'prunes the failures counter' do
|
16
|
+
config.client_redis_pool.acquire do |conn|
|
17
|
+
conn.call('SET', config.failures_counter, 5)
|
18
|
+
end
|
19
|
+
cleaner.prune_failures_counter
|
20
|
+
config.client_redis_pool.acquire do |conn|
|
21
|
+
expect(conn.call('GET', config.failures_counter)).to be nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#prune_processed_counter' do
|
27
|
+
it 'prunes the processed counter' do
|
28
|
+
config.client_redis_pool.acquire do |conn|
|
29
|
+
conn.call('SET', config.processed_counter, 5)
|
30
|
+
end
|
31
|
+
cleaner.prune_processed_counter
|
32
|
+
config.client_redis_pool.acquire do |conn|
|
33
|
+
expect(conn.call('GET', config.processed_counter)).to be nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#prune_all_processes' do
|
39
|
+
it 'prunes all processes' do
|
40
|
+
config.client_redis_pool.acquire do |conn|
|
41
|
+
conn.call(
|
42
|
+
'SET',
|
43
|
+
'jiggler:svr:uuid-cleaner-test-0:process',
|
44
|
+
'{}',
|
45
|
+
ex: 10
|
46
|
+
)
|
47
|
+
end
|
48
|
+
cleaner.prune_all_processes
|
49
|
+
config.client_redis_pool.acquire do |conn|
|
50
|
+
expect(conn.call('SCAN', '0', 'MATCH', config.process_scan_key).last).to be_empty
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#prune_process' do
|
56
|
+
let(:uuid) { 'jiggler:svr:uuid-cleaner-test-1:process' }
|
57
|
+
it 'prunes a process by its uuid' do
|
58
|
+
config.client_redis_pool.acquire do |conn|
|
59
|
+
conn.call(
|
60
|
+
'SET',
|
61
|
+
uuid,
|
62
|
+
'{}',
|
63
|
+
ex: 10
|
64
|
+
)
|
65
|
+
end
|
66
|
+
cleaner.prune_process(uuid: uuid)
|
67
|
+
config.client_redis_pool.acquire do |conn|
|
68
|
+
expect(
|
69
|
+
conn.call('GET', uuid)
|
70
|
+
).to be nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#prune_process_by_hex' do
|
76
|
+
let(:uuid) { 'jiggler:svr:uuid-cleaner-test-2:process' }
|
77
|
+
it 'prunes a process by its hex' do
|
78
|
+
config.client_redis_pool.acquire do |conn|
|
79
|
+
conn.call(
|
80
|
+
'SET',
|
81
|
+
uuid,
|
82
|
+
'{}',
|
83
|
+
ex: 10
|
84
|
+
)
|
85
|
+
end
|
86
|
+
cleaner.prune_process_by_hex(hex: 'jiggler:svr:uuid-cleaner-test-2')
|
87
|
+
config.client_redis_pool.acquire do |conn|
|
88
|
+
expect(
|
89
|
+
conn.call('GET', uuid)
|
90
|
+
).to be nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#prune_dead_set' do
|
96
|
+
it 'prunes the dead set' do
|
97
|
+
config.client_redis_pool.acquire do |conn|
|
98
|
+
conn.call('ZADD', config.dead_set, 1, '{}')
|
99
|
+
end
|
100
|
+
cleaner.prune_dead_set
|
101
|
+
config.client_redis_pool.acquire do |conn|
|
102
|
+
expect(conn.call('ZCARD', config.dead_set)).to be 0
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#prune_retries_set' do
|
108
|
+
it 'prunes the retries set' do
|
109
|
+
config.client_redis_pool.acquire do |conn|
|
110
|
+
conn.call('ZADD', config.retries_set, 1, '{}')
|
111
|
+
end
|
112
|
+
cleaner.prune_retries_set
|
113
|
+
config.client_redis_pool.acquire do |conn|
|
114
|
+
expect(conn.call('ZCARD', config.retries_set)).to be 0
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#prune_scheduled_set' do
|
120
|
+
it 'prunes the scheduled set' do
|
121
|
+
config.client_redis_pool.acquire do |conn|
|
122
|
+
conn.call('ZADD', config.scheduled_set, 1, '{}')
|
123
|
+
end
|
124
|
+
cleaner.prune_scheduled_set
|
125
|
+
config.client_redis_pool.acquire do |conn|
|
126
|
+
expect(conn.call('ZCARD', config.scheduled_set)).to be 0
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#prune_all_queues' do
|
132
|
+
it 'prunes all queues' do
|
133
|
+
config.client_redis_pool.acquire do |conn|
|
134
|
+
conn.call('LPUSH', 'jiggler:list:cleaner-test', '{}')
|
135
|
+
end
|
136
|
+
cleaner.prune_all_queues
|
137
|
+
config.client_redis_pool.acquire do |conn|
|
138
|
+
expect(conn.call('LRANGE', 'jiggler:list:cleaner-test-0', 0, -1)).to be_empty
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '#prune_queue' do
|
144
|
+
it 'prunes a queue by its name' do
|
145
|
+
config.client_redis_pool.acquire do |conn|
|
146
|
+
conn.call('LPUSH', 'jiggler:list:cleaner-test-1', '{}')
|
147
|
+
conn.call('LPUSH', 'jiggler:list:cleaner-test-2', '{}')
|
148
|
+
end
|
149
|
+
cleaner.prune_queue(name: 'cleaner-test-1')
|
150
|
+
config.client_redis_pool.acquire do |conn|
|
151
|
+
expect(conn.call('LRANGE', 'jiggler:list:cleaner-test-1', 0, -1)).to be_empty
|
152
|
+
expect(conn.call('LRANGE', 'jiggler:list:cleaner-test-2', 0, -1)).to eq(['{}'])
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#prune_all' do
|
158
|
+
it 'prunes all data' do
|
159
|
+
cleaner.prune_all
|
160
|
+
config.client_redis_pool.acquire do |conn|
|
161
|
+
expect(conn.call('SCAN', '0', 'MATCH', config.process_scan_key).last).to be_empty
|
162
|
+
expect(conn.call('SMEMBERS', config.dead_set)).to be_empty
|
163
|
+
expect(conn.call('SMEMBERS', config.retries_set)).to be_empty
|
164
|
+
expect(conn.call('SMEMBERS', config.scheduled_set)).to be_empty
|
165
|
+
expect(conn.call('KEYS', "#{config.queue_prefix}*")).to be_empty
|
166
|
+
expect(conn.call('GET', config.failures_counter)).to be nil
|
167
|
+
expect(conn.call('GET', config.processed_counter)).to be nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/jiggler/cli.rb'
|
4
|
+
|
5
|
+
RSpec.describe Jiggler::CLI do
|
6
|
+
let(:cli) { Jiggler::CLI.instance }
|
7
|
+
|
8
|
+
describe '.parse_and_init' do
|
9
|
+
context 'with no args' do
|
10
|
+
it 'uses default config' do
|
11
|
+
cli.parse_and_init
|
12
|
+
expect(cli.config[:concurrency]).to be 10
|
13
|
+
expect(cli.config[:queues]).to eq ['default']
|
14
|
+
expect(cli.config[:require]).to be nil
|
15
|
+
expect(cli.config[:timeout]).to be 25
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with args' do
|
20
|
+
before { cli.instance_variable_set(:@config, Jiggler::Config.new(verbose: true)) }
|
21
|
+
after { cli.instance_variable_set(:@config, Jiggler::Config.new) }
|
22
|
+
let(:path) { './spec/fixtures/config/jiggler.yml' }
|
23
|
+
|
24
|
+
it 'fetches config file' do
|
25
|
+
cli.parse_and_init(['-C', path])
|
26
|
+
expect(cli.config[:config_file]).to eq(path)
|
27
|
+
expect(cli.config[:concurrency]).to be 1
|
28
|
+
expect(cli.config[:queues]).to eq(['users', 'blep'])
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'fetches concurrency' do
|
32
|
+
cli.parse_and_init(['-c', '11'])
|
33
|
+
expect(cli.config[:concurrency]).to be 11
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'fetches queues' do
|
37
|
+
cli.parse_and_init(['-q', 'test,qwerty'])
|
38
|
+
expect(cli.config[:queues]).to eq(['test', 'qwerty'])
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'fetches require' do
|
42
|
+
cli.parse_and_init(['-r', path])
|
43
|
+
expect(cli.config[:require]).to eq(path)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'fetches timeout' do
|
47
|
+
cli.parse_and_init(['-t', '10'])
|
48
|
+
expect(cli.config[:timeout]).to be 10
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'fetches verbose' do
|
52
|
+
cli.parse_and_init(['-v'])
|
53
|
+
expect(cli.config[:verbose]).to be true
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'fetches version & exits' do
|
57
|
+
expect do
|
58
|
+
expect { cli.parse_and_init(['-V']) }.to output('Jiggler #{Jiggler::VERSION}').to_stdout
|
59
|
+
end.to raise_error(SystemExit)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'fetches help & exits' do
|
63
|
+
expect do
|
64
|
+
expect { cli.parse_and_init(['-h']) }.to output.to_stdout
|
65
|
+
end.to raise_error(SystemExit)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'fetches environment' do
|
69
|
+
cli.parse_and_init(['-e', 'test'])
|
70
|
+
expect(cli.config[:environment]).to eq('test')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'with invalid args' do
|
75
|
+
after { cli.instance_variable_set(:@config, Jiggler::Config.new) }
|
76
|
+
|
77
|
+
it { expect { cli.parse_and_init(['-c', 'invalid']) }.to raise_error(ArgumentError) }
|
78
|
+
it { expect { cli.parse_and_init(['-c', '-1']) }.to raise_error(ArgumentError) }
|
79
|
+
it { expect { cli.parse_and_init(['-t', 'yo']) }.to raise_error(ArgumentError) }
|
80
|
+
it do
|
81
|
+
cli.parse_and_init(['-r', 'test.rb'])
|
82
|
+
expect { cli.send(:load_app) }.to raise_error(SystemExit)
|
83
|
+
end
|
84
|
+
it { expect { cli.parse_and_init(['-q', 'in:va:lid']) }.to raise_error(ArgumentError) }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Jiggler::Config do
|
4
|
+
let(:config) do
|
5
|
+
Jiggler::Config.new(
|
6
|
+
concurrency: 1,
|
7
|
+
client_concurrency: 1,
|
8
|
+
environment: 'test',
|
9
|
+
timeout: 1,
|
10
|
+
verbose: true,
|
11
|
+
queues: ['test', 'test2'],
|
12
|
+
require: 'test.rb',
|
13
|
+
max_dead_jobs: 100,
|
14
|
+
dead_timeout: 100,
|
15
|
+
redis_url: 'redis://localhost:6379'
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#initialize' do
|
20
|
+
it 'sets correct attrs' do
|
21
|
+
expect(config[:concurrency]).to be 1
|
22
|
+
expect(config[:environment]).to eq 'test'
|
23
|
+
expect(config[:timeout]).to be 1
|
24
|
+
expect(config[:verbose]).to be true
|
25
|
+
expect(config[:queues]).to eq ['test', 'test2']
|
26
|
+
expect(config[:require]).to eq 'test.rb'
|
27
|
+
expect(config[:max_dead_jobs]).to be 100
|
28
|
+
expect(config[:dead_timeout]).to be 100
|
29
|
+
expect(config[:stats_interval]).to be 10
|
30
|
+
expect(config[:poller_enabled]).to be true
|
31
|
+
expect(config[:poll_interval]).to be 5
|
32
|
+
expect(config[:client_async]).to be false
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'generates prefixed queues' do
|
36
|
+
expect(config.prefixed_queues).to eq ['jiggler:list:test', 'jiggler:list:test2']
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'gets redis options for server' do
|
40
|
+
expect(config.redis_options).to eq(
|
41
|
+
concurrency: 4,
|
42
|
+
async: true,
|
43
|
+
redis_url: 'redis://localhost:6379'
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'gets redis options for client' do
|
48
|
+
expect(config.client_redis_options).to eq(
|
49
|
+
concurrency: 1,
|
50
|
+
async: false,
|
51
|
+
client_redis_pool: nil,
|
52
|
+
redis_url: 'redis://localhost:6379'
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe 'Core' do
|
4
|
+
describe '.configure' do
|
5
|
+
it 'applies the configuration' do
|
6
|
+
Jiggler.configure do |config|
|
7
|
+
config[:concurrency] = 1
|
8
|
+
config[:client_concurrency] = 2
|
9
|
+
config[:timeout] = 3
|
10
|
+
config[:stats_interval] = 4
|
11
|
+
config[:max_dead_jobs] = 5
|
12
|
+
config[:dead_timeout] = 6
|
13
|
+
config[:poll_interval] = 7
|
14
|
+
config[:poller_enabled] = false
|
15
|
+
config[:client_async] = true
|
16
|
+
config[:queues] = %w[foo bar]
|
17
|
+
config[:require] = 'foo'
|
18
|
+
config[:environment] = 'bar'
|
19
|
+
end
|
20
|
+
expect(Jiggler.config[:concurrency]).to be 1
|
21
|
+
expect(Jiggler.config[:client_concurrency]).to be 2
|
22
|
+
expect(Jiggler.config[:timeout]).to be 3
|
23
|
+
expect(Jiggler.config[:stats_interval]).to be 4
|
24
|
+
expect(Jiggler.config[:max_dead_jobs]).to be 5
|
25
|
+
expect(Jiggler.config[:dead_timeout]).to be 6
|
26
|
+
expect(Jiggler.config[:poll_interval]).to be 7
|
27
|
+
expect(Jiggler.config[:poller_enabled]).to be false
|
28
|
+
expect(Jiggler.config[:client_async]).to be true
|
29
|
+
expect(Jiggler.config[:queues]).to eq %w[foo bar]
|
30
|
+
expect(Jiggler.config[:require]).to eq 'foo'
|
31
|
+
expect(Jiggler.config[:environment]).to eq 'bar'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Jiggler::Job do
|
4
|
+
before(:all) do
|
5
|
+
Jiggler.instance_variable_set(:@config, Jiggler::Config.new(
|
6
|
+
concurrency: 1,
|
7
|
+
client_concurrency: 1,
|
8
|
+
timeout: 1,
|
9
|
+
poller_enabled: false
|
10
|
+
))
|
11
|
+
end
|
12
|
+
after(:all) do
|
13
|
+
Jiggler.instance_variable_set(:@config, Jiggler::Config.new)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.job_options' do
|
17
|
+
context 'on default' do
|
18
|
+
let(:job) { MyJob.new }
|
19
|
+
|
20
|
+
before do
|
21
|
+
MyJob.job_options # reset to default
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'has correct attrs' do
|
25
|
+
expect(job.class.queue).to eq 'default'
|
26
|
+
expect(job.class.retries).to be 0
|
27
|
+
expect(job.class.retry_queue).to eq 'default'
|
28
|
+
expect(job.class.name).to eq 'MyJob'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with custom options' do
|
33
|
+
let(:job) { MyJob.new }
|
34
|
+
|
35
|
+
before do
|
36
|
+
MyJob.job_options(queue: 'custom', retries: 3, retry_queue: 'custom_retry')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'has correct attrs' do
|
40
|
+
expect(job.class.queue).to eq 'custom'
|
41
|
+
expect(job.class.retries).to be 3
|
42
|
+
expect(job.class.retry_queue).to eq 'custom_retry'
|
43
|
+
expect(job.class.name).to eq 'MyJob'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.with_options' do
|
49
|
+
it 'allows to override options on job level' do
|
50
|
+
expect { MyJob.with_options(queue: 'woo').enqueue }.to change {
|
51
|
+
Jiggler.config.client_redis_pool.acquire { |conn| conn.call('LLEN', 'jiggler:list:woo') }
|
52
|
+
}.by(1)
|
53
|
+
Jiggler.config.client_redis_pool.acquire { |conn| conn.call('DEL', 'jiggler:list:woo') }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '.enqueue' do
|
58
|
+
it 'adds the job to the queue' do
|
59
|
+
expect { MyJob.with_options(queue: 'mine').enqueue }.to change {
|
60
|
+
Jiggler.config.client_redis_pool.acquire { |conn| conn.call('LLEN', 'jiggler:list:mine') }
|
61
|
+
}.by(1)
|
62
|
+
Jiggler.config.client_redis_pool.acquire { |conn| conn.call('DEL', 'jiggler:list:mine') }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '.enqueue_in' do
|
67
|
+
it 'adds the job to the scheduled set' do
|
68
|
+
expect { MyJob.with_options(queue: 'mine').enqueue_in(1) }.to change {
|
69
|
+
Jiggler.config.client_redis_pool.acquire do |conn|
|
70
|
+
conn.call('ZCARD', Jiggler.config.scheduled_set)
|
71
|
+
end
|
72
|
+
}.by(1)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'supports multiple args' do
|
76
|
+
expect { MyJobWithArgs.with_options(queue: 'mine').enqueue_in(1, '1', 1, 1.0, true, [1], { '1' => 1 }) }.to change {
|
77
|
+
Jiggler.config.client_redis_pool.acquire do |conn|
|
78
|
+
conn.call('ZCARD', Jiggler.config.scheduled_set)
|
79
|
+
end
|
80
|
+
}.by(1)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#enqueue_bulk' do
|
85
|
+
let(:args_arr) do
|
86
|
+
[
|
87
|
+
['1', 1, 1.0, true, [1], { '1' => 1 }],
|
88
|
+
['2', 2, 2.0, true, [2], { '2' => 2 }],
|
89
|
+
['3', 3, 3.0, true, [3], { '3' => 3 }]
|
90
|
+
]
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'adds the jobs to the queue' do
|
94
|
+
expect { MyJobWithArgs.enqueue_bulk(args_arr) }.to change {
|
95
|
+
Jiggler.config.client_redis_pool.acquire { |conn| conn.call('LLEN', 'jiggler:list:default') }
|
96
|
+
}.by(3)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Jiggler::Launcher do
|
4
|
+
let(:config) do
|
5
|
+
Jiggler::Config.new(
|
6
|
+
concurrency: 1,
|
7
|
+
timeout: 1,
|
8
|
+
server_mode: true
|
9
|
+
)
|
10
|
+
end
|
11
|
+
let(:launcher) { described_class.new(config) }
|
12
|
+
|
13
|
+
describe '#initialize' do
|
14
|
+
it 'sets correct attrs' do
|
15
|
+
expect(launcher.config).to eq config
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#start' do
|
20
|
+
it 'starts the launcher' do
|
21
|
+
task = Async do
|
22
|
+
Async do
|
23
|
+
expect(launcher.send(:manager)).to receive(:start).and_call_original
|
24
|
+
expect(launcher.send(:poller)).to receive(:start).and_call_original
|
25
|
+
expect(launcher.send(:monitor)).to receive(:start).and_call_original
|
26
|
+
launcher.start
|
27
|
+
end
|
28
|
+
sleep(1)
|
29
|
+
launcher.stop
|
30
|
+
end
|
31
|
+
task.wait
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#suspend' do
|
36
|
+
it 'suspends the launcher' do
|
37
|
+
task = Async do
|
38
|
+
Async do
|
39
|
+
expect(launcher.send(:manager)).to receive(:suspend).and_call_original
|
40
|
+
expect(launcher.send(:poller)).to receive(:terminate).and_call_original
|
41
|
+
expect(launcher.send(:monitor)).to receive(:terminate).and_call_original
|
42
|
+
launcher.start
|
43
|
+
end
|
44
|
+
sleep(1)
|
45
|
+
launcher.suspend
|
46
|
+
end
|
47
|
+
task.wait
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#stop' do
|
52
|
+
it 'stops the launcher' do
|
53
|
+
task = Async do
|
54
|
+
Async do
|
55
|
+
expect(launcher.send(:manager)).to receive(:terminate).and_call_original
|
56
|
+
expect(launcher.send(:poller)).to receive(:terminate).and_call_original
|
57
|
+
expect(launcher.send(:monitor)).to receive(:terminate).and_call_original
|
58
|
+
launcher.start
|
59
|
+
end
|
60
|
+
sleep(1)
|
61
|
+
launcher.stop
|
62
|
+
end
|
63
|
+
task.wait
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Jiggler::Manager do
|
4
|
+
let(:config) do
|
5
|
+
Jiggler::Config.new(
|
6
|
+
concurrency: 4,
|
7
|
+
timeout: 1,
|
8
|
+
server_mode: true
|
9
|
+
)
|
10
|
+
end
|
11
|
+
let(:collection) { Jiggler::Stats::Collection.new(config) }
|
12
|
+
let(:manager) { described_class.new(config, collection) }
|
13
|
+
|
14
|
+
it { expect(manager.instance_variable_get(:@workers).count).to be 4 }
|
15
|
+
|
16
|
+
describe '#start' do
|
17
|
+
it 'starts the manager' do
|
18
|
+
expect(manager.instance_variable_get(:@workers)).to all(receive(:run))
|
19
|
+
Async { manager.start }
|
20
|
+
sleep(0.5)
|
21
|
+
manager.terminate
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#suspend' do
|
26
|
+
it 'suspends the manager' do
|
27
|
+
expect(manager.instance_variable_get(:@workers)).to all(receive(:suspend).and_call_original)
|
28
|
+
task = Async do
|
29
|
+
Async { manager.start }
|
30
|
+
sleep(0.5)
|
31
|
+
manager.suspend
|
32
|
+
expect(manager.instance_variable_get(:@done)).to be true
|
33
|
+
end
|
34
|
+
task.wait
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#terminate' do
|
39
|
+
it 'terminates the manager' do
|
40
|
+
expect(manager.instance_variable_get(:@workers)).to all(receive(:suspend).and_call_original)
|
41
|
+
expect(manager.instance_variable_get(:@workers)).to all(receive(:terminate).and_call_original)
|
42
|
+
task = Async do
|
43
|
+
Async { manager.start }
|
44
|
+
sleep(0.5)
|
45
|
+
manager.terminate
|
46
|
+
sleep(2) # wait for timeout
|
47
|
+
expect(manager.instance_variable_get(:@done)).to be true
|
48
|
+
end
|
49
|
+
task.wait
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Jiggler::RedisStore do
|
4
|
+
describe '#pool' do
|
5
|
+
let(:options) { { concurrency: 4, async: true } }
|
6
|
+
let(:redis_store) { described_class.new(options) }
|
7
|
+
|
8
|
+
it 'returns an async pool' do
|
9
|
+
expect(redis_store.pool).to be_a Async::Pool::Controller
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'when connection is sync' do
|
13
|
+
let(:options) { { concurrency: 4, async: false } }
|
14
|
+
|
15
|
+
it 'returns a sync pool' do
|
16
|
+
expect(redis_store.pool).to be_a RedisClient::Pooled
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Jiggler::Retrier do
|
4
|
+
let(:config) do
|
5
|
+
Jiggler::Config.new(
|
6
|
+
concurrency: 1,
|
7
|
+
timeout: 1,
|
8
|
+
queues: ['test']
|
9
|
+
)
|
10
|
+
end
|
11
|
+
let(:collection) { Jiggler::Stats::Collection.new('test-retrier-uuid') }
|
12
|
+
let(:retrier) { Jiggler::Retrier.new(config, collection) }
|
13
|
+
|
14
|
+
describe '#wrapped' do
|
15
|
+
context 'failed jobs' do
|
16
|
+
let(:job) { MyFailedJob.new }
|
17
|
+
|
18
|
+
it 'increments attempt if mex retries are not reached' do
|
19
|
+
msg = { 'jid' => '1', 'retries' => 1 }
|
20
|
+
Sync { config.cleaner.prune_failures_counter }
|
21
|
+
expect(config.logger).to receive(:error).twice
|
22
|
+
retrier.wrapped(job, msg, 'test') do
|
23
|
+
job.perform
|
24
|
+
end
|
25
|
+
expect(msg['attempt']).to be 1
|
26
|
+
expect(msg['error_message']).to eq 'Oh no!'
|
27
|
+
expect(msg['error_class']).to eq 'StandardError'
|
28
|
+
expect(collection.data[:failures]).to be 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'does not increment attempt if max retries are reached' do
|
32
|
+
msg = { 'attempt' => 3, 'jid' => '123', 'name' => 'MyFailedJob' }
|
33
|
+
Sync { config.cleaner.prune_failures_counter }
|
34
|
+
expect(config.logger).to receive(:error).twice
|
35
|
+
retrier.wrapped(job, msg, 'test') do
|
36
|
+
job.perform
|
37
|
+
end
|
38
|
+
|
39
|
+
expect(msg['attempt']).to be 3
|
40
|
+
expect(msg['error_message']).to eq 'Oh no!'
|
41
|
+
expect(msg['error_class']).to eq 'StandardError'
|
42
|
+
expect(collection.data[:failures]).to be 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'successful jobs' do
|
47
|
+
it 'does not throw any exceptions' do
|
48
|
+
msg = { 'jid' => '2' }
|
49
|
+
expect(config.logger).to_not receive(:error)
|
50
|
+
retrier.wrapped(MyJob.new, msg, 'default') { 'success' }
|
51
|
+
expect(collection.data[:failures]).to be 0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|