chore-core 1.9.0 → 1.10.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 +13 -5
- data/README.md +2 -0
- data/lib/chore.rb +2 -0
- data/lib/chore/strategies/consumer/batcher.rb +8 -9
- data/lib/chore/strategies/consumer/threaded_consumer_strategy.rb +2 -1
- data/lib/chore/unit_of_work.rb +8 -0
- data/lib/chore/version.rb +1 -1
- data/spec/chore/strategies/consumer/batcher_spec.rb +50 -0
- data/spec/chore/strategies/consumer/threaded_consumer_strategy_spec.rb +1 -0
- metadata +19 -18
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
OWVhNTgxZTYxMzNkNDVjODU1NTI5YmJjOGYxOWU4NmUzNDkzMDljYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTliZWFkNzRhZDZlZDBiZWZlNjg4MDcxNDEzMTlhNDkwNmJkOWU4NA==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MDJkYmVlYWY4YTljMTA4ZjkxNGFlNGYxMDRiYThhNTJiYTJjNjhkYzVkNWFl
|
10
|
+
YTUzNjBlMDIxYjM1MTcxNjE4NzU1YjdkNGVmOWY1Y2IzODg1MTQ5YjIxZGUx
|
11
|
+
ZWI5N2U4ZGQwZGRhYTVkZTRiZmMxYzY0NGM1MjQ1YzM5NmJiZTM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Mjc2ODk3ZmFjZjc2ZDdiODlkMTA4ODQzM2I1N2JiZDI5M2QwZjU2ODNkOWRl
|
14
|
+
NGY4ZDg3NDRiMDAzMTUxNzM0MGQxNTNiNDM4ZmMwMmVhOTQzZTgwOTc4ZTc2
|
15
|
+
MTg1YjI4Y2M1ZjE2YTM5ZDJiNTMwY2NkMjNjNTlmMTAyZmM1YTQ=
|
data/README.md
CHANGED
@@ -33,6 +33,7 @@ Other options include:
|
|
33
33
|
--threads-per-queue 4 # number of threads per queue for consuming from a given queue.
|
34
34
|
--dedupe-servers # if using SQS or similiar queue with at-least once delivery and your memcache is running on something other than localhost
|
35
35
|
--batch-size 50 # how many messages are batched together before handing them to a worker
|
36
|
+
--batch-timeout 20 # maximum number of seconds to wait until handing a message over to a worker
|
36
37
|
--queue_prefix prefixy # A prefix to prepend to queue names, mainly for development and qa testing purposes
|
37
38
|
--max-attempts 100 # The maximum number of times a job can be attempted
|
38
39
|
--dupe-on-cache-failure # Determines the deduping behavior when a cache connection error occurs. When set to `false`, the message is assumed not to be a duplicate. Defaults to `false`.
|
@@ -92,6 +93,7 @@ Chore.configure do |c|
|
|
92
93
|
c.max_attempts = 100
|
93
94
|
...
|
94
95
|
c.batch_size = 50
|
96
|
+
c.batch_timeout = 20
|
95
97
|
end
|
96
98
|
```
|
97
99
|
|
data/lib/chore.rb
CHANGED
@@ -34,6 +34,7 @@ module Chore #:nodoc:
|
|
34
34
|
:fetcher => Fetcher,
|
35
35
|
:consumer_strategy => Strategy::ThreadedConsumerStrategy,
|
36
36
|
:batch_size => 50,
|
37
|
+
:batch_timeout => 20,
|
37
38
|
:log_level => Logger::WARN,
|
38
39
|
:log_path => STDOUT,
|
39
40
|
:default_queue_timeout => (12 * 60 * 60), # 12 hours
|
@@ -186,6 +187,7 @@ module Chore #:nodoc:
|
|
186
187
|
# Chore.configure do |c|
|
187
188
|
# c.consumer = Chore::Queues::SQS::Consumer
|
188
189
|
# c.batch_size = 50
|
190
|
+
# c.batch_timeout = 20
|
189
191
|
# end
|
190
192
|
def self.configure(opts={})
|
191
193
|
@config = (@config ? @config.merge_hash(opts) : Chore::Configuration.new(DEFAULT_OPTIONS.merge(opts)))
|
@@ -11,27 +11,27 @@ module Chore
|
|
11
11
|
@size = size
|
12
12
|
@batch = []
|
13
13
|
@mutex = Mutex.new
|
14
|
-
@last_message = nil
|
15
14
|
@callback = nil
|
16
15
|
@running = true
|
17
16
|
end
|
18
17
|
|
19
18
|
# The main entry point of the Batcher, <tt>schedule</tt> begins a thread with the provided +batch_timeout+
|
20
19
|
# as the only argument. While the Batcher is running, it will attempt to check if either the batch is full,
|
21
|
-
# or if the +batch_timeout+ has elapsed since the
|
22
|
-
#
|
20
|
+
# or if the +batch_timeout+ has elapsed since the oldest message was added. If either case is true, the
|
21
|
+
# items in the batch will be executed.
|
23
22
|
#
|
24
23
|
# Calling <tt>stop</tt> will cause the thread to finish it's current check, and exit
|
25
|
-
def schedule(batch_timeout
|
24
|
+
def schedule(batch_timeout)
|
26
25
|
@thread = Thread.new(batch_timeout) do |timeout|
|
27
26
|
Chore.logger.info "Batching timeout thread starting"
|
28
27
|
while @running do
|
29
28
|
begin
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
oldest_item = @batch.first
|
30
|
+
timestamp = oldest_item && oldest_item.created_at
|
31
|
+
Chore.logger.debug "Oldest message in batch: #{timestamp}, size: #{@batch.size}"
|
32
|
+
if timestamp && Time.now > (timestamp + timeout)
|
33
|
+
Chore.logger.debug "Batching timeout reached (#{timestamp + timeout}), current size: #{@batch.size}"
|
33
34
|
self.execute(true)
|
34
|
-
@last_message = nil
|
35
35
|
end
|
36
36
|
sleep(1)
|
37
37
|
rescue => e
|
@@ -44,7 +44,6 @@ module Chore
|
|
44
44
|
# Adds the +item+ to the current batch
|
45
45
|
def add(item)
|
46
46
|
@batch << item
|
47
|
-
@last_message = Time.now
|
48
47
|
execute if ready?
|
49
48
|
end
|
50
49
|
|
@@ -5,13 +5,14 @@ module Chore
|
|
5
5
|
attr_accessor :batcher
|
6
6
|
|
7
7
|
Chore::CLI.register_option 'batch_size', '--batch-size SIZE', Integer, 'Number of items to collect for a single worker to process'
|
8
|
+
Chore::CLI.register_option 'batch_timeout', '--batch-timeout SIZE', Integer, 'Maximum number of seconds to wait until processing a message'
|
8
9
|
Chore::CLI.register_option 'threads_per_queue', '--threads-per-queue NUM_THREADS', Integer, 'Number of threads to create for each named queue'
|
9
10
|
|
10
11
|
def initialize(fetcher)
|
11
12
|
@fetcher = fetcher
|
12
13
|
@batcher = Batcher.new(Chore.config.batch_size)
|
13
14
|
@batcher.callback = lambda { |batch| @fetcher.manager.assign(batch) }
|
14
|
-
@batcher.schedule
|
15
|
+
@batcher.schedule(Chore.config.batch_timeout)
|
15
16
|
@running = true
|
16
17
|
end
|
17
18
|
|
data/lib/chore/unit_of_work.rb
CHANGED
@@ -9,6 +9,14 @@ module Chore
|
|
9
9
|
# * +:consumer+ The consumer instance used to fetch this message. Most queue implementations won't need access to this, but some (RabbitMQ) will. So we
|
10
10
|
# make sure to pass it along with each message. This instance will be used by the Worker for things like <tt>complete</tt> and </tt>reject</tt>.
|
11
11
|
class UnitOfWork < Struct.new(:id,:queue_name,:queue_timeout,:message,:previous_attempts,:consumer,:decoded_message, :klass)
|
12
|
+
# The time at which this unit of work was created
|
13
|
+
attr_accessor :created_at
|
14
|
+
|
15
|
+
def initialize(*) #:nodoc:
|
16
|
+
super
|
17
|
+
@created_at = Time.now
|
18
|
+
end
|
19
|
+
|
12
20
|
# The current attempt number for the worker processing this message.
|
13
21
|
def current_attempt
|
14
22
|
previous_attempts + 1
|
data/lib/chore/version.rb
CHANGED
@@ -90,4 +90,54 @@ describe Chore::Strategy::Batcher do
|
|
90
90
|
subject.batch.should == ['test']
|
91
91
|
end
|
92
92
|
end
|
93
|
+
|
94
|
+
describe 'schedule' do
|
95
|
+
let(:timeout) { 5 }
|
96
|
+
let(:batch) { [] }
|
97
|
+
|
98
|
+
before(:each) do
|
99
|
+
Thread.stub(:new) do |&block|
|
100
|
+
# Stop the batcher on the next iteration
|
101
|
+
subject.stub(:sleep) { subject.stop }
|
102
|
+
|
103
|
+
# Run the scheduling thread
|
104
|
+
block.call(timeout)
|
105
|
+
end
|
106
|
+
|
107
|
+
subject.batch = batch.dup
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'with no items' do
|
111
|
+
it 'should not invoke the callback' do
|
112
|
+
callback.should_not_receive(:call)
|
113
|
+
subject.schedule(timeout)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with new items' do
|
118
|
+
let(:batch) do
|
119
|
+
[
|
120
|
+
Chore::UnitOfWork.new.tap {|work| work.created_at = Time.now - 2}
|
121
|
+
]
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should not invoke the callback' do
|
125
|
+
callback.should_not_receive(:call).with(batch)
|
126
|
+
subject.schedule(timeout)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with old items' do
|
131
|
+
let(:batch) do
|
132
|
+
[
|
133
|
+
Chore::UnitOfWork.new.tap {|work| work.created_at = Time.now - 6}
|
134
|
+
]
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should invoke the callback' do
|
138
|
+
callback.should_receive(:call).with(batch)
|
139
|
+
subject.schedule(timeout)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
93
143
|
end
|
metadata
CHANGED
@@ -1,103 +1,103 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chore-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.10.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-
|
11
|
+
date: 2016-03-24 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
28
|
name: aws-sdk-v1
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.56'
|
34
|
-
- -
|
34
|
+
- - ! '>='
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: 1.56.0
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- -
|
41
|
+
- - ~>
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '1.56'
|
44
|
-
- -
|
44
|
+
- - ! '>='
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 1.56.0
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: thread
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: 0.1.3
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- -
|
58
|
+
- - ~>
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: 0.1.3
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: rspec
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- -
|
65
|
+
- - ~>
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: 3.3.0
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- -
|
72
|
+
- - ~>
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: 3.3.0
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: rdoc
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
|
-
- -
|
79
|
+
- - ~>
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '3.12'
|
82
82
|
type: :development
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
|
-
- -
|
86
|
+
- - ~>
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '3.12'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: bundler
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- -
|
93
|
+
- - ! '>='
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
|
-
- -
|
100
|
+
- - ! '>='
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '0'
|
103
103
|
description: Job processing with pluggable backends and strategies
|
@@ -176,12 +176,12 @@ require_paths:
|
|
176
176
|
- lib
|
177
177
|
required_ruby_version: !ruby/object:Gem::Requirement
|
178
178
|
requirements:
|
179
|
-
- -
|
179
|
+
- - ! '>='
|
180
180
|
- !ruby/object:Gem::Version
|
181
181
|
version: '0'
|
182
182
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
183
|
requirements:
|
184
|
-
- -
|
184
|
+
- - ! '>='
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '0'
|
187
187
|
requirements: []
|
@@ -191,3 +191,4 @@ signing_key:
|
|
191
191
|
specification_version: 4
|
192
192
|
summary: Job processing... for the future!
|
193
193
|
test_files: []
|
194
|
+
has_rdoc:
|