Skip to content

Commit

Permalink
Generate the traceId in the DdTracer using the new strategy in the Co…
Browse files Browse the repository at this point in the history
…reTracer
  • Loading branch information
mariusc83 committed Jun 27, 2024
1 parent 60ee7be commit ce9e1f3
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.datadog.opentracing.DDSpan;
import com.datadog.opentracing.DDTracer;

import java.math.BigDecimal;
import java.math.BigInteger;

Expand All @@ -16,37 +17,37 @@
* tracers for other languages
*/
public class DeterministicSampler implements RateSampler {
private static final BigInteger KNUTH_FACTOR = new BigInteger("1111111111111111111");
private static final BigDecimal TRACE_ID_MAX_AS_BIG_DECIMAL =
new BigDecimal(DDTracer.TRACE_ID_128_BITS_MAX);
private static final BigInteger MODULUS = new BigInteger("2").pow(128);

private final BigInteger cutoff;
private final double rate;

public DeterministicSampler(final double rate) {
this.rate = rate;
cutoff = new BigDecimal(rate).multiply(TRACE_ID_MAX_AS_BIG_DECIMAL).toBigInteger();

}

@Override
public boolean sample(final DDSpan span) {
final boolean sampled;
if (rate == 1) {
sampled = true;
} else if (rate == 0) {
sampled = false;
} else {
sampled = span.getTraceId().multiply(KNUTH_FACTOR).mod(MODULUS).compareTo(cutoff) < 0;
}
private static final BigInteger KNUTH_FACTOR = new BigInteger("1111111111111111111");
private static final BigDecimal SPAN_ID_MAX_AS_BIG_DECIMAL =
new BigDecimal(DDTracer.TRACE_ID_64_BITS_MAX);
private static final BigInteger MODULUS = new BigInteger("2").pow(64);

private final BigInteger cutoff;
private final double rate;

public DeterministicSampler(final double rate) {
this.rate = rate;
cutoff = new BigDecimal(rate).multiply(SPAN_ID_MAX_AS_BIG_DECIMAL).toBigInteger();
}

return sampled;
}
@Override
public boolean sample(final DDSpan span) {
final boolean sampled;
if (rate == 1) {
sampled = true;
} else if (rate == 0) {
sampled = false;
} else {
// we are going to use the spanId here instead as it is 64 bits and will not affect the
// sampling decision. This is the same approach used by the new CoreTracer code.
sampled = span.getSpanId().multiply(KNUTH_FACTOR).mod(MODULUS).compareTo(cutoff) < 0;
}

return sampled;
}

@Override
public double getSampleRate() {
return rate;
}
@Override
public double getSampleRate() {
return rate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.datadog.legacy.trace.common.writer.LoggingWriter;
import com.datadog.legacy.trace.common.writer.Writer;
import com.datadog.legacy.trace.context.ScopeListener;
import com.datadog.trace.api.IdGenerationStrategy;

import io.opentracing.References;
import io.opentracing.Scope;
Expand All @@ -53,8 +54,10 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, Tracer {
public static final BigInteger TRACE_ID_128_BITS_MAX =
BigInteger.valueOf(2).pow(128).subtract(BigInteger.ONE);

// UINT64 max value
public static final BigInteger TRACE_ID_64_BITS_MAX =
BigInteger.valueOf(2).pow(64).subtract(BigInteger.ONE);

public static final BigInteger TRACE_ID_MIN = BigInteger.ZERO;

/**
Expand Down Expand Up @@ -116,6 +119,9 @@ public int compare(final TraceInterceptor o1, final TraceInterceptor o2) {
private final HttpCodec.Injector injector;
private final HttpCodec.Extractor extractor;

private final IdGenerationStrategy idGenerationStrategy =
IdGenerationStrategy.fromName("SECURE_RANDOM", true);

// On Android, the same zygote is reused for every single application,
// meaning that the ThreadLocalRandom reuses the same exact state,
// resulting in conflicting TraceIds.
Expand Down Expand Up @@ -583,7 +589,7 @@ private DDSpanBuilder withTag(final String tag, final Object value) {
return this;
}

private BigInteger generateNewId() {
private BigInteger generateNewSpanId() {
// It is **extremely** unlikely to generate the value "0" but we still need to handle that
// case
BigInteger value;
Expand All @@ -597,12 +603,10 @@ private BigInteger generateNewId() {
}

private BigInteger generateNewTraceId() {
// It is **extremely** unlikely to generate the value "0" but we still need to handle that
// case
BigInteger value;
do {
synchronized (random) {
value = new StringCachingBigInteger(128, random);
synchronized (idGenerationStrategy) {
value = new BigInteger(idGenerationStrategy.generateTraceId().toHexString(), 16);
}
} while (value.signum() == 0);

Expand All @@ -617,7 +621,7 @@ private BigInteger generateNewTraceId() {
*/
private DDSpanContext buildSpanContext() {
final BigInteger traceId;
final BigInteger spanId = generateNewId();
final BigInteger spanId = generateNewSpanId();
final BigInteger parentSpanId;
final Map<String, String> baggage;
final PendingTrace parentTrace;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ internal class SpanContextExtTest {
}

@Test
fun `M return the expected hexa padded string W traceIdAsHexString()`() {
fun `M return the expected hex padded string W traceIdAsHexString()`() {
// Given
whenever(mockSpan.context()).thenReturn(mockDDSpanContext)
whenever(mockDDSpanContext.traceId).thenReturn(fakeTraceIdAsBigInteger)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ internal class DatadogHttpCodecTest {

private lateinit var fakeTaggedHeaders: Map<String, String>

private lateinit var fakeIdAsHexaString: String
private lateinit var fakeIdAsHexString: String

@BeforeEach
fun `set up`(forge: Forge) {
fakeIdAsHexaString = fakeDDSpanContext.traceId.toString(16)
fakeIdAsHexString = fakeDDSpanContext.traceId.toString(16)
fakeTaggedHeaders = forge.aMap(size = forge.anInt(min = 1, max = 10)) {
anAlphabeticalString() to anAlphabeticalString()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*/

package com.datadog.trace.api

import com.datadog.android.utils.forge.Configurator
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
import fr.xgouchet.elmyr.junit5.ForgeExtension
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.extension.Extensions
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.junit.jupiter.MockitoSettings
import org.mockito.kotlin.mock
import org.mockito.quality.Strictness
import java.security.SecureRandom

@Extensions(
ExtendWith(MockitoExtension::class),
ExtendWith(ForgeExtension::class)
)
@MockitoSettings(strictness = Strictness.LENIENT)
@ForgeConfiguration(Configurator::class)
internal class IdGenerationStrategyTest {

// Please note that these tests were just ported from the Groovy CoreTracer tests in APM java code

@ParameterizedTest
@ValueSource(strings = ["RANDOM", "SEQUENTIAL", "SECURE_RANDOM"])
fun `M generate id with strategyName and tIdSize bits`(strategyName: String) {
val isTid128 = listOf(false, true)
isTid128.forEach { tId128b ->
val strategy = IdGenerationStrategy.fromName(strategyName, tId128b)
val traceIds = (0..32768).map { strategy.generateTraceId() }
val checked = HashSet<DDTraceId>()

traceIds.forEach { traceId ->
assertThat(traceId).isNotNull
assertThat(traceId.equals("foo")).isFalse()
assertThat(traceId.equals(DDTraceId.ZERO)).isFalse()
assertThat(traceId.equals(traceId)).isTrue()
val hashCode =
(
traceId.toHighOrderLong() xor (traceId.toHighOrderLong() ushr 32)
xor traceId.toLong() xor (traceId.toLong() ushr 32)
).toInt()
assertThat(traceId.hashCode()).isEqualTo(hashCode)
assertThat(checked).doesNotContain(traceId)
checked.add(traceId)
}
}
}

@ParameterizedTest
@ValueSource(strings = ["SOME", "UNKNOWN", "STRATEGIES"])
fun `M return null for non existing strategy strategyName`(strategyName: String) {
val strategy = IdGenerationStrategy.fromName(strategyName)
assertThat(strategy).isNull()
}

@Test
fun `exception created on SecureRandom strategy`() {
// Given
val illegalArgumentException = IllegalArgumentException("SecureRandom init exception")
val provider: IdGenerationStrategy.ThrowingSupplier<SecureRandom> = mock {
on { get() }.thenThrow(illegalArgumentException)
}

// Then
assertThatThrownBy { IdGenerationStrategy.SRandom(true, provider) }
.isInstanceOf(ExceptionInInitializerError::class.java)
.hasCause(illegalArgumentException)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
hasService(stubSdkCore.getDatadogContext().service)
hasVersion(stubSdkCore.getDatadogContext().version)
Expand Down Expand Up @@ -180,6 +182,8 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
hasService(stubSdkCore.getDatadogContext().service)
hasVersion(stubSdkCore.getDatadogContext().version)
Expand Down Expand Up @@ -233,6 +237,8 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
hasService(stubSdkCore.getDatadogContext().service)
hasVersion(stubSdkCore.getDatadogContext().version)
Expand Down Expand Up @@ -288,6 +294,8 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
hasService(stubSdkCore.getDatadogContext().service)
hasVersion(stubSdkCore.getDatadogContext().version)
Expand Down Expand Up @@ -362,6 +370,8 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
hasService(stubSdkCore.getDatadogContext().service)
hasVersion(stubSdkCore.getDatadogContext().version)
Expand Down Expand Up @@ -407,6 +417,8 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
hasService(stubSdkCore.getDatadogContext().service)
hasVersion(stubSdkCore.getDatadogContext().version)
Expand Down Expand Up @@ -454,6 +466,8 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
hasService(stubSdkCore.getDatadogContext().service)
hasVersion(stubSdkCore.getDatadogContext().version)
Expand Down Expand Up @@ -500,13 +514,17 @@ class AndroidTracerTest {
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(leastSignificantTraceId)
hasMostSignificant64BitsTraceId(mostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(spanId)
}
SpansPayloadAssert.assertThat(payload1)
.hasEnv(stubSdkCore.getDatadogContext().env)
.hasSpanAtIndexWith(0) {
hasLeastSignificant64BitsTraceId(childLeastSignificantTraceId)
hasMostSignificant64BitsTraceId(childMostSignificantTraceId)
hasValidMostSignificant64BitsTraceId()
hasValidLeastSignificant64BitsTraceId()
hasSpanId(childSpanId)
}
}
Expand Down
Loading

0 comments on commit ce9e1f3

Please sign in to comment.