-
Notifications
You must be signed in to change notification settings - Fork 247
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
Simplify and correct ProbabilitySampler behaviour #111
Changes from 1 commit
a083295
517917d
6b6af7e
d651d01
0947b60
9c51f3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,12 +12,16 @@ module Samplers | |
# | ||
# Implements sampling based on a probability. | ||
class ProbabilitySampler | ||
def initialize(probability, hints:, ignore_parent:, apply_to_root_spans:, apply_to_remote_parent:, apply_to_all_spans:) | ||
HINT_RECORD_AND_PROPAGATE = OpenTelemetry::Trace::SamplingHint::RECORD_AND_PROPAGATE | ||
HINT_RECORD = OpenTelemetry::Trace::SamplingHint::RECORD | ||
|
||
private_constant(:HINT_RECORD_AND_PROPAGATE, :HINT_RECORD) | ||
|
||
def initialize(probability, ignore_hints:, ignore_parent:, apply_to_remote_parent:, apply_to_all_spans:) | ||
@probability = probability | ||
@id_upper_bound = format('%016x', (probability * (2**64 - 1)).ceil) | ||
@result_from_hint = hints.map { |hint| [hint, Result.new(decision: hint)] }.to_h.freeze | ||
@ignore_parent = ignore_parent | ||
@apply_to_root_spans = apply_to_root_spans | ||
@ignored_hints = ignore_hints | ||
@use_parent_sampled_flag = !ignore_parent | ||
@apply_to_remote_parent = apply_to_remote_parent | ||
@apply_to_all_spans = apply_to_all_spans | ||
end | ||
|
@@ -26,51 +30,48 @@ def initialize(probability, hints:, ignore_parent:, apply_to_root_spans:, apply_ | |
# | ||
# Callable interface for probability sampler. See {Samplers}. | ||
def call(trace_id:, span_id:, parent_context:, hint:, links:, name:, kind:, attributes:) | ||
take_hint(hint) || | ||
use_parent_sampling(parent_context) || | ||
use_link_sampling(links) || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The OTEP talks about links, but the decision table for |
||
maybe_dont_apply(parent_context) || | ||
use_probability_sampling(trace_id) || | ||
NOT_RECORD | ||
end | ||
ignore(links, name, kind, attributes) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to explicitly ignore the parameters the
fbogsany marked this conversation as resolved.
Show resolved
Hide resolved
|
||
hint = filter_hint(hint) | ||
|
||
private | ||
sampled_flag = sample(hint, trace_id, parent_context) | ||
record_events = hint == HINT_RECORD || sampled_flag | ||
|
||
# Take the hint if one is provided and we're not ignoring it. | ||
def take_hint(hint) | ||
@result_from_hint[hint] if hint | ||
if sampled_flag && record_events | ||
RECORD_AND_PROPAGATE | ||
elsif record_events | ||
RECORD | ||
else | ||
NOT_RECORD | ||
end | ||
end | ||
|
||
# If the parent is sampled and we're not ignoring it keep the sampling decision. | ||
def use_parent_sampling(parent_context) | ||
RECORD_AND_PROPAGATE if !@ignore_parent && parent_context&.trace_flags&.sampled? | ||
end | ||
private | ||
|
||
# If any link is sampled keep the sampling decision. | ||
def use_link_sampling(links) | ||
RECORD_AND_PROPAGATE if links&.any? { |link| link.context.trace_flags.sampled? } | ||
end | ||
# Explicitly ignore these parameters. | ||
def ignore(_links, _name, _kind, _attributes); end | ||
|
||
def maybe_dont_apply(parent_context) | ||
dont_apply_to_root_span(parent_context) || | ||
dont_apply_to_remote_parent(parent_context) || | ||
dont_apply_to_local_child(parent_context) | ||
def filter_hint(hint) | ||
hint unless @ignored_hints.include?(hint) | ||
end | ||
|
||
def dont_apply_to_root_span(parent_context) | ||
NOT_RECORD if !@apply_to_root_spans && parent_context.nil? | ||
def sample(hint, trace_id, parent_context) | ||
fbogsany marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if parent_context.nil? | ||
hint == HINT_RECORD_AND_PROPAGATE || probably(trace_id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
else | ||
parent_sampled_flag(parent_context) || hint == HINT_RECORD_AND_PROPAGATE || probably_for_child(parent_context, trace_id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
end | ||
end | ||
|
||
def dont_apply_to_remote_parent(parent_context) | ||
NOT_RECORD if !@apply_to_remote_parent && parent_context&.remote? | ||
def parent_sampled_flag(parent_context) | ||
@use_parent_sampled_flag && parent_context.trace_flags.sampled? | ||
end | ||
|
||
def dont_apply_to_local_child(parent_context) | ||
NOT_RECORD if !@apply_to_all_spans && parent_context && !parent_context.remote? | ||
def probably_for_child(parent_context, trace_id) | ||
(@apply_to_all_spans || (@apply_to_remote_parent && parent_context.remote?)) && probably(trace_id) | ||
end | ||
|
||
def use_probability_sampling(trace_id) | ||
RECORD_AND_PROPAGATE if @probability == 1.0 || trace_id[16, 16] < @id_upper_bound | ||
def probably(trace_id) | ||
fbogsany marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@probability == 1.0 || trace_id[16, 16] < @id_upper_bound | ||
end | ||
end | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,12 +60,6 @@ | |
result.wont_be :sampled? | ||
end | ||
|
||
it 'respects link sampling' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
link = OpenTelemetry::Trace::Link.new(context) | ||
result = call_sampler(sampler, links: [link]) | ||
result.must_be :sampled? | ||
end | ||
|
||
it 'returns a result' do | ||
result = call_sampler(sampler, trace_id: trace_id(123)) | ||
result.must_be_instance_of(Result) | ||
|
@@ -83,35 +77,30 @@ | |
result.wont_be :sampled? | ||
end | ||
|
||
it 'does not sample a root span unless apply_to_root_spans' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a supported case for |
||
sampler = Samplers.probability(1, apply_to_root_spans: false) | ||
result = call_sampler(sampler, parent_context: nil) | ||
result.wont_be :sampled? | ||
end | ||
|
||
it 'does not sample a remote parent unless apply_to_remote_parent' do | ||
it 'does not sample a remote parent if apply_probability_to == :root_spans' do | ||
context = OpenTelemetry::Trace::SpanContext.new( | ||
trace_flags: OpenTelemetry::Trace::TraceFlags.from_byte(0), | ||
remote: true | ||
) | ||
sampler = Samplers.probability(1, apply_to_remote_parent: false) | ||
sampler = Samplers.probability(1, apply_probability_to: :root_spans) | ||
result = call_sampler(sampler, parent_context: context) | ||
result.wont_be :sampled? | ||
end | ||
|
||
it 'samples a local child span if apply_to_all_spans' do | ||
sampler = Samplers.probability(1, apply_to_all_spans: true) | ||
it 'samples a local child span if apply_probability_to == :all_spans' do | ||
sampler = Samplers.probability(1, apply_probability_to: :all_spans) | ||
result = call_sampler(sampler, parent_context: context, trace_id: trace_id(1)) | ||
result.must_be :sampled? | ||
end | ||
|
||
it 'returns result with hint if supplied' do | ||
sampler = Samplers.probability(1, ignore_hints: nil) | ||
not_record_result = call_sampler(sampler, hint: Decision::NOT_RECORD) | ||
record_result = call_sampler(sampler, hint: Decision::RECORD) | ||
record_and_propagate_result = call_sampler(sampler, hint: Decision::RECORD_AND_PROPAGATE) | ||
not_record_result.wont_be :sampled? | ||
not_record_result.wont_be :record_events? | ||
sampler = Samplers.probability(0, ignore_hints: nil) | ||
# TODO: Resolve the contradiction in OTEP 6. | ||
# not_record_result = call_sampler(sampler, hint: OpenTelemetry::Trace::SamplingHint::NOT_RECORD) | ||
record_result = call_sampler(sampler, hint: OpenTelemetry::Trace::SamplingHint::RECORD) | ||
record_and_propagate_result = call_sampler(sampler, hint: OpenTelemetry::Trace::SamplingHint::RECORD_AND_PROPAGATE) | ||
# not_record_result.wont_be :sampled? | ||
# not_record_result.wont_be :record_events? | ||
record_result.wont_be :sampled? | ||
record_result.must_be :record_events? | ||
record_and_propagate_result.must_be :sampled? | ||
|
@@ -162,8 +151,8 @@ def trace_id(id) | |
|
||
def call_sampler(sampler, trace_id: nil, span_id: nil, parent_context: nil, hint: nil, links: nil, name: nil, kind: nil, attributes: nil) | ||
sampler.call( | ||
trace_id: trace_id, | ||
span_id: span_id, | ||
trace_id: trace_id || OpenTelemetry::Trace.generate_trace_id, | ||
span_id: span_id || OpenTelemetry::Trace.generate_span_id, | ||
parent_context: parent_context, | ||
hint: hint, | ||
links: links, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The collection of boolean flags was confusing, interdependent, incorrectly implemented, and didn't capture either the spirit or intent of the OTEP. Since we're in Ruby, we can have Symbols, so this uses a single Symbol argument to distinguish the 3 supported cases.