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

Propagated Sampling Rates #1793

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 15 additions & 9 deletions src/State/Hub.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ public function startTransaction(TransactionContext $context, array $customSampl
$samplingContext->setAdditionalContext($customSamplingContext);

$sampleSource = 'context';
$sampleRand = $context->getMetadata()->getSampleRand();

if ($transaction->getSampled() === null) {
$tracesSampler = $options->getTracesSampler();
Expand All @@ -278,12 +279,17 @@ public function startTransaction(TransactionContext $context, array $customSampl

$sampleSource = 'config:traces_sampler';
} else {
$sampleRate = $this->getSampleRate(
$samplingContext->getParentSampled(),
$options->getTracesSampleRate() ?? 0
);

$sampleSource = $samplingContext->getParentSampled() ? 'parent' : 'config:traces_sample_rate';
$parentSampleRate = $context->getMetadata()->getParentSamplingRate();
if ($parentSampleRate !== null) {
$sampleRate = $parentSampleRate;
$sampleSource = 'parent';
} else {
$sampleRate = $this->getSampleRate(
$samplingContext->getParentSampled(),
$options->getTracesSampleRate() ?? 0
);
$sampleSource = $samplingContext->getParentSampled() ? 'parent' : 'config:traces_sample_rate';
}
}

if (!$this->isValidSampleRate($sampleRate)) {
Expand All @@ -304,7 +310,7 @@ public function startTransaction(TransactionContext $context, array $customSampl
return $transaction;
}

$transaction->setSampled($this->sample($sampleRate));
$transaction->setSampled($sampleRand < $sampleRate);
}

if (!$transaction->getSampled()) {
Expand Down Expand Up @@ -376,11 +382,11 @@ private function getStackTop(): Layer
private function getSampleRate(?bool $hasParentBeenSampled, float $fallbackSampleRate): float
{
if ($hasParentBeenSampled === true) {
return 1;
return 1.0;
}

if ($hasParentBeenSampled === false) {
return 0;
return 0.0;
}

return $fallbackSampleRate;
Expand Down
5 changes: 5 additions & 0 deletions src/Tracing/DynamicSamplingContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ public static function fromTransaction(Transaction $transaction, HubInterface $h
$samplingContext->set('sampled', $transaction->getSampled() ? 'true' : 'false');
}

if ($transaction->getMetadata()->getSampleRand() !== null) {
$samplingContext->set('sample_rand', (string) $transaction->getMetadata()->getSampleRand());
}

$samplingContext->freeze();

return $samplingContext;
Expand All @@ -195,6 +199,7 @@ public static function fromOptions(Options $options, Scope $scope): self
{
$samplingContext = new self();
$samplingContext->set('trace_id', (string) $scope->getPropagationContext()->getTraceId());
$samplingContext->set('sample_rand', (string) $scope->getPropagationContext()->getSampleRand());

if ($options->getTracesSampleRate() !== null) {
$samplingContext->set('sample_rate', (string) $options->getTracesSampleRate());
Expand Down
55 changes: 55 additions & 0 deletions src/Tracing/PropagationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ final class PropagationContext
*/
private $parentSpanId;

/**
* @var bool|null The parent's sampling decision
*/
private $parentSampled;

/**
* @var float|null
*/
private $sampleRand;

/**
* @var DynamicSamplingContext|null The dynamic sampling context
*/
Expand All @@ -44,6 +54,9 @@ public static function fromDefaults(): self
$context->traceId = TraceId::generate();
$context->spanId = SpanId::generate();
$context->parentSpanId = null;
$context->parentSampled = null;
// TODO check if this is precise enough
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6);
$context->dynamicSamplingContext = null;

return $context;
Expand Down Expand Up @@ -159,6 +172,19 @@ public function setDynamicSamplingContext(DynamicSamplingContext $dynamicSamplin
return $this;
}

public function getSampleRand(): ?float
{
return $this->sampleRand;
}

public function setSampleRand(?float $sampleRand): self
{
$this->sampleRand = $sampleRand;

return $this;
}

// TODO add same logic as in TransactionContext
private static function parseTraceparentAndBaggage(string $traceparent, string $baggage): self
{
$context = self::fromDefaults();
Expand All @@ -174,6 +200,11 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $
$context->parentSpanId = new SpanId($matches['span_id']);
$hasSentryTrace = true;
}

if (isset($matches['sampled'])) {
$context->parentSampled = $matches['sampled'] === '1';
$hasSentryTrace = true;
}
} elseif (preg_match(self::W3C_TRACEPARENT_HEADER_REGEX, $traceparent, $matches)) {
if (!empty($matches['trace_id'])) {
$context->traceId = new TraceId($matches['trace_id']);
Expand All @@ -184,6 +215,11 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $
$context->parentSpanId = new SpanId($matches['span_id']);
$hasSentryTrace = true;
}

if (isset($matches['sampled'])) {
$context->parentSampled = $matches['sampled'] === '01';
$hasSentryTrace = true;
}
}

$samplingContext = DynamicSamplingContext::fromHeader($baggage);
Expand All @@ -201,6 +237,25 @@ private static function parseTraceparentAndBaggage(string $traceparent, string $
$context->dynamicSamplingContext = $samplingContext;
}

// Store the propagated trace sample rand or generate a new one
if ($samplingContext->has('sample_rand')) {
// TODO check for 1e13 etc.
$context->sampleRand = (float) $samplingContext->get('sample_rand');
} else {
if ($samplingContext->has('sample_rate') && $context->parentSampled !== null) {
if ($context->parentSampled === true) {
// [0, rate)
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (float) $samplingContext->get('sample_rate'), 6);
} else {
// [rate, 1]
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (1 - (float) $samplingContext->get('sample_rate')) + (float) $samplingContext->get('sample-rate'), 6);
}
} elseif ($context->parentSampled !== null) {
// [0, 1)
$context->sampleRand = round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6);
}
}

return $context;
}
}
11 changes: 11 additions & 0 deletions src/Tracing/SamplingContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ final class SamplingContext
*/
private $parentSampled;

/**
* @var float|null The parent sample rate
*/
private $sampleRand;

/**
* @var array<string, mixed>|null Additional context, depending on where the SDK runs
*/
Expand All @@ -29,6 +34,7 @@ public static function getDefault(TransactionContext $transactionContext): self
$context = new self();
$context->transactionContext = $transactionContext;
$context->parentSampled = $transactionContext->getParentSampled();
$context->sampleRand = $transactionContext->getMetadata()->getSampleRand();

return $context;
}
Expand All @@ -46,6 +52,11 @@ public function getParentSampled(): ?bool
return $this->parentSampled;
}

public function getSampleRand(): ?float
{
return $this->sampleRand;
}

/**
* Sets the sampling decision from the parent transaction, if any.
*/
Expand Down
24 changes: 24 additions & 0 deletions src/Tracing/TransactionContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,30 @@ private static function parseTraceAndBaggage(string $sentryTrace, string $baggag
$context->getMetadata()->setDynamicSamplingContext($samplingContext);
}

// Store the propagated traces sample rate
if ($samplingContext->has('sample_rate')) {
$context->getMetadata()->setParentSamplingRate((float) $samplingContext->get('sample_rate'));
}

// Store the propagated trace sample rand or generate a new one
if ($samplingContext->has('sample_rand')) {
// TODO check for 1e13 etc.
$context->getMetadata()->setSampleRand((float) $samplingContext->get('sample_rand'));
} else {
if ($samplingContext->has('sample_rate') && $context->parentSampled !== null) {
if ($context->parentSampled === true) {
// [0, rate)
$context->getMetadata()->setSampleRand(round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (float) $samplingContext->get('sample_rate'), 6));
} else {
// [rate, 1]
$context->getMetadata()->setSampleRand(round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax() * (1 - (float) $samplingContext->get('sample_rate')) + (float) $samplingContext->get('sample-rate'), 6));
}
} elseif ($context->parentSampled !== null) {
// [0, 1)
$context->getMetadata()->setSampleRand(round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6));
}
}

return $context;
}
}
44 changes: 43 additions & 1 deletion src/Tracing/TransactionMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,39 @@ final class TransactionMetadata
*/
private $source;

/**
* @var float|int|null
*/
private $parentSamplingRate;

/**
* @var float|int|null
*/
private $sampleRand;

/**
* Constructor.
*
* @param float|int|null $samplingRate The sampling rate
* @param DynamicSamplingContext|null $dynamicSamplingContext The Dynamic Sampling Context
* @param TransactionSource|null $source The transaction source
* @param float|null $parentSamplingRate The parent sampling rate
* @param float|null $sampleRand The trace sample rand
*/
public function __construct(
$samplingRate = null,
?DynamicSamplingContext $dynamicSamplingContext = null,
?TransactionSource $source = null
?TransactionSource $source = null,
?float $parentSamplingRate = null,
?float $sampleRand = null
) {
$this->samplingRate = $samplingRate;
$this->dynamicSamplingContext = $dynamicSamplingContext;
$this->source = $source ?? TransactionSource::custom();
$this->parentSamplingRate = $parentSamplingRate;

// TODO check if this is precise enough
$this->sampleRand = $sampleRand ?? round(mt_rand(0, mt_getrandmax() - 1) / mt_getrandmax(), 6);
}

/**
Expand All @@ -56,6 +74,30 @@ public function setSamplingRate($samplingRate): self
return $this;
}

public function getParentSamplingRate(): ?float
{
return $this->parentSamplingRate;
}

public function setParentSamplingRate(?float $parentSamplingRate): self
{
$this->parentSamplingRate = $parentSamplingRate;

return $this;
}

public function getSampleRand(): ?float
{
return $this->sampleRand;
}

public function setSampleRand(?float $sampleRand): self
{
$this->sampleRand = $sampleRand;

return $this;
}

public function getDynamicSamplingContext(): ?DynamicSamplingContext
{
return $this->dynamicSamplingContext;
Expand Down
6 changes: 4 additions & 2 deletions tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ public function testBaggageWithTracingDisabled(): void
{
$propagationContext = PropagationContext::fromDefaults();
$propagationContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
$propagationContext->setSampleRand(0.25);

$scope = new Scope($propagationContext);

Expand All @@ -520,7 +521,7 @@ public function testBaggageWithTracingDisabled(): void

$baggage = getBaggage();

$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-release=1.0.0,sentry-environment=development', $baggage);
$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rand=0.25,sentry-release=1.0.0,sentry-environment=development', $baggage);
}

public function testBaggageWithTracingEnabled(): void
Expand All @@ -541,6 +542,7 @@ public function testBaggageWithTracingEnabled(): void
$transactionContext = new TransactionContext();
$transactionContext->setName('Test');
$transactionContext->setTraceId(new TraceId('566e3688a61d4bc888951642d6f14a19'));
$transactionContext->getMetadata()->setSampleRand(0.25);

$transaction = startTransaction($transactionContext);

Expand All @@ -552,7 +554,7 @@ public function testBaggageWithTracingEnabled(): void

$baggage = getBaggage();

$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rate=1,sentry-transaction=Test,sentry-release=1.0.0,sentry-environment=development,sentry-sampled=true', $baggage);
$this->assertSame('sentry-trace_id=566e3688a61d4bc888951642d6f14a19,sentry-sample_rate=1,sentry-transaction=Test,sentry-release=1.0.0,sentry-environment=development,sentry-sampled=true,sentry-sample_rand=0.25', $baggage);
}

public function testContinueTrace(): void
Expand Down
Loading