Skip to content

Commit 2915319

Browse files
committed
Add sample_rand to transaction
1 parent 924db2d commit 2915319

File tree

6 files changed

+232
-38
lines changed

6 files changed

+232
-38
lines changed

sentry-ruby/lib/sentry/transaction.rb

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require "sentry/baggage"
44
require "sentry/profiler"
5+
require "sentry/utils/sample_rand"
56
require "sentry/propagation_context"
67

78
module Sentry
@@ -57,12 +58,17 @@ class Transaction < Span
5758
# @return [Profiler]
5859
attr_reader :profiler
5960

61+
# Sample rand value generated from trace_id
62+
# @return [String]
63+
attr_reader :sample_rand
64+
6065
def initialize(
6166
hub:,
6267
name: nil,
6368
source: :custom,
6469
parent_sampled: nil,
6570
baggage: nil,
71+
sample_rand: nil,
6672
**options
6773
)
6874
super(transaction: self, **options)
@@ -82,12 +88,15 @@ def initialize(
8288
@effective_sample_rate = nil
8389
@contexts = {}
8490
@measurements = {}
91+
@sample_rand = sample_rand
8592

8693
unless @hub.profiler_running?
8794
@profiler = @configuration.profiler_class.new(@configuration)
8895
end
8996

9097
init_span_recorder
98+
99+
@sample_rand ||= Utils::SampleRand.generate_from_trace_id(@trace_id)
91100
end
92101

93102
# @deprecated use Sentry.continue_trace instead.
@@ -123,12 +132,15 @@ def self.from_sentry_trace(sentry_trace, baggage: nil, hub: Sentry.get_current_h
123132

124133
baggage.freeze!
125134

135+
sample_rand = extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled)
136+
126137
new(
127138
trace_id: trace_id,
128139
parent_span_id: parent_span_id,
129140
parent_sampled: parent_sampled,
130141
hub: hub,
131142
baggage: baggage,
143+
sample_rand: sample_rand,
132144
**options
133145
)
134146
end
@@ -139,6 +151,24 @@ def self.extract_sentry_trace(sentry_trace)
139151
PropagationContext.extract_sentry_trace(sentry_trace)
140152
end
141153

154+
def self.extract_sample_rand_from_baggage(baggage, trace_id, parent_sampled)
155+
return Utils::SampleRand.generate_from_trace_id(trace_id) unless baggage&.items
156+
157+
sample_rand_str = baggage.items["sample_rand"]
158+
if sample_rand_str && Utils::SampleRand.valid?(sample_rand_str.to_f)
159+
sample_rand_str.to_f
160+
else
161+
sample_rate_str = baggage.items["sample_rate"]
162+
sample_rate = sample_rate_str&.to_f
163+
164+
if sample_rate && parent_sampled != nil
165+
Utils::SampleRand.generate_from_sampling_decision(parent_sampled, sample_rate, trace_id)
166+
else
167+
Utils::SampleRand.generate_from_trace_id(trace_id)
168+
end
169+
end
170+
end
171+
142172
# @return [Hash]
143173
def to_hash
144174
hash = super
@@ -153,6 +183,13 @@ def to_hash
153183
hash
154184
end
155185

186+
def parent_sample_rate
187+
return nil unless @baggage&.items
188+
189+
sample_rate_str = @baggage.items["sample_rate"]
190+
sample_rate_str&.to_f
191+
end
192+
156193
# @return [Transaction]
157194
def deep_dup
158195
copy = super
@@ -225,7 +262,7 @@ def set_initial_sample_decision(sampling_context:)
225262
@effective_sample_rate /= 2**factor
226263
end
227264

228-
@sampled = Random.rand < @effective_sample_rate
265+
@sampled = @sample_rand < @effective_sample_rate
229266
end
230267

231268
if @sampled
@@ -331,6 +368,7 @@ def populate_head_baggage
331368
items = {
332369
"trace_id" => trace_id,
333370
"sample_rate" => effective_sample_rate&.to_s,
371+
"sample_rand" => Utils::SampleRand.format(@sample_rand),
334372
"sampled" => sampled&.to_s,
335373
"environment" => @environment,
336374
"release" => @release,

sentry-ruby/spec/sentry/client_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ def sentry_context
273273
expect(event.dynamic_sampling_context).to eq({
274274
"environment" => "development",
275275
"public_key" => "12345",
276+
"sample_rand" => Sentry::Utils::SampleRand.format(transaction.sample_rand),
276277
"sample_rate" => "1.0",
277278
"sampled" => "true",
278279
"transaction" => "test transaction",

sentry-ruby/spec/sentry/net/http_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
expect(request["baggage"]).to eq(
154154
"sentry-trace_id=#{transaction.trace_id},"\
155155
"sentry-sample_rate=1.0,"\
156+
"sentry-sample_rand=#{Sentry::Utils::SampleRand.format(transaction.sample_rand)},"\
156157
"sentry-sampled=true,"\
157158
"sentry-environment=development,"\
158159
"sentry-public_key=foobarbaz"

sentry-ruby/spec/sentry/rack/capture_exceptions_spec.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,11 @@ def verify_transaction_doesnt_inherit_external_transaction(transaction, external
279279
end
280280

281281
def wont_be_sampled_by_sdk
282-
allow(Random).to receive(:rand).and_return(1.0)
282+
allow(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(1.0)
283283
end
284284

285285
def will_be_sampled_by_sdk
286-
allow(Random).to receive(:rand).and_return(0.3)
286+
allow(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(0.3)
287287
end
288288

289289
before do
@@ -430,7 +430,7 @@ def will_be_sampled_by_sdk
430430

431431
context "when the transaction is sampled" do
432432
before do
433-
allow(Random).to receive(:rand).and_return(0.4)
433+
allow(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(0.4)
434434
end
435435

436436
it "starts a transaction and finishes it" do
@@ -488,7 +488,7 @@ def will_be_sampled_by_sdk
488488

489489
context "when the transaction is not sampled" do
490490
before do
491-
allow(Random).to receive(:rand).and_return(0.6)
491+
allow(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(1.0)
492492
end
493493

494494
it "doesn't do anything" do
@@ -506,7 +506,7 @@ def will_be_sampled_by_sdk
506506

507507
context "when there's an exception" do
508508
before do
509-
allow(Random).to receive(:rand).and_return(0.4)
509+
allow(Sentry::Utils::SampleRand).to receive(:generate_from_trace_id).and_return(0.4)
510510
end
511511

512512
it "still finishes the transaction" do

sentry-ruby/spec/sentry/transaction_spec.rb

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -257,22 +257,24 @@
257257
end
258258

259259
it "uses traces_sample_rate for sampling (positive result)" do
260-
allow(Random).to receive(:rand).and_return(0.4)
260+
# Create transaction with sample_rand < sample_rate (0.5) to ensure sampled
261+
transaction = described_class.new(op: "rack.request", hub: Sentry.get_current_hub, sample_rand: 0.4)
261262

262-
subject.set_initial_sample_decision(sampling_context: {})
263-
expect(subject.sampled).to eq(true)
264-
expect(subject.effective_sample_rate).to eq(0.5)
263+
transaction.set_initial_sample_decision(sampling_context: {})
264+
expect(transaction.sampled).to eq(true)
265+
expect(transaction.effective_sample_rate).to eq(0.5)
265266
expect(string_io.string).to include(
266267
"[Tracing] Starting <rack.request> transaction"
267268
)
268269
end
269270

270271
it "uses traces_sample_rate for sampling (negative result)" do
271-
allow(Random).to receive(:rand).and_return(0.6)
272+
# Create transaction with sample_rand > sample_rate (0.5) to ensure not sampled
273+
transaction = described_class.new(op: "rack.request", hub: Sentry.get_current_hub, sample_rand: 0.6)
272274

273-
subject.set_initial_sample_decision(sampling_context: {})
274-
expect(subject.sampled).to eq(false)
275-
expect(subject.effective_sample_rate).to eq(0.5)
275+
transaction.set_initial_sample_decision(sampling_context: {})
276+
expect(transaction.sampled).to eq(false)
277+
expect(transaction.effective_sample_rate).to eq(0.5)
276278
expect(string_io.string).to include(
277279
"[Tracing] Discarding <rack.request> transaction because it's not included in the random sample (sampling rate = 0.5)"
278280
)
@@ -474,11 +476,21 @@
474476
end
475477

476478
it "submits the event with the transaction's hub by default" do
477-
subject.instance_variable_set(:@hub, another_hub)
479+
# Create transaction with the specific hub from the beginning
480+
transaction = described_class.new(
481+
op: "sql.query",
482+
description: "SELECT * FROM users;",
483+
status: "ok",
484+
sampled: true,
485+
parent_sampled: true,
486+
name: "foo",
487+
source: :view,
488+
hub: another_hub
489+
)
478490

479491
expect(another_hub).to receive(:capture_event)
480492

481-
subject.finish
493+
transaction.finish
482494
end
483495
end
484496

@@ -511,9 +523,13 @@
511523
it "records lost event with reason backpressure" do
512524
expect(Sentry.get_current_client.transport).to receive(:any_rate_limited?).and_return(true)
513525
Sentry.backpressure_monitor.run
514-
allow(Random).to receive(:rand).and_return(0.6)
515526

516-
subject.finish
527+
# Create transaction with sample_rand that will be rejected after backpressure downsampling
528+
# With traces_sample_rate = 1.0 and downsample_factor = 1, effective rate becomes 0.5
529+
# So sample_rand = 0.6 > 0.5 will be rejected
530+
transaction = described_class.new(hub: Sentry.get_current_hub, sample_rand: 0.6)
531+
532+
transaction.finish
517533
expect(Sentry.get_current_client.transport).to have_recorded_lost_event(:backpressure, 'transaction')
518534
expect(Sentry.get_current_client.transport).to have_recorded_lost_event(:backpressure, 'span')
519535
end
@@ -556,7 +572,8 @@
556572
name: "foo",
557573
source: source,
558574
hub: Sentry.get_current_hub,
559-
baggage: incoming_baggage
575+
baggage: incoming_baggage,
576+
sample_rand: 0.123456 # Use a known value for predictable testing
560577
)
561578

562579
transaction.set_initial_sample_decision(sampling_context: {})
@@ -574,6 +591,7 @@
574591
expect(baggage.items).to eq({
575592
"environment" => "development",
576593
"public_key" => "12345",
594+
"sample_rand" => "0.123456", # Known value from transaction creation
577595
"trace_id" => subject.trace_id,
578596
"transaction"=>"foo",
579597
"sample_rate" => "1.0",
@@ -630,6 +648,7 @@
630648
expect(baggage.items).to eq({
631649
"environment" => "development",
632650
"public_key" => "12345",
651+
"sample_rand" => "0.123456", # Known value from transaction creation
633652
"trace_id" => subject.trace_id,
634653
"transaction"=>"foo",
635654
"sample_rate" => "1.0",

0 commit comments

Comments
 (0)