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

fix OpenTracing trace context propagation with Fault Tolerance @Asynchronous methods #7987

Merged
merged 2 commits into from
Apr 14, 2020
Merged
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
7 changes: 6 additions & 1 deletion bom/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<smallrye-metrics.version>2.4.1</smallrye-metrics.version>
<smallrye-open-api.version>1.2.1</smallrye-open-api.version>
<smallrye-opentracing.version>1.3.4</smallrye-opentracing.version>
<smallrye-fault-tolerance.version>4.1.1</smallrye-fault-tolerance.version>
<smallrye-fault-tolerance.version>4.2.0</smallrye-fault-tolerance.version>
<smallrye-jwt.version>2.1.1</smallrye-jwt.version>
<smallrye-context-propagation.version>1.0.11</smallrye-context-propagation.version>
<smallrye-reactive-streams-operators.version>1.0.10</smallrye-reactive-streams-operators.version>
Expand Down Expand Up @@ -1447,6 +1447,11 @@
<artifactId>smallrye-fault-tolerance-context-propagation</artifactId>
<version>${smallrye-fault-tolerance.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-fault-tolerance-tracing-propagation</artifactId>
<version>${smallrye-fault-tolerance.version}</version>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-context-propagation</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import javax.inject.Inject;

import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
Expand All @@ -21,13 +22,15 @@ public class JaegerProcessor {
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void setupTracer(JaegerDeploymentRecorder jdr, JaegerBuildTimeConfig buildTimeConfig, JaegerConfig jaeger,
ApplicationConfig appConfig) {
ApplicationConfig appConfig, Capabilities capabilities) {

// Indicates that this extension would like the SSL support to be enabled
extensionSslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(FeatureBuildItem.JAEGER));

if (buildTimeConfig.enabled) {
jdr.registerTracer(jaeger, appConfig);
boolean metricsEnabled = capabilities.isCapabilityPresent(Capabilities.METRICS);

jdr.registerTracer(jaeger, appConfig, metricsEnabled);
}
}

Expand Down
11 changes: 4 additions & 7 deletions extensions/jaeger/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-core</artifactId>
</dependency>
Expand All @@ -31,12 +31,9 @@
<artifactId>svm</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-metrics</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.metrics</groupId>
<artifactId>microprofile-metrics-api</artifactId>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-metrics</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class JaegerDeploymentRecorder {
private static final Optional UNKNOWN_SERVICE_NAME = Optional.of("quarkus/unknown");
private static final QuarkusJaegerTracer quarkusTracer = new QuarkusJaegerTracer();

synchronized public void registerTracer(JaegerConfig jaeger, ApplicationConfig appConfig) {
synchronized public void registerTracer(JaegerConfig jaeger, ApplicationConfig appConfig, boolean metricsEnabled) {
if (!jaeger.serviceName.isPresent()) {
if (appConfig.name.isPresent()) {
jaeger.serviceName = appConfig.name;
Expand All @@ -24,6 +24,7 @@ synchronized public void registerTracer(JaegerConfig jaeger, ApplicationConfig a
}
}
initTracerConfig(jaeger);
quarkusTracer.setMetricsEnabled(metricsEnabled);
quarkusTracer.reset();
// register Quarkus tracer to GlobalTracer.
// Usually the tracer will be registered only here, although consumers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import io.jaegertracing.Configuration;
import io.jaegertracing.internal.JaegerTracer;
import io.jaegertracing.internal.metrics.NoopMetricsFactory;
import io.jaegertracing.spi.MetricsFactory;
import io.opentracing.ScopeManager;
import io.opentracing.Span;
import io.opentracing.SpanContext;
Expand All @@ -14,11 +16,16 @@ public class QuarkusJaegerTracer implements Tracer {
private volatile JaegerTracer tracer;

private boolean logTraceContext;
private boolean metricsEnabled;

void setLogTraceContext(boolean logTraceContext) {
this.logTraceContext = logTraceContext;
}

void setMetricsEnabled(boolean metricsEnabled) {
this.metricsEnabled = metricsEnabled;
}

@Override
public String toString() {
return tracer().toString();
Expand All @@ -35,8 +42,11 @@ private Tracer tracer() {
if (tracer == null) {
synchronized (this) {
if (tracer == null) {
MetricsFactory metricsFactory = metricsEnabled
? new QuarkusJaegerMetricsFactory()
: new NoopMetricsFactory();
tracer = Configuration.fromEnv()
.withMetricsFactory(new QuarkusJaegerMetricsFactory())
.withMetricsFactory(metricsFactory)
.getTracerBuilder()
.withScopeManager(getScopeManager())
.build();
Expand Down
5 changes: 5 additions & 0 deletions extensions/smallrye-opentracing/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
<artifactId>quarkus-jsonp-deployment</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-deployment</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.quarkus.smallrye.opentracing.deployment;

import java.util.List;
import java.util.concurrent.CompletionStage;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.eclipse.microprofile.opentracing.Traced;

@Path("/")
public interface RestService {

Expand All @@ -22,7 +25,11 @@ public interface RestService {
Response restClient();

@GET
@Traced(false)
@Path("/faultTolerance")
Response faultTolerance();
CompletionStage<String> faultTolerance();

@GET
@Path("/jpa")
@Produces(MediaType.APPLICATION_JSON)
List<Fruit> jpa();
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,53 @@
package io.quarkus.smallrye.opentracing.deployment;

import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.eclipse.microprofile.opentracing.Traced;

import io.opentracing.Tracer;

@Traced
@ApplicationScoped
public class Service {

@Inject
Tracer tracer;

@Inject
EntityManager em;

@Traced
public void foo() {
}

// @Asynchronous methods (and their fallback methods) shouldn't be @Traced
// because https://github.com/eclipse/microprofile-opentracing/issues/189
@Asynchronous
@Fallback(fallbackMethod = "fallback")
@Timeout(value = 20L, unit = ChronoUnit.MILLIS)
@Retry(delay = 10L, maxRetries = 2)
public String faultTolerance() {
public CompletionStage<String> faultTolerance() {
tracer.buildSpan("ft").start().finish();
throw new RuntimeException();
}

public String fallback() {
return "fallback";
public CompletionStage<String> fallback() {
tracer.buildSpan("fallback").start().finish();
return CompletableFuture.completedFuture("fallback");
}

@Traced
public List<Fruit> getFruits() {
return em.createNamedQuery("Fruits.findAll", Fruit.class).getResultList();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.smallrye.opentracing.deployment;

import java.util.List;
import java.util.concurrent.CompletionStage;

import javax.inject.Inject;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
Expand Down Expand Up @@ -38,8 +41,11 @@ public Response restClient() {
}

@Override
public Response faultTolerance() {
String ret = service.faultTolerance();
return Response.ok(ret).build();
public CompletionStage<String> faultTolerance() {
return service.faultTolerance();
}

public List<Fruit> jpa() {
return service.getFruits();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.smallrye.opentracing.deployment;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;

import java.util.List;
import java.util.concurrent.TimeUnit;

Expand All @@ -20,7 +23,6 @@
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.restassured.parsing.Parser;
import io.restassured.response.Response;

public class TracingTest {

Expand All @@ -41,7 +43,7 @@ public class TracingTest {
}

@BeforeEach
public void after() {
public void before() {
mockTracer.reset();
}

Expand Down Expand Up @@ -104,9 +106,10 @@ public void testMPRestClient() {
public void testContextPropagationInFaultTolerance() {
try {
RestAssured.defaultParser = Parser.TEXT;
Response response = RestAssured.when().get("/faultTolerance");
response.then().statusCode(200);
Assertions.assertEquals("fallback", response.body().asString());
RestAssured.when().get("/faultTolerance")
.then()
.statusCode(200)
.body(equalTo("fallback"));
Awaitility.await().atMost(5, TimeUnit.SECONDS)
.until(() -> mockTracer.finishedSpans().size() == 5);
List<MockSpan> spans = mockTracer.finishedSpans();
Expand All @@ -115,13 +118,47 @@ public void testContextPropagationInFaultTolerance() {
for (MockSpan mockSpan : spans) {
Assertions.assertEquals(spans.get(0).context().traceId(), mockSpan.context().traceId());
}
Assertions.assertEquals("ft", mockTracer.finishedSpans().get(0).operationName());
Assertions.assertEquals("ft", mockTracer.finishedSpans().get(1).operationName());
Assertions.assertEquals("ft", mockTracer.finishedSpans().get(2).operationName());
Assertions.assertEquals("io.quarkus.smallrye.opentracing.deployment.Service.fallback",
mockTracer.finishedSpans().get(3).operationName());
Assertions.assertEquals("io.quarkus.smallrye.opentracing.deployment.Service.faultTolerance",
mockTracer.finishedSpans().get(4).operationName());

// if timeout occurs, subsequent retries/fallback can be interleaved with the execution that timed out,
// resulting in varying span order
Assertions.assertEquals(3, countSpansWithOperationName(spans, "ft"));
Assertions.assertEquals(1, countSpansWithOperationName(spans, "fallback"));
Assertions.assertEquals(1, countSpansWithOperationName(spans,
"GET:io.quarkus.smallrye.opentracing.deployment.TestResource.faultTolerance"));
} finally {
RestAssured.reset();
}
}

private long countSpansWithOperationName(List<MockSpan> spans, String operationName) {
return spans.stream().filter(span -> span.operationName().equals(operationName)).count();
}

@Test
public void testJPA() {
try {
RestAssured.defaultParser = Parser.JSON;
RestAssured.when().get("/jpa")
.then()
.statusCode(200)
.body("", hasSize(3))
.body("name[0]", equalTo("Apple"))
.body("name[1]", equalTo("Banana"))
.body("name[2]", equalTo("Cherry"));
List<MockSpan> spans = mockTracer.finishedSpans();

Assertions.assertEquals(3, spans.size());
for (MockSpan mockSpan : spans) {
Assertions.assertEquals(spans.get(0).context().traceId(), mockSpan.context().traceId());
}
MockSpan firstSpan = mockTracer.finishedSpans().get(0);
Assertions.assertEquals("Query", firstSpan.operationName());
Assertions.assertTrue(firstSpan.tags().containsKey("db.statement"));
Assertions.assertTrue(firstSpan.tags().get("db.statement").toString().contains("known_fruits"));
Assertions.assertEquals("io.quarkus.smallrye.opentracing.deployment.Service.getFruits",
mockTracer.finishedSpans().get(1).operationName());
Assertions.assertEquals("GET:io.quarkus.smallrye.opentracing.deployment.TestResource.jpa",
mockTracer.finishedSpans().get(2).operationName());
} finally {
RestAssured.reset();
}
Expand Down
10 changes: 10 additions & 0 deletions extensions/smallrye-opentracing/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-fault-tolerance-tracing-propagation</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
Expand Down