Skip to content

Commit 0be9b79

Browse files
committed
wip
1 parent 3a53e4f commit 0be9b79

File tree

6 files changed

+157
-8
lines changed

6 files changed

+157
-8
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ eval_gemfile("#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION.split('.').take(2).join('.')}
22

33
# gem 'rails', '8.0.1'
44
# # gem 'rails', '7.0.8.7'
5+
# # gem 'rails', '~> 6.1.0'
56
# gem 'pg'
67
# gem 'lograge'
78
# gem 'redis'

gemfiles/ruby_3.4_rails61_postgres_redis.gemfile.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gemfiles/ruby_3.4_rails61_trilogy.gemfile.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/datadog/tracing/contrib/active_support/cache/events/cache.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,12 @@ def trace?(event, _payload)
6262
end
6363

6464
def on_start(span, event, _id, payload)
65-
# Fall back to `key` in case new APIs are introduced that don't directly call `normalize_key`.
66-
# This is a defensive measure, as of Rails 8, this does not happen.
67-
raise "payload[:dd_original_keys] is nil: #{payload[:key]}" if payload[:dd_original_keys].nil?
65+
# Since Rails 8, `dd_original_keys` contains the denormalized key provided by the user.
66+
# In previous versions, the denormalized key is stored in the official `key` attribute.
67+
# We fall back to `key`, even in Rails 8, as a defensive measure.
68+
#
69+
# TODO: Remove this raise
70+
raise "payload[:dd_original_keys] is nil: #{payload[:key]}" if payload[:dd_original_keys].nil? && ::Rails.version.to_i >= 8
6871
key = payload[:dd_original_keys] || payload[:key]
6972
store = payload[:store]
7073

@@ -99,9 +102,6 @@ def set_cache_key(span, key, multi_key)
99102
cache_key = Core::Utils.truncate(resolved_key, Ext::QUANTIZE_CACHE_MAX_KEY_SIZE)
100103
span.set_tag(Ext::TAG_CACHE_KEY_MULTI, cache_key)
101104
else
102-
# `dd_original_keys` is an Array with one element when `multi_key` is false.
103-
# But if using the fallback `key`, it is a simple scalar value.
104-
key = key.is_a?(Array) ? key[0] : key
105105
resolved_key = ::ActiveSupport::Cache.expand_cache_key(key)
106106
cache_key = Core::Utils.truncate(resolved_key, Ext::QUANTIZE_CACHE_MAX_KEY_SIZE)
107107
span.set_tag(Ext::TAG_CACHE_KEY, cache_key)

spec/datadog/tracing/contrib/rails/cache_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
require 'datadog/tracing/contrib/rails/rails_helper'
88

9-
RSpec.describe 'Rails cache', execute_in_fork: true do
9+
RSpec.describe 'Rails cache', execute_in_fork: false do
1010
include_context 'Rails test application'
1111

1212
before do

spec/support/execute_in_fork.rb

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,163 @@
11
module ForkableExample
22
def finish(reporter)
33
# TODO: better name than execute_in_fork?
4-
if @metadata[:execute_in_fork]
4+
if @metadata[:execute_in_fork] && Process.ppid != 1
55
super ? exit(0) : exit(1)
66
else
77
super
88
end
99
end
1010

11+
EXAMPLE_SENTINEL = Module.new
12+
MODULE_VALUE = Struct.new(:name)
13+
1114
def run(example_group_instance, reporter)
1215
if @metadata[:execute_in_fork]
16+
reader, writer = IO.pipe
17+
18+
reader.binmode
19+
writer.binmode
20+
1321
pid = fork do
22+
reporter.singleton_class.prepend(ForkableExample::Reporter)
23+
execution_result.singleton_class.prepend(ForkableExample::ExecutionResult)
24+
execution_result.instance_variable_set(:@fork_writer_pipe, writer)
25+
reporter.instance_variable_set(:@fork_writer_pipe, writer)
26+
27+
reporter.instance_variable_set(:@fork_example_sentinel, self)
28+
29+
reader.close
30+
1431
super
32+
33+
# writer.puts "Hello from child process"
34+
writer.close
1535
end
1636

37+
writer.close
38+
1739
_, status = Process.wait2(pid)
40+
41+
while (read_size = reader.gets)
42+
call = Marshal.load(reader.read(Integer(read_size)))
43+
44+
args = call[:args]
45+
args.map! do |arg|
46+
if arg == EXAMPLE_SENTINEL
47+
self
48+
elsif arg.is_a?(MODULE_VALUE)
49+
::Object.const_get(arg.name)
50+
elsif arg.is_a?(RSpec::Core::Notifications::ExampleNotification) && arg.example == EXAMPLE_SENTINEL
51+
::RSpec::Core::Notifications::ExampleNotification.for(self)
52+
else
53+
arg
54+
end
55+
end
56+
57+
# puts("call: #{call}")
58+
59+
if call[:receiver] == :reporter
60+
reporter.send(call[:method], *args)
61+
elsif call[:receiver] == :execution_result
62+
execution_result.send(call[:method], *args)
63+
end
64+
end
65+
66+
reader.close
67+
1868
status.success?
1969
else
2070
super
2171
end
2272
end
73+
74+
module Reporter
75+
76+
77+
# PUBLIC_INTERNAL_METHODS = [:start, :stop, :publish, :finish, :close_after, :notify, :fail_fast_limit_met?]
78+
RSpec::Core::Reporter.instance_methods(false).each do |method|
79+
define_method(method) do |*args|
80+
original_args = args
81+
82+
83+
unless @fork_instrumenting # Nested calls are skipped, since they will invoked by the first call site
84+
begin
85+
@fork_instrumenting = true
86+
87+
args = args.map do |arg|
88+
if arg == @fork_example_sentinel
89+
EXAMPLE_SENTINEL
90+
elsif arg.is_a?(Module)
91+
MODULE_VALUE.new(arg.name)
92+
elsif arg.is_a?(::RSpec::Core::Notifications::ExampleNotification) && arg.example == @fork_example_sentinel
93+
::RSpec::Core::Notifications::ExampleNotification.send(:new, EXAMPLE_SENTINEL)
94+
else
95+
arg
96+
end
97+
end
98+
99+
# puts({ method: method, args: args })
100+
101+
dump = Marshal.dump({ receiver: :reporter, method: method, args: args })
102+
# Write a header hex int with the size of the next object dump
103+
@fork_writer_pipe.write(sprintf("0x%x\n", dump.bytesize))
104+
@fork_writer_pipe.write(dump)
105+
106+
# puts({ method: method, args: args })
107+
rescue => e
108+
puts "ERRORERRORERROR 1111"
109+
puts args
110+
puts e
111+
end
112+
end
113+
114+
super(*original_args)
115+
ensure
116+
@fork_instrumenting = false
117+
end
118+
end
119+
end
120+
121+
module ExecutionResult
122+
::RSpec::Core::Example::ExecutionResult.instance_methods(false).reject{|m|m.end_with?('?')}.each do |method|
123+
define_method(method) do |*args|
124+
original_args = args
125+
126+
unless @fork_instrumenting # Nested calls are skipped, since they will invoked by the first call site
127+
begin
128+
@fork_instrumenting = true
129+
130+
# args = args.map do |arg|
131+
# if arg == @fork_example_sentinel
132+
# EXAMPLE_SENTINEL
133+
# elsif arg.is_a?(Module)
134+
# MODULE_VALUE.new(arg.name)
135+
# else
136+
# arg
137+
# end
138+
# end
139+
140+
# puts({ method: method, args: args })
141+
142+
dump = Marshal.dump({ receiver: :execution_result, method: method, args: args })
143+
# Write a header hex int with the size of the next object dump
144+
@fork_writer_pipe.write(sprintf("0x%x\n", dump.bytesize))
145+
@fork_writer_pipe.write(dump)
146+
147+
# puts({ method: method, args: args })
148+
rescue => e
149+
puts "ERRORERRORERROR 2222"
150+
puts args
151+
puts e
152+
end
153+
end
154+
155+
super(*original_args)
156+
ensure
157+
@fork_instrumenting = false
158+
end
159+
end
160+
end
23161
end
24162

25163
RSpec::Core::Example.prepend(ForkableExample)

0 commit comments

Comments
 (0)