appsignal 2.11.2 → 2.11.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2207fe155b6aec1d0fa6e271f8257e5006db693f3d30d45da0f522185b5498a6
4
- data.tar.gz: 42f76f14cf36b7309ade272924f9137e763f0ca56446189d97ea83b8f95a12f6
3
+ metadata.gz: 140c8dc48aa610503d589cbadbd8080234498628f73570eb9dec1fe40d43e7c6
4
+ data.tar.gz: 9d3bda2c23f46391752218d6a08290e19ffe59a9cdabd3d89841f208a5a4499c
5
5
  SHA512:
6
- metadata.gz: af7b2665f84a0ef9dc8fb7806e0095b00f48aa6273a5e5d5a75bf3c59e0910123fb50fd0380b16d6b154bf0a6c0ec8a1b42ce990b02511eaa648372a1178ac1d
7
- data.tar.gz: cdbc11ff5c3e7e3b756a8e3ad4dcb1483d7e6772346e652b042e7b3e08e00130d39c2160ea7ef5e47c44f1c8818a678d0393828bf9730f456bab401393754ad0
6
+ metadata.gz: 1496d2e2ab87742b5ede043d513a290aa95679445c7dd5a1f041c90bb2e05fc983e558733b974a60ed880c1db3ac9faabfc431ed4c3e1cf68fad150dd5cd4e61
7
+ data.tar.gz: 62052287406db0bb8e95c6e9130886f9db8f7e9822b9d835a9faec3090facd4990addf13b99f107a26e777a37354bbe8ea795c392917cfe2c0884772000b0b3c
@@ -31,7 +31,7 @@ global_job_config:
31
31
  prologue:
32
32
  commands:
33
33
  - checkout
34
- - git -C /home/semaphore/.rbenv/plugins/ruby-build pull
34
+ - rm -f $HOME/.rbenv/plugins/rbenv-gem-rehash/etc/rbenv.d/exec/~gem-rehash.bash
35
35
  - sem-version ruby $RUBY_VERSION
36
36
  - "./support/check_versions"
37
37
  - cache restore $_BUNDLER_CACHE-bundler-$RUBY_VERSION-$GEMSET-$(checksum $BUNDLE_GEMFILE)
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ # 2.11.3
4
+ - Support Shoryuken batch workers. PR #687
5
+
3
6
  # 2.11.2
4
7
  - Support Ruby 3.0. PR #681
5
8
  - Support breadcrumbs. PR #666
@@ -32,7 +32,7 @@ semaphore: # Default `.semaphore/semaphore.yml` contents
32
32
  prologue:
33
33
  commands:
34
34
  - checkout
35
- - git -C /home/semaphore/.rbenv/plugins/ruby-build pull
35
+ - rm -f $HOME/.rbenv/plugins/rbenv-gem-rehash/etc/rbenv.d/exec/~gem-rehash.bash
36
36
  - sem-version ruby $RUBY_VERSION
37
37
  - ./support/check_versions
38
38
  - cache restore $_BUNDLER_CACHE-bundler-$RUBY_VERSION-$GEMSET-$(checksum $BUNDLE_GEMFILE)
@@ -5,21 +5,60 @@ module Appsignal
5
5
  # @api private
6
6
  class ShoryukenMiddleware
7
7
  def call(worker_instance, queue, sqs_msg, body)
8
- metadata = { :queue => queue }.merge(sqs_msg.attributes)
8
+ batch = sqs_msg.is_a?(Array)
9
+ attributes =
10
+ if batch
11
+ # We can't instrument batched message separately, the `yield` will
12
+ # perform all the batched messages.
13
+ # To provide somewhat useful metadata, Get first message based on
14
+ # SentTimestamp, and use its attributes as metadata for the
15
+ # transaction. We can't combine them all because then they would
16
+ # overwrite each other and the last message (in an sorted order)
17
+ # would be used as the source of the metadata. With the
18
+ # oldest/first message at least some useful information is stored
19
+ # such as the first received time and the number of retries for the
20
+ # first message. The newer message should have lower values and
21
+ # timestamps in their metadata.
22
+ first_msg = sqs_msg.min do |a, b|
23
+ a.attributes["SentTimestamp"].to_i <=> b.attributes["SentTimestamp"].to_i
24
+ end
25
+ # Add batch => true metadata so people can recognize when a
26
+ # transaction is about a batch of messages.
27
+ first_msg.attributes.merge(:batch => true)
28
+ else
29
+ sqs_msg.attributes.merge(:message_id => sqs_msg.message_id)
30
+ end
31
+ metadata = { :queue => queue }.merge(attributes)
9
32
  options = {
10
33
  :class => worker_instance.class.name,
11
34
  :method => "perform",
12
35
  :metadata => metadata
13
36
  }
14
37
 
15
- args = body.is_a?(Hash) ? body : { :params => body }
38
+ args =
39
+ if batch
40
+ bodies = {}
41
+ sqs_msg.each_with_index do |msg, index|
42
+ # Store all separate bodies on a hash with the key being the
43
+ # message_id
44
+ bodies[msg.message_id] = body[index]
45
+ end
46
+ bodies
47
+ else
48
+ case body
49
+ when Hash
50
+ body
51
+ else
52
+ { :params => body }
53
+ end
54
+ end
16
55
  options[:params] = Appsignal::Utils::HashSanitizer.sanitize(
17
56
  args,
18
57
  Appsignal.config[:filter_parameters]
19
58
  )
20
59
 
21
- if sqs_msg.attributes.key?("SentTimestamp")
22
- options[:queue_start] = Time.at(sqs_msg.attributes["SentTimestamp"].to_i / 1000)
60
+ if attributes.key?("SentTimestamp")
61
+ options[:queue_start] = Time.at(attributes["SentTimestamp"].to_i / 1000)
23
62
  end
24
63
 
25
64
  Appsignal.monitor_transaction("perform_job.shoryuken", options) do
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "2.11.2".freeze
4
+ VERSION = "2.11.3".freeze
5
5
  end
@@ -1,55 +1,73 @@
1
1
  describe Appsignal::Hooks::ShoryukenMiddleware do
2
- let(:current_transaction) { background_job_transaction }
3
-
4
2
  class DemoShoryukenWorker
5
3
  end
6
4
 
5
+ let(:time) { "2010-01-01 10:01:00UTC" }
7
6
  let(:worker_instance) { DemoShoryukenWorker.new }
8
- let(:queue) { double }
9
- let(:sqs_msg) { double(:attributes => {}) }
7
+ let(:queue) { "some-funky-queue-name" }
8
+ let(:sqs_msg) { double(:message_id => "msg1", :attributes => {}) }
10
9
  let(:body) { {} }
11
-
12
- before do
13
- allow(Appsignal::Transaction).to receive(:current).and_return(current_transaction)
14
- start_agent
10
+ before(:context) { start_agent }
11
+ around { |example| keep_transactions { example.run } }
12
+
13
+ def perform_job(&block)
14
+ block ||= lambda {}
15
+ Timecop.freeze(Time.parse(time)) do
16
+ Appsignal::Hooks::ShoryukenMiddleware.new.call(
17
+ worker_instance,
18
+ queue,
19
+ sqs_msg,
20
+ body,
21
+ &block
22
+ )
23
+ end
15
24
  end
16
25
 
17
26
  context "with a performance call" do
18
- let(:queue) { "some-funky-queue-name" }
27
+ let(:sent_timestamp) { Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 }
19
28
  let(:sqs_msg) do
20
- double(:attributes => { "SentTimestamp" => Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 })
29
+ double(:message_id => "msg1", :attributes => { "SentTimestamp" => sent_timestamp })
21
30
  end
22
31
 
23
32
  context "with complex argument" do
24
- let(:body) do
25
- {
26
- :foo => "Foo",
27
- :bar => "Bar"
28
- }
29
- end
30
- after do
31
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
32
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
33
- # nothing
34
- end
35
- end
36
- end
33
+ let(:body) { { :foo => "Foo", :bar => "Bar" } }
37
34
 
38
35
  it "wraps the job in a transaction with the correct params" do
39
- expect(Appsignal).to receive(:monitor_transaction).with(
40
- "perform_job.shoryuken",
41
- :class => "DemoShoryukenWorker",
42
- :method => "perform",
43
- :metadata => {
44
- :queue => "some-funky-queue-name",
45
- "SentTimestamp" => 217_123_200_000
46
- },
47
- :params => {
48
- :foo => "Foo",
49
- :bar => "Bar"
50
- },
51
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
36
+ allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
37
+ expect { perform_job }.to change { created_transactions.length }.by(1)
38
+
39
+ transaction = last_transaction
40
+ expect(transaction).to be_completed
41
+ transaction_hash = transaction.to_h
42
+ expect(transaction_hash).to include(
43
+ "action" => "DemoShoryukenWorker#perform",
44
+ "id" => kind_of(String), # AppSignal generated id
45
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
46
+ "error" => nil
52
47
  )
48
+ expect(transaction_hash["events"].first).to include(
49
+ "allocation_count" => kind_of(Integer),
50
+ "body" => "",
51
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
52
+ "child_allocation_count" => kind_of(Integer),
53
+ "child_duration" => kind_of(Float),
54
+ "child_gc_duration" => kind_of(Float),
55
+ "count" => 1,
56
+ "gc_duration" => kind_of(Float),
57
+ "start" => kind_of(Float),
58
+ "duration" => kind_of(Float),
59
+ "name" => "perform_job.shoryuken",
60
+ "title" => ""
61
+ )
62
+ expect(transaction_hash["sample_data"]).to include(
63
+ "params" => { "foo" => "Foo", "bar" => "Bar" },
64
+ "metadata" => {
65
+ "message_id" => "msg1",
66
+ "queue" => queue,
67
+ "SentTimestamp" => sent_timestamp
68
+ }
69
+ )
70
+ expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
53
71
  end
54
72
 
55
73
  context "with parameter filtering" do
@@ -57,21 +75,16 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
57
75
  Appsignal.config = project_fixture_config("production")
58
76
  Appsignal.config[:filter_parameters] = ["foo"]
59
77
  end
78
+ after do
79
+ Appsignal.config[:filter_parameters] = []
80
+ end
60
81
 
61
82
  it "filters selected arguments" do
62
- expect(Appsignal).to receive(:monitor_transaction).with(
63
- "perform_job.shoryuken",
64
- :class => "DemoShoryukenWorker",
65
- :method => "perform",
66
- :metadata => {
67
- :queue => "some-funky-queue-name",
68
- "SentTimestamp" => 217_123_200_000
69
- },
70
- :params => {
71
- :foo => "[FILTERED]",
72
- :bar => "Bar"
73
- },
74
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
83
+ perform_job
84
+
85
+ transaction_hash = last_transaction.to_h
86
+ expect(transaction_hash["sample_data"]).to include(
87
+ "params" => { "foo" => "[FILTERED]", "bar" => "Bar" }
75
88
  )
76
89
  end
77
90
  end
@@ -81,23 +94,12 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
81
94
  let(:body) { "foo bar" }
82
95
 
83
96
  it "handles string arguments" do
84
- expect(Appsignal).to receive(:monitor_transaction).with(
85
- "perform_job.shoryuken",
86
- :class => "DemoShoryukenWorker",
87
- :method => "perform",
88
- :metadata => {
89
- :queue => "some-funky-queue-name",
90
- "SentTimestamp" => 217_123_200_000
91
- },
92
- :params => { :params => body },
93
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
94
- )
97
+ perform_job
95
98
 
96
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
97
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
98
- # nothing
99
- end
100
- end
99
+ transaction_hash = last_transaction.to_h
100
+ expect(transaction_hash["sample_data"]).to include(
101
+ "params" => { "params" => body }
102
+ )
101
103
  end
102
104
  end
103
105
 
@@ -105,58 +107,103 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
105
107
  let(:body) { 1 }
106
108
 
107
109
  it "handles primitive types as arguments" do
108
- expect(Appsignal).to receive(:monitor_transaction).with(
109
- "perform_job.shoryuken",
110
- :class => "DemoShoryukenWorker",
111
- :method => "perform",
112
- :metadata => {
113
- :queue => "some-funky-queue-name",
114
- "SentTimestamp" => 217_123_200_000
115
- },
116
- :params => { :params => body },
117
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
118
- )
110
+ perform_job
119
111
 
120
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
121
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
122
- # nothing
123
- end
124
- end
112
+ transaction_hash = last_transaction.to_h
113
+ expect(transaction_hash["sample_data"]).to include(
114
+ "params" => { "params" => body }
115
+ )
125
116
  end
126
117
  end
127
118
  end
128
119
 
129
120
  context "with exception" do
130
- let(:transaction) do
131
- Appsignal::Transaction.new(
132
- SecureRandom.uuid,
133
- Appsignal::Transaction::BACKGROUND_JOB,
134
- Appsignal::Transaction::GenericRequest.new({})
121
+ it "sets the exception on the transaction" do
122
+ expect do
123
+ expect do
124
+ perform_job { raise ExampleException, "error message" }
125
+ end.to raise_error(ExampleException)
126
+ end.to change { created_transactions.length }.by(1)
127
+
128
+ transaction = last_transaction
129
+ expect(transaction).to be_completed
130
+ transaction_hash = transaction.to_h
131
+ expect(transaction_hash).to include(
132
+ "action" => "DemoShoryukenWorker#perform",
133
+ "id" => kind_of(String), # AppSignal generated id
134
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
135
+ "error" => {
136
+ "name" => "ExampleException",
137
+ "message" => "error message",
138
+ "backtrace" => kind_of(String)
139
+ }
135
140
  )
136
141
  end
142
+ end
137
143
 
138
- before do
139
- allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
140
- expect(Appsignal::Transaction).to receive(:create)
141
- .with(
142
- kind_of(String),
143
- Appsignal::Transaction::BACKGROUND_JOB,
144
- kind_of(Appsignal::Transaction::GenericRequest)
145
- ).and_return(transaction)
144
+ context "with batched jobs" do
145
+ let(:sqs_msg) do
146
+ [
147
+ double(
148
+ :message_id => "msg2",
149
+ :attributes => { "SentTimestamp" => (Time.parse("1976-11-18 01:00:00UTC").to_i * 1000).to_s }
150
+ ),
151
+ double(
152
+ :message_id => "msg1",
153
+ :attributes => { "SentTimestamp" => sent_timestamp.to_s }
154
+ )
155
+ ]
146
156
  end
147
-
148
- it "sets the exception on the transaction" do
149
- expect(transaction).to receive(:set_error).with(ExampleException)
157
+ let(:body) do
158
+ [
159
+ "foo bar",
160
+ { :id => "123", :foo => "Foo", :bar => "Bar" }
161
+ ]
150
162
  end
163
+ let(:sent_timestamp) { Time.parse("1976-11-18 01:00:00UTC").to_i * 1000 }
151
164
 
152
- after do
165
+ it "creates a transaction for the batch" do
166
+ allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
153
167
  expect do
154
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
155
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
156
- raise ExampleException
157
- end
158
- end
159
- end.to raise_error(ExampleException)
168
+ perform_job {}
169
+ end.to change { created_transactions.length }.by(1)
170
+
171
+ transaction = last_transaction
172
+ expect(transaction).to be_completed
173
+ transaction_hash = transaction.to_h
174
+ expect(transaction_hash).to include(
175
+ "action" => "DemoShoryukenWorker#perform",
176
+ "id" => kind_of(String), # AppSignal generated id
177
+ "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
178
+ "error" => nil
179
+ )
180
+ expect(transaction_hash["events"].first).to include(
181
+ "allocation_count" => kind_of(Integer),
182
+ "body" => "",
183
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
184
+ "child_allocation_count" => kind_of(Integer),
185
+ "child_duration" => kind_of(Float),
186
+ "child_gc_duration" => kind_of(Float),
187
+ "count" => 1,
188
+ "gc_duration" => kind_of(Float),
189
+ "start" => kind_of(Float),
190
+ "duration" => kind_of(Float),
191
+ "name" => "perform_job.shoryuken",
192
+ "title" => ""
193
+ )
194
+ expect(transaction_hash["sample_data"]).to include(
195
+ "params" => {
196
+ "msg2" => "foo bar",
197
+ "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
198
+ },
199
+ "metadata" => {
200
+ "batch" => true,
201
+ "queue" => "some-funky-queue-name",
202
+ "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
203
+ }
204
+ )
205
+ # Queue time based on earliest/oldest timestamp from messages
206
+ expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
160
207
  end
161
208
  end
162
209
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.11.2
4
+ version: 2.11.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-01-11 00:00:00.000000000 Z
13
+ date: 2021-01-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack
@@ -415,7 +415,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
415
415
  - !ruby/object:Gem::Version
416
416
  version: '0'
417
417
  requirements: []
418
- rubygems_version: 3.1.4
418
+ rubygems_version: 3.2.5
419
419
  signing_key:
420
420
  specification_version: 4
421
421
  summary: Logs performance and exception data from your app to appsignal.com