freddy 0.3.7 → 0.4.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 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