freddy 0.4.4 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -8
- data/freddy.gemspec +1 -1
- data/lib/freddy.rb +79 -69
- data/lib/freddy/adapters.rb +13 -0
- data/lib/freddy/adapters/bunny_adapter.rb +66 -0
- data/lib/freddy/adapters/march_hare_adapter.rb +67 -0
- data/lib/freddy/consumer.rb +3 -3
- data/lib/freddy/error_response.rb +21 -0
- data/lib/freddy/invalid_request_error.rb +4 -0
- data/lib/freddy/message_handlers.rb +5 -5
- data/lib/freddy/producer.rb +0 -16
- data/lib/freddy/request.rb +5 -5
- data/lib/freddy/request_manager.rb +1 -1
- data/lib/freddy/responder_handler.rb +15 -12
- data/lib/freddy/sync_response_container.rb +12 -3
- data/lib/freddy/timeout_error.rb +4 -0
- data/lib/freddy/utils.rb +32 -0
- data/spec/freddy/responder_handler_spec.rb +42 -8
- data/spec/freddy/sync_response_container_spec.rb +13 -0
- metadata +11 -3
- data/lib/freddy/adaptive_queue.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c9e0020e6933bdc25c5845a3bad630dc56b4ae9
|
4
|
+
data.tar.gz: 55da31d069e4dd316260d37a0db00965a737039e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78b24b50ec3a7779572c1a45ce1b59d6ce345a058dda2c6781048b65a5312f210cb4595fc678450ea9a73dbf95951a902598def1e1bad3493071b4f903db1e5f
|
7
|
+
data.tar.gz: 7ee8fd7a118ef27a164becdf4bab34b977ba31dada15040233aff1807c56648d6b0dd1d58ce691a0bf3f563304d0f94a7201ac1b14c625a86b318b2d56a978e5
|
data/README.md
CHANGED
@@ -131,16 +131,9 @@ The following operations are supported:
|
|
131
131
|
|
132
132
|
* stop responding
|
133
133
|
```ruby
|
134
|
-
responder_handler.
|
134
|
+
responder_handler.shutdown
|
135
135
|
```
|
136
136
|
|
137
|
-
* delete the destination
|
138
|
-
```ruby
|
139
|
-
responder_handler.destroy_destination
|
140
|
-
```
|
141
|
-
|
142
|
-
* Primary use case is in tests to not leave dangling destinations. It deletes the destination even if there are responders for the same destination in other parts of the system. Use with caution in production code.
|
143
|
-
|
144
137
|
|
145
138
|
## Notes about concurrency
|
146
139
|
|
data/freddy.gemspec
CHANGED
data/lib/freddy.rb
CHANGED
@@ -1,83 +1,36 @@
|
|
1
|
-
if RUBY_PLATFORM == 'java'
|
2
|
-
require 'march_hare'
|
3
|
-
else
|
4
|
-
require 'bunny'
|
5
|
-
end
|
6
|
-
|
7
1
|
require 'json'
|
8
2
|
require 'thread/pool'
|
9
3
|
|
10
|
-
require_relative 'freddy/
|
4
|
+
require_relative 'freddy/adapters'
|
11
5
|
require_relative 'freddy/consumer'
|
12
6
|
require_relative 'freddy/producer'
|
13
7
|
require_relative 'freddy/request'
|
14
8
|
require_relative 'freddy/payload'
|
9
|
+
require_relative 'freddy/error_response'
|
10
|
+
require_relative 'freddy/invalid_request_error'
|
11
|
+
require_relative 'freddy/timeout_error'
|
12
|
+
require_relative 'freddy/utils'
|
15
13
|
|
16
14
|
class Freddy
|
17
|
-
class ErrorResponse < StandardError
|
18
|
-
DEFAULT_ERROR_MESSAGE = 'Use #response to get the error response'
|
19
|
-
|
20
|
-
attr_reader :response
|
21
|
-
|
22
|
-
def initialize(response)
|
23
|
-
@response = response
|
24
|
-
super(format_message(response) || DEFAULT_ERROR_MESSAGE)
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def format_message(response)
|
30
|
-
return unless response.is_a?(Hash)
|
31
|
-
|
32
|
-
message = [response[:error], response[:message]].compact.join(': ')
|
33
|
-
message.empty? ? nil : message
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class InvalidRequestError < ErrorResponse
|
38
|
-
end
|
39
|
-
|
40
|
-
class TimeoutError < ErrorResponse
|
41
|
-
end
|
42
|
-
|
43
15
|
FREDDY_TOPIC_EXCHANGE_NAME = 'freddy-topic'.freeze
|
44
16
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
parameters: parameters
|
63
|
-
})
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.notify_exception(exception, parameters={})
|
68
|
-
if defined? Airbrake
|
69
|
-
Airbrake.notify_or_ignore(exception, cgi_data: ENV.to_hash, parameters: parameters)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.build(logger = Logger.new(STDOUT), config)
|
74
|
-
if RUBY_PLATFORM == 'java'
|
75
|
-
connection = MarchHare.connect(config)
|
76
|
-
else
|
77
|
-
connection = Bunny.new(config)
|
78
|
-
connection.start
|
79
|
-
connection
|
80
|
-
end
|
17
|
+
# Creates a new freddy instance
|
18
|
+
#
|
19
|
+
# @param [Logger] logger
|
20
|
+
# instance of a logger, defaults to the STDOUT logger
|
21
|
+
# @param [Hash] config
|
22
|
+
# rabbitmq connection information
|
23
|
+
# @option config [String] :host ('localhost')
|
24
|
+
# @option config [Integer] :port (5672)
|
25
|
+
# @option config [String] :user ('guest')
|
26
|
+
# @option config [String] :pass ('guest')
|
27
|
+
#
|
28
|
+
# @return [Freddy]
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# Freddy.build(Logger.new(STDOUT), user: 'thumper', pass: 'howdy')
|
32
|
+
def self.build(logger = Logger.new(STDOUT), config = {})
|
33
|
+
connection = Adapters.determine.connect(config)
|
81
34
|
|
82
35
|
new(connection, logger, config.fetch(:max_concurrency, 4))
|
83
36
|
end
|
@@ -92,6 +45,7 @@ class Freddy
|
|
92
45
|
@producer = Producer.new channel, logger
|
93
46
|
@request = Request.new channel, logger, @producer, @consumer
|
94
47
|
end
|
48
|
+
private :initialize
|
95
49
|
|
96
50
|
def respond_to(destination, &callback)
|
97
51
|
@request.respond_to destination, &callback
|
@@ -101,6 +55,27 @@ class Freddy
|
|
101
55
|
@consumer.tap_into pattern, &callback
|
102
56
|
end
|
103
57
|
|
58
|
+
# Sends a message to given destination
|
59
|
+
#
|
60
|
+
# This is *send and forget* type of delivery. It sends a message to given
|
61
|
+
# destination and does not wait for response. This is useful when there are
|
62
|
+
# multiple consumers that are using #tap_into or you just do not care about
|
63
|
+
# the response.
|
64
|
+
#
|
65
|
+
# @param [String] destination
|
66
|
+
# the queue name
|
67
|
+
# @param [Hash] payload
|
68
|
+
# the payload that can be serialized to json
|
69
|
+
# @param [Hash] options
|
70
|
+
# the options for delivery
|
71
|
+
# @option options [Integer] :timeout (0)
|
72
|
+
# discards the message after given seconds if nobody consumes it. Message
|
73
|
+
# won't be discarded if timeout it set to 0 (default).
|
74
|
+
#
|
75
|
+
# @return [void]
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# freddy.deliver 'Metrics', user_id: 5, metric: 'signed_in'
|
104
79
|
def deliver(destination, payload, options = {})
|
105
80
|
timeout = options.fetch(:timeout, 0)
|
106
81
|
opts = {}
|
@@ -109,6 +84,35 @@ class Freddy
|
|
109
84
|
@producer.produce destination, payload, opts
|
110
85
|
end
|
111
86
|
|
87
|
+
# Sends a message and waits for the response
|
88
|
+
#
|
89
|
+
# @param [String] destination
|
90
|
+
# the queue name
|
91
|
+
# @param [Hash] payload
|
92
|
+
# the payload that can be serialized to json
|
93
|
+
# @param [Hash] options
|
94
|
+
# the options for delivery
|
95
|
+
# @option options [Integer] :timeout (3)
|
96
|
+
# throws a time out exception after given seconds when there is no response
|
97
|
+
# @option options [Boolean] :delete_on_timeout (true)
|
98
|
+
# discards the message when timeout error is raised
|
99
|
+
#
|
100
|
+
# @raise [Freddy::TimeoutError]
|
101
|
+
# if nobody responded to the request
|
102
|
+
# @raise [Freddy::InvalidRequestError]
|
103
|
+
# if the responder responded with an error response
|
104
|
+
#
|
105
|
+
# @return [Hash] the response
|
106
|
+
#
|
107
|
+
# @example
|
108
|
+
# begin
|
109
|
+
# response = freddy.deliver_with_response 'Users', type: 'fetch_all'
|
110
|
+
# puts "Got response #{response}"
|
111
|
+
# rescue Freddy::TimeoutError
|
112
|
+
# puts "Service unavailable"
|
113
|
+
# rescue Freddy::InvalidRequestError => e
|
114
|
+
# puts "Got error response: #{e.response}"
|
115
|
+
# end
|
112
116
|
def deliver_with_response(destination, payload, options = {})
|
113
117
|
timeout = options.fetch(:timeout, 3)
|
114
118
|
delete_on_timeout = options.fetch(:delete_on_timeout, true)
|
@@ -118,6 +122,12 @@ class Freddy
|
|
118
122
|
}
|
119
123
|
end
|
120
124
|
|
125
|
+
# Closes the connection with message queue
|
126
|
+
#
|
127
|
+
# @return [void]
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# freddy.close
|
121
131
|
def close
|
122
132
|
@connection.close
|
123
133
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
|
3
|
+
class Freddy
|
4
|
+
module Adapters
|
5
|
+
class BunnyAdapter
|
6
|
+
def self.connect(config)
|
7
|
+
bunny = Bunny.new(config)
|
8
|
+
bunny.start
|
9
|
+
new(bunny)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(bunny)
|
13
|
+
@bunny = bunny
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_channel
|
17
|
+
Channel.new(@bunny.create_channel)
|
18
|
+
end
|
19
|
+
|
20
|
+
def close
|
21
|
+
@bunny.close
|
22
|
+
end
|
23
|
+
|
24
|
+
class Channel
|
25
|
+
extend Forwardable
|
26
|
+
|
27
|
+
def initialize(channel)
|
28
|
+
@channel = channel
|
29
|
+
end
|
30
|
+
|
31
|
+
def_delegators :@channel, :topic, :default_exchange, :consumers
|
32
|
+
|
33
|
+
def queue(*args)
|
34
|
+
Queue.new(@channel.queue(*args))
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_return(&block)
|
38
|
+
default_exchange.on_return do |return_info, properties, content|
|
39
|
+
block.call(return_info[:reply_code], properties[:correlation_id])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Queue
|
45
|
+
def initialize(queue)
|
46
|
+
@queue = queue
|
47
|
+
end
|
48
|
+
|
49
|
+
def subscribe(&block)
|
50
|
+
@queue.subscribe do |info, properties, payload|
|
51
|
+
block.call(payload, Delivery.new(properties, info.routing_key))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def bind(*args)
|
56
|
+
@queue.bind(*args)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def name
|
61
|
+
@queue.name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'march_hare'
|
2
|
+
|
3
|
+
class Freddy
|
4
|
+
module Adapters
|
5
|
+
class MarchHareAdapter
|
6
|
+
def self.connect(config)
|
7
|
+
hare = MarchHare.connect(config)
|
8
|
+
new(hare)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(hare)
|
12
|
+
@hare = hare
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_channel
|
16
|
+
Channel.new(@hare.create_channel)
|
17
|
+
end
|
18
|
+
|
19
|
+
def close
|
20
|
+
@hare.close
|
21
|
+
end
|
22
|
+
|
23
|
+
class Channel
|
24
|
+
extend Forwardable
|
25
|
+
|
26
|
+
def initialize(channel)
|
27
|
+
@channel = channel
|
28
|
+
end
|
29
|
+
|
30
|
+
def_delegators :@channel, :topic, :default_exchange, :consumers
|
31
|
+
|
32
|
+
def queue(*args)
|
33
|
+
Queue.new(@channel.queue(*args))
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_return(&block)
|
37
|
+
@channel.on_return do |reply_code, _, exchange_name, _, properties|
|
38
|
+
if exchange_name != Freddy::FREDDY_TOPIC_EXCHANGE_NAME
|
39
|
+
block.call(reply_code, properties.correlation_id)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Queue
|
46
|
+
def initialize(queue)
|
47
|
+
@queue = queue
|
48
|
+
end
|
49
|
+
|
50
|
+
def subscribe(&block)
|
51
|
+
@queue.subscribe do |meta, payload|
|
52
|
+
block.call(payload, Delivery.new(meta, meta.routing_key))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def bind(*args)
|
57
|
+
@queue.bind(*args)
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def name
|
62
|
+
@queue.name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/freddy/consumer.rb
CHANGED
@@ -37,7 +37,7 @@ class Freddy
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
@logger.debug "Tapping into messages that match #{pattern}"
|
40
|
-
ResponderHandler.new consumer, @
|
40
|
+
ResponderHandler.new consumer, @consume_thread_pool
|
41
41
|
end
|
42
42
|
|
43
43
|
private
|
@@ -51,11 +51,11 @@ class Freddy
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
@logger.debug "Consuming messages on #{queue.name}"
|
54
|
-
ResponderHandler.new consumer,
|
54
|
+
ResponderHandler.new consumer, pool
|
55
55
|
end
|
56
56
|
|
57
57
|
def create_queue(destination, options={})
|
58
|
-
|
58
|
+
@channel.queue(destination, options)
|
59
59
|
end
|
60
60
|
|
61
61
|
def log_receive_event(queue_name, payload, correlation_id)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Freddy
|
2
|
+
class ErrorResponse < StandardError
|
3
|
+
DEFAULT_ERROR_MESSAGE = 'Use #response to get the error response'
|
4
|
+
|
5
|
+
attr_reader :response
|
6
|
+
|
7
|
+
def initialize(response)
|
8
|
+
@response = response
|
9
|
+
super(format_message(response) || DEFAULT_ERROR_MESSAGE)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def format_message(response)
|
15
|
+
return unless response.is_a?(Hash)
|
16
|
+
|
17
|
+
message = [response[:error], response[:message]].compact.join(': ')
|
18
|
+
message.empty? ? nil : message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -14,8 +14,8 @@ class Freddy
|
|
14
14
|
def handle_message(payload, msg_handler, &block)
|
15
15
|
block.call payload, msg_handler
|
16
16
|
rescue Exception => e
|
17
|
-
@logger.error "Exception occured while processing message from #{
|
18
|
-
|
17
|
+
@logger.error "Exception occured while processing message from #{Utils.format_exception(e)}"
|
18
|
+
Utils.notify_exception(e, destination: @destination)
|
19
19
|
end
|
20
20
|
|
21
21
|
def success(*)
|
@@ -39,13 +39,13 @@ class Freddy
|
|
39
39
|
|
40
40
|
if !@correlation_id
|
41
41
|
@logger.error "Received request without correlation_id"
|
42
|
-
|
42
|
+
Utils.notify_exception(e)
|
43
43
|
else
|
44
44
|
block.call payload, msg_handler
|
45
45
|
end
|
46
46
|
rescue Exception => e
|
47
|
-
@logger.error "Exception occured while handling the request with correlation_id #{@correlation_id}: #{
|
48
|
-
|
47
|
+
@logger.error "Exception occured while handling the request with correlation_id #{@correlation_id}: #{Utils.format_exception(e)}"
|
48
|
+
Utils.notify_exception(e, correlation_id: @correlation_id, destination: @destination)
|
49
49
|
end
|
50
50
|
|
51
51
|
def success(reply_to, response)
|
data/lib/freddy/producer.rb
CHANGED
@@ -22,21 +22,5 @@ class Freddy
|
|
22
22
|
@topic_exchange.publish json_payload, properties.dup
|
23
23
|
@exchange.publish json_payload, properties.dup
|
24
24
|
end
|
25
|
-
|
26
|
-
def on_return(&block)
|
27
|
-
if @exchange.respond_to? :on_return # Bunny
|
28
|
-
@exchange.on_return do |return_info, properties, content|
|
29
|
-
block.call(return_info[:reply_code], properties[:correlation_id])
|
30
|
-
end
|
31
|
-
elsif @channel.respond_to? :on_return # Hare
|
32
|
-
@channel.on_return do |reply_code, _, exchange_name, _, properties|
|
33
|
-
if exchange_name != Freddy::FREDDY_TOPIC_EXCHANGE_NAME
|
34
|
-
block.call(reply_code, properties.correlation_id)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
else
|
38
|
-
raise OnReturnNotImplemented.new "AMQP implementation doesn't implement on_return"
|
39
|
-
end
|
40
|
-
end
|
41
25
|
end
|
42
26
|
end
|
data/lib/freddy/request.rb
CHANGED
@@ -23,7 +23,7 @@ class Freddy
|
|
23
23
|
@request_map = Hamster.mutable_hash
|
24
24
|
@request_manager = RequestManager.new @request_map, @logger
|
25
25
|
|
26
|
-
@
|
26
|
+
@channel.on_return do |reply_code, correlation_id|
|
27
27
|
if reply_code == NO_ROUTE
|
28
28
|
@request_manager.no_route(correlation_id)
|
29
29
|
end
|
@@ -80,7 +80,7 @@ class Freddy
|
|
80
80
|
private
|
81
81
|
|
82
82
|
def create_response_queue
|
83
|
-
|
83
|
+
@channel.queue("", exclusive: true)
|
84
84
|
end
|
85
85
|
|
86
86
|
def handle_response(payload, delivery)
|
@@ -92,12 +92,12 @@ class Freddy
|
|
92
92
|
request[:callback].call payload, delivery
|
93
93
|
else
|
94
94
|
@logger.warn "Got rpc response for correlation_id #{correlation_id} but there is no requester"
|
95
|
-
|
95
|
+
Utils.notify 'NoRequesterForResponse', "Got rpc response but there is no requester", correlation_id: correlation_id
|
96
96
|
end
|
97
97
|
rescue Exception => e
|
98
98
|
destination_report = request ? "to #{request[:destination]}" : ''
|
99
|
-
@logger.error "Exception occured while handling the response of request made #{destination_report} with correlation_id #{correlation_id}: #{
|
100
|
-
|
99
|
+
@logger.error "Exception occured while handling the response of request made #{destination_report} with correlation_id #{correlation_id}: #{Utils.format_exception e}"
|
100
|
+
Utils.notify_exception(e, destination: request[:destination], correlation_id: correlation_id)
|
101
101
|
end
|
102
102
|
|
103
103
|
def ensure_response_queue_exists
|
@@ -33,7 +33,7 @@ class Freddy
|
|
33
33
|
@requests.delete correlation_id
|
34
34
|
|
35
35
|
@logger.warn "Request timed out waiting response from #{request[:destination]}, correlation id #{correlation_id}"
|
36
|
-
|
36
|
+
Utils.notify 'RequestTimeout', "Request timed out waiting for response from #{request[:destination]}", {
|
37
37
|
correlation_id: correlation_id,
|
38
38
|
destination: request[:destination],
|
39
39
|
timeout: request[:timeout]
|
@@ -1,21 +1,24 @@
|
|
1
1
|
class Freddy
|
2
2
|
class ResponderHandler
|
3
|
-
|
4
|
-
def initialize(consumer, channel)
|
3
|
+
def initialize(consumer, consume_thread_pool)
|
5
4
|
@consumer = consumer
|
6
|
-
@
|
5
|
+
@consume_thread_pool = consume_thread_pool
|
7
6
|
end
|
8
7
|
|
9
|
-
|
8
|
+
# Shutdown responder
|
9
|
+
#
|
10
|
+
# Stop responding to messages immediately, Waits until all workers are
|
11
|
+
# finished and then returns.
|
12
|
+
#
|
13
|
+
# @return [void]
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# responder = freddy.respond_to 'Queue' do |msg, handler|
|
17
|
+
# end
|
18
|
+
# responder.shutdown
|
19
|
+
def shutdown
|
10
20
|
@consumer.cancel
|
11
|
-
|
12
|
-
|
13
|
-
def queue
|
14
|
-
@consumer.queue
|
15
|
-
end
|
16
|
-
|
17
|
-
def destroy_destination
|
18
|
-
@consumer.queue.delete
|
21
|
+
@consume_thread_pool.wait(:done)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
@@ -1,18 +1,27 @@
|
|
1
|
+
require 'thread'
|
1
2
|
require 'timeout'
|
2
3
|
|
3
4
|
class Freddy
|
4
5
|
class SyncResponseContainer
|
6
|
+
def initialize
|
7
|
+
@mutex = Mutex.new
|
8
|
+
end
|
9
|
+
|
5
10
|
def call(response, delivery)
|
6
11
|
@response = response
|
7
12
|
@delivery = delivery
|
13
|
+
@mutex.synchronize { @waiting.wakeup }
|
8
14
|
end
|
9
15
|
|
10
16
|
def wait_for_response(timeout)
|
11
|
-
|
12
|
-
|
17
|
+
@mutex.synchronize do
|
18
|
+
@waiting = Thread.current
|
19
|
+
@mutex.sleep(timeout)
|
13
20
|
end
|
14
21
|
|
15
|
-
if @response
|
22
|
+
if @response.nil?
|
23
|
+
raise Timeout::Error, 'execution expired'
|
24
|
+
elsif @response[:error] == 'RequestTimeout'
|
16
25
|
raise TimeoutError.new(@response)
|
17
26
|
elsif !@delivery || @delivery.metadata.type == 'error'
|
18
27
|
raise InvalidRequestError.new(@response)
|
data/lib/freddy/utils.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
class Freddy
|
2
|
+
class Utils
|
3
|
+
def self.format_exception(exception)
|
4
|
+
backtrace = exception.backtrace.map do |x|
|
5
|
+
x.match(/^(.+?):(\d+)(|:in `(.+)')$/);
|
6
|
+
[$1, $2, $4]
|
7
|
+
end.join("\n")
|
8
|
+
|
9
|
+
"#{exception.exception}\n#{backtrace}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.notify(name, message, parameters={})
|
13
|
+
return unless defined?(Airbrake)
|
14
|
+
|
15
|
+
Airbrake.notify_or_ignore(
|
16
|
+
error_class: name,
|
17
|
+
error_message: message,
|
18
|
+
cgi_data: ENV.to_hash,
|
19
|
+
parameters: parameters
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.notify_exception(exception, parameters={})
|
24
|
+
return unless defined?(Airbrake)
|
25
|
+
|
26
|
+
Airbrake.notify_or_ignore(exception,
|
27
|
+
cgi_data: ENV.to_hash,
|
28
|
+
parameters: parameters
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -8,15 +8,49 @@ describe Freddy::ResponderHandler do
|
|
8
8
|
|
9
9
|
after { freddy.close }
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
describe '#shutdown' do
|
12
|
+
it 'lets ongoing workers to finish' do
|
13
|
+
count = 0
|
14
|
+
|
15
|
+
consumer_handler = freddy.respond_to destination do
|
16
|
+
sleep 0.1
|
17
|
+
count += 1
|
18
|
+
end
|
19
|
+
deliver
|
20
|
+
|
21
|
+
sleep 0.05
|
22
|
+
consumer_handler.shutdown
|
23
|
+
|
24
|
+
expect(count).to eq(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does not accept new jobs' do
|
28
|
+
count = 0
|
29
|
+
|
30
|
+
consumer_handler = freddy.respond_to destination do
|
31
|
+
count += 1
|
32
|
+
end
|
33
|
+
|
34
|
+
consumer_handler.shutdown
|
35
|
+
deliver
|
36
|
+
|
37
|
+
expect(count).to eq(0)
|
15
38
|
end
|
16
|
-
deliver
|
17
|
-
consumer_handler.cancel
|
18
|
-
deliver
|
19
39
|
|
20
|
-
|
40
|
+
it 'does not touch other handlers' do
|
41
|
+
count = 0
|
42
|
+
|
43
|
+
freddy.respond_to destination do
|
44
|
+
count += 1
|
45
|
+
end
|
46
|
+
|
47
|
+
consumer_handler2 = freddy.respond_to random_destination do
|
48
|
+
count += 1
|
49
|
+
end
|
50
|
+
consumer_handler2.shutdown
|
51
|
+
|
52
|
+
deliver
|
53
|
+
expect(count).to eq(1)
|
54
|
+
end
|
21
55
|
end
|
22
56
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Freddy::SyncResponseContainer do
|
4
|
+
let(:container) { described_class.new }
|
5
|
+
|
6
|
+
context 'when timeout' do
|
7
|
+
subject { container.wait_for_response(0.01) }
|
8
|
+
|
9
|
+
it 'raises timeout error' do
|
10
|
+
expect { subject }.to raise_error(Timeout::Error, 'execution expired')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
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.4.
|
4
|
+
version: 0.4.6
|
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-11-
|
11
|
+
date: 2015-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -113,9 +113,13 @@ files:
|
|
113
113
|
- Rakefile
|
114
114
|
- freddy.gemspec
|
115
115
|
- lib/freddy.rb
|
116
|
-
- lib/freddy/
|
116
|
+
- lib/freddy/adapters.rb
|
117
|
+
- lib/freddy/adapters/bunny_adapter.rb
|
118
|
+
- lib/freddy/adapters/march_hare_adapter.rb
|
117
119
|
- lib/freddy/consumer.rb
|
118
120
|
- lib/freddy/delivery.rb
|
121
|
+
- lib/freddy/error_response.rb
|
122
|
+
- lib/freddy/invalid_request_error.rb
|
119
123
|
- lib/freddy/message_handler.rb
|
120
124
|
- lib/freddy/message_handlers.rb
|
121
125
|
- lib/freddy/payload.rb
|
@@ -124,12 +128,15 @@ files:
|
|
124
128
|
- lib/freddy/request_manager.rb
|
125
129
|
- lib/freddy/responder_handler.rb
|
126
130
|
- lib/freddy/sync_response_container.rb
|
131
|
+
- lib/freddy/timeout_error.rb
|
132
|
+
- lib/freddy/utils.rb
|
127
133
|
- spec/freddy/consumer_spec.rb
|
128
134
|
- spec/freddy/error_response_spec.rb
|
129
135
|
- spec/freddy/freddy_spec.rb
|
130
136
|
- spec/freddy/message_handler_spec.rb
|
131
137
|
- spec/freddy/request_spec.rb
|
132
138
|
- spec/freddy/responder_handler_spec.rb
|
139
|
+
- spec/freddy/sync_response_container_spec.rb
|
133
140
|
- spec/integration/concurrency_spec.rb
|
134
141
|
- spec/integration/logging_spec.rb
|
135
142
|
- spec/spec_helper.rb
|
@@ -164,6 +171,7 @@ test_files:
|
|
164
171
|
- spec/freddy/message_handler_spec.rb
|
165
172
|
- spec/freddy/request_spec.rb
|
166
173
|
- spec/freddy/responder_handler_spec.rb
|
174
|
+
- spec/freddy/sync_response_container_spec.rb
|
167
175
|
- spec/integration/concurrency_spec.rb
|
168
176
|
- spec/integration/logging_spec.rb
|
169
177
|
- spec/spec_helper.rb
|
@@ -1,34 +0,0 @@
|
|
1
|
-
class Freddy
|
2
|
-
class AdaptiveQueue
|
3
|
-
def initialize(queue)
|
4
|
-
@queue = queue
|
5
|
-
end
|
6
|
-
|
7
|
-
def subscribe(&block)
|
8
|
-
if hare?
|
9
|
-
@queue.subscribe do |meta, payload|
|
10
|
-
block.call(payload, Delivery.new(meta, meta.routing_key))
|
11
|
-
end
|
12
|
-
else
|
13
|
-
@queue.subscribe do |info, properties, payload|
|
14
|
-
block.call(payload, Delivery.new(properties, info.routing_key))
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def bind(*args)
|
20
|
-
@queue.bind(*args)
|
21
|
-
self
|
22
|
-
end
|
23
|
-
|
24
|
-
def name
|
25
|
-
@queue.name
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def hare?
|
31
|
-
RUBY_PLATFORM == 'java'
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|