freddy 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5c84cfbe692660ec399eb036e8e83fcc697cb8e8
4
- data.tar.gz: 485a80604842982f1232a11586f53f45df824828
3
+ metadata.gz: 787721a42efa35a8721680b14d607b17f6b77e84
4
+ data.tar.gz: e12ccb2fa37c6fcd85bdb32abee295f862884c1f
5
5
  SHA512:
6
- metadata.gz: da4cdbe93fc7c1371697689dc5394b125593d2c72c8abcf2380f6adaf4fae78e93d087fdece9cbcda9adde4243a211fe7b6fead6304619632b3cf784f7084813
7
- data.tar.gz: 715807ff88027841c78f2d2ca1c89645f8aad845a245855a8d0911adad07d1f3e45e430c318f61d81832fdf4c0353c9137186c922820d7d01e8f2449ae807cc3
6
+ metadata.gz: 23aca1f868ea2871d1b2056d52a9f8264168c7ce7d8d2890ec8d2d51edcbe580aad35f908bd320aa8c6f4f41f05386613e28da71cd5bda4b3cb7586dea43d338
7
+ data.tar.gz: 18bc2c87a793eb9521112882a6f5353fb9fd467c9079bb5e44ac82696a19f138566bf4f880fef006bf48a0374460ece4c42c0af63a85673cc5fee1fc94ec67f8
@@ -1,16 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- freddy (0.3.7)
4
+ freddy (0.4.0)
5
5
  bunny (= 1.6.3)
6
6
  hamster (~> 1.0.1.pre.rc3)
7
7
  symbolizer
8
+ thread (~> 0.2)
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
11
12
  specs:
12
13
  amq-protocol (1.9.2)
13
- atomic (1.1.16)
14
+ atomic (1.1.99)
14
15
  bunny (1.6.3)
15
16
  amq-protocol (>= 1.9.2)
16
17
  coderay (1.1.0)
@@ -37,6 +38,7 @@ GEM
37
38
  rspec-support (3.1.2)
38
39
  slop (3.6.0)
39
40
  symbolizer (0.0.1)
41
+ thread (0.2.2)
40
42
 
41
43
  PLATFORMS
42
44
  ruby
data/README.md CHANGED
@@ -144,10 +144,20 @@ responder_handler.destroy_destination
144
144
  ## Notes about concurrency
145
145
 
146
146
  The underlying bunny implementation uses 1 responder thread by default. This means that if there is a time-consuming process or a sleep call in a responder then other responders will not receive messages concurrently.
147
+ To resolve this problem *freddy* uses a thread pool for running concurrent responders.
148
+ The thread pool is shared between *tap_into* and *respond_to* callbacks and the default size is 4.
149
+ The thread pool size can be configured by passing the configuration option *max_concurrency*.
147
150
 
148
- This is especially devious when using `deliver_with_response` in a responder because `deliver_with_response` creates a new anonymous responder which will not receive the response if the parent responder uses a sleep call.
149
151
 
150
- To resolve this problem *freddy* creates separate threads for processing. Read more from <http://rubybunny.info/articles/concurrency.html>.
152
+ Note that while it is possible to use *deliver_with_response* inside a *respond_to* block,
153
+ it is not possible to use another *respond_to* block inside a different *respond_to* block.
154
+
155
+
156
+ Note also that other configuration options for freddy users
157
+ such as pool sizes for DB connections need to match or exceed *max_concurrency*
158
+ to avoid running out of resources.
159
+
160
+ Read more from <http://rubybunny.info/articles/concurrency.html>.
151
161
 
152
162
  ## Credits
153
163
 
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "freddy"
7
- spec.version = '0.3.7'
7
+ spec.version = '0.4.0'
8
8
  spec.authors = ["Urmas Talimaa"]
9
9
  spec.email = ["urmas.talimaa@gmail.com"]
10
10
  spec.description = %q{Messaging API}
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_dependency "bunny", "1.6.3"
23
23
  spec.add_dependency "symbolizer"
24
24
  spec.add_dependency "hamster", "~> 1.0.1.pre.rc3"
25
+ spec.add_dependency "thread", "~> 0.2"
25
26
  end
@@ -1,6 +1,7 @@
1
1
  require 'bunny'
2
2
  require 'json'
3
3
  require 'symbolizer'
4
+ require 'thread/pool'
4
5
 
5
6
  require_relative 'freddy/consumer'
6
7
  require_relative 'freddy/producer'
@@ -68,16 +69,17 @@ class Freddy
68
69
  bunny.start
69
70
 
70
71
  channel = bunny.create_channel
71
- new(channel, logger)
72
+ new(channel, logger, bunny_config.fetch(:max_concurrency, 4))
72
73
  end
73
74
 
74
75
  attr_reader :channel, :consumer, :producer, :request
75
76
 
76
- def initialize(channel, logger)
77
+ def initialize(channel, logger, max_concurrency)
77
78
  @channel = channel
78
- @consumer = Consumer.new channel, logger
79
+ @consume_thread_pool = Thread.pool(max_concurrency)
80
+ @consumer = Consumer.new channel, logger, @consume_thread_pool
79
81
  @producer = Producer.new channel, logger
80
- @request = Request.new channel, logger
82
+ @request = Request.new channel, logger, @producer, @consumer
81
83
  end
82
84
 
83
85
  def respond_to(destination, &callback)
@@ -9,9 +9,11 @@ class Freddy
9
9
  class EmptyConsumer < Exception
10
10
  end
11
11
 
12
- def initialize(channel, logger)
12
+ def initialize(channel, logger, consume_thread_pool)
13
13
  @channel, @logger = channel, logger
14
14
  @topic_exchange = @channel.topic Freddy::FREDDY_TOPIC_EXCHANGE_NAME
15
+ @consume_thread_pool = consume_thread_pool
16
+ @dedicated_thread_pool = Thread.pool(1) # used only internally
15
17
  end
16
18
 
17
19
  def consume(destination, options = {}, &block)
@@ -20,21 +22,17 @@ class Freddy
20
22
  end
21
23
 
22
24
  def consume_from_queue(queue, options = {}, &block)
23
- consumer = queue.subscribe options do |delivery_info, properties, payload|
24
- Thread.new do
25
- parsed_payload = parse_payload(payload)
26
- log_receive_event(queue.name, parsed_payload)
27
- block.call parsed_payload, Delivery.new(delivery_info, properties)
28
- end
29
- end
30
- @logger.debug "Consuming messages on #{queue.name}"
31
- ResponderHandler.new consumer, @channel
25
+ consume_using_pool(queue, options, @consume_thread_pool, &block)
26
+ end
27
+
28
+ def dedicated_consume(queue, &block)
29
+ consume_using_pool(queue, {}, @dedicated_thread_pool, &block)
32
30
  end
33
31
 
34
32
  def tap_into(pattern, &block)
35
33
  queue = @channel.queue("", exclusive: true).bind(@topic_exchange, routing_key: pattern)
36
34
  consumer = queue.subscribe do |delivery_info, properties, payload|
37
- Thread.new do
35
+ @consume_thread_pool.process do
38
36
  block.call parse_payload(payload), delivery_info.routing_key
39
37
  end
40
38
  end
@@ -44,6 +42,18 @@ class Freddy
44
42
 
45
43
  private
46
44
 
45
+ def consume_using_pool(queue, options, pool, &block)
46
+ consumer = queue.subscribe options do |delivery_info, properties, payload|
47
+ pool.process do
48
+ parsed_payload = parse_payload(payload)
49
+ log_receive_event(queue.name, parsed_payload)
50
+ block.call parsed_payload, Delivery.new(delivery_info, properties)
51
+ end
52
+ end
53
+ @logger.debug "Consuming messages on #{queue.name}"
54
+ ResponderHandler.new consumer, @channel
55
+ end
56
+
47
57
  def parse_payload(payload)
48
58
  if payload == 'null'
49
59
  {}
@@ -17,9 +17,9 @@ class Freddy
17
17
  class EmptyResponder < Exception
18
18
  end
19
19
 
20
- def initialize(channel, logger)
20
+ def initialize(channel, logger, producer, consumer)
21
21
  @channel, @logger = channel, logger
22
- @producer, @consumer = Producer.new(channel, logger), Consumer.new(channel, logger)
22
+ @producer, @consumer = producer, consumer
23
23
  @request_map = Hamster.mutable_hash
24
24
  @request_manager = RequestManager.new @request_map, @logger
25
25
 
@@ -115,7 +115,7 @@ class Freddy
115
115
  else
116
116
  ensure_response_queue_exists
117
117
  @request_manager.start
118
- @consumer.consume_from_queue @response_queue do |payload, delivery|
118
+ @consumer.dedicated_consume @response_queue do |payload, delivery|
119
119
  handle_response payload, delivery
120
120
  end
121
121
  @listening_for_responses = true
@@ -1,34 +1,36 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'Concurrency' do
4
- let(:freddy) { Freddy.build(logger, config) }
4
+ let(:freddy1) { Freddy.build(logger, config) }
5
+ let(:freddy2) { Freddy.build(logger, config) }
6
+ let(:freddy3) { Freddy.build(logger, config) }
5
7
 
6
- it 'supports nested calls in #respond_to' do
7
- freddy.respond_to 'Concurrency1' do |payload, msg_handler|
8
+ it 'supports multiple requests in #respond_to' do
9
+ freddy1.respond_to 'Concurrency1' do |payload, msg_handler|
8
10
  begin
9
- result = freddy.deliver_with_response 'Concurrency2', msg: 'noop'
10
- msg_handler.success(result)
11
+ freddy1.deliver_with_response 'Concurrency2', msg: 'noop'
12
+ result2 = freddy1.deliver_with_response 'Concurrency3', msg: 'noop'
13
+ msg_handler.success(result2)
11
14
  rescue Freddy::InvalidRequestError => e
12
15
  msg_handler.error(e.response)
13
16
  end
14
17
  end
15
18
 
16
- freddy.respond_to 'Concurrency2' do |payload, msg_handler|
19
+ freddy2.respond_to 'Concurrency2' do |payload, msg_handler|
17
20
  begin
18
- result = freddy.deliver_with_response 'Concurrency3', msg: 'noop'
19
- msg_handler.success(result)
21
+ msg_handler.success({from: 'Concurrency2'})
20
22
  rescue Freddy::InvalidRequestError => e
21
23
  msg_handler.error(e.response)
22
24
  end
23
25
  end
24
26
 
25
- freddy.respond_to 'Concurrency3' do |payload, msg_handler|
27
+ freddy3.respond_to 'Concurrency3' do |payload, msg_handler|
26
28
  msg_handler.success({from: 'Concurrency3'})
27
29
  end
28
30
 
29
31
  result =
30
32
  begin
31
- freddy.deliver_with_response 'Concurrency1', msg: 'noop'
33
+ freddy1.deliver_with_response 'Concurrency1', msg: 'noop'
32
34
  rescue Freddy::InvalidRequestError => e
33
35
  e.response
34
36
  end
@@ -40,18 +42,18 @@ describe 'Concurrency' do
40
42
  received1 = false
41
43
  received2 = false
42
44
 
43
- freddy.tap_into 'concurrency.*.queue1' do
44
- result = freddy.deliver_with_response 'TapConcurrency', msg: 'noop'
45
+ freddy1.tap_into 'concurrency.*.queue1' do
46
+ result = freddy1.deliver_with_response 'TapConcurrency', msg: 'noop'
45
47
  expect(result).to eq(from: 'TapConcurrency')
46
48
  received1 = true
47
49
  end
48
50
 
49
- freddy.respond_to 'TapConcurrency' do |payload, msg_handler|
51
+ freddy2.respond_to 'TapConcurrency' do |payload, msg_handler|
50
52
  msg_handler.success({from: 'TapConcurrency'})
51
53
  received2 = true
52
54
  end
53
55
 
54
- freddy.deliver 'concurrency.q.queue1', msg: 'noop'
56
+ freddy1.deliver 'concurrency.q.queue1', msg: 'noop'
55
57
 
56
58
  wait_for { received1 && received2 }
57
59
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freddy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Urmas Talimaa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-05 00:00:00.000000000 Z
11
+ date: 2015-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 1.0.1.pre.rc3
83
+ - !ruby/object:Gem::Dependency
84
+ name: thread
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.2'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.2'
83
97
  description: Messaging API
84
98
  email:
85
99
  - urmas.talimaa@gmail.com
@@ -138,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
152
  version: '0'
139
153
  requirements: []
140
154
  rubyforge_project:
141
- rubygems_version: 2.2.2
155
+ rubygems_version: 2.4.8
142
156
  signing_key:
143
157
  specification_version: 4
144
158
  summary: API for inter-application messaging supporting acknowledgements and request-response