freddy 1.4.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +49 -0
  3. data/.travis.yml +1 -1
  4. data/Gemfile +4 -2
  5. data/Rakefile +6 -4
  6. data/freddy.gemspec +21 -21
  7. data/lib/freddy.rb +7 -8
  8. data/lib/freddy/adapters.rb +2 -0
  9. data/lib/freddy/adapters/bunny_adapter.rb +7 -7
  10. data/lib/freddy/adapters/march_hare_adapter.rb +6 -4
  11. data/lib/freddy/consumers.rb +2 -0
  12. data/lib/freddy/consumers/respond_to_consumer.rb +14 -17
  13. data/lib/freddy/consumers/response_consumer.rb +4 -2
  14. data/lib/freddy/consumers/tap_into_consumer.rb +11 -14
  15. data/lib/freddy/delivery.rb +2 -0
  16. data/lib/freddy/error_response.rb +2 -0
  17. data/lib/freddy/invalid_request_error.rb +2 -0
  18. data/lib/freddy/message_handler.rb +3 -1
  19. data/lib/freddy/message_handler_adapaters.rb +2 -0
  20. data/lib/freddy/payload.rb +5 -3
  21. data/lib/freddy/producers.rb +2 -0
  22. data/lib/freddy/producers/reply_producer.rb +6 -6
  23. data/lib/freddy/producers/send_and_forget_producer.rb +8 -8
  24. data/lib/freddy/producers/send_and_wait_response_producer.rb +39 -31
  25. data/lib/freddy/request_manager.rb +7 -4
  26. data/lib/freddy/responder_handler.rb +2 -0
  27. data/lib/freddy/sync_response_container.rb +4 -4
  28. data/lib/freddy/timeout_error.rb +2 -0
  29. data/lib/freddy/trace_carrier.rb +4 -2
  30. data/spec/freddy/consumers/respond_to_consumer_spec.rb +1 -1
  31. data/spec/freddy/error_response_spec.rb +9 -9
  32. data/spec/freddy/freddy_spec.rb +38 -38
  33. data/spec/freddy/message_handler_spec.rb +2 -2
  34. data/spec/freddy/payload_spec.rb +5 -5
  35. data/spec/freddy/responder_handler_spec.rb +1 -1
  36. data/spec/freddy/sync_response_container_spec.rb +8 -8
  37. data/spec/freddy/trace_carrier_spec.rb +5 -5
  38. data/spec/integration/concurrency_spec.rb +9 -9
  39. data/spec/integration/reply_spec.rb +4 -4
  40. data/spec/integration/tap_into_with_group_spec.rb +2 -2
  41. data/spec/integration/tracing_spec.rb +19 -19
  42. data/spec/spec_helper.rb +6 -6
  43. metadata +9 -8
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Dir[File.dirname(__FILE__) + '/producers/*.rb'].each(&method(:require))
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Freddy
2
4
  module Producers
3
5
  class ReplyProducer
4
- CONTENT_TYPE = 'application/json'.freeze
6
+ CONTENT_TYPE = 'application/json'
5
7
 
6
8
  def initialize(channel, logger)
7
9
  @logger = logger
@@ -9,11 +11,9 @@ class Freddy
9
11
  end
10
12
 
11
13
  def produce(destination, payload, properties)
12
- OpenTracing.active_span.log_kv(
13
- event: 'Sending response',
14
- queue: destination,
15
- payload: payload
16
- )
14
+ if (span = OpenTracing.active_span)
15
+ span.set_tag('message_bus.destination', destination)
16
+ end
17
17
 
18
18
  properties = properties.merge(
19
19
  routing_key: destination,
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Freddy
2
4
  module Producers
3
5
  class SendAndForgetProducer
4
- CONTENT_TYPE = 'application/json'.freeze
6
+ CONTENT_TYPE = 'application/json'
5
7
 
6
8
  def initialize(channel, logger)
7
9
  @logger = logger
@@ -11,13 +13,11 @@ class Freddy
11
13
 
12
14
  def produce(destination, payload, properties)
13
15
  span = OpenTracing.start_span("freddy:notify:#{destination}",
14
- tags: {
15
- 'message_bus.destination': destination,
16
- 'component': 'freddy',
17
- 'span.kind': 'producer' # Message Bus
18
- }
19
- )
20
- span.log_kv event: 'Sending message', payload: payload
16
+ tags: {
17
+ 'message_bus.destination' => destination,
18
+ 'component' => 'freddy',
19
+ 'span.kind' => 'producer' # Message Bus
20
+ })
21
21
 
22
22
  properties = properties.merge(
23
23
  routing_key: destination,
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Freddy
2
4
  module Producers
3
5
  class SendAndWaitResponseProducer
4
- CONTENT_TYPE = 'application/json'.freeze
6
+ CONTENT_TYPE = 'application/json'
5
7
 
6
8
  def initialize(channel, logger)
7
9
  @logger = logger
@@ -16,36 +18,36 @@ class Freddy
16
18
  @request_manager.no_route(correlation_id)
17
19
  end
18
20
 
19
- @response_queue = @channel.queue("", exclusive: true)
21
+ @response_queue = @channel.queue('', exclusive: true)
20
22
 
21
23
  @response_consumer = Consumers::ResponseConsumer.new(@logger)
22
24
  @response_consumer.consume(@channel, @response_queue, &method(:handle_response))
23
25
  end
24
26
 
25
27
  def produce(destination, payload, timeout_in_seconds:, delete_on_timeout:, **properties)
26
- span = OpenTracing.start_span("freddy:request:#{destination}",
27
- tags: {
28
- 'component': 'freddy',
29
- 'span.kind': 'client', # RPC
30
- 'payload.type': payload[:type] || 'unknown'
31
- }
32
- )
33
-
34
28
  correlation_id = SecureRandom.uuid
35
29
 
30
+ span = OpenTracing.start_span("freddy:request:#{destination}",
31
+ tags: {
32
+ 'component' => 'freddy',
33
+ 'span.kind' => 'client', # RPC
34
+ 'payload.type' => payload[:type] || 'unknown',
35
+ 'message_bus.destination' => destination,
36
+ 'message_bus.response_queue' => @response_queue.name,
37
+ 'message_bus.correlation_id' => correlation_id,
38
+ 'freddy.timeout_in_seconds' => timeout_in_seconds
39
+ })
40
+
36
41
  container = SyncResponseContainer.new(
37
42
  on_timeout(correlation_id, destination, timeout_in_seconds, span)
38
43
  )
39
44
 
40
45
  @request_manager.store(correlation_id,
41
- callback: container,
42
- trace: span,
43
- destination: destination
44
- )
46
+ callback: container,
47
+ span: span,
48
+ destination: destination)
45
49
 
46
- if delete_on_timeout
47
- properties[:expiration] = (timeout_in_seconds * 1000).to_i
48
- end
50
+ properties[:expiration] = (timeout_in_seconds * 1000).to_i if delete_on_timeout
49
51
 
50
52
  properties = properties.merge(
51
53
  routing_key: destination, content_type: CONTENT_TYPE,
@@ -55,13 +57,6 @@ class Freddy
55
57
  OpenTracing.global_tracer.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, TraceCarrier.new(properties))
56
58
  json_payload = Payload.dump(payload)
57
59
 
58
- span.log_kv(
59
- event: 'Publishing request',
60
- payload: payload,
61
- response_queue: @response_queue.name,
62
- correlation_id: correlation_id
63
- )
64
-
65
60
  # Connection adapters handle thread safety for #publish themselves. No
66
61
  # need to lock these.
67
62
  @topic_exchange.publish json_payload, properties.dup
@@ -73,11 +68,11 @@ class Freddy
73
68
  def handle_response(delivery)
74
69
  correlation_id = delivery.correlation_id
75
70
 
76
- if request = @request_manager.delete(correlation_id)
71
+ if (request = @request_manager.delete(correlation_id))
77
72
  process_response(request, delivery)
78
73
  else
79
74
  message = "Got rpc response for correlation_id #{correlation_id} "\
80
- "but there is no requester"
75
+ 'but there is no requester'
81
76
  @logger.warn message
82
77
  end
83
78
  end
@@ -86,17 +81,30 @@ class Freddy
86
81
  @logger.debug "Got response for request to #{request[:destination]} "\
87
82
  "with correlation_id #{delivery.correlation_id}"
88
83
  request[:callback].call(delivery.payload, delivery)
84
+ rescue InvalidRequestError => e
85
+ request[:span].set_tag('error', true)
86
+ request[:span].log_kv(
87
+ event: 'invalid request',
88
+ message: e.message,
89
+ 'error.object': e
90
+ )
91
+ raise e
89
92
  ensure
90
- request[:trace].finish
93
+ request[:span].finish
91
94
  end
92
95
 
93
- def on_timeout(correlation_id, destination, timeout_in_seconds, trace)
94
- Proc.new do
96
+ def on_timeout(correlation_id, destination, timeout_in_seconds, span)
97
+ proc do
95
98
  @logger.warn "Request timed out waiting response from #{destination}"\
96
- ", correlation id #{correlation_id}"
99
+ ", correlation id #{correlation_id}, timeout #{timeout_in_seconds}s"
97
100
 
98
101
  @request_manager.delete(correlation_id)
99
- trace.finish
102
+ span.set_tag('error', true)
103
+ span.log_kv(
104
+ event: 'timed out',
105
+ message: "Timed out waiting response from #{destination}"
106
+ )
107
+ span.finish
100
108
  end
101
109
  end
102
110
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Freddy
2
4
  class RequestManager
3
5
  def initialize(logger)
@@ -6,10 +8,11 @@ class Freddy
6
8
  end
7
9
 
8
10
  def no_route(correlation_id)
9
- if request = @requests[correlation_id]
10
- delete(correlation_id)
11
- request[:callback].call({error: 'Specified queue does not exist'}, nil)
12
- end
11
+ request = @requests[correlation_id]
12
+ return unless request
13
+
14
+ delete(correlation_id)
15
+ request[:callback].call({ error: 'Specified queue does not exist' }, nil)
13
16
  end
14
17
 
15
18
  def store(correlation_id, opts)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Freddy
2
4
  class ResponderHandler
3
5
  def initialize(consumer, consume_thread_pool)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'timeout'
2
4
 
3
5
  class Freddy
@@ -9,9 +11,7 @@ class Freddy
9
11
  end
10
12
 
11
13
  def call(response, delivery)
12
- if response.nil?
13
- raise StandardError, 'unexpected nil value for response'
14
- end
14
+ raise StandardError, 'unexpected nil value for response' if response.nil?
15
15
 
16
16
  @response = response
17
17
  @delivery = delivery
@@ -28,7 +28,7 @@ class Freddy
28
28
  message: 'Timed out waiting for response'
29
29
  )
30
30
  elsif !@delivery || @delivery.type == 'error'
31
- raise InvalidRequestError.new(@response)
31
+ raise InvalidRequestError, @response
32
32
  else
33
33
  @response
34
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'error_response'
2
4
 
3
5
  class Freddy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Freddy
2
4
  # Carrier for rabbitmq following OpenTracing API
3
5
  # See https://github.com/opentracing/opentracing-ruby/blob/master/lib/opentracing/carrier.rb
@@ -18,8 +20,8 @@ class Freddy
18
20
  def each(&block)
19
21
  Hash[
20
22
  (@properties.headers || {})
21
- .select {|key, _| key =~ /^x-trace/}
22
- .map {|key, value| [key.sub(/x-trace-/, ''), value]}
23
+ .select { |key, _| key =~ /^x-trace/ }
24
+ .map { |key, value| [key.sub(/x-trace-/, ''), value] }
23
25
  ].each(&block)
24
26
  end
25
27
  end
@@ -12,7 +12,7 @@ describe Freddy::Consumers::RespondToConsumer do
12
12
 
13
13
  let(:connection) { Freddy::Adapters.determine.connect(config) }
14
14
  let(:destination) { random_destination }
15
- let(:payload) { {pay: 'load'} }
15
+ let(:payload) { { pay: 'load' } }
16
16
  let(:msg_handler_adapter_factory) { double(for: msg_handler_adapter) }
17
17
  let(:msg_handler_adapter) { Freddy::MessageHandlerAdapters::NoOpHandler.new }
18
18
  let(:prefetch_buffer_size) { 2 }
@@ -4,55 +4,55 @@ describe Freddy::ErrorResponse do
4
4
  subject(:error) { described_class.new(input) }
5
5
 
6
6
  context 'with an error type' do
7
- let(:input) { {error: 'SomeError'} }
7
+ let(:input) { { error: 'SomeError' } }
8
8
 
9
9
  describe '#response' do
10
10
  subject { error.response }
11
11
 
12
- it { should eq(input) }
12
+ it { is_expected.to eq(input) }
13
13
  end
14
14
 
15
15
  describe '#message' do
16
16
  subject { error.message }
17
17
 
18
18
  it 'uses error type as a message' do
19
- should eq('SomeError')
19
+ is_expected.to eq('SomeError')
20
20
  end
21
21
  end
22
22
  end
23
23
 
24
24
  context 'with an error type and message' do
25
- let(:input) { {error: 'SomeError', message: 'extra info'} }
25
+ let(:input) { { error: 'SomeError', message: 'extra info' } }
26
26
 
27
27
  describe '#response' do
28
28
  subject { error.response }
29
29
 
30
- it { should eq(input) }
30
+ it { is_expected.to eq(input) }
31
31
  end
32
32
 
33
33
  describe '#message' do
34
34
  subject { error.message }
35
35
 
36
36
  it 'uses error type as a message' do
37
- should eq('SomeError: extra info')
37
+ is_expected.to eq('SomeError: extra info')
38
38
  end
39
39
  end
40
40
  end
41
41
 
42
42
  context 'without an error type' do
43
- let(:input) { {something: 'else'} }
43
+ let(:input) { { something: 'else' } }
44
44
 
45
45
  describe '#response' do
46
46
  subject { error.response }
47
47
 
48
- it { should eq(input) }
48
+ it { is_expected.to eq(input) }
49
49
  end
50
50
 
51
51
  describe '#message' do
52
52
  subject { error.message }
53
53
 
54
54
  it 'uses default error message as a message' do
55
- should eq('Use #response to get the error response')
55
+ is_expected.to eq('Use #response to get the error response')
56
56
  end
57
57
  end
58
58
  end
@@ -5,7 +5,7 @@ describe Freddy do
5
5
 
6
6
  let(:destination) { random_destination }
7
7
  let(:destination2) { random_destination }
8
- let(:payload) { {pay: 'load'} }
8
+ let(:payload) { { pay: 'load' } }
9
9
 
10
10
  after { freddy.close }
11
11
 
@@ -18,7 +18,7 @@ describe Freddy do
18
18
  it 'removes the message from the queue after the timeout' do
19
19
  # Assume that there already is a queue. Otherwise will get an early
20
20
  # return.
21
- consumer = freddy.respond_to(destination) { }
21
+ consumer = freddy.respond_to(destination) {}
22
22
  consumer.shutdown
23
23
 
24
24
  freddy.deliver(destination, {}, timeout: 0.1)
@@ -36,7 +36,7 @@ describe Freddy do
36
36
  it 'keeps the message in the queue' do
37
37
  # Assume that there already is a queue. Otherwise will get an early
38
38
  # return.
39
- consumer = freddy.respond_to(destination) { }
39
+ consumer = freddy.respond_to(destination) {}
40
40
  consumer.shutdown
41
41
 
42
42
  freddy.deliver(destination, {})
@@ -53,50 +53,50 @@ describe Freddy do
53
53
 
54
54
  context 'when making a synchronized request' do
55
55
  it 'returns response as soon as possible' do
56
- respond_to { |payload, msg_handler| msg_handler.success(res: 'yey') }
57
- response = freddy.deliver_with_response(destination, {a: 'b'})
56
+ respond_to { |_payload, msg_handler| msg_handler.success(res: 'yey') }
57
+ response = freddy.deliver_with_response(destination, a: 'b')
58
58
 
59
59
  expect(response).to eq(res: 'yey')
60
60
  end
61
61
 
62
62
  it 'raises an error if the message was errored' do
63
- respond_to { |payload, msg_handler| msg_handler.error(error: 'not today') }
63
+ respond_to { |_payload, msg_handler| msg_handler.error(error: 'not today') }
64
64
 
65
- expect {
65
+ expect do
66
66
  freddy.deliver_with_response(destination, payload)
67
- }.to raise_error(Freddy::InvalidRequestError) {|error|
67
+ end.to raise_error(Freddy::InvalidRequestError) { |error|
68
68
  expect(error.response).to eq(error: 'not today')
69
69
  }
70
70
  end
71
71
 
72
72
  it 'responds to the correct requester' do
73
- respond_to { |payload, msg_handler| msg_handler.success(res: 'yey') }
73
+ respond_to { |_payload, msg_handler| msg_handler.success(res: 'yey') }
74
74
 
75
75
  response = freddy.deliver_with_response(destination, payload)
76
76
  expect(response).to eq(res: 'yey')
77
77
 
78
- expect {
78
+ expect do
79
79
  freddy.deliver_with_response(destination2, payload)
80
- }.to raise_error(Freddy::InvalidRequestError)
80
+ end.to raise_error(Freddy::InvalidRequestError)
81
81
  end
82
82
 
83
83
  context 'when queue does not exist' do
84
84
  it 'gives a no route error' do
85
- expect {
86
- freddy.deliver_with_response(destination, {a: 'b'}, timeout: 1)
87
- }.to raise_error(Freddy::InvalidRequestError) {|error|
85
+ expect do
86
+ freddy.deliver_with_response(destination, { a: 'b' }, timeout: 1)
87
+ end.to raise_error(Freddy::InvalidRequestError) { |error|
88
88
  expect(error.response).to eq(error: 'Specified queue does not exist')
89
89
  }
90
90
  end
91
91
  end
92
92
 
93
- context 'on timeout' do
93
+ context 'when timeout' do
94
94
  it 'gives timeout error' do
95
- respond_to { |payload, msg_handler| sleep 0.2 }
95
+ respond_to { |_payload, _msg_handler| sleep 0.2 }
96
96
 
97
- expect {
98
- freddy.deliver_with_response(destination, {a: 'b'}, timeout: 0.1)
99
- }.to raise_error(Freddy::TimeoutError) {|error|
97
+ expect do
98
+ freddy.deliver_with_response(destination, { a: 'b' }, timeout: 0.1)
99
+ end.to raise_error(Freddy::TimeoutError) { |error|
100
100
  expect(error.response).to eq(error: 'RequestTimeout', message: 'Timed out waiting for response')
101
101
  }
102
102
  end
@@ -105,12 +105,12 @@ describe Freddy do
105
105
  it 'removes the message from the queue' do
106
106
  # Assume that there already is a queue. Otherwise will get an early
107
107
  # return.
108
- consumer = freddy.respond_to(destination) { }
108
+ consumer = freddy.respond_to(destination) {}
109
109
  consumer.shutdown
110
110
 
111
- expect {
111
+ expect do
112
112
  freddy.deliver_with_response(destination, {}, timeout: 0.1)
113
- }.to raise_error(Freddy::TimeoutError)
113
+ end.to raise_error(Freddy::TimeoutError)
114
114
  default_sleep # to ensure everything is properly cleaned
115
115
 
116
116
  processed_after_timeout = false
@@ -125,12 +125,12 @@ describe Freddy do
125
125
  it 'removes the message from the queue' do
126
126
  # Assume that there already is a queue. Otherwise will get an early
127
127
  # return.
128
- consumer = freddy.respond_to(destination) { }
128
+ consumer = freddy.respond_to(destination) {}
129
129
  consumer.shutdown
130
130
 
131
- expect {
131
+ expect do
132
132
  freddy.deliver_with_response(destination, {}, timeout: 0.1, delete_on_timeout: false)
133
- }.to raise_error(Freddy::TimeoutError)
133
+ end.to raise_error(Freddy::TimeoutError)
134
134
  default_sleep # to ensure everything is properly cleaned
135
135
 
136
136
  processed_after_timeout = false
@@ -149,7 +149,7 @@ describe Freddy do
149
149
  end
150
150
 
151
151
  it 'receives messages' do
152
- tap {|msg| @tapped_message = msg }
152
+ tap { |msg| @tapped_message = msg }
153
153
  deliver
154
154
 
155
155
  wait_for { @tapped_message }
@@ -157,13 +157,13 @@ describe Freddy do
157
157
  end
158
158
 
159
159
  it 'has the destination' do
160
- tap "somebody.*.love" do |message, destination|
160
+ tap 'somebody.*.love' do |_message, destination|
161
161
  @destination = destination
162
162
  end
163
- deliver "somebody.to.love"
163
+ deliver 'somebody.to.love'
164
164
 
165
165
  wait_for { @destination }
166
- expect(@destination).to eq("somebody.to.love")
166
+ expect(@destination).to eq('somebody.to.love')
167
167
  end
168
168
 
169
169
  it "doesn't consume the message" do
@@ -178,28 +178,28 @@ describe Freddy do
178
178
  expect(@message_received).to be(true)
179
179
  end
180
180
 
181
- it "allows * wildcard" do
182
- tap("somebody.*.love") { @tapped = true }
181
+ it 'allows * wildcard' do
182
+ tap('somebody.*.love') { @tapped = true }
183
183
 
184
- deliver "somebody.to.love"
184
+ deliver 'somebody.to.love'
185
185
 
186
186
  wait_for { @tapped }
187
187
  expect(@tapped).to be(true)
188
188
  end
189
189
 
190
- it "* matches only one word" do
191
- tap("somebody.*.love") { @tapped = true }
190
+ it '* matches only one word' do
191
+ tap('somebody.*.love') { @tapped = true }
192
192
 
193
- deliver "somebody.not.to.love"
193
+ deliver 'somebody.not.to.love'
194
194
 
195
195
  default_sleep
196
196
  expect(@tapped).to be_falsy
197
197
  end
198
198
 
199
- it "allows # wildcard" do
200
- tap("i.#.free") { @tapped = true }
199
+ it 'allows # wildcard' do
200
+ tap('i.#.free') { @tapped = true }
201
201
 
202
- deliver "i.want.to.break.free"
202
+ deliver 'i.want.to.break.free'
203
203
 
204
204
  wait_for { @tapped }
205
205
  expect(@tapped).to be(true)