jiggler 0.1.0.rc2

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.
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