Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add instrumentation for Concurrent Ruby #1682

Merged
merged 70 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
9ce6360
concurrent_ruby scaffold
hannahramadan Dec 1, 2022
298fa15
Merge branch 'dev' into concurrent_ruby
fallwith Dec 7, 2022
0b22218
Merge branch 'dev' into concurrent_ruby
kaylareopelle Dec 7, 2022
0d5dff4
Merge branch 'dev' into concurrent_ruby_promises
kaylareopelle Dec 7, 2022
cbd49b0
WIP Instrument Concurrent::Promises#future
kaylareopelle Dec 8, 2022
c40a516
Rubocop
kaylareopelle Dec 8, 2022
c187a55
Merge branch 'dev' into concurrent_ruby
kaylareopelle Dec 8, 2022
63e3a50
concurrent_ruby scaffold
hannahramadan Dec 1, 2022
293d423
WIP Instrument Concurrent::Promises#future
kaylareopelle Dec 8, 2022
561274d
Rubocop
kaylareopelle Dec 8, 2022
d0791ff
Instrument Concurrent::ThreadPoolExecutor#post
kaylareopelle Dec 14, 2022
ae47f85
Update chain instrumenation
kaylareopelle Dec 14, 2022
2f9a058
Merge branch 'dev' into concurrent_ruby
kaylareopelle Dec 14, 2022
c444728
Merge branch 'concurrent_ruby' into concurrent_ruby_promises
kaylareopelle Dec 14, 2022
6e8cbee
WIP concurrent ruby tracing thread block
tannalynn Dec 16, 2022
8d70c30
DRYed up the block tracing methods for concurent
tannalynn Dec 16, 2022
572bf20
removed check current txn check from thread block
tannalynn Dec 16, 2022
47c56eb
resolve intermittent failures
tannalynn Dec 16, 2022
f7a8830
enable both chain and prepend for concurrent ruby
tannalynn Dec 16, 2022
3784309
WIP instrument Rejected state
tannalynn Dec 17, 2022
728cf1e
Moved concurrent ruby instrumentation out
tannalynn Dec 27, 2022
b1e90a3
Merge branch 'dev' into concurrent_ruby_promises
tannalynn Dec 27, 2022
6753e08
removed rubocop disables that aren't needed
tannalynn Dec 27, 2022
233f0d0
remove specific rubygems version to upgrade to
tannalynn Dec 27, 2022
f74f0fd
pin rubys 2.3 2.4 2.5 to rubygems 3.0.6
tannalynn Dec 27, 2022
f9eca26
what errors exactly happen with 3.0.6 rubygems
tannalynn Dec 27, 2022
17fd78e
specific rubygems versions for different rubys
tannalynn Dec 27, 2022
d9cf479
more debugging statements for bundler script
tannalynn Dec 27, 2022
e6396f6
silent rubygems update
tannalynn Dec 27, 2022
68ae6fe
echo command in same one line
tannalynn Dec 27, 2022
fe03b0d
dont return early on some ruby versions
tannalynn Dec 27, 2022
8257fd2
pin 2.6 to 306
tannalynn Dec 28, 2022
4c6e749
remove commented otu code
tannalynn Dec 28, 2022
8de24bb
Merge branch 'update_minitest_failures' into concurrent_ruby_promises
tannalynn Dec 28, 2022
4268a4c
remove :: rubocop doesn't like
tannalynn Dec 28, 2022
c490b39
Merge branch 'dev' into concurrent_ruby_promises
tannalynn Dec 28, 2022
4590d5a
Update config descriptions and comments
kaylareopelle Dec 28, 2022
c074f30
Merge branch 'dev' into concurrent_ruby_promises
kaylareopelle Dec 28, 2022
bd217e8
Merge branch 'concurrent_ruby_promises' of github.com:newrelic/newrel…
kaylareopelle Dec 28, 2022
d4a1f44
Clean up test file
kaylareopelle Dec 28, 2022
3afaa97
More test refactors
kaylareopelle Dec 28, 2022
7147f51
Pass segment parent into thread block
kaylareopelle Dec 28, 2022
0bbc679
Move tracing_enabled? check to prepend/chain
kaylareopelle Dec 28, 2022
8aadf54
Prevent nil output errors
kaylareopelle Dec 28, 2022
c720645
Rubocop
kaylareopelle Dec 29, 2022
153ef94
Add test for transaction noticed error
kaylareopelle Dec 29, 2022
ac5fbaf
Pass txn to assertion
kaylareopelle Dec 29, 2022
24942ce
Add tests for thread_ids_enabled config
kaylareopelle Dec 29, 2022
68db668
Record only one segment per call
kaylareopelle Dec 29, 2022
e776ea7
Record supportability metric on first invocation
kaylareopelle Dec 29, 2022
02d604e
Merge branch 'dev' into concurrent_ruby_promises
kaylareopelle Dec 29, 2022
c7091f3
Add CHANGELOG entry
kaylareopelle Dec 29, 2022
6ff329b
Remove guard clause
kaylareopelle Dec 29, 2022
4bcfb43
Replace assertion
kaylareopelle Dec 29, 2022
226d3f9
Make the match condition more flexible
kaylareopelle Dec 29, 2022
654470c
Require 'fiber'
kaylareopelle Jan 3, 2023
1ce60a6
Replace `elasticsearch` with `concurrent-ruby`
kaylareopelle Jan 3, 2023
af037da
Merge branch 'dev' into concurrent_ruby_promises
kaylareopelle Jan 3, 2023
c1f3412
Merge branch 'concurrent_ruby_promises' of github.com:newrelic/newrel…
kaylareopelle Jan 3, 2023
90ff58e
Return unless args.last is Exception
kaylareopelle Jan 3, 2023
22fb1ea
Refactor segments within transaction
kaylareopelle Jan 3, 2023
4c97f2b
Capture segment errors outside current_txn
hannahramadan Jan 4, 2023
b3d4531
Small typo fix
hannahramadan Jan 4, 2023
654965b
Lock support to 1.1.5 and above
kaylareopelle Jan 4, 2023
8315414
Merge branch 'concurrent_ruby_promises' of github.com:newrelic/newrel…
kaylareopelle Jan 4, 2023
428e568
Make parent an optional kwarg
kaylareopelle Jan 4, 2023
da5c467
Merge branch 'dev' into concurrent_ruby_promises
kaylareopelle Jan 4, 2023
1822b46
Update CHANGELOG.md
kaylareopelle Jan 4, 2023
2bd9620
Update Envfile to call create_gemfiles
kaylareopelle Jan 5, 2023
1775b57
Fix variable name
kaylareopelle Jan 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

Following the 3.2.0 release of Ruby, the New Relic Ruby Agent has confirmed compatibility with and now supports the official release of Ruby 3.2.0. [PR#1715](https://github.com/newrelic/newrelic-ruby-agent/pull/1715)

- **Add instrumentation for concurrent-ruby**

Instrumentation for the [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby) gem has been added to the agent. for versions 1.1.5 and above. When a transaction is already in progress and a call to a `Concurrent::` method that routes through `Concurrent::ThreadPoolExecutor#post` is made, a segment will be added to the transaction. Any content within the block passed to the `Concurrent::` method that is instrumented by the agent, such as a call to `Net::HTTP.get`, will have a nested segment created. [PR#1682](https://github.com/newrelic/newrelic-ruby-agent/pull/1682)

| Configuration name | Default | Behavior |
| --------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `instrumentation.concurrent_ruby` | auto | Controls auto-instrumentation of the concurrent-ruby library at start up. May be one of `auto`, `prepend`, `chain`, `disabled`. |

- **Infinite Tracing: Use batching and compression**

For [Infinite Tracing](https://docs.newrelic.com/docs/distributed-tracing/infinite-tracing/introduction-infinite-tracing/) which Ruby applications can leverage with the `newrelic-infinite_tracing` gem, payloads will now be batched and compressed to signficantly decrease the amount of outbound network traffic. [PR#1723](https://github.com/newrelic/newrelic-ruby-agent/pull/1723)
Expand Down
15 changes: 15 additions & 0 deletions lib/new_relic/agent/configuration/default_source.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1569,6 +1569,14 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of bunny at start up. May be one of [auto|prepend|chain|disabled].'
},
:'instrumentation.concurrent_ruby' => {
:default => 'auto',
:public => true,
:type => String,
:dynamic_name => true,
:allowed_from_server => false,
:description => 'Controls auto-instrumentation of the concurrent-ruby library at start up. May be one of [auto|prepend|chain|disabled].'
},
:'instrumentation.curb' => {
:default => instrumentation_value_of(:disable_curb),
:documentation_default => 'auto',
Expand Down Expand Up @@ -1802,6 +1810,13 @@ def self.enforce_fallback(allowed_values: nil, fallback: nil)
:allowed_from_server => false,
:description => "Controls auto-instrumentation of the Thread class at start up to automatically add tracing to all Threads created in the application."
},
:'thread_ids_enabled' => {
:default => false,
:public => false,
:type => Boolean,
:allowed_from_server => false,
:description => "If enabled, will append the current Thread and Fiber object ids onto the segment names of segments created in Threads and concurrent-ruby"
},
fallwith marked this conversation as resolved.
Show resolved Hide resolved
:'instrumentation.tilt' => {
:default => "auto",
:public => true,
Expand Down
31 changes: 31 additions & 0 deletions lib/new_relic/agent/instrumentation/concurrent_ruby.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require_relative 'concurrent_ruby/instrumentation'
require_relative 'concurrent_ruby/chain'
require_relative 'concurrent_ruby/prepend'

DependencyDetection.defer do
named :'concurrent_ruby'

depends_on do
defined?(Concurrent) &&
Gem::Version.new(Concurrent::VERSION) >= Gem::Version.new('1.1.5')
end

executes do
NewRelic::Agent.logger.info('Installing concurrent-ruby instrumentation')

if use_prepend?
prepend_instrument(Concurrent::ThreadPoolExecutor, NewRelic::Agent::Instrumentation::ConcurrentRuby::Prepend)

[Concurrent::Promises.const_get(:'InternalStates')::Rejected,
Concurrent::Promises.const_get(:'InternalStates')::PartiallyRejected].each do |klass|
klass.prepend(NewRelic::Agent::Instrumentation::ConcurrentRuby::ErrorPrepend)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After chatting with @tannalynn, klass.prepend is used instead of prepend_instrument because all prepend_instrument adds is logging around the instrumented library. Since the logging occurs on line 20, we don't need to repeat it here.

end
else
chain_instrument NewRelic::Agent::Instrumentation::ConcurrentRuby::Chain
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this class has to have a ::Chain suffix to have the log messages for what was instrumented show up correctly. This method looks for the second-to-last namespace and puts that in the logs. Without the ::Chain suffix, "Instrumentation" is logged instead of "ConcurrentRuby". I'll make a separate PR to fix this in the instrumentation generator.

end
end
end
36 changes: 36 additions & 0 deletions lib/new_relic/agent/instrumentation/concurrent_ruby/chain.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

module NewRelic::Agent::Instrumentation
module ConcurrentRuby::Chain
def self.instrument!
::Concurrent::ThreadPoolExecutor.class_eval do
include NewRelic::Agent::Instrumentation::ConcurrentRuby

alias_method(:post_without_new_relic, :post)

def post(*args, &task)
return post_without_new_relic(*args, &task) unless NewRelic::Agent::Tracer.tracing_enabled?

traced_task = add_task_tracing(*args, &task)
post_without_new_relic(*args, &traced_task)
end
end

[::Concurrent::Promises.const_get(:'InternalStates')::Rejected,
::Concurrent::Promises.const_get(:'InternalStates')::PartiallyRejected].each do |klass|
klass.class_eval do
alias_method(:initialize_without_new_relic, :initialize)

# Uses args.last to record the error becuase the methods that this will monkey patch
# look like: initialize(reason) & initialize(value, reason)
def initialize(*args)
NewRelic::Agent.notice_error(args.last) if args.last.is_a?(Exception)
initialize_without_new_relic(*args)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

module NewRelic::Agent::Instrumentation
module ConcurrentRuby
SEGMENT_NAME = 'Concurrent/Task'
SUPPORTABILITY_METRIC = 'Supportability/ConcurrentRuby/Invoked'

def add_task_tracing(*args, &task)
NewRelic::Agent.record_metric_once(SUPPORTABILITY_METRIC)

NewRelic::Agent::Tracer.thread_block_with_current_transaction(
*args,
segment_name: SEGMENT_NAME,
parent: NewRelic::Agent::Tracer.current_segment,
&task
)
end
end
end
27 changes: 27 additions & 0 deletions lib/new_relic/agent/instrumentation/concurrent_ruby/prepend.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

module NewRelic::Agent::Instrumentation
module ConcurrentRuby
module Prepend
include NewRelic::Agent::Instrumentation::ConcurrentRuby

def post(*args, &task)
return super(*args, &task) unless NewRelic::Agent::Tracer.tracing_enabled?

traced_task = add_task_tracing(*args, &task)
super(*args, &traced_task)
end
end

module ErrorPrepend
# Uses args.last to record the error because the methods that this will be prepended to
# look like: initialize(reason) & initialize(value, reason)
def initialize(*args)
NewRelic::Agent.notice_error(args.last) if args.last.is_a?(Exception)
super
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def self.class_name(traced_obj, options = {})
#
# Seldomly used options:
#
# * <tt>:class_name => aClass.name</tt> is used to override the name
# * <tt>:class_name => Class.name</tt> is used to override the name
# of the class when used inside the metric name. Default is the
# current class.
# * <tt>:path => metric_path</tt> is *deprecated* in the public API. It
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ def initialize_with_newrelic_tracing
def add_thread_tracing(*args, &block)
return block if skip_tracing?

NewRelic::Agent::Tracer.thread_block_with_current_transaction(*args, &block)
NewRelic::Agent::Tracer.thread_block_with_current_transaction(
*args,
segment_name: 'Ruby/Thread',
&block
)
end

def skip_tracing?
Expand Down
10 changes: 7 additions & 3 deletions lib/new_relic/agent/tracer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

require 'fiber'
require 'new_relic/agent/transaction'
require 'new_relic/agent/transaction/segment'
require 'new_relic/agent/transaction/datastore_segment'
Expand Down Expand Up @@ -409,15 +410,18 @@ def clear_state

alias_method :tl_clear, :clear_state

def thread_block_with_current_transaction(*args, &block)
def thread_block_with_current_transaction(*args, segment_name:, parent: nil, &block)
current_txn = ::Thread.current[:newrelic_tracer_state].current_transaction if ::Thread.current[:newrelic_tracer_state] && ::Thread.current[:newrelic_tracer_state].is_execution_traced?
proc do
begin
if current_txn
NewRelic::Agent::Tracer.state.current_transaction = current_txn
segment = NewRelic::Agent::Tracer.start_segment(name: "Ruby/Thread/#{::Thread.current.object_id}")
segment_name += "/Thread#{::Thread.current.object_id}/Fiber#{::Fiber.current.object_id}" if NewRelic::Agent.config[:'thread_ids_enabled']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We refactored this method so that it could be used by both concurrent ruby and threads. (also fibers in future!).
We also removed the thread id from the segment name to prevent thread related MGIs from occurring. I really wanted to option to turn on thread ids because it's helped me out with figuring issues out in the past, so we added the new thread_ids_enabled so that we can turn it on whenever we think it would help out.

segment = NewRelic::Agent::Tracer.start_segment(name: segment_name, parent: parent)
end
NewRelic::Agent::Tracer.capture_segment_error(segment) do
yield(*args)
end
yield(*args) if block.respond_to?(:call)
ensure
::NewRelic::Agent::Transaction::Segment.finish(segment)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/new_relic/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def run_command(command)
raise NewRelic::CommandRunFailedError.new("Failed to run command '#{command}': #{message}")
end

output.chomp
output.chomp if output
end

# TODO: Open3 defers the actual execution of a binary to Process.spawn,
Expand Down
6 changes: 5 additions & 1 deletion lib/new_relic/traced_thread.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ def initialize(*args, &block)
def create_traced_block(*args, &block)
return block if NewRelic::Agent.config[:'instrumentation.thread.tracing'] # if this is on, don't double trace

NewRelic::Agent::Tracer.thread_block_with_current_transaction(*args, &block)
NewRelic::Agent::Tracer.thread_block_with_current_transaction(
*args,
segment_name: 'Ruby/TracedThread',
&block
)
end
end
end
4 changes: 4 additions & 0 deletions newrelic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ common: &default_settings
# May be one of [auto|prepend|chain|disabled].
# instrumentation.bunny: auto

# Controls auto-instrumentation of concurrent_ruby at start up.
# May be one of [auto|prepend|chain|disabled]
# instrumentation.concurrent_ruby: auto

# Controls auto-instrumentation of Curb at start up.
# May be one of [auto|prepend|chain|disabled].
# instrumentation.curb: auto
Expand Down
21 changes: 21 additions & 0 deletions test/multiverse/suites/concurrent_ruby/Envfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

instrumentation_methods :chain, :prepend

# The 1.1.x series of Concurrent Ruby starts to use the error classes
# we reference in our logic to notice errors after 1.1.5
# 1.1.4 and below do not use these classes.
CONCURRENT_RUBY_VERSIONS = [
[nil, 2.2],
['1.1.5', 2.2]
]

def gem_list(concurrent_version = nil)
<<-RB
gem 'concurrent-ruby'#{concurrent_version}
RB
end

create_gemfiles(CONCURRENT_RUBY_VERSIONS, gem_list)
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

kaylareopelle marked this conversation as resolved.
Show resolved Hide resolved
class ConcurrentRubyInstrumentationTest < Minitest::Test
EXPECTED_SEGMENTS_FOR_NESTED_CALLS = [
'Concurrent/Task',
'External/www.example.com/Net::HTTP/GET'
]

# Helper methods
def future_in_transaction(&block)
in_transaction do
future = Concurrent::Promises.future { yield }
future.wait!
end
end

def concurrent_promises_calls_net_http_in_block
future_in_transaction { Net::HTTP.get(URI('http://www.example.com')) }
end

def simulate_error
future = Concurrent::Promises.future { raise 'hi' }
future.wait!
end

def assert_segment_noticed_simulated_error(txn)
assert_segment_noticed_error txn, /Concurrent\/Task$/, /RuntimeError/, /hi/i
end

def assert_expected_segments_in_transaction(txn)
assert_predicate (txn.segments.map(&:name) & EXPECTED_SEGMENTS_FOR_NESTED_CALLS), :any?
end

# Tests
def test_promises_future_creates_segment_with_default_name
txn = future_in_transaction { 'time keeps on slipping' }
expected_segment = 'Concurrent/Task'

assert_equal(2, txn.segments.length)
assert_includes txn.segments.map(&:name), expected_segment
end

def test_promises_future_creates_segments_for_nested_instrumented_calls
with_config(:'instrumentation.thread.tracing' => false) do
txn = concurrent_promises_calls_net_http_in_block

assert_equal(3, txn.segments.length)
assert_expected_segments_in_transaction(txn)
end
end

def test_promises_future_creates_segments_for_nested_instrumented_calls_with_thread_tracing_enabled
with_config(:'instrumentation.thread.tracing' => true) do
txn = concurrent_promises_calls_net_http_in_block

# We can't check the number of segments when thread tracing is enabled because we cannot rely on concurrent-ruby
# creating threads during this transaction, as it can reuse threads that were created previously.
# Instead, we check to make sure the segments that should be present are.
assert_expected_segments_in_transaction(txn)
end
end

def test_promises_future_captures_segment_error
txn = in_transaction do
# TODO: OLD RUBIES - RUBY_VERSION 2.2
# specific "begin" in block can be removed once we drop support for 2.2
begin
simulate_error
rescue StandardError => e
# NOOP -- allowing span to notice error
end
end

assert_segment_noticed_simulated_error(txn)
end

def test_noticed_error_at_segment_and_txn_on_error
txn = nil
begin
in_transaction do |test_txn|
txn = test_txn
simulate_error
end
rescue StandardError => e
kaylareopelle marked this conversation as resolved.
Show resolved Hide resolved
# NOOP -- allowing span and transaction to notice error
end

assert_segment_noticed_simulated_error(txn)
assert_transaction_noticed_error txn, /RuntimeError/
end

def test_task_segment_has_correct_parent
txn = future_in_transaction { 'are you my mother?' }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a movie that has a character deliver this line and follow it up with "Am I part of you both?" but I now I can't remember it's name. Something about a garden I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting! I was thinking Dr. Seuss when I wrote this one. If you think of the title, let me know!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, the word "garden" was in the quote itself and it's from a book - not a movie.

“Are these Gardens my mother?” she asked suddenly, and he told her they were. “Am I a part of you both?” she asked, and he told her yes.

The Druid of Shannara

Now I'm wondering why I thought it was a movie. I definitely have an old (15+ years?) audio clip of the quote. Maybe there was an audio book? Weird.

task_segment = txn.segments.find { |n| n.name == 'Concurrent/Task' }

assert_equal task_segment.parent.name, txn.best_name
end

def test_segment_not_created_if_tracing_disabled
NewRelic::Agent::Tracer.stub :tracing_enabled?, false do
txn = future_in_transaction { 'the revolution will not be televised' }

assert_predicate txn.segments, :one?
assert_equal txn.segments.first.name, txn.best_name
end
end

def test_supportability_metric_recorded_once
in_transaction do
Concurrent::Promises.future { 'one-banana' }
end

in_transaction do
Concurrent::Promises.future { 'two-banana' }
end

assert_metrics_recorded(NewRelic::Agent::Instrumentation::ConcurrentRuby::SUPPORTABILITY_METRIC)
end
end
Loading