jiggler 0.1.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +6 -0
  3. data/LICENSE +4 -0
  4. data/README.md +423 -0
  5. data/bin/jiggler +31 -0
  6. data/lib/jiggler/cleaner.rb +130 -0
  7. data/lib/jiggler/cli.rb +263 -0
  8. data/lib/jiggler/config.rb +165 -0
  9. data/lib/jiggler/core.rb +22 -0
  10. data/lib/jiggler/errors.rb +5 -0
  11. data/lib/jiggler/job.rb +116 -0
  12. data/lib/jiggler/launcher.rb +69 -0
  13. data/lib/jiggler/manager.rb +73 -0
  14. data/lib/jiggler/redis_store.rb +55 -0
  15. data/lib/jiggler/retrier.rb +122 -0
  16. data/lib/jiggler/scheduled/enqueuer.rb +78 -0
  17. data/lib/jiggler/scheduled/poller.rb +97 -0
  18. data/lib/jiggler/stats/collection.rb +26 -0
  19. data/lib/jiggler/stats/monitor.rb +103 -0
  20. data/lib/jiggler/summary.rb +101 -0
  21. data/lib/jiggler/support/helper.rb +35 -0
  22. data/lib/jiggler/version.rb +5 -0
  23. data/lib/jiggler/web/assets/stylesheets/application.css +64 -0
  24. data/lib/jiggler/web/views/application.erb +329 -0
  25. data/lib/jiggler/web.rb +80 -0
  26. data/lib/jiggler/worker.rb +179 -0
  27. data/lib/jiggler.rb +10 -0
  28. data/spec/examples.txt +79 -0
  29. data/spec/fixtures/config/jiggler.yml +4 -0
  30. data/spec/fixtures/jobs.rb +5 -0
  31. data/spec/fixtures/my_failed_job.rb +10 -0
  32. data/spec/fixtures/my_job.rb +9 -0
  33. data/spec/fixtures/my_job_with_args.rb +18 -0
  34. data/spec/jiggler/cleaner_spec.rb +171 -0
  35. data/spec/jiggler/cli_spec.rb +87 -0
  36. data/spec/jiggler/config_spec.rb +56 -0
  37. data/spec/jiggler/core_spec.rb +34 -0
  38. data/spec/jiggler/job_spec.rb +99 -0
  39. data/spec/jiggler/launcher_spec.rb +66 -0
  40. data/spec/jiggler/manager_spec.rb +52 -0
  41. data/spec/jiggler/redis_store_spec.rb +20 -0
  42. data/spec/jiggler/retrier_spec.rb +55 -0
  43. data/spec/jiggler/scheduled/enqueuer_spec.rb +81 -0
  44. data/spec/jiggler/scheduled/poller_spec.rb +40 -0
  45. data/spec/jiggler/stats/monitor_spec.rb +40 -0
  46. data/spec/jiggler/summary_spec.rb +168 -0
  47. data/spec/jiggler/web_spec.rb +37 -0
  48. data/spec/jiggler/worker_spec.rb +110 -0
  49. data/spec/spec_helper.rb +54 -0
  50. metadata +230 -0
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './my_job'
4
+ require_relative './my_failed_job'
5
+ require_relative './my_job_with_args'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyFailedJob
4
+ include Jiggler::Job
5
+ job_options queue: 'test', retries: 3
6
+
7
+ def perform(name = 'Yo')
8
+ raise StandardError, 'Oh no!'
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MyJob
4
+ include Jiggler::Job
5
+
6
+ def perform
7
+ puts 'Hello World'
8
+ end
9
+ end
@@ -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