freddy 2.0.0 → 2.2.2

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
  SHA256:
3
- metadata.gz: 7ae99c2d347a67110923b71ffac1af46c706d177b39b82c3adaae4db3f686a1d
4
- data.tar.gz: 557210126d3c4cdff8f0946ee5b6882e3334126c3cb0ae900df823f6e762fdd9
3
+ metadata.gz: 445e69f7be1822b3d6f8703086134081d944758ca5013c1d0e24331ec0d3e6b3
4
+ data.tar.gz: 9d918d1713c809253187727bd4adfa84584171abc07921e97429a8f8381a4ab6
5
5
  SHA512:
6
- metadata.gz: a2ee88b1ec7e51523ae6b3851f07b398b9093d90b193712bce172a09764d2a683d0b5209ced2a4a524248d8ffab36074b8a0b65c662ef9844f319424ccb32e93
7
- data.tar.gz: ea65f5f23e18c768098c1eec7edc299a146f0fc830a0a57b5e9d1f725b5fe6f0b442b017ecd08b80f6242424a6a0958b57c8514cdf015bca07e4d316e88edc56
6
+ metadata.gz: b76ebf99d84beea8fdb5e6557f780ab5ef0932cc177329e5524da4cf2a073ef8376e42f2c127614a60bd97566d3e460541d1c6a3e1a8ebe6f915e008b51e2577
7
+ data.tar.gz: 618367f3bb21b08a68d914c6f011b128764730a3555c7a46bd610703df13547b81aa4f09bc075058168b91fa5e9a72206de49ea6442d7c6cc1ae95ccf7901570
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  strategy:
13
13
  matrix:
14
- ruby-version: ['2.7']
14
+ ruby-version: ['2.6']
15
15
  services:
16
16
  rabbitmq:
17
17
  image: rabbitmq
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7
1
+ 2.6
data/README.md CHANGED
@@ -22,39 +22,60 @@ These message queues have been tested and are working with Freddy. Other queues
22
22
  ### Simple delivery
23
23
 
24
24
  #### Send and forget
25
+
25
26
  Sends a `message` to the given `destination`. If there is no consumer then the
26
27
  message stays in the queue until somebody consumes it.
28
+
27
29
  ```ruby
28
- freddy.deliver(destination, message)
30
+ freddy.deliver(destination, message)
29
31
  ```
30
32
 
31
33
  #### Expiring messages
34
+
32
35
  Sends a `message` to the given `destination`. If nobody consumes the message in
33
36
  `timeout` seconds then the message is discarded. This is useful for showing
34
37
  notifications that must happen in a certain timeframe but where we don't really
35
38
  care if it reached the destination or not.
39
+
36
40
  ```ruby
37
41
  freddy.deliver(destination, message, timeout: 5)
38
42
  ```
39
43
 
44
+ #### Compressed messages
45
+
46
+ Sends a `message` to the given `destination`. If `compress` is passed as an option parameter, it will compress the
47
+ payload according to the algorithm provided. Currently the `zlib` algorithm is implemented to compress the payload.
48
+ The message heading specify the `content-encoding` indicating the compression algorithm used.
49
+
50
+ ```ruby
51
+ freddy.deliver(destination, message, compress: 'zlib')
52
+ ```
53
+
40
54
  ### Request delivery
55
+
41
56
  #### Expiring messages
57
+
42
58
  Sends a `message` to the given `destination`. Has a default timeout of 3 and
43
59
  discards the message from the queue if a response hasn't been returned in that
44
60
  time.
61
+
45
62
  ```ruby
46
63
  response = freddy.deliver_with_response(destination, message)
47
64
  ```
48
65
 
49
66
  #### Persistant messages
67
+
50
68
  Sends a `message` to the given `destination`. Keeps the message in the queue if
51
69
  a timeout occurs.
70
+
52
71
  ```ruby
53
72
  response = freddy.deliver_with_response(destination, message, timeout: 4, delete_on_timeout: false)
54
73
  ```
55
74
 
56
75
  #### Errors
76
+
57
77
  `deliver_with_response` raises an error if an error is returned. This can be handled by rescuing from `Freddy::InvalidRequestError` and `Freddy::TimeoutError` as:
78
+
58
79
  ```ruby
59
80
  begin
60
81
  response = freddy.deliver_with_response 'Q', {}
@@ -66,6 +87,7 @@ rescue Freddy::TimeoutError => e
66
87
  ```
67
88
 
68
89
  ## Responding to messages
90
+
69
91
  ```ruby
70
92
  freddy.respond_to destination do |message, msg_handler|
71
93
  # ...
@@ -73,8 +95,9 @@ end
73
95
  ```
74
96
 
75
97
  The callback is called with 2 arguments
76
- * the parsed message (note that in the message all keys are symbolized)
77
- * the `MessageHandler` (described further down)
98
+
99
+ * the parsed message (note that in the message all keys are symbolized)
100
+ * the `MessageHandler` (described further down)
78
101
 
79
102
  ## The MessageHandler
80
103
 
@@ -82,17 +105,20 @@ When responding to messages the MessageHandler is given as the second argument.
82
105
 
83
106
  The following operations are supported:
84
107
 
85
- * responding with a successful response
108
+ * responding with a successful response
109
+
86
110
  ```ruby
87
111
  msg_handler.success(response = nil)
88
112
  ```
89
113
 
90
- * responding with an error response
114
+ * responding with an error response
115
+
91
116
  ```ruby
92
117
  msg_handler.error(error: "Couldn't process message")
93
118
  ```
94
119
 
95
120
  ## Tapping into messages
121
+
96
122
  When it's necessary to receive messages but not consume them, consider tapping.
97
123
 
98
124
  ```ruby
@@ -132,13 +158,15 @@ end
132
158
  ## The ResponderHandler
133
159
 
134
160
  When responding to a message or tapping the ResponderHandler is returned.
161
+
135
162
  ```ruby
136
163
  responder_handler = freddy.respond_to ....
137
164
  ```
138
165
 
139
166
  The following operations are supported:
140
167
 
141
- * stop responding
168
+ * stop responding
169
+
142
170
  ```ruby
143
171
  responder_handler.shutdown
144
172
  ```
@@ -155,11 +183,9 @@ This is not compatible with `opentelemetry-instrumentation-bunny` library.
155
183
 
156
184
  *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.
157
185
 
158
-
159
186
  Note that while it is possible to use *deliver_with_response* inside a *respond_to* block,
160
187
  it is not possible to use another *respond_to* block inside a different *respond_to* block.
161
188
 
162
-
163
189
  Note also that other configuration options for freddy users
164
190
  such as pool sizes for DB connections need to match or exceed *max_concurrency*
165
191
  to avoid running out of resources.
data/freddy.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = 'API for inter-application messaging supporting acknowledgements and request-response'
12
12
  spec.license = 'MIT'
13
13
  spec.homepage = 'https://github.com/salemove/freddy'
14
- spec.required_ruby_version = '>= 2.7'
14
+ spec.required_ruby_version = '>= 2.6'
15
15
 
16
16
  spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency 'opentelemetry-api', '~> 1.0.0.rc3'
27
27
  spec.add_dependency 'opentelemetry-semantic_conventions', '~> 1.0'
28
28
  spec.add_dependency 'thread', '~> 0.1'
29
+ spec.add_dependency 'zlib', '~> 1.1'
29
30
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bunny'
4
+ require 'forwardable'
4
5
 
5
6
  class Freddy
6
7
  module Adapters
@@ -67,7 +68,7 @@ class Freddy
67
68
 
68
69
  def subscribe(manual_ack: false)
69
70
  @queue.subscribe(manual_ack: manual_ack) do |info, properties, payload|
70
- parsed_payload = Payload.parse(payload)
71
+ parsed_payload = Payload.parse(payload, properties[:content_encoding])
71
72
  delivery = Delivery.new(
72
73
  parsed_payload, properties, info.routing_key, info.delivery_tag, info.exchange
73
74
  )
@@ -25,7 +25,7 @@ class Freddy
25
25
  end
26
26
 
27
27
  def in_span(force_follows_from: false, &block)
28
- name = "#{@exchange}.#{@routing_key} receive"
28
+ name = "#{@exchange}.#{@routing_key} process"
29
29
  kind = OpenTelemetry::Trace::SpanKind::CONSUMER
30
30
  producer_context = OpenTelemetry.propagation.extract(@metadata[:headers] || {})
31
31
 
@@ -35,12 +35,12 @@ class Freddy
35
35
  links = []
36
36
  links << OpenTelemetry::Trace::Link.new(producer_span_context) if producer_span_context.valid?
37
37
 
38
- # In general we should start a new trace here and just link two traces
39
- # together. But Zipkin (which we currently use) doesn't support links.
40
- # So even though the root trace could finish before anything here
41
- # starts executing, we'll continue with the root trace here as well.
42
- OpenTelemetry::Context.with_current(producer_context) do
43
- Freddy.tracer.in_span(name, attributes: span_attributes, links: links, kind: kind, &block)
38
+ root_span = Freddy.tracer.start_root_span(name, attributes: span_attributes, links: links, kind: kind)
39
+
40
+ OpenTelemetry::Trace.with_span(root_span) do
41
+ block.call
42
+ ensure
43
+ root_span.finish
44
44
  end
45
45
  else
46
46
  OpenTelemetry::Context.with_current(producer_context) do
@@ -60,7 +60,7 @@ class Freddy
60
60
  OpenTelemetry::SemanticConventions::Trace::MESSAGING_DESTINATION => @exchange,
61
61
  OpenTelemetry::SemanticConventions::Trace::MESSAGING_DESTINATION_KIND => destination_kind,
62
62
  OpenTelemetry::SemanticConventions::Trace::MESSAGING_RABBITMQ_ROUTING_KEY => @routing_key,
63
- OpenTelemetry::SemanticConventions::Trace::MESSAGING_OPERATION => 'receive'
63
+ OpenTelemetry::SemanticConventions::Trace::MESSAGING_OPERATION => 'process'
64
64
  }
65
65
 
66
66
  # There's no correlation_id when a message was sent using
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+
5
+ class Freddy
6
+ class Encoding
7
+ ZLIB_CONTENT_ENCODING = 'zlib'
8
+
9
+ def self.compress(data, encoding)
10
+ case encoding
11
+ when ZLIB_CONTENT_ENCODING
12
+ ::Zlib::Deflate.deflate(data)
13
+ else
14
+ data
15
+ end
16
+ end
17
+
18
+ def self.uncompress(data, encoding)
19
+ case encoding
20
+ when ZLIB_CONTENT_ENCODING
21
+ ::Zlib::Inflate.inflate(data)
22
+ else
23
+ data
24
+ end
25
+ end
26
+ end
27
+ end
@@ -4,9 +4,10 @@ require 'oj'
4
4
 
5
5
  class Freddy
6
6
  class Payload
7
- def self.parse(payload)
7
+ def self.parse(payload, encoding)
8
8
  return {} if payload == 'null'
9
9
 
10
+ payload = Freddy::Encoding.uncompress(payload, encoding)
10
11
  json_handler.parse(payload)
11
12
  end
12
13
 
@@ -20,7 +20,10 @@ class Freddy
20
20
  )
21
21
  Tracing.inject_tracing_information_to_properties!(properties)
22
22
 
23
- json_payload = Payload.dump(payload)
23
+ json_payload = Freddy::Encoding.compress(
24
+ Payload.dump(payload),
25
+ properties[:content_encoding]
26
+ )
24
27
 
25
28
  # Connection adapters handle thread safety for #publish themselves. No
26
29
  # need to lock these.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Freddy
4
- VERSION = '2.0.0'
4
+ VERSION = '2.2.2'
5
5
  end
data/lib/freddy.rb CHANGED
@@ -65,7 +65,7 @@ class Freddy
65
65
  # Received message as a ruby hash with symbolized keys
66
66
  # @yieldparam [#success, #error] handler
67
67
  # Handler for responding to messages. Use handler#success for successful
68
- # respone and handler#error for error response.
68
+ # response and handler#error for error response.
69
69
  #
70
70
  # @return [#shutdown]
71
71
  #
@@ -99,7 +99,7 @@ class Freddy
99
99
  # consuming them. It is useful for general messages that two or more clients
100
100
  # are interested.
101
101
  #
102
- # @param [String] pattern
102
+ # @param [String] pattern_or_patterns
103
103
  # the destination pattern. Use `#` wildcard for matching 0 or more words.
104
104
  # Use `*` to match exactly one word.
105
105
  # @param [Hash] options
@@ -152,15 +152,18 @@ class Freddy
152
152
  # @option options [Integer] :timeout (0)
153
153
  # discards the message after given seconds if nobody consumes it. Message
154
154
  # won't be discarded if timeout it set to 0 (default).
155
- #
155
+ # @option options [String] :compress (nil)
156
+ # - 'zlib' - compresses the payload with zlib
156
157
  # @return [void]
157
158
  #
158
159
  # @example
159
160
  # freddy.deliver 'Metrics', user_id: 5, metric: 'signed_in'
160
161
  def deliver(destination, payload, options = {})
161
162
  timeout = options.fetch(:timeout, 0)
163
+ compression_algorithm = options.fetch(:compress, nil)
162
164
  opts = {}
163
165
  opts[:expiration] = (timeout * 1000).to_i if timeout.positive?
166
+ opts[:content_encoding] = compression_algorithm if compression_algorithm
164
167
 
165
168
  @send_and_forget_producer.produce(destination, payload, opts)
166
169
  end
@@ -49,6 +49,29 @@ describe Freddy do
49
49
  expect(processed_after_timeout).to be(true)
50
50
  end
51
51
  end
52
+
53
+ context 'with compress' do
54
+ it 'compresses the payload' do
55
+ expect(Freddy::Encoding).to receive(:compress).with(anything, 'zlib').and_call_original
56
+
57
+ freddy.tap_into(destination) { |msg| @tapped_message = msg }
58
+ freddy.deliver(destination, payload, compress: 'zlib')
59
+ default_sleep
60
+
61
+ wait_for { @tapped_message }
62
+ expect(@tapped_message).to eq(payload)
63
+ end
64
+ end
65
+
66
+ context 'without compress' do
67
+ it 'does not compress the payload' do
68
+ freddy.tap_into(destination) { |msg| @tapped_message = msg }
69
+ deliver
70
+
71
+ wait_for { @tapped_message }
72
+ expect(@tapped_message).to eq(payload)
73
+ end
74
+ end
52
75
  end
53
76
 
54
77
  context 'when making a synchronized request' do
@@ -1,7 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'Tracing' do
4
- let(:logger) { spy }
4
+ let(:exporter) { SPAN_EXPORTER }
5
+
6
+ before do
7
+ exporter.reset
8
+ end
5
9
 
6
10
  context 'when receiving a traced request' do
7
11
  let(:freddy) { Freddy.build(logger, config) }
@@ -106,11 +110,48 @@ describe 'Tracing' do
106
110
  end
107
111
  end
108
112
 
113
+ context 'when receiving a broadcast' do
114
+ let(:freddy) { Freddy.build(logger, config) }
115
+ let(:destination) { random_destination }
116
+
117
+ before do
118
+ freddy.tap_into(destination) do
119
+ @deliver_span = current_span_attributes
120
+ end
121
+ end
122
+
123
+ after do
124
+ freddy.close
125
+ end
126
+
127
+ it 'creates a new trace and links it with the sender' do
128
+ initiator_span = nil
129
+ Freddy.tracer.in_span('test') do
130
+ initiator_span = current_span_attributes
131
+ freddy.deliver(destination, {})
132
+ end
133
+ wait_for { @deliver_span }
134
+
135
+ expect(exporter.finished_spans.map(&:name))
136
+ .to match([
137
+ /\.\w+ send/,
138
+ 'test',
139
+ /freddy-topic\.\w+ process/
140
+ ])
141
+
142
+ expect(@deliver_span.fetch(:trace_id)).not_to eq(initiator_span.fetch(:trace_id))
143
+
144
+ link = @deliver_span.fetch(:links)[0]
145
+ expect(link.span_context.trace_id).to eq(initiator_span.fetch(:trace_id))
146
+ end
147
+ end
148
+
109
149
  def current_span_attributes
110
150
  {
111
151
  trace_id: current_span.context.trace_id,
112
152
  parent_id: current_span.parent_span_id,
113
- span_id: current_span.context.span_id
153
+ span_id: current_span.context.span_id,
154
+ links: current_span.links || []
114
155
  }
115
156
  end
116
157
 
data/spec/spec_helper.rb CHANGED
@@ -11,6 +11,13 @@ require 'freddy'
11
11
  require 'logger'
12
12
  require 'hamster/experimental/mutable_set'
13
13
 
14
+ SPAN_EXPORTER = OpenTelemetry::SDK::Trace::Export::InMemorySpanExporter.new
15
+ span_processor = OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(SPAN_EXPORTER)
16
+
17
+ OpenTelemetry::SDK.configure do |c|
18
+ c.add_span_processor(span_processor)
19
+ end
20
+
14
21
  Thread.abort_on_exception = true
15
22
 
16
23
  RSpec.configure do |config|
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: 2.0.0
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Glia TechMovers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-17 00:00:00.000000000 Z
11
+ date: 2021-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.1'
111
+ - !ruby/object:Gem::Dependency
112
+ name: zlib
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.1'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.1'
111
125
  description: Messaging API
112
126
  email:
113
127
  - techmovers@salemove.com
@@ -134,6 +148,7 @@ files:
134
148
  - lib/freddy/consumers/response_consumer.rb
135
149
  - lib/freddy/consumers/tap_into_consumer.rb
136
150
  - lib/freddy/delivery.rb
151
+ - lib/freddy/encoding.rb
137
152
  - lib/freddy/error_response.rb
138
153
  - lib/freddy/invalid_request_error.rb
139
154
  - lib/freddy/message_handler.rb
@@ -174,14 +189,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
189
  requirements:
175
190
  - - ">="
176
191
  - !ruby/object:Gem::Version
177
- version: '2.7'
192
+ version: '2.6'
178
193
  required_rubygems_version: !ruby/object:Gem::Requirement
179
194
  requirements:
180
195
  - - ">="
181
196
  - !ruby/object:Gem::Version
182
197
  version: '0'
183
198
  requirements: []
184
- rubygems_version: 3.1.4
199
+ rubygems_version: 3.0.9
185
200
  signing_key:
186
201
  specification_version: 4
187
202
  summary: API for inter-application messaging supporting acknowledgements and request-response