proletariat 0.0.6 → 0.1.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 +4 -4
- data/Gemfile +2 -0
- data/README.md +2 -1
- data/lib/proletariat.rb +15 -2
- data/lib/proletariat/concurrency/actor.rb +12 -67
- data/lib/proletariat/concurrency/actor_common.rb +30 -0
- data/lib/proletariat/concurrency/poolable_actor.rb +25 -0
- data/lib/proletariat/configuration.rb +15 -0
- data/lib/proletariat/cucumber.rb +2 -4
- data/lib/proletariat/exception_handler.rb +45 -0
- data/lib/proletariat/exception_handler/drop.rb +15 -0
- data/lib/proletariat/exception_handler/exponential_backoff.rb +130 -0
- data/lib/proletariat/manager.rb +30 -28
- data/lib/proletariat/message.rb +9 -0
- data/lib/proletariat/publisher.rb +19 -41
- data/lib/proletariat/runner.rb +18 -30
- data/lib/proletariat/subscriber.rb +91 -177
- data/lib/proletariat/tasks.rb +1 -1
- data/lib/proletariat/testing/expectation_guarantor.rb +36 -30
- data/lib/proletariat/version.rb +1 -1
- data/lib/proletariat/worker.rb +47 -34
- data/proletariat.gemspec +3 -3
- data/spec/lib/proletariat_spec.rb +18 -7
- data/spec/lib/testing/expectation_guarantor_spec.rb +0 -32
- data/spec/lib/worker_spec.rb +13 -15
- metadata +15 -12
- data/lib/proletariat/concurrency/supervisor.rb +0 -31
- data/spec/lib/publisher_spec.rb +0 -36
data/lib/proletariat/tasks.rb
CHANGED
@@ -22,17 +22,15 @@ module Proletariat
|
|
22
22
|
@counters = []
|
23
23
|
@subscribers = []
|
24
24
|
|
25
|
-
expectations.
|
26
|
-
|
27
|
-
|
25
|
+
expectations.each_with_index do |expectation, i|
|
26
|
+
config = generate_queue_config_for_topic(expectation.topics)
|
27
|
+
suffix = "_#{i}_#{object_id}"
|
28
|
+
counter = MessageCounter.spawn!("c#{suffix}", expectation.quantity)
|
28
29
|
counters << counter
|
29
|
-
subscribers << Subscriber.
|
30
|
+
subscribers << Subscriber.spawn!("s#{suffix}", counter, config, Drop)
|
30
31
|
end
|
31
32
|
|
32
33
|
@block = block
|
33
|
-
@noop = true if expectations.length == 0
|
34
|
-
|
35
|
-
run_subscribers
|
36
34
|
end
|
37
35
|
|
38
36
|
# Public: Execute the blocks and waits for the expectations to be met.
|
@@ -44,13 +42,7 @@ module Proletariat
|
|
44
42
|
|
45
43
|
return nil if noop
|
46
44
|
|
47
|
-
|
48
|
-
|
49
|
-
until passed?
|
50
|
-
fail MessageTimeoutError if timer > MESSAGE_TIMEOUT
|
51
|
-
sleep MESSAGE_CHECK_INTERVAL
|
52
|
-
timer += MESSAGE_CHECK_INTERVAL
|
53
|
-
end
|
45
|
+
wait!
|
54
46
|
|
55
47
|
nil
|
56
48
|
ensure
|
@@ -66,9 +58,6 @@ module Proletariat
|
|
66
58
|
# Internal: Returns an array of MessageCounter instances.
|
67
59
|
attr_reader :counters
|
68
60
|
|
69
|
-
# Internal: Returns true if there aren't any expectations.
|
70
|
-
attr_reader :noop
|
71
|
-
|
72
61
|
# Internal: Returns an array of Subscriber instances.
|
73
62
|
attr_reader :subscribers
|
74
63
|
|
@@ -82,30 +71,43 @@ module Proletariat
|
|
82
71
|
# Returns false if one or more counters are not satisfied.
|
83
72
|
def passed?
|
84
73
|
counters
|
85
|
-
.map(
|
74
|
+
.map { |c| c.ask!(:expected_messages_received?) }
|
86
75
|
.reduce { |a, e| a && e }
|
87
76
|
end
|
88
77
|
|
89
|
-
# Internal:
|
78
|
+
# Internal: Returns true if there aren't any expectations.
|
79
|
+
def noop
|
80
|
+
subscribers.length == 0
|
81
|
+
end
|
82
|
+
|
83
|
+
# Internal: Stops each subscriber.
|
90
84
|
#
|
91
85
|
# Returns nil.
|
92
|
-
def
|
93
|
-
subscribers.each { |subscriber| subscriber
|
86
|
+
def stop_subscribers
|
87
|
+
subscribers.each { |subscriber| subscriber << :terminate! }
|
94
88
|
|
95
89
|
nil
|
96
90
|
end
|
97
91
|
|
98
|
-
# Internal:
|
92
|
+
# Internal: Sleeps the thread at regular intervals until the expectation
|
93
|
+
# is passed.
|
99
94
|
#
|
100
|
-
# Returns nil.
|
101
|
-
|
102
|
-
|
95
|
+
# Returns nil if expectation passes within timeout.
|
96
|
+
# Raises MessageTimeoutError if expectation does not pass within timeout.
|
97
|
+
def wait!
|
98
|
+
timer = 0.0
|
99
|
+
|
100
|
+
until passed?
|
101
|
+
fail MessageTimeoutError if timer > MESSAGE_TIMEOUT
|
102
|
+
sleep MESSAGE_CHECK_INTERVAL
|
103
|
+
timer += MESSAGE_CHECK_INTERVAL
|
104
|
+
end
|
103
105
|
|
104
106
|
nil
|
105
107
|
end
|
106
108
|
|
107
109
|
# Internal: Counts incoming messages to test expection satisfaction.
|
108
|
-
class MessageCounter
|
110
|
+
class MessageCounter < Actor
|
109
111
|
# Public: Creates a new MessageCounter instance.
|
110
112
|
#
|
111
113
|
# expected - The number of messages expected.
|
@@ -130,10 +132,14 @@ module Proletariat
|
|
130
132
|
# headers - Hash of message headers.
|
131
133
|
#
|
132
134
|
# Returns a future-like object holding an :ok Symbol.
|
133
|
-
def
|
134
|
-
|
135
|
-
|
136
|
-
|
135
|
+
def work(message)
|
136
|
+
if message.is_a?(Message)
|
137
|
+
self.count = count + 1
|
138
|
+
|
139
|
+
Concurrent::IVar.new(:ok)
|
140
|
+
else
|
141
|
+
expected_messages_received?
|
142
|
+
end
|
137
143
|
end
|
138
144
|
|
139
145
|
private
|
data/lib/proletariat/version.rb
CHANGED
data/lib/proletariat/worker.rb
CHANGED
@@ -3,43 +3,14 @@ require 'proletariat/concerns/logging'
|
|
3
3
|
module Proletariat
|
4
4
|
# Public: Handles messages for Background processing. Subclasses should
|
5
5
|
# overwrite the #work method.
|
6
|
-
class Worker
|
6
|
+
class Worker < PoolableActor
|
7
7
|
include Concerns::Logging
|
8
8
|
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# Returns nil.
|
12
|
-
def started
|
13
|
-
log_info 'Now online'
|
14
|
-
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
|
18
|
-
# Public: Logs the 'offline' status of the worker.
|
19
|
-
#
|
20
|
-
# Returns nil.
|
21
|
-
def stopped
|
22
|
-
log_info 'Now offline'
|
23
|
-
|
24
|
-
nil
|
25
|
-
end
|
26
|
-
|
27
|
-
# Public: Logs the 'shutting down' status of the worker.
|
9
|
+
# Internal: Handles the Actor mailbox. Delegates work to #work.
|
28
10
|
#
|
29
|
-
#
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
nil
|
34
|
-
end
|
35
|
-
|
36
|
-
# Public: Handles an incoming message to perform background work.
|
37
|
-
#
|
38
|
-
# message - The incoming message.
|
39
|
-
#
|
40
|
-
# Raises NotImplementedError unless implemented in subclass.
|
41
|
-
def work(message, routing_key, headers)
|
42
|
-
fail NotImplementedError
|
11
|
+
# message - A Message to send.
|
12
|
+
def actor_work(message)
|
13
|
+
work message.body, message.to, message.headers if message.is_a?(Message)
|
43
14
|
end
|
44
15
|
|
45
16
|
# Public: Helper method to ease accessing the logger from within #work.
|
@@ -81,8 +52,50 @@ module Proletariat
|
|
81
52
|
nil
|
82
53
|
end
|
83
54
|
|
55
|
+
# Public: Logs the 'online' status of the worker.
|
56
|
+
#
|
57
|
+
# Returns nil.
|
58
|
+
def started
|
59
|
+
log_info 'Now online'
|
60
|
+
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Public: Logs the 'offline' status of the worker.
|
65
|
+
#
|
66
|
+
# Returns nil.
|
67
|
+
def stopped
|
68
|
+
log_info 'Now offline'
|
69
|
+
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Handles an incoming message to perform background work.
|
74
|
+
#
|
75
|
+
# message - The incoming message.
|
76
|
+
# routing_key - The incoming message's routing key.
|
77
|
+
# headers - The incoming message's headers.
|
78
|
+
#
|
79
|
+
# Raises NotImplementedError unless implemented in subclass.
|
80
|
+
def work(message, routing_key, headers)
|
81
|
+
fail NotImplementedError
|
82
|
+
end
|
83
|
+
|
84
|
+
# Public: Use #actor_work to handle the actor mailbox.
|
85
|
+
def work_method
|
86
|
+
:actor_work
|
87
|
+
end
|
88
|
+
|
84
89
|
# Internal: Class methods on Worker to provide configuration DSL.
|
85
90
|
module ConfigurationMethods
|
91
|
+
def exception_handler(value = nil)
|
92
|
+
if value
|
93
|
+
@exception_handler = value
|
94
|
+
else
|
95
|
+
@exception_handler || :exponential_backoff
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
86
99
|
# Public: A configuration method for adding a routing key to be used when
|
87
100
|
# binding this worker type's queue to an exchange.
|
88
101
|
#
|
data/proletariat.gemspec
CHANGED
@@ -16,9 +16,9 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
17
|
s.require_paths = ['lib']
|
18
18
|
|
19
|
-
s.add_development_dependency 'rspec', '3.
|
19
|
+
s.add_development_dependency 'rspec', '3.1.0'
|
20
20
|
s.add_development_dependency 'rubocop'
|
21
21
|
|
22
|
-
s.add_runtime_dependency 'concurrent-ruby', '~> 0.
|
23
|
-
s.add_runtime_dependency 'bunny', '~> 1.3
|
22
|
+
s.add_runtime_dependency 'concurrent-ruby', '~> 0.7'
|
23
|
+
s.add_runtime_dependency 'bunny', '~> 1.6.3'
|
24
24
|
end
|
@@ -30,7 +30,7 @@ class PongWorker < Proletariat::Worker
|
|
30
30
|
|
31
31
|
def work(message, routing_key, headers)
|
32
32
|
if self.class.fail_mode == true
|
33
|
-
fail 'Error' unless headers['
|
33
|
+
fail 'Error' unless headers['x-death'] && headers['x-death'].length == 2
|
34
34
|
end
|
35
35
|
|
36
36
|
self.class.ponged = true
|
@@ -46,36 +46,47 @@ end
|
|
46
46
|
describe Proletariat do
|
47
47
|
before do
|
48
48
|
Proletariat.configure do
|
49
|
+
config.exchange_name = 'proletariat-test-suite'
|
49
50
|
config.logger = Logger.new('/dev/null')
|
51
|
+
config.test_mode!
|
50
52
|
config.worker_classes = [PingWorker, PongWorker]
|
51
53
|
end
|
52
54
|
|
53
55
|
PongWorker.fail_mode = false
|
54
56
|
|
55
|
-
Proletariat.
|
56
|
-
|
57
|
-
sleep
|
57
|
+
Proletariat.run
|
58
|
+
|
59
|
+
sleep 1
|
58
60
|
end
|
59
61
|
|
60
62
|
after do
|
61
63
|
Proletariat.stop
|
62
|
-
|
64
|
+
|
63
65
|
PingWorker.pinged = false
|
64
66
|
PongWorker.ponged = false
|
67
|
+
|
68
|
+
sleep 0.5
|
65
69
|
end
|
66
70
|
|
67
71
|
it 'should roughly work' do
|
68
72
|
Proletariat.publish 'ping', ''
|
69
|
-
sleep
|
73
|
+
sleep 1
|
70
74
|
|
71
75
|
expect(PingWorker.pinged).to be_truthy
|
72
76
|
expect(PongWorker.ponged).to be_truthy
|
73
77
|
end
|
74
78
|
|
79
|
+
it 'should purge between tests' do
|
80
|
+
sleep 1
|
81
|
+
|
82
|
+
expect(PingWorker.pinged).to be_falsey
|
83
|
+
expect(PongWorker.ponged).to be_falsey
|
84
|
+
end
|
85
|
+
|
75
86
|
it 'should work in error conditions' do
|
76
87
|
PongWorker.fail_mode = true
|
77
88
|
Proletariat.publish 'ping', ''
|
78
|
-
sleep
|
89
|
+
sleep 15
|
79
90
|
|
80
91
|
expect(PingWorker.pinged).to be_truthy
|
81
92
|
expect(PongWorker.ponged).to be_truthy
|
@@ -18,38 +18,6 @@ module Proletariat
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
22
|
-
describe '#post?' do
|
23
|
-
class FakeBlockTaker
|
24
|
-
attr_reader :block
|
25
|
-
|
26
|
-
def initialize(&block)
|
27
|
-
@block = block
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
before do
|
32
|
-
stub_const 'Concurrent::Future', FakeBlockTaker
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'should increment the count' do
|
36
|
-
counter = ExpectationGuarantor::MessageCounter.new(1)
|
37
|
-
counter.post?('message', 'key', {})
|
38
|
-
expect(counter.expected_messages_received?).to be_truthy
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'should return a future containing :ok' do
|
42
|
-
counter = ExpectationGuarantor::MessageCounter.new(1)
|
43
|
-
expect(Concurrent::Future).to receive(:new)
|
44
|
-
counter.post?('message', 'key', {})
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'should ensure the returned future contains :ok' do
|
48
|
-
counter = ExpectationGuarantor::MessageCounter.new(1)
|
49
|
-
future = counter.post?('message', 'key', {})
|
50
|
-
expect(future.block.call).to eq :ok
|
51
|
-
end
|
52
|
-
end
|
53
21
|
end
|
54
22
|
end
|
55
23
|
end
|
data/spec/lib/worker_spec.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
require 'proletariat/concurrency/actor_common'
|
3
|
+
require 'proletariat/concurrency/poolable_actor'
|
1
4
|
require 'proletariat/worker'
|
2
5
|
|
3
6
|
module Proletariat
|
4
7
|
describe Worker do
|
5
|
-
let(:
|
8
|
+
let(:balancer) { double.as_null_object }
|
9
|
+
let(:logger) { double.as_null_object }
|
10
|
+
let(:worker) { Worker.new(balancer) }
|
6
11
|
|
7
12
|
before do
|
8
13
|
allow(Proletariat).to receive(:logger).and_return(logger)
|
@@ -11,27 +16,20 @@ module Proletariat
|
|
11
16
|
describe '#started' do
|
12
17
|
it 'should log status' do
|
13
18
|
expect(logger).to receive(:info).with /online/
|
14
|
-
|
19
|
+
worker.started
|
15
20
|
end
|
16
21
|
end
|
17
22
|
|
18
23
|
describe '#stopped' do
|
19
24
|
it 'should log status' do
|
20
25
|
expect(logger).to receive(:info).with /offline/
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe '#stopping' do
|
26
|
-
it 'should log status' do
|
27
|
-
expect(logger).to receive(:info).with /graceful shutdown/
|
28
|
-
Worker.new.stopping
|
26
|
+
worker.stopped
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
30
|
describe '#work' do
|
33
31
|
it 'should raise NotImplementedError' do
|
34
|
-
expect {
|
32
|
+
expect { worker.work('message', 'key', {}) }.to \
|
35
33
|
raise_exception NotImplementedError
|
36
34
|
end
|
37
35
|
end
|
@@ -40,13 +38,13 @@ module Proletariat
|
|
40
38
|
context 'when message is provided' do
|
41
39
|
it 'should log the message directly' do
|
42
40
|
expect(logger).to receive(:info).with 'message to log'
|
43
|
-
|
41
|
+
worker.log 'message to log'
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
45
|
context 'when no message is provided' do
|
48
46
|
it 'should return the logger instance' do
|
49
|
-
expect(
|
47
|
+
expect(worker.log).to eq logger
|
50
48
|
end
|
51
49
|
end
|
52
50
|
end
|
@@ -54,12 +52,12 @@ module Proletariat
|
|
54
52
|
describe '#publish' do
|
55
53
|
it 'should forward the message to the publisher' do
|
56
54
|
expect(Proletariat).to receive(:publish).with('topic', 'message', {})
|
57
|
-
|
55
|
+
worker.publish 'topic', 'message', {}
|
58
56
|
end
|
59
57
|
|
60
58
|
it 'should have a blank default message' do
|
61
59
|
expect(Proletariat).to receive(:publish).with('topic', '', {})
|
62
|
-
|
60
|
+
worker.publish 'topic'
|
63
61
|
end
|
64
62
|
end
|
65
63
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: proletariat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Edwards
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 3.
|
19
|
+
version: 3.1.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 3.
|
26
|
+
version: 3.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubocop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: '0.7'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: '0.7'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bunny
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.3
|
61
|
+
version: 1.6.3
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.3
|
68
|
+
version: 1.6.3
|
69
69
|
description: Lightweight background processing powered by RabbitMQ
|
70
70
|
email:
|
71
71
|
- me@sebastianedwards.co.nz
|
@@ -84,10 +84,15 @@ files:
|
|
84
84
|
- lib/proletariat.rb
|
85
85
|
- lib/proletariat/concerns/logging.rb
|
86
86
|
- lib/proletariat/concurrency/actor.rb
|
87
|
-
- lib/proletariat/concurrency/
|
87
|
+
- lib/proletariat/concurrency/actor_common.rb
|
88
|
+
- lib/proletariat/concurrency/poolable_actor.rb
|
88
89
|
- lib/proletariat/configuration.rb
|
89
90
|
- lib/proletariat/cucumber.rb
|
91
|
+
- lib/proletariat/exception_handler.rb
|
92
|
+
- lib/proletariat/exception_handler/drop.rb
|
93
|
+
- lib/proletariat/exception_handler/exponential_backoff.rb
|
90
94
|
- lib/proletariat/manager.rb
|
95
|
+
- lib/proletariat/message.rb
|
91
96
|
- lib/proletariat/publisher.rb
|
92
97
|
- lib/proletariat/queue_config.rb
|
93
98
|
- lib/proletariat/runner.rb
|
@@ -103,7 +108,6 @@ files:
|
|
103
108
|
- proletariat.gemspec
|
104
109
|
- spec/lib/configuration_spec.rb
|
105
110
|
- spec/lib/proletariat_spec.rb
|
106
|
-
- spec/lib/publisher_spec.rb
|
107
111
|
- spec/lib/queue_config_spec.rb
|
108
112
|
- spec/lib/testing/expectation_guarantor_spec.rb
|
109
113
|
- spec/lib/testing/expectation_spec.rb
|
@@ -128,14 +132,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
132
|
version: '0'
|
129
133
|
requirements: []
|
130
134
|
rubyforge_project:
|
131
|
-
rubygems_version: 2.2
|
135
|
+
rubygems_version: 2.4.2
|
132
136
|
signing_key:
|
133
137
|
specification_version: 4
|
134
138
|
summary: Lightweight background processing powered by RabbitMQ
|
135
139
|
test_files:
|
136
140
|
- spec/lib/configuration_spec.rb
|
137
141
|
- spec/lib/proletariat_spec.rb
|
138
|
-
- spec/lib/publisher_spec.rb
|
139
142
|
- spec/lib/queue_config_spec.rb
|
140
143
|
- spec/lib/testing/expectation_guarantor_spec.rb
|
141
144
|
- spec/lib/testing/expectation_spec.rb
|