chore-core 1.10.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -13
  2. data/LICENSE.txt +1 -1
  3. data/README.md +172 -153
  4. data/chore-core.gemspec +3 -3
  5. data/lib/chore.rb +29 -5
  6. data/lib/chore/cli.rb +22 -4
  7. data/lib/chore/configuration.rb +1 -1
  8. data/lib/chore/consumer.rb +54 -12
  9. data/lib/chore/fetcher.rb +12 -7
  10. data/lib/chore/hooks.rb +2 -1
  11. data/lib/chore/job.rb +19 -0
  12. data/lib/chore/manager.rb +17 -2
  13. data/lib/chore/publisher.rb +18 -2
  14. data/lib/chore/queues/filesystem/consumer.rb +126 -64
  15. data/lib/chore/queues/filesystem/filesystem_queue.rb +19 -0
  16. data/lib/chore/queues/filesystem/publisher.rb +10 -16
  17. data/lib/chore/queues/sqs.rb +22 -13
  18. data/lib/chore/queues/sqs/consumer.rb +64 -51
  19. data/lib/chore/queues/sqs/publisher.rb +26 -17
  20. data/lib/chore/strategies/consumer/batcher.rb +6 -6
  21. data/lib/chore/strategies/consumer/single_consumer_strategy.rb +5 -5
  22. data/lib/chore/strategies/consumer/threaded_consumer_strategy.rb +7 -6
  23. data/lib/chore/strategies/consumer/throttled_consumer_strategy.rb +120 -0
  24. data/lib/chore/strategies/worker/forked_worker_strategy.rb +5 -6
  25. data/lib/chore/strategies/worker/helpers/ipc.rb +87 -0
  26. data/lib/chore/strategies/worker/helpers/preforked_worker.rb +163 -0
  27. data/lib/chore/strategies/worker/helpers/work_distributor.rb +65 -0
  28. data/lib/chore/strategies/worker/helpers/worker_info.rb +13 -0
  29. data/lib/chore/strategies/worker/helpers/worker_killer.rb +40 -0
  30. data/lib/chore/strategies/worker/helpers/worker_manager.rb +183 -0
  31. data/lib/chore/strategies/worker/preforked_worker_strategy.rb +150 -0
  32. data/lib/chore/unit_of_work.rb +2 -1
  33. data/lib/chore/util.rb +5 -1
  34. data/lib/chore/version.rb +2 -2
  35. data/lib/chore/worker.rb +30 -3
  36. data/spec/chore/cli_spec.rb +2 -2
  37. data/spec/chore/consumer_spec.rb +1 -5
  38. data/spec/chore/duplicate_detector_spec.rb +17 -5
  39. data/spec/chore/fetcher_spec.rb +0 -11
  40. data/spec/chore/manager_spec.rb +7 -0
  41. data/spec/chore/queues/filesystem/filesystem_consumer_spec.rb +74 -16
  42. data/spec/chore/queues/sqs/consumer_spec.rb +117 -78
  43. data/spec/chore/queues/sqs/publisher_spec.rb +49 -60
  44. data/spec/chore/queues/sqs_spec.rb +32 -41
  45. data/spec/chore/strategies/consumer/single_consumer_strategy_spec.rb +3 -3
  46. data/spec/chore/strategies/consumer/threaded_consumer_strategy_spec.rb +6 -6
  47. data/spec/chore/strategies/consumer/throttled_consumer_strategy_spec.rb +165 -0
  48. data/spec/chore/strategies/worker/forked_worker_strategy_spec.rb +6 -1
  49. data/spec/chore/strategies/worker/helpers/ipc_spec.rb +127 -0
  50. data/spec/chore/strategies/worker/helpers/preforked_worker_spec.rb +236 -0
  51. data/spec/chore/strategies/worker/helpers/work_distributor_spec.rb +131 -0
  52. data/spec/chore/strategies/worker/helpers/worker_info_spec.rb +14 -0
  53. data/spec/chore/strategies/worker/helpers/worker_killer_spec.rb +97 -0
  54. data/spec/chore/strategies/worker/helpers/worker_manager_spec.rb +304 -0
  55. data/spec/chore/strategies/worker/preforked_worker_strategy_spec.rb +183 -0
  56. data/spec/chore/strategies/worker/single_worker_strategy_spec.rb +1 -1
  57. data/spec/chore/worker_spec.rb +70 -15
  58. data/spec/spec_helper.rb +1 -1
  59. data/spec/support/queues/sqs/fake_objects.rb +18 -0
  60. metadata +53 -29
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Chore::Strategy::SingleWorkerStrategy do
4
4
  let(:manager) { double('Manager') }
5
5
  let(:job_timeout) { 60 }
6
- let(:job) { Chore::UnitOfWork.new(SecureRandom.uuid, 'test', job_timeout, Chore::Encoder::JsonEncoder.encode(TestJob.job_hash([1,2,"3"])), 0) }
6
+ let(:job) { Chore::UnitOfWork.new(SecureRandom.uuid, nil, 'test', job_timeout, Chore::Encoder::JsonEncoder.encode(TestJob.job_hash([1,2,"3"])), 0) }
7
7
  subject { described_class.new(manager) }
8
8
 
9
9
  describe '#stop!' do
@@ -2,6 +2,10 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe Chore::Worker do
4
4
 
5
+ before(:each) do
6
+ allow(consumer).to receive(:duplicate_message?).and_return(false)
7
+ end
8
+
5
9
  class SimpleJob
6
10
  include Chore::Job
7
11
  queue_options :name => 'test',
@@ -13,6 +17,30 @@ describe Chore::Worker do
13
17
  end
14
18
  end
15
19
 
20
+ class SimpleDedupeJob
21
+ include Chore::Job
22
+ queue_options :name => 'dedupe_test',
23
+ :publisher => FakePublisher,
24
+ :max_attempts => 100,
25
+ :dedupe_lambda => lambda { |first, second, third| first }
26
+
27
+ def perform(first, second, third)
28
+ return second
29
+ end
30
+ end
31
+
32
+ class InvalidDedupeJob
33
+ include Chore::Job
34
+ queue_options :name => 'invalid_dedupe_test',
35
+ :publisher => FakePublisher,
36
+ :max_attempts => 100,
37
+ :dedupe_lambda => lambda { |first, second, third| first }
38
+
39
+ def perform(first, second)
40
+ return second
41
+ end
42
+ end
43
+
16
44
  let(:consumer) { double('consumer', :complete => nil, :reject => nil) }
17
45
  let(:job_args) { [1,2,'3'] }
18
46
  let(:job) { SimpleJob.job_hash(job_args) }
@@ -24,9 +52,9 @@ describe Chore::Worker do
24
52
 
25
53
  shared_examples_for "a worker" do
26
54
  it 'processing a single job' do
27
- work = Chore::UnitOfWork.new('1', 'test', 60, encoded_job, 0, consumer)
55
+ work = Chore::UnitOfWork.new('1', nil, 'test', 60, encoded_job, 0, consumer)
28
56
  SimpleJob.should_receive(:perform).with(*payload)
29
- consumer.should_receive(:complete).with('1')
57
+ consumer.should_receive(:complete).with('1', nil)
30
58
  w = Chore::Worker.new(work, {:payload_handler => payload_handler})
31
59
  w.start
32
60
  end
@@ -34,12 +62,39 @@ describe Chore::Worker do
34
62
  it 'processing multiple jobs' do
35
63
  work = []
36
64
  10.times do |i|
37
- work << Chore::UnitOfWork.new(i, 'test', 60, encoded_job, 0, consumer)
65
+ work << Chore::UnitOfWork.new(i, nil, 'test', 60, encoded_job, 0, consumer)
38
66
  end
39
67
  SimpleJob.should_receive(:perform).exactly(10).times
40
68
  consumer.should_receive(:complete).exactly(10).times
41
69
  Chore::Worker.start(work, {:payload_handler => payload_handler})
42
70
  end
71
+
72
+ context 'when the job has a dedupe_lambda defined' do
73
+ context 'when the value being deduped on is unique' do
74
+ let(:job_args) { [rand,2,'3'] }
75
+ let(:encoded_job) { Chore::Encoder::JsonEncoder.encode(job) }
76
+ let(:job) { SimpleDedupeJob.job_hash(job_args) }
77
+ it 'should call complete for each unique value' do
78
+ allow(consumer).to receive(:duplicate_message?).and_return(false)
79
+ work = []
80
+ work << Chore::UnitOfWork.new(1, nil, 'dedupe_test', 60, Chore::Encoder::JsonEncoder.encode(SimpleDedupeJob.job_hash([rand,2,'3'])), 0, consumer)
81
+ SimpleDedupeJob.should_receive(:perform).exactly(1).times
82
+ consumer.should_receive(:complete).exactly(1).times
83
+ Chore::Worker.start(work, {:payload_handler => payload_handler})
84
+ end
85
+ end
86
+
87
+ context 'when the dedupe lambda does not take the same number of arguments as perform' do
88
+ it 'should raise an error and not complete the job' do
89
+ work = []
90
+ work << Chore::UnitOfWork.new(1, nil, 'invalid_dedupe_test', 60, Chore::Encoder::JsonEncoder.encode(InvalidDedupeJob.job_hash([rand,2,'3'])), 0, consumer)
91
+ work << Chore::UnitOfWork.new(2, nil, 'invalid_dedupe_test', 60, Chore::Encoder::JsonEncoder.encode(InvalidDedupeJob.job_hash([rand,2,'3'])), 0, consumer)
92
+ work << Chore::UnitOfWork.new(1, nil, 'invalid_dedupe_test', 60, Chore::Encoder::JsonEncoder.encode(InvalidDedupeJob.job_hash([rand,2,'3'])), 0, consumer)
93
+ consumer.should_not_receive(:complete)
94
+ Chore::Worker.start(work, {:payload_handler => payload_handler})
95
+ end
96
+ end
97
+ end
43
98
  end
44
99
 
45
100
  describe "with default payload handler" do
@@ -54,7 +109,7 @@ describe Chore::Worker do
54
109
  let(:queue_timeouts) { [10, 20, 30] }
55
110
  let(:work) do
56
111
  queue_timeouts.map do |queue_timeout|
57
- Chore::UnitOfWork.new('1', 'test', queue_timeout, Chore::Encoder::JsonEncoder.encode(job), 0, consumer)
112
+ Chore::UnitOfWork.new('1', nil, 'test', queue_timeout, Chore::Encoder::JsonEncoder.encode(job), 0, consumer)
58
113
  end
59
114
  end
60
115
  let(:worker) do
@@ -85,14 +140,14 @@ describe Chore::Worker do
85
140
  let(:job) { "Not-A-Valid-Json-String" }
86
141
 
87
142
  it 'should fail cleanly' do
88
- work = Chore::UnitOfWork.new(2,'test',60,job,0,consumer)
143
+ work = Chore::UnitOfWork.new(2,nil,'test',60,job,0,consumer)
89
144
  consumer.should_not_receive(:complete)
90
145
  Chore.should_receive(:run_hooks_for).with(:on_failure, job, anything())
91
146
  Chore::Worker.start(work)
92
147
  end
93
148
 
94
149
  it 'should reject job' do
95
- work = Chore::UnitOfWork.new(2,'test',60,job,0,consumer)
150
+ work = Chore::UnitOfWork.new(2,nil,'test',60,job,0,consumer)
96
151
  consumer.should_receive(:reject).with(2)
97
152
  Chore::Worker.start(work)
98
153
  end
@@ -103,14 +158,14 @@ describe Chore::Worker do
103
158
  end
104
159
 
105
160
  it 'should permanently fail' do
106
- work = Chore::UnitOfWork.new(2,'test',60,job,9,consumer)
161
+ work = Chore::UnitOfWork.new(2,nil,'test',60,job,9,consumer)
107
162
  Chore.should_receive(:run_hooks_for).with(:on_permanent_failure, 'test', job, anything())
108
163
  Chore::Worker.start(work)
109
164
  end
110
165
 
111
166
  it 'should mark the item as completed' do
112
- work = Chore::UnitOfWork.new(2,'test',60,job,9,consumer)
113
- consumer.should_receive(:complete).with(2)
167
+ work = Chore::UnitOfWork.new(2,nil,'test',60,job,9,consumer)
168
+ consumer.should_receive(:complete).with(2, nil)
114
169
  Chore::Worker.start(work)
115
170
  end
116
171
  end
@@ -126,7 +181,7 @@ describe Chore::Worker do
126
181
  end
127
182
 
128
183
  it 'should fail cleanly' do
129
- work = Chore::UnitOfWork.new(2,'test',60,encoded_job,0,consumer)
184
+ work = Chore::UnitOfWork.new(2,nil,'test',60,encoded_job,0,consumer)
130
185
  consumer.should_not_receive(:complete)
131
186
  SimpleJob.should_receive(:run_hooks_for).with(:on_failure, parsed_job, anything())
132
187
 
@@ -134,7 +189,7 @@ describe Chore::Worker do
134
189
  end
135
190
 
136
191
  it 'should reject job' do
137
- work = Chore::UnitOfWork.new(2,'test',60,encoded_job,0,consumer)
192
+ work = Chore::UnitOfWork.new(2,nil,'test',60,encoded_job,0,consumer)
138
193
  consumer.should_receive(:reject).with(2)
139
194
 
140
195
  Chore::Worker.start(work)
@@ -142,14 +197,14 @@ describe Chore::Worker do
142
197
 
143
198
  context 'more than the maximum allowed times' do
144
199
  it 'should permanently fail' do
145
- work = Chore::UnitOfWork.new(2,'test',60,encoded_job,999,consumer)
200
+ work = Chore::UnitOfWork.new(2,nil,'test',60,encoded_job,999,consumer)
146
201
  SimpleJob.should_receive(:run_hooks_for).with(:on_permanent_failure, 'test', parsed_job, anything())
147
202
  Chore::Worker.start(work)
148
203
  end
149
204
 
150
205
  it 'should mark the item as completed' do
151
- work = Chore::UnitOfWork.new(2,'test',60,encoded_job,999,consumer)
152
- consumer.should_receive(:complete).with(2)
206
+ work = Chore::UnitOfWork.new(2,nil,'test',60,encoded_job,999,consumer)
207
+ consumer.should_receive(:complete).with(2, nil)
153
208
  Chore::Worker.start(work)
154
209
  end
155
210
  end
@@ -159,7 +214,7 @@ describe Chore::Worker do
159
214
  describe 'delaying retries' do
160
215
  let(:encoded_job) { Chore::Encoder::JsonEncoder.encode(job) }
161
216
  let(:parsed_job) { JSON.parse(encoded_job) }
162
- let(:work) { Chore::UnitOfWork.new(2, 'test', 60, encoded_job, 0, consumer) }
217
+ let(:work) { Chore::UnitOfWork.new(2, nil, 'test', 60, encoded_job, 0, consumer) }
163
218
 
164
219
  before(:each) do
165
220
  SimpleJob.options[:backoff] = lambda { |work| work.current_attempt }
@@ -26,7 +26,7 @@ class FakePublisher < Chore::Publisher
26
26
  end
27
27
  end
28
28
 
29
- TestMessage = Struct.new(:handle, :queue, :body, :receive_count) do
29
+ TestMessage = Struct.new(:handle, :receipt_handle, :queue, :body, :receive_count) do
30
30
  def empty?
31
31
  false
32
32
  end
@@ -0,0 +1,18 @@
1
+ describe Chore::Queues::SQS do
2
+ RSpec.shared_context 'fake objects' do
3
+ let(:queue_name) { 'test_queue' }
4
+ let(:queue_url) { "http://amazon.sqs.url/queues/#{queue_name}" }
5
+
6
+ let(:queue) do
7
+ double(Aws::SQS::Queue,
8
+ attributes: {'VisibilityTimeout' => rand(10)}
9
+ )
10
+ end
11
+
12
+ let(:sqs) do
13
+ double(Aws::SQS::Client,
14
+ get_queue_url: double(Aws::SQS::Types::GetQueueUrlResult, :queue_url => queue_url),
15
+ )
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,103 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chore-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tapjoy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-24 00:00:00.000000000 Z
11
+ date: 2020-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: aws-sdk-v1
28
+ name: aws-sdk-sqs
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.56'
34
- - - ! '>='
35
- - !ruby/object:Gem::Version
36
- version: 1.56.0
33
+ version: '1'
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
- - - ~>
42
- - !ruby/object:Gem::Version
43
- version: '1.56'
44
- - - ! '>='
38
+ - - "~>"
45
39
  - !ruby/object:Gem::Version
46
- version: 1.56.0
40
+ version: '1'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: thread
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
- - - ~>
45
+ - - "~>"
52
46
  - !ruby/object:Gem::Version
53
47
  version: 0.1.3
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
51
  requirements:
58
- - - ~>
52
+ - - "~>"
59
53
  - !ruby/object:Gem::Version
60
54
  version: 0.1.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: get_process_mem
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.2.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.2.0
61
69
  - !ruby/object:Gem::Dependency
62
70
  name: rspec
63
71
  requirement: !ruby/object:Gem::Requirement
64
72
  requirements:
65
- - - ~>
73
+ - - "~>"
66
74
  - !ruby/object:Gem::Version
67
- version: 3.3.0
75
+ version: '3.3'
68
76
  type: :development
69
77
  prerelease: false
70
78
  version_requirements: !ruby/object:Gem::Requirement
71
79
  requirements:
72
- - - ~>
80
+ - - "~>"
73
81
  - !ruby/object:Gem::Version
74
- version: 3.3.0
82
+ version: '3.3'
75
83
  - !ruby/object:Gem::Dependency
76
84
  name: rdoc
77
85
  requirement: !ruby/object:Gem::Requirement
78
86
  requirements:
79
- - - ~>
87
+ - - "~>"
80
88
  - !ruby/object:Gem::Version
81
89
  version: '3.12'
82
90
  type: :development
83
91
  prerelease: false
84
92
  version_requirements: !ruby/object:Gem::Requirement
85
93
  requirements:
86
- - - ~>
94
+ - - "~>"
87
95
  - !ruby/object:Gem::Version
88
96
  version: '3.12'
89
97
  - !ruby/object:Gem::Dependency
90
98
  name: bundler
91
99
  requirement: !ruby/object:Gem::Requirement
92
100
  requirements:
93
- - - ! '>='
101
+ - - ">="
94
102
  - !ruby/object:Gem::Version
95
103
  version: '0'
96
104
  type: :development
97
105
  prerelease: false
98
106
  version_requirements: !ruby/object:Gem::Requirement
99
107
  requirements:
100
- - - ! '>='
108
+ - - ">="
101
109
  - !ruby/object:Gem::Version
102
110
  version: '0'
103
111
  description: Job processing with pluggable backends and strategies
@@ -137,7 +145,15 @@ files:
137
145
  - lib/chore/strategies/consumer/batcher.rb
138
146
  - lib/chore/strategies/consumer/single_consumer_strategy.rb
139
147
  - lib/chore/strategies/consumer/threaded_consumer_strategy.rb
148
+ - lib/chore/strategies/consumer/throttled_consumer_strategy.rb
140
149
  - lib/chore/strategies/worker/forked_worker_strategy.rb
150
+ - lib/chore/strategies/worker/helpers/ipc.rb
151
+ - lib/chore/strategies/worker/helpers/preforked_worker.rb
152
+ - lib/chore/strategies/worker/helpers/work_distributor.rb
153
+ - lib/chore/strategies/worker/helpers/worker_info.rb
154
+ - lib/chore/strategies/worker/helpers/worker_killer.rb
155
+ - lib/chore/strategies/worker/helpers/worker_manager.rb
156
+ - lib/chore/strategies/worker/preforked_worker_strategy.rb
141
157
  - lib/chore/strategies/worker/single_worker_strategy.rb
142
158
  - lib/chore/tasks/queues.task
143
159
  - lib/chore/unit_of_work.rb
@@ -160,11 +176,20 @@ files:
160
176
  - spec/chore/strategies/consumer/batcher_spec.rb
161
177
  - spec/chore/strategies/consumer/single_consumer_strategy_spec.rb
162
178
  - spec/chore/strategies/consumer/threaded_consumer_strategy_spec.rb
179
+ - spec/chore/strategies/consumer/throttled_consumer_strategy_spec.rb
163
180
  - spec/chore/strategies/worker/forked_worker_strategy_spec.rb
181
+ - spec/chore/strategies/worker/helpers/ipc_spec.rb
182
+ - spec/chore/strategies/worker/helpers/preforked_worker_spec.rb
183
+ - spec/chore/strategies/worker/helpers/work_distributor_spec.rb
184
+ - spec/chore/strategies/worker/helpers/worker_info_spec.rb
185
+ - spec/chore/strategies/worker/helpers/worker_killer_spec.rb
186
+ - spec/chore/strategies/worker/helpers/worker_manager_spec.rb
187
+ - spec/chore/strategies/worker/preforked_worker_strategy_spec.rb
164
188
  - spec/chore/strategies/worker/single_worker_strategy_spec.rb
165
189
  - spec/chore/worker_spec.rb
166
190
  - spec/chore_spec.rb
167
191
  - spec/spec_helper.rb
192
+ - spec/support/queues/sqs/fake_objects.rb
168
193
  - spec/test_job.rb
169
194
  homepage: http://github.com/Tapjoy/chore
170
195
  licenses:
@@ -176,19 +201,18 @@ require_paths:
176
201
  - lib
177
202
  required_ruby_version: !ruby/object:Gem::Requirement
178
203
  requirements:
179
- - - ! '>='
204
+ - - ">="
180
205
  - !ruby/object:Gem::Version
181
206
  version: '0'
182
207
  required_rubygems_version: !ruby/object:Gem::Requirement
183
208
  requirements:
184
- - - ! '>='
209
+ - - ">="
185
210
  - !ruby/object:Gem::Version
186
211
  version: '0'
187
212
  requirements: []
188
213
  rubyforge_project:
189
- rubygems_version: 2.4.8
214
+ rubygems_version: 2.5.1
190
215
  signing_key:
191
216
  specification_version: 4
192
217
  summary: Job processing... for the future!
193
218
  test_files: []
194
- has_rdoc: