freddy 1.0.1 → 1.1.0

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
  SHA1:
3
- metadata.gz: 7f70c4bc02ac5f44642acf811ecc4aa895d76327
4
- data.tar.gz: 56373103982583fc804f8a90783de797f8cc371b
3
+ metadata.gz: 55a9bea9337f7d20d2e713bd012ed8f216fef328
4
+ data.tar.gz: 2c49767c9226fdf1b882cf221cec8ff7726203f9
5
5
  SHA512:
6
- metadata.gz: 1e7c1f7fd21ef20f040af3cfadfcf42c9e8e2e44b05d08617e1225d60eb75f9b223dc6cade579bffa0dd08708da89d71896ca4d463d0f44be15ffb81535a047d
7
- data.tar.gz: c3f9e83af176f8c8731adf77449e6b32c6adddce91dbb1025db00156bf45c96caac47b68c93607e311624f06a8ed39c43bc878678b9bc4a9d4df001fd40547d4
6
+ metadata.gz: 7d3c30c1d5d54c72539821bc465f0887371ee30fdcbed3fa4711ee3b9f65137922025a4aa6956ca862b08b851d5bc8bff2d4aac7279a80250475c45a90e475ab
7
+ data.tar.gz: 13f606726903475c3619ee4f7dd1d18ebe257cbb48844cb0831cf81809b468eeb7143c4d4519b8cd1e6fa00e9d4703dcf9ce1282ebf70d4867419c85a89f42ae
data/README.md CHANGED
@@ -135,6 +135,35 @@ The following operations are supported:
135
135
  responder_handler.shutdown
136
136
  ```
137
137
 
138
+ ## Request Tracing
139
+
140
+ Freddy helps application developers to implement distributed tracing. Freddy provides trace ID, parent ID and span ID when receiving a request. These can be accessed through a thread-local variable `Freddy.trace`. Calling `deliver` or `deliver_with_response` will pass these IDs to the next service.
141
+
142
+ ```ruby
143
+ freddy1 = Freddy.build
144
+ freddy1.respond_do('service1') do |payload, msg_handler|
145
+ puts "Trace id: #{Freddy.trace.id}"
146
+ puts "Span id: #{Freddy.trace.span_id}"
147
+
148
+ freddy1.deliver('service2', {})
149
+ end
150
+
151
+ freddy2 = Freddy.build
152
+ freddy2.tap_into('service2') do |payload|
153
+ puts "Has same trace_id as the request in service1: #{Freddy.trace.id}"
154
+ puts "Has service1 request span id as parent id: #{Freddy.trace.parent_id}"
155
+ puts "Has its own generated span id: #{Freddy.trace.span_id}"
156
+ end
157
+ ```
158
+
159
+ In case you already have trace IDs (e.g. provided by REST API) then you can set them yourself:
160
+ ```ruby
161
+ Freddy.trace = Freddy::Traces::Trace.new({
162
+ id: 'YourTraceId',
163
+ parent_id: 'YourParentId', # Use nil in case this is the root of the trace
164
+ span_id: 'YourSpanId'
165
+ })
166
+ ```
138
167
 
139
168
  ## Notes about concurrency
140
169
 
data/freddy.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  else
9
9
  spec.name = "freddy"
10
10
  end
11
- spec.version = '1.0.1'
11
+ spec.version = '1.1.0'
12
12
  spec.authors = ["Salemove TechMovers"]
13
13
  spec.email = ["techmovers@salemove.com"]
14
14
  spec.description = %q{Messaging API}
@@ -37,9 +37,11 @@ class Freddy
37
37
  def process_message(delivery, &block)
38
38
  @consume_thread_pool.process do
39
39
  begin
40
+ Freddy.trace = delivery.trace
40
41
  block.call(delivery)
41
42
  ensure
42
43
  @channel.acknowledge(delivery.tag, false)
44
+ Freddy.trace = nil
43
45
  end
44
46
  end
45
47
  end
@@ -44,9 +44,11 @@ class Freddy
44
44
  @consume_thread_pool.process do
45
45
  begin
46
46
  Consumers.log_receive_event(@logger, queue.name, delivery)
47
+ Freddy.trace = delivery.trace
47
48
  block.call delivery.payload, delivery.routing_key
48
49
  ensure
49
50
  @channel.acknowledge(delivery.tag, false)
51
+ Freddy.trace = nil
50
52
  end
51
53
  end
52
54
  end
@@ -5,7 +5,8 @@ class Freddy
5
5
  message: 'Received message',
6
6
  queue: queue_name,
7
7
  payload: delivery.payload,
8
- correlation_id: delivery.correlation_id
8
+ correlation_id: delivery.correlation_id,
9
+ trace: Freddy.trace.to_h
9
10
  )
10
11
  end
11
12
  end
@@ -1,12 +1,13 @@
1
1
  class Freddy
2
2
  class Delivery
3
- attr_reader :routing_key, :payload, :tag
3
+ attr_reader :routing_key, :payload, :tag, :trace
4
4
 
5
5
  def initialize(payload, metadata, routing_key, tag)
6
6
  @payload = payload
7
7
  @metadata = metadata
8
8
  @routing_key = routing_key
9
9
  @tag = tag
10
+ @trace = build_trace(metadata.headers || {})
10
11
  end
11
12
 
12
13
  def correlation_id
@@ -20,5 +21,18 @@ class Freddy
20
21
  def reply_to
21
22
  @metadata.reply_to
22
23
  end
24
+
25
+ private
26
+
27
+ def build_trace(headers)
28
+ if headers['x-trace-id'] && headers['x-span-id']
29
+ Traces::Trace.build_from_existing_trace(
30
+ id: headers['x-trace-id'],
31
+ parent_id: headers['x-span-id']
32
+ )
33
+ else
34
+ Traces::Trace.build
35
+ end
36
+ end
23
37
  end
24
38
  end
@@ -12,7 +12,12 @@ class Freddy
12
12
  Producers.log_send_event(@logger, payload, destination)
13
13
 
14
14
  properties = properties.merge(
15
- routing_key: destination, content_type: CONTENT_TYPE
15
+ routing_key: destination,
16
+ content_type: CONTENT_TYPE,
17
+ headers: {
18
+ 'x-trace-id' => Freddy.trace.id,
19
+ 'x-span-id' => Freddy.trace.span_id
20
+ }
16
21
  )
17
22
 
18
23
  @exchange.publish Payload.dump(payload), properties
@@ -12,7 +12,14 @@ class Freddy
12
12
  def produce(destination, payload, properties)
13
13
  Producers.log_send_event(@logger, payload, destination)
14
14
 
15
- properties = properties.merge(routing_key: destination, content_type: CONTENT_TYPE)
15
+ properties = properties.merge(
16
+ routing_key: destination,
17
+ content_type: CONTENT_TYPE,
18
+ headers: {
19
+ 'x-trace-id' => Freddy.trace.id,
20
+ 'x-span-id' => Freddy.trace.span_id
21
+ }
22
+ )
16
23
  json_payload = Payload.dump(payload)
17
24
 
18
25
  # Connection adapters handle thread safety for #publish themselves. No
@@ -41,7 +41,11 @@ class Freddy
41
41
  properties = properties.merge(
42
42
  routing_key: destination, content_type: CONTENT_TYPE,
43
43
  correlation_id: correlation_id, reply_to: @response_queue.name,
44
- mandatory: true, type: 'request'
44
+ mandatory: true, type: 'request',
45
+ headers: {
46
+ 'x-trace-id' => Freddy.trace.id,
47
+ 'x-span-id' => Freddy.trace.span_id
48
+ }
45
49
  )
46
50
  json_payload = Payload.dump(payload)
47
51
 
@@ -50,7 +54,8 @@ class Freddy
50
54
  queue: destination,
51
55
  payload: payload,
52
56
  response_queue: @response_queue.name,
53
- correlation_id: correlation_id
57
+ correlation_id: correlation_id,
58
+ trace: Freddy.trace.to_h
54
59
  )
55
60
 
56
61
  # Connection adapters handle thread safety for #publish themselves. No
@@ -1,7 +1,7 @@
1
1
  class Freddy
2
2
  module Producers
3
3
  def self.log_send_event(logger, payload, destination)
4
- logger.debug message: 'Sending message', queue: destination, payload: payload
4
+ logger.debug message: 'Sending message', queue: destination, payload: payload, trace: Freddy.trace.to_h
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,40 @@
1
+ class Freddy
2
+ module Traces
3
+ class Trace
4
+ def self.build
5
+ id = TraceId.generate
6
+ span_id = TraceId.generate
7
+ new(id: id, parent_id: nil, span_id: span_id)
8
+ end
9
+
10
+ def self.build_from_existing_trace(id:, parent_id:)
11
+ span_id = TraceId.generate
12
+ new(id: id, parent_id: parent_id, span_id: span_id)
13
+ end
14
+
15
+ attr_reader :id, :parent_id, :span_id
16
+
17
+ def initialize(id:, parent_id:, span_id:)
18
+ @id = id
19
+ @parent_id = parent_id
20
+ @span_id = span_id
21
+ end
22
+
23
+ def to_h
24
+ {id: @id, parent_id: @parent_id, span_id: @span_id}
25
+ end
26
+ end
27
+
28
+ module TraceId
29
+ TRACE_ID_UPPER_BOUND = 2 ** 64
30
+
31
+ # Generates 64-bit lower-hex encoded ID. This was chosen to be compatible
32
+ # with tracing frameworks like zipkin.
33
+ def self.generate
34
+ rand(TRACE_ID_UPPER_BOUND).to_s(16)
35
+ end
36
+ end
37
+
38
+ NO_TRACE = Trace.new(id: nil, parent_id: nil, span_id: nil)
39
+ end
40
+ end
data/lib/freddy.rb CHANGED
@@ -30,6 +30,14 @@ class Freddy
30
30
  new(connection, logger, max_concurrency)
31
31
  end
32
32
 
33
+ def self.trace
34
+ Thread.current[:freddy_trace] || Traces::NO_TRACE
35
+ end
36
+
37
+ def self.trace=(trace)
38
+ Thread.current[:freddy_trace] = trace
39
+ end
40
+
33
41
  def initialize(connection, logger, max_concurrency)
34
42
  @connection = connection
35
43
  @logger = logger
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Freddy::Traces::TraceId do
4
+ describe '.generate' do
5
+ it 'generates a trace id' do
6
+ expect(described_class.generate).to_not be_nil
7
+ end
8
+ end
9
+ end
@@ -27,7 +27,8 @@ describe 'Logging' do
27
27
  message: 'Received message',
28
28
  queue: destination,
29
29
  payload: payload,
30
- correlation_id: anything
30
+ correlation_id: anything,
31
+ trace: anything
31
32
  )
32
33
  end
33
34
 
@@ -38,7 +39,8 @@ describe 'Logging' do
38
39
  queue: destination,
39
40
  payload: payload,
40
41
  response_queue: anything,
41
- correlation_id: anything
42
+ correlation_id: anything,
43
+ trace: anything
42
44
  )
43
45
  end
44
46
  end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Tracing' do
4
+ context 'when receiving an untraced request' do
5
+ let(:freddy) { Freddy.build(logger, config) }
6
+ let(:destination) { random_destination }
7
+
8
+ before do
9
+ freddy.respond_to(destination) do |payload, msg_handler|
10
+ msg_handler.success({
11
+ trace_id: Freddy.trace.id,
12
+ parent_id: Freddy.trace.parent_id,
13
+ span_id: Freddy.trace.span_id
14
+ })
15
+ end
16
+ end
17
+
18
+ after { freddy.close }
19
+
20
+ it 'has generated trace_id' do
21
+ response = freddy.deliver_with_response(destination, {})
22
+ expect(response.fetch(:trace_id)).to_not be_nil
23
+ end
24
+
25
+ it 'has no parent_id' do
26
+ response = freddy.deliver_with_response(destination, {})
27
+ expect(response.fetch(:parent_id)).to be_nil
28
+ end
29
+
30
+ it 'has generated span_id' do
31
+ response = freddy.deliver_with_response(destination, {})
32
+ expect(response.fetch(:span_id)).to_not be_nil
33
+ end
34
+ end
35
+
36
+ context 'when receiving a traced request' do
37
+ let(:freddy) { Freddy.build(logger, config) }
38
+ let(:freddy2) { Freddy.build(logger, config) }
39
+
40
+ let(:destination) { random_destination }
41
+ let(:destination2) { random_destination }
42
+
43
+ before do
44
+ freddy.respond_to(destination) do |payload, msg_handler|
45
+ msg_handler.success({
46
+ trace_initiator: {
47
+ trace_id: Freddy.trace.id,
48
+ parent_id: Freddy.trace.parent_id,
49
+ span_id: Freddy.trace.span_id
50
+ },
51
+ current_receiver: freddy.deliver_with_response(destination2, {})
52
+ })
53
+ end
54
+
55
+ freddy2.respond_to(destination2) do |payload, msg_handler|
56
+ msg_handler.success({
57
+ trace_id: Freddy.trace.id,
58
+ parent_id: Freddy.trace.parent_id,
59
+ span_id: Freddy.trace.span_id
60
+ })
61
+ end
62
+ end
63
+
64
+ after do
65
+ freddy.close
66
+ freddy2.close
67
+ end
68
+
69
+ it 'has trace_id from the trace initiator' do
70
+ response = freddy.deliver_with_response(destination, {})
71
+ trace_initiator = response.fetch(:trace_initiator)
72
+ current_receiver = response.fetch(:current_receiver)
73
+ expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
74
+ end
75
+
76
+ it 'has request initiator span_id as parent_id' do
77
+ response = freddy.deliver_with_response(destination, {})
78
+ trace_initiator = response.fetch(:trace_initiator)
79
+ current_receiver = response.fetch(:current_receiver)
80
+ expect(trace_initiator.fetch(:span_id)).to eq(current_receiver.fetch(:parent_id))
81
+ end
82
+
83
+ it 'has generated span_id' do
84
+ response = freddy.deliver_with_response(destination, {})
85
+ trace_initiator = response.fetch(:trace_initiator)
86
+ current_receiver = response.fetch(:current_receiver)
87
+ expect(current_receiver.fetch(:span_id)).to_not be_nil
88
+ expect(current_receiver.fetch(:span_id)).to_not eq(trace_initiator.fetch(:span_id))
89
+ end
90
+ end
91
+
92
+ context 'when receiving a nested traced request' do
93
+ let(:freddy) { Freddy.build(logger, config) }
94
+ let(:freddy2) { Freddy.build(logger, config) }
95
+ let(:freddy3) { Freddy.build(logger, config) }
96
+
97
+ let(:destination) { random_destination }
98
+ let(:destination2) { random_destination }
99
+ let(:destination3) { random_destination }
100
+
101
+ before do
102
+ freddy.respond_to(destination) do |payload, msg_handler|
103
+ msg_handler.success({
104
+ trace_initiator: {
105
+ trace_id: Freddy.trace.id,
106
+ parent_id: Freddy.trace.parent_id,
107
+ span_id: Freddy.trace.span_id
108
+ }
109
+ }.merge(freddy.deliver_with_response(destination2, {})))
110
+ end
111
+
112
+ freddy2.respond_to(destination2) do |payload, msg_handler|
113
+ msg_handler.success({
114
+ previous_receiver: {
115
+ trace_id: Freddy.trace.id,
116
+ parent_id: Freddy.trace.parent_id,
117
+ span_id: Freddy.trace.span_id
118
+ },
119
+ current_receiver: freddy2.deliver_with_response(destination3, {})
120
+ })
121
+ end
122
+
123
+ freddy3.respond_to(destination3) do |payload, msg_handler|
124
+ msg_handler.success({
125
+ trace_id: Freddy.trace.id,
126
+ parent_id: Freddy.trace.parent_id,
127
+ span_id: Freddy.trace.span_id
128
+ })
129
+ end
130
+ end
131
+
132
+ after do
133
+ freddy.close
134
+ freddy2.close
135
+ freddy3.close
136
+ end
137
+
138
+ it 'has trace_id from the trace initiator' do
139
+ response = freddy.deliver_with_response(destination, {})
140
+ trace_initiator = response.fetch(:trace_initiator)
141
+ current_receiver = response.fetch(:current_receiver)
142
+ expect(trace_initiator.fetch(:trace_id)).to eq(current_receiver.fetch(:trace_id))
143
+ end
144
+
145
+ it 'has request initiator span_id as parent_id' do
146
+ response = freddy.deliver_with_response(destination, {})
147
+ previous_receiver = response.fetch(:previous_receiver)
148
+ current_receiver = response.fetch(:current_receiver)
149
+ expect(previous_receiver.fetch(:span_id)).to eq(current_receiver.fetch(:parent_id))
150
+ end
151
+
152
+ it 'has generated span_id' do
153
+ response = freddy.deliver_with_response(destination, {})
154
+ previous_receiver = response.fetch(:previous_receiver)
155
+ current_receiver = response.fetch(:current_receiver)
156
+ expect(current_receiver.fetch(:span_id)).to_not be_nil
157
+ expect(current_receiver.fetch(:span_id)).to_not eq(previous_receiver.fetch(:span_id))
158
+ end
159
+ end
160
+ 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: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Salemove TechMovers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-08 00:00:00.000000000 Z
11
+ date: 2017-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -120,6 +120,7 @@ files:
120
120
  - lib/freddy/responder_handler.rb
121
121
  - lib/freddy/sync_response_container.rb
122
122
  - lib/freddy/timeout_error.rb
123
+ - lib/freddy/traces.rb
123
124
  - spec/freddy/consumers/respond_to_consumer_spec.rb
124
125
  - spec/freddy/error_response_spec.rb
125
126
  - spec/freddy/freddy_spec.rb
@@ -127,10 +128,12 @@ files:
127
128
  - spec/freddy/payload_spec.rb
128
129
  - spec/freddy/responder_handler_spec.rb
129
130
  - spec/freddy/sync_response_container_spec.rb
131
+ - spec/freddy/traces/trace_id_spec.rb
130
132
  - spec/integration/concurrency_spec.rb
131
133
  - spec/integration/logging_spec.rb
132
134
  - spec/integration/reply_spec.rb
133
135
  - spec/integration/tap_into_with_group_spec.rb
136
+ - spec/integration/tracing_spec.rb
134
137
  - spec/spec_helper.rb
135
138
  homepage:
136
139
  licenses:
@@ -164,8 +167,10 @@ test_files:
164
167
  - spec/freddy/payload_spec.rb
165
168
  - spec/freddy/responder_handler_spec.rb
166
169
  - spec/freddy/sync_response_container_spec.rb
170
+ - spec/freddy/traces/trace_id_spec.rb
167
171
  - spec/integration/concurrency_spec.rb
168
172
  - spec/integration/logging_spec.rb
169
173
  - spec/integration/reply_spec.rb
170
174
  - spec/integration/tap_into_with_group_spec.rb
175
+ - spec/integration/tracing_spec.rb
171
176
  - spec/spec_helper.rb