freddy-jruby 0.6.0 → 0.7.2
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 +1 -0
- data/README.md +1 -4
- data/freddy.gemspec +5 -6
- data/lib/freddy.rb +27 -12
- data/lib/freddy/adapters.rb +4 -0
- data/lib/freddy/adapters/bunny_adapter.rb +11 -6
- data/lib/freddy/adapters/march_hare_adapter.rb +11 -6
- data/lib/freddy/consumers/respond_to_consumer.rb +20 -9
- data/lib/freddy/consumers/response_consumer.rb +6 -10
- data/lib/freddy/consumers/tap_into_consumer.rb +30 -12
- data/lib/freddy/delivery.rb +3 -2
- data/lib/freddy/producers/send_and_wait_response_producer.rb +1 -1
- data/lib/freddy/request_manager.rb +11 -3
- data/lib/freddy/sync_response_container.rb +0 -1
- data/spec/freddy/consumers/respond_to_consumer_spec.rb +70 -10
- data/spec/freddy/responder_handler_spec.rb +2 -2
- data/spec/integration/concurrency_spec.rb +2 -1
- data/spec/integration/tap_into_with_group_spec.rb +24 -0
- data/spec/spec_helper.rb +4 -1
- metadata +9 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d8614a6a2bc844445d26bf035b0ecdbd5c2e12d
|
4
|
+
data.tar.gz: c64f8114a61bd258bef376c4927110b338041d44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 648e9031be023cab9fdce260b27925331444c8ca35d4dd8321d06df494f3f0f5730f0df68b54e1f9fcf667691ef311c669260da35b99bab2f9d408aa885bba67
|
7
|
+
data.tar.gz: a1d06a5f8361ea94c18b698a035bf643bc9858b4541de331bad0a670b2f17051c458bb1383d877c8afbcabe2a78c9f0502c9d279a7dc7b080c7113268c816d68
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -138,10 +138,7 @@ responder_handler.shutdown
|
|
138
138
|
|
139
139
|
## Notes about concurrency
|
140
140
|
|
141
|
-
The
|
142
|
-
To resolve this problem *freddy* uses a thread pool for running concurrent responders.
|
143
|
-
The thread pool is shared between *tap_into* and *respond_to* callbacks and the default size is 4.
|
144
|
-
The thread pool size can be configured by passing the configuration option *max_concurrency*.
|
141
|
+
*freddy* uses a thread pool to run concurrent responders. The thread pool is unique for each *tap_into* and *respond_to* responder. Thread pool size can be configured by passing the configuration option *max_concurrency*. Its default value is 4. e.g. If your application has 2 *respond_to* responders and 1 *tap_into* responder with *max_concurrency* set to 3 then your application may process up to 9 messages in parallel.
|
145
142
|
|
146
143
|
|
147
144
|
Note that while it is possible to use *deliver_with_response* inside a *respond_to* block,
|
data/freddy.gemspec
CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
|
|
8
8
|
else
|
9
9
|
spec.name = "freddy"
|
10
10
|
end
|
11
|
-
spec.version = '0.
|
12
|
-
spec.authors = ["
|
13
|
-
spec.email = ["
|
11
|
+
spec.version = '0.7.2'
|
12
|
+
spec.authors = ["Salemove TechMovers"]
|
13
|
+
spec.email = ["techmovers@salemove.com"]
|
14
14
|
spec.description = %q{Messaging API}
|
15
15
|
spec.summary = %q{API for inter-application messaging supporting acknowledgements and request-response}
|
16
16
|
spec.license = "Private"
|
@@ -27,10 +27,9 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_dependency 'march_hare', '~> 2.12.0'
|
28
28
|
spec.add_dependency 'symbolizer'
|
29
29
|
else
|
30
|
-
spec.add_dependency "bunny", "~> 2.
|
30
|
+
spec.add_dependency "bunny", "~> 2.6"
|
31
31
|
spec.add_dependency "oj", "~> 2.13"
|
32
32
|
end
|
33
33
|
|
34
|
-
spec.add_dependency "
|
35
|
-
spec.add_dependency "thread", "~> 0.2"
|
34
|
+
spec.add_dependency "thread", "~> 0.1"
|
36
35
|
end
|
data/lib/freddy.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'thread/pool'
|
3
|
-
require '
|
3
|
+
require 'securerandom'
|
4
4
|
|
5
5
|
Dir[File.dirname(__FILE__) + '/freddy/*.rb'].each(&method(:require))
|
6
6
|
|
@@ -18,6 +18,7 @@ class Freddy
|
|
18
18
|
# @option config [Integer] :port (5672)
|
19
19
|
# @option config [String] :user ('guest')
|
20
20
|
# @option config [String] :pass ('guest')
|
21
|
+
# @option config [Integer] :max_concurrency (4)
|
21
22
|
#
|
22
23
|
# @return [Freddy]
|
23
24
|
#
|
@@ -25,17 +26,14 @@ class Freddy
|
|
25
26
|
# Freddy.build(Logger.new(STDOUT), user: 'thumper', pass: 'howdy')
|
26
27
|
def self.build(logger = Logger.new(STDOUT), max_concurrency: DEFAULT_MAX_CONCURRENCY, **config)
|
27
28
|
connection = Adapters.determine.connect(config)
|
28
|
-
consume_thread_pool = Thread.pool(max_concurrency)
|
29
29
|
|
30
|
-
new(connection, logger,
|
30
|
+
new(connection, logger, max_concurrency)
|
31
31
|
end
|
32
32
|
|
33
|
-
def initialize(connection, logger,
|
33
|
+
def initialize(connection, logger, max_concurrency)
|
34
34
|
@connection = connection
|
35
35
|
@logger = logger
|
36
|
-
|
37
|
-
@tap_into_consumer = Consumers::TapIntoConsumer.new(consume_thread_pool, @logger)
|
38
|
-
@respond_to_consumer = Consumers::RespondToConsumer.new(consume_thread_pool, @logger)
|
36
|
+
@prefetch_buffer_size = max_concurrency
|
39
37
|
|
40
38
|
@send_and_forget_producer = Producers::SendAndForgetProducer.new(
|
41
39
|
connection.create_channel, logger
|
@@ -73,12 +71,17 @@ class Freddy
|
|
73
71
|
def respond_to(destination, &callback)
|
74
72
|
@logger.info "Listening for requests on #{destination}"
|
75
73
|
|
76
|
-
channel = @connection.create_channel
|
74
|
+
channel = @connection.create_channel(prefetch: @prefetch_buffer_size)
|
77
75
|
producer = Producers::ReplyProducer.new(channel, @logger)
|
78
76
|
handler_adapter_factory = MessageHandlerAdapters::Factory.new(producer)
|
79
77
|
|
80
|
-
|
81
|
-
|
78
|
+
Consumers::RespondToConsumer.consume(
|
79
|
+
logger: @logger,
|
80
|
+
thread_pool: Thread.pool(@prefetch_buffer_size),
|
81
|
+
destination: destination,
|
82
|
+
channel: channel,
|
83
|
+
handler_adapter_factory: handler_adapter_factory,
|
84
|
+
&callback
|
82
85
|
)
|
83
86
|
end
|
84
87
|
|
@@ -91,6 +94,10 @@ class Freddy
|
|
91
94
|
# @param [String] pattern
|
92
95
|
# the destination pattern. Use `#` wildcard for matching 0 or more words.
|
93
96
|
# Use `*` to match exactly one word.
|
97
|
+
# @param [Hash] options
|
98
|
+
# @option options [String] :group
|
99
|
+
# only one of the listeners in given group will receive a message. All
|
100
|
+
# listeners will receive a message if the group is not specified.
|
94
101
|
#
|
95
102
|
# @yield [message] Yields received message to the block
|
96
103
|
#
|
@@ -100,9 +107,17 @@ class Freddy
|
|
100
107
|
# freddy.tap_into 'notifications.*' do |message|
|
101
108
|
# puts "Notification showed #{message.inspect}"
|
102
109
|
# end
|
103
|
-
def tap_into(pattern, &callback)
|
110
|
+
def tap_into(pattern, options = {}, &callback)
|
104
111
|
@logger.debug "Tapping into messages that match #{pattern}"
|
105
|
-
|
112
|
+
|
113
|
+
Consumers::TapIntoConsumer.consume(
|
114
|
+
logger: @logger,
|
115
|
+
thread_pool: Thread.pool(@prefetch_buffer_size),
|
116
|
+
pattern: pattern,
|
117
|
+
channel: @connection.create_channel(prefetch: @prefetch_buffer_size),
|
118
|
+
options: options,
|
119
|
+
&callback
|
120
|
+
)
|
106
121
|
end
|
107
122
|
|
108
123
|
# Sends a message to given destination
|
data/lib/freddy/adapters.rb
CHANGED
@@ -13,8 +13,10 @@ class Freddy
|
|
13
13
|
@bunny = bunny
|
14
14
|
end
|
15
15
|
|
16
|
-
def create_channel
|
17
|
-
|
16
|
+
def create_channel(prefetch: nil)
|
17
|
+
bunny_channel = @bunny.create_channel
|
18
|
+
bunny_channel.prefetch(prefetch) if prefetch
|
19
|
+
Channel.new(bunny_channel)
|
18
20
|
end
|
19
21
|
|
20
22
|
def close
|
@@ -30,7 +32,7 @@ class Freddy
|
|
30
32
|
@channel = channel
|
31
33
|
end
|
32
34
|
|
33
|
-
def_delegators :@channel, :topic, :default_exchange, :consumers
|
35
|
+
def_delegators :@channel, :topic, :default_exchange, :consumers, :acknowledge
|
34
36
|
|
35
37
|
def queue(*args)
|
36
38
|
Queue.new(@channel.queue(*args))
|
@@ -46,10 +48,13 @@ class Freddy
|
|
46
48
|
end
|
47
49
|
|
48
50
|
class Queue < Shared::Queue
|
49
|
-
def subscribe(&block)
|
50
|
-
@queue.subscribe do |info, properties, payload|
|
51
|
+
def subscribe(manual_ack: false, &block)
|
52
|
+
@queue.subscribe(manual_ack: manual_ack) do |info, properties, payload|
|
51
53
|
parsed_payload = Payload.parse(payload)
|
52
|
-
|
54
|
+
delivery = Delivery.new(
|
55
|
+
parsed_payload, properties, info.routing_key, info.delivery_tag
|
56
|
+
)
|
57
|
+
block.call(delivery)
|
53
58
|
end
|
54
59
|
end
|
55
60
|
end
|
@@ -12,8 +12,10 @@ class Freddy
|
|
12
12
|
@hare = hare
|
13
13
|
end
|
14
14
|
|
15
|
-
def create_channel
|
16
|
-
|
15
|
+
def create_channel(prefetch: nil)
|
16
|
+
hare_channel = @hare.create_channel
|
17
|
+
hare_channel.basic_qos(prefetch) if prefetch
|
18
|
+
Channel.new(hare_channel)
|
17
19
|
end
|
18
20
|
|
19
21
|
def close
|
@@ -29,7 +31,7 @@ class Freddy
|
|
29
31
|
@channel = channel
|
30
32
|
end
|
31
33
|
|
32
|
-
def_delegators :@channel, :topic, :default_exchange, :consumers
|
34
|
+
def_delegators :@channel, :topic, :default_exchange, :consumers, :acknowledge
|
33
35
|
|
34
36
|
def queue(*args)
|
35
37
|
Queue.new(@channel.queue(*args))
|
@@ -45,10 +47,13 @@ class Freddy
|
|
45
47
|
end
|
46
48
|
|
47
49
|
class Queue < Shared::Queue
|
48
|
-
def subscribe(&block)
|
49
|
-
@queue.subscribe do |meta, payload|
|
50
|
+
def subscribe(manual_ack: false, &block)
|
51
|
+
@queue.subscribe(manual_ack: manual_ack) do |meta, payload|
|
50
52
|
parsed_payload = Payload.parse(payload)
|
51
|
-
|
53
|
+
delivery = Delivery.new(
|
54
|
+
parsed_payload, meta, meta.routing_key, meta.delivery_tag
|
55
|
+
)
|
56
|
+
block.call(delivery)
|
52
57
|
end
|
53
58
|
end
|
54
59
|
end
|
@@ -1,16 +1,23 @@
|
|
1
1
|
class Freddy
|
2
2
|
module Consumers
|
3
3
|
class RespondToConsumer
|
4
|
-
def
|
5
|
-
|
4
|
+
def self.consume(*attrs, &block)
|
5
|
+
new(*attrs).consume(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(logger:, thread_pool:, destination:, channel:, handler_adapter_factory:)
|
6
9
|
@logger = logger
|
10
|
+
@consume_thread_pool = thread_pool
|
11
|
+
@destination = destination
|
12
|
+
@channel = channel
|
13
|
+
@handler_adapter_factory = handler_adapter_factory
|
7
14
|
end
|
8
15
|
|
9
|
-
def consume(
|
10
|
-
consumer = consume_from_destination
|
11
|
-
Consumers.log_receive_event(@logger, destination, delivery)
|
16
|
+
def consume(&block)
|
17
|
+
consumer = consume_from_destination do |delivery|
|
18
|
+
Consumers.log_receive_event(@logger, @destination, delivery)
|
12
19
|
|
13
|
-
adapter = handler_adapter_factory.for(delivery)
|
20
|
+
adapter = @handler_adapter_factory.for(delivery)
|
14
21
|
|
15
22
|
msg_handler = MessageHandler.new(adapter, delivery)
|
16
23
|
block.call(delivery.payload, msg_handler)
|
@@ -21,15 +28,19 @@ class Freddy
|
|
21
28
|
|
22
29
|
private
|
23
30
|
|
24
|
-
def consume_from_destination(
|
25
|
-
channel.queue(destination).subscribe do |delivery|
|
31
|
+
def consume_from_destination(&block)
|
32
|
+
@channel.queue(@destination).subscribe(manual_ack: true) do |delivery|
|
26
33
|
process_message(delivery, &block)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
30
37
|
def process_message(delivery, &block)
|
31
38
|
@consume_thread_pool.process do
|
32
|
-
|
39
|
+
begin
|
40
|
+
block.call(delivery)
|
41
|
+
ensure
|
42
|
+
@channel.acknowledge(delivery.tag, false)
|
43
|
+
end
|
33
44
|
end
|
34
45
|
end
|
35
46
|
end
|
@@ -3,24 +3,20 @@ class Freddy
|
|
3
3
|
class ResponseConsumer
|
4
4
|
def initialize(logger)
|
5
5
|
@logger = logger
|
6
|
-
@dedicated_thread_pool = Thread.pool(1)
|
7
6
|
end
|
8
7
|
|
9
|
-
def consume(queue, &block)
|
8
|
+
def consume(channel, queue, &block)
|
10
9
|
@logger.debug "Consuming messages on #{queue.name}"
|
11
|
-
|
12
|
-
process_message(queue, delivery, &block)
|
10
|
+
queue.subscribe do |delivery|
|
11
|
+
process_message(channel, queue, delivery, &block)
|
13
12
|
end
|
14
|
-
ResponderHandler.new(consumer, @dedicated_thread_pool)
|
15
13
|
end
|
16
14
|
|
17
15
|
private
|
18
16
|
|
19
|
-
def process_message(queue, delivery, &block)
|
20
|
-
@
|
21
|
-
|
22
|
-
block.call(delivery)
|
23
|
-
end
|
17
|
+
def process_message(channel, queue, delivery, &block)
|
18
|
+
Consumers.log_receive_event(@logger, queue.name, delivery)
|
19
|
+
block.call(delivery)
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|
@@ -1,15 +1,22 @@
|
|
1
1
|
class Freddy
|
2
2
|
module Consumers
|
3
3
|
class TapIntoConsumer
|
4
|
-
def
|
4
|
+
def self.consume(*attrs, &block)
|
5
|
+
new(*attrs).consume(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(logger:, thread_pool:, pattern:, channel:, options:)
|
5
9
|
@logger = logger
|
6
|
-
@consume_thread_pool =
|
10
|
+
@consume_thread_pool = thread_pool
|
11
|
+
@pattern = pattern
|
12
|
+
@channel = channel
|
13
|
+
@options = options
|
7
14
|
end
|
8
15
|
|
9
|
-
def consume(
|
10
|
-
queue = create_queue
|
16
|
+
def consume(&block)
|
17
|
+
queue = create_queue
|
11
18
|
|
12
|
-
consumer = queue.subscribe do |delivery|
|
19
|
+
consumer = queue.subscribe(manual_ack: true) do |delivery|
|
13
20
|
process_message(queue, delivery, &block)
|
14
21
|
end
|
15
22
|
|
@@ -18,18 +25,29 @@ class Freddy
|
|
18
25
|
|
19
26
|
private
|
20
27
|
|
21
|
-
def create_queue
|
22
|
-
topic_exchange = channel.topic(Freddy::FREDDY_TOPIC_EXCHANGE_NAME)
|
28
|
+
def create_queue
|
29
|
+
topic_exchange = @channel.topic(Freddy::FREDDY_TOPIC_EXCHANGE_NAME)
|
30
|
+
group = @options.fetch(:group, nil)
|
23
31
|
|
24
|
-
|
25
|
-
|
26
|
-
|
32
|
+
if group
|
33
|
+
@channel
|
34
|
+
.queue("groups.#{group}")
|
35
|
+
.bind(topic_exchange, routing_key: @pattern)
|
36
|
+
else
|
37
|
+
@channel
|
38
|
+
.queue('', exclusive: true)
|
39
|
+
.bind(topic_exchange, routing_key: @pattern)
|
40
|
+
end
|
27
41
|
end
|
28
42
|
|
29
43
|
def process_message(queue, delivery, &block)
|
30
44
|
@consume_thread_pool.process do
|
31
|
-
|
32
|
-
|
45
|
+
begin
|
46
|
+
Consumers.log_receive_event(@logger, queue.name, delivery)
|
47
|
+
block.call delivery.payload, delivery.routing_key
|
48
|
+
ensure
|
49
|
+
@channel.acknowledge(delivery.tag, false)
|
50
|
+
end
|
33
51
|
end
|
34
52
|
end
|
35
53
|
end
|
data/lib/freddy/delivery.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
class Freddy
|
2
2
|
class Delivery
|
3
|
-
attr_reader :routing_key, :payload
|
3
|
+
attr_reader :routing_key, :payload, :tag
|
4
4
|
|
5
|
-
def initialize(payload, metadata, routing_key)
|
5
|
+
def initialize(payload, metadata, routing_key, tag)
|
6
6
|
@payload = payload
|
7
7
|
@metadata = metadata
|
8
8
|
@routing_key = routing_key
|
9
|
+
@tag = tag
|
9
10
|
end
|
10
11
|
|
11
12
|
def correlation_id
|
@@ -19,7 +19,7 @@ class Freddy
|
|
19
19
|
@response_queue = @channel.queue("", exclusive: true)
|
20
20
|
|
21
21
|
@response_consumer = Consumers::ResponseConsumer.new(@logger)
|
22
|
-
@response_consumer.consume(@response_queue, &method(:handle_response))
|
22
|
+
@response_consumer.consume(@channel, @response_queue, &method(:handle_response))
|
23
23
|
end
|
24
24
|
|
25
25
|
def produce(destination, payload, timeout_in_seconds:, delete_on_timeout:, **properties)
|
@@ -1,23 +1,31 @@
|
|
1
1
|
class Freddy
|
2
2
|
class RequestManager
|
3
3
|
def initialize(logger)
|
4
|
-
@requests =
|
4
|
+
@requests = ConcurrentHash.new
|
5
5
|
@logger = logger
|
6
6
|
end
|
7
7
|
|
8
8
|
def no_route(correlation_id)
|
9
9
|
if request = @requests[correlation_id]
|
10
|
-
|
10
|
+
delete(correlation_id)
|
11
11
|
request[:callback].call({error: 'Specified queue does not exist'}, nil)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
def store(correlation_id, opts)
|
16
|
-
@requests
|
16
|
+
@requests[correlation_id] = opts
|
17
17
|
end
|
18
18
|
|
19
19
|
def delete(correlation_id)
|
20
20
|
@requests.delete(correlation_id)
|
21
21
|
end
|
22
|
+
|
23
|
+
class ConcurrentHash < Hash
|
24
|
+
# CRuby hash does not need any locks. Only adding when using JRuby.
|
25
|
+
if RUBY_PLATFORM == 'java'
|
26
|
+
require 'jruby/synchronized'
|
27
|
+
include JRuby::Synchronized
|
28
|
+
end
|
29
|
+
end
|
22
30
|
end
|
23
31
|
end
|
@@ -1,24 +1,84 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Freddy::Consumers::RespondToConsumer do
|
4
|
-
let(:consumer)
|
4
|
+
let(:consumer) do
|
5
|
+
described_class.new(
|
6
|
+
logger: logger,
|
7
|
+
thread_pool: thread_pool,
|
8
|
+
destination: destination,
|
9
|
+
channel: channel,
|
10
|
+
handler_adapter_factory: msg_handler_adapter_factory
|
11
|
+
)
|
12
|
+
end
|
5
13
|
|
6
|
-
let(:connection)
|
7
|
-
let(:thread_pool) { Thread.pool(1) }
|
14
|
+
let(:connection) { Freddy::Adapters.determine.connect(config) }
|
8
15
|
let(:destination) { random_destination }
|
9
|
-
let(:payload)
|
10
|
-
let(:
|
16
|
+
let(:payload) { {pay: 'load'} }
|
17
|
+
let(:msg_handler_adapter_factory) { double(for: msg_handler_adapter) }
|
18
|
+
let(:msg_handler_adapter) { Freddy::MessageHandlerAdapters::NoOpHandler.new }
|
19
|
+
let(:prefetch_buffer_size) { 2 }
|
20
|
+
let(:thread_pool) { Thread.pool(prefetch_buffer_size) }
|
11
21
|
|
12
22
|
after do
|
13
23
|
connection.close
|
14
24
|
end
|
15
25
|
|
16
|
-
|
17
|
-
|
18
|
-
|
26
|
+
context 'when no messages' do
|
27
|
+
let(:channel) { connection.create_channel }
|
28
|
+
|
29
|
+
it "doesn't call passed block" do
|
30
|
+
consumer.consume do
|
31
|
+
@message_received = true
|
32
|
+
end
|
33
|
+
default_sleep
|
34
|
+
|
35
|
+
expect(@message_received).to be_falsy
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when thread pool is full' do
|
40
|
+
let(:prefetch_buffer_size) { 1 }
|
41
|
+
let(:msg_count) { prefetch_buffer_size + 1 }
|
42
|
+
let(:channel) { connection.create_channel(prefetch: prefetch_buffer_size) }
|
43
|
+
let(:mutex) { Mutex.new }
|
44
|
+
let(:consume_lock) { ConditionVariable.new }
|
45
|
+
let(:queue) { channel.queue(destination) }
|
46
|
+
|
47
|
+
after do
|
48
|
+
# Release the final queued message before finishing the test to avoid
|
49
|
+
# bunny warnings.
|
50
|
+
process_message
|
19
51
|
end
|
20
|
-
default_sleep
|
21
52
|
|
22
|
-
|
53
|
+
it 'does not consume more messages' do
|
54
|
+
consumer.consume do
|
55
|
+
wait_until_released
|
56
|
+
end
|
57
|
+
|
58
|
+
msg_count.times { deliver_message }
|
59
|
+
|
60
|
+
sleep default_sleep
|
61
|
+
expect(queue.message_count).to eq(msg_count - prefetch_buffer_size)
|
62
|
+
|
63
|
+
process_message
|
64
|
+
expect(queue.message_count).to eq(0)
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_message
|
68
|
+
release_consume_lock
|
69
|
+
sleep default_sleep
|
70
|
+
end
|
71
|
+
|
72
|
+
def deliver_message
|
73
|
+
channel.default_exchange.publish('{}', routing_key: destination)
|
74
|
+
end
|
75
|
+
|
76
|
+
def wait_until_released
|
77
|
+
mutex.synchronize { consume_lock.wait(mutex) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def release_consume_lock
|
81
|
+
mutex.synchronize { consume_lock.broadcast }
|
82
|
+
end
|
23
83
|
end
|
24
84
|
end
|
@@ -13,12 +13,12 @@ describe Freddy::ResponderHandler do
|
|
13
13
|
count = 0
|
14
14
|
|
15
15
|
consumer_handler = freddy.respond_to destination do
|
16
|
-
sleep 0.
|
16
|
+
sleep 0.3
|
17
17
|
count += 1
|
18
18
|
end
|
19
19
|
deliver
|
20
20
|
|
21
|
-
sleep 0.
|
21
|
+
sleep 0.15
|
22
22
|
consumer_handler.shutdown
|
23
23
|
|
24
24
|
expect(count).to eq(1)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'hamster/experimental/mutable_set'
|
2
3
|
|
3
4
|
describe 'Concurrency' do
|
4
5
|
let(:freddy1) { Freddy.build(logger, config) }
|
@@ -88,7 +89,7 @@ describe 'Concurrency' do
|
|
88
89
|
context 'concurrent executions of deliver_with_response' do
|
89
90
|
let(:nr_of_threads) { 50 }
|
90
91
|
let(:payload) { {pay: 'load'} }
|
91
|
-
let(:msg_counter) { Hamster
|
92
|
+
let(:msg_counter) { Hamster::MutableSet[] }
|
92
93
|
let(:queue_name) { random_destination }
|
93
94
|
|
94
95
|
before do
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hamster/experimental/mutable_set'
|
3
|
+
|
4
|
+
describe 'Tapping into with group identifier' do
|
5
|
+
let(:deliverer) { Freddy.build(logger, config) }
|
6
|
+
let(:responder1) { Freddy.build(logger, config) }
|
7
|
+
let(:responder2) { Freddy.build(logger, config) }
|
8
|
+
|
9
|
+
let(:destination) { random_destination }
|
10
|
+
|
11
|
+
after { [deliverer, responder1, responder2].each(&:close) }
|
12
|
+
|
13
|
+
it 'receives a message once' do
|
14
|
+
msg_counter = Hamster::MutableSet[]
|
15
|
+
|
16
|
+
group_id = arbitrary_id
|
17
|
+
responder1.tap_into(destination, group: group_id) {|msg| msg_counter << 'r1' }
|
18
|
+
responder2.tap_into(destination, group: group_id) {|msg| msg_counter << 'r2' }
|
19
|
+
deliverer.deliver(destination, {})
|
20
|
+
|
21
|
+
default_sleep
|
22
|
+
expect(msg_counter.count).to eq(1)
|
23
|
+
end
|
24
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -12,7 +12,6 @@ SimpleCov.start do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
require 'pry'
|
15
|
-
require 'securerandom'
|
16
15
|
require 'freddy'
|
17
16
|
require 'logger'
|
18
17
|
require 'hamster/experimental/mutable_set'
|
@@ -29,6 +28,10 @@ def random_destination
|
|
29
28
|
SecureRandom.hex
|
30
29
|
end
|
31
30
|
|
31
|
+
def arbitrary_id
|
32
|
+
SecureRandom.hex
|
33
|
+
end
|
34
|
+
|
32
35
|
def default_sleep
|
33
36
|
sleep 0.05
|
34
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freddy-jruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Salemove TechMovers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,21 +71,7 @@ dependencies:
|
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
75
|
-
name: hamster
|
76
|
-
prerelease: false
|
77
|
-
type: :runtime
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 1.0.1.pre.rc3
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
requirement: !ruby/object:Gem::Requirement
|
85
|
-
requirements:
|
86
|
-
- - "~>"
|
87
|
-
- !ruby/object:Gem::Version
|
88
|
-
version: '0.2'
|
74
|
+
version: '0.1'
|
89
75
|
name: thread
|
90
76
|
prerelease: false
|
91
77
|
type: :runtime
|
@@ -93,10 +79,10 @@ dependencies:
|
|
93
79
|
requirements:
|
94
80
|
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0.
|
82
|
+
version: '0.1'
|
97
83
|
description: Messaging API
|
98
84
|
email:
|
99
|
-
-
|
85
|
+
- techmovers@salemove.com
|
100
86
|
executables: []
|
101
87
|
extensions: []
|
102
88
|
extra_rdoc_files: []
|
@@ -143,6 +129,7 @@ files:
|
|
143
129
|
- spec/integration/concurrency_spec.rb
|
144
130
|
- spec/integration/logging_spec.rb
|
145
131
|
- spec/integration/reply_spec.rb
|
132
|
+
- spec/integration/tap_into_with_group_spec.rb
|
146
133
|
- spec/spec_helper.rb
|
147
134
|
homepage:
|
148
135
|
licenses:
|
@@ -164,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
151
|
version: '0'
|
165
152
|
requirements: []
|
166
153
|
rubyforge_project:
|
167
|
-
rubygems_version: 2.
|
154
|
+
rubygems_version: 2.6.8
|
168
155
|
signing_key:
|
169
156
|
specification_version: 4
|
170
157
|
summary: API for inter-application messaging supporting acknowledgements and request-response
|
@@ -178,4 +165,5 @@ test_files:
|
|
178
165
|
- spec/integration/concurrency_spec.rb
|
179
166
|
- spec/integration/logging_spec.rb
|
180
167
|
- spec/integration/reply_spec.rb
|
168
|
+
- spec/integration/tap_into_with_group_spec.rb
|
181
169
|
- spec/spec_helper.rb
|