Skip to content

Commit

Permalink
SmallRye-GraphQL metrics rework
Browse files Browse the repository at this point in the history
  • Loading branch information
mskacelik committed Aug 2, 2023
1 parent f4c8261 commit 2e52275
Show file tree
Hide file tree
Showing 12 changed files with 647 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.configuration.ConfigurationException;
import io.quarkus.runtime.metrics.MetricsFactory;
import io.quarkus.smallrye.graphql.runtime.SmallRyeGraphQLConfig;
import io.quarkus.smallrye.graphql.runtime.SmallRyeGraphQLConfigMapping;
import io.quarkus.smallrye.graphql.runtime.SmallRyeGraphQLLocaleResolver;
Expand Down Expand Up @@ -84,10 +85,10 @@
import io.smallrye.graphql.api.federation.Requires;
import io.smallrye.graphql.api.federation.Shareable;
import io.smallrye.graphql.api.federation.Tag;
import io.smallrye.graphql.cdi.config.ConfigKey;
import io.smallrye.graphql.cdi.config.MicroProfileConfig;
import io.smallrye.graphql.cdi.producer.GraphQLProducer;
import io.smallrye.graphql.cdi.tracing.TracingService;
import io.smallrye.graphql.config.ConfigKey;
import io.smallrye.graphql.schema.Annotations;
import io.smallrye.graphql.schema.SchemaBuilder;
import io.smallrye.graphql.schema.model.Argument;
Expand All @@ -102,6 +103,7 @@
import io.smallrye.graphql.schema.model.UnionType;
import io.smallrye.graphql.spi.EventingService;
import io.smallrye.graphql.spi.LookupService;
import io.smallrye.graphql.spi.config.Config;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

Expand Down Expand Up @@ -592,15 +594,21 @@ void printDataFetcherExceptionInDevMode(SmallRyeGraphQLConfig graphQLConfig,
void activateMetrics(Capabilities capabilities,
Optional<MetricsCapabilityBuildItem> metricsCapability,
SmallRyeGraphQLConfig graphQLConfig,
BuildProducer<SystemPropertyBuildItem> systemProperties) {

boolean activate = shouldActivateService(graphQLConfig.metricsEnabled,
metricsCapability.isPresent(),
"quarkus-smallrye-metrics",
"metrics",
"quarkus.smallrye-graphql.metrics.enabled",
false);
if (activate) {
BuildProducer<SystemPropertyBuildItem> systemProperties, BuildProducer<ServiceProviderBuildItem> serviceProvider) {

if (graphQLConfig.metricsEnabled.orElse(false)
|| Config.get().getConfigValue(ConfigKey.ENABLE_METRICS, boolean.class, false)) {
metricsCapability.ifPresentOrElse(capability -> {
if (capability.metricsSupported(MetricsFactory.MICROMETER)) {
serviceProvider.produce(new ServiceProviderBuildItem("io.smallrye.graphql.spi.MetricsService",
"io.smallrye.graphql.cdi.metrics.MicrometerMetricsService"));
}
if (capability.metricsSupported(MetricsFactory.MP_METRICS)) {
serviceProvider.produce(new ServiceProviderBuildItem("io.smallrye.graphql.spi.MetricsService",
"io.smallrye.graphql.cdi.metrics.MPMetricsService"));
}
}, () -> LOG
.warn("GraphQL metrics are enabled but no supported metrics implementation is available on the classpath"));
systemProperties.produce(new SystemPropertyBuildItem(ConfigKey.ENABLE_METRICS, TRUE));
} else {
systemProperties.produce(new SystemPropertyBuildItem(ConfigKey.ENABLE_METRICS, FALSE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ public class MetricsTest {
@Test
public void testQuery() {
MetricRegistry metricRegistry = MetricRegistries.get(MetricRegistry.Type.VENDOR);
SimpleTimer metric = metricRegistry.getSimpleTimers()
.get(new MetricID("mp_graphql", new Tag("type", "QUERY"), new Tag("name", "ping"), new Tag("source", "false")));
assertNotNull(metric, "Metrics should be registered eagerly");

String pingRequest = getPayload("{\n" +
" ping {\n" +
Expand All @@ -55,6 +52,10 @@ public void testQuery() {
.and()
.body(CoreMatchers.containsString("{\"data\":{\"ping\":{\"message\":\"pong\"}}}"));

SimpleTimer metric = metricRegistry.getSimpleTimers()
.get(new MetricID("mp_graphql", new Tag("name", "ping"), new Tag("source", "false"), new Tag("type", "QUERY")));
assertNotNull(metric, "Metrics should be registered eagerly");

assertEquals(1L, metric.getCount(), "Metric should be updated after querying");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import io.smallrye.config.ConfigSourceInterceptorContext;
import io.smallrye.config.RelocateConfigSourceInterceptor;
import io.smallrye.graphql.cdi.config.ConfigKey;
import io.smallrye.graphql.config.ConfigKey;

/**
* Maps config from MicroProfile and SmallRye to Quarkus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ protected <O> O invokeAndTransform(
DataFetchingEnvironment dfe,
DataFetcherResult.Builder<Object> resultBuilder,
Object[] transformedArguments) throws Exception {

ManagedContext requestContext = Arc.container().requestContext();

try {
measurementIds.add(metricsEmitter.start(c));
RequestContextHelper.reactivate(requestContext, dfe);
Uni<?> uni = handleUserMethodCall(dfe, transformedArguments);
return (O) uni
Expand Down Expand Up @@ -69,6 +70,7 @@ protected <O> O invokeAndTransform(
te.appendDataFetcherResult(resultBuilder, dfe);
} finally {
eventEmitter.fireAfterDataFetch(c);
metricsEmitter.end(measurementIds.remove());
}
}
emitter.complete(resultBuilder.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ private <T> T invokeAndTransformBlocking(final io.smallrye.graphql.api.Context c
final Promise<T> result = Promise.promise();

// We need some make sure that we call given the context
measurementIds.add(metricsEmitter.start(c));
@SuppressWarnings("unchecked")
Callable<Object> contextualCallable = threadContext.contextualCallable(() -> {
try {
Expand All @@ -101,14 +102,14 @@ private <T> T invokeAndTransformBlocking(final io.smallrye.graphql.api.Context c
throw ex;
}
});

// Here call blocking with context
BlockingHelper.runBlocking(vc, contextualCallable, result);

return (T) Uni.createFrom().completionStage(result.future().toCompletionStage()).onItemOrFailure()
.invoke((item, error) -> {
if (item != null) {
eventEmitter.fireAfterDataFetch(c);
metricsEmitter.end(measurementIds.remove());
} else {
eventEmitter.fireOnDataFetchError(c, error);
}
Expand Down Expand Up @@ -136,6 +137,7 @@ private CompletionStage<List<T>> invokeBatchBlocking(DataFetchingEnvironment dfe
BlockingHelper.runBlocking(vc, contextualCallable, result);
return result.future().toCompletionStage()
.whenComplete((resultList, error) -> {
eventEmitter.fireAfterDataFetch(dfe.getGraphQlContext().get("context"));
if (error != null) {
onErrorConsumer.accept(error);
}
Expand Down
5 changes: 4 additions & 1 deletion integration-tests/smallrye-graphql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.quarkus.it.smallrye.graphql.metricresources;

import java.util.Arrays;
import java.util.List;

/**
* Just a test pojo
*/
public class TestPojo {
private String message;
private List<String> list = Arrays.asList("a", "b", "c");

private Number number;

public TestPojo() {
super();
}

public TestPojo(String message) {
super();
this.message = message;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public List<String> getList() {
return list;
}

public void setList(List<String> list) {
this.list = list;
}

public Number getNumber() {
return number;
}

public void setNumber(Number number) {
this.number = number;
}

// <placeholder>

@Override
public String toString() {
return "TestPojo{" + "message=" + message + ", list=" + list + ", number=" + number + '}';
}

enum Number {
ONE,
TWO,
THREE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.it.smallrye.graphql.metricresources;

/**
* Just a test pojo that contains a random number
*/
public class TestRandom {
private double value;

public TestRandom() {
this(Math.random());
}

public TestRandom(double value) {
super();
this.value = value;
}

public double getValue() {
return value;
}

public void setValue(double value) {
this.value = value;
}

@Override
public String toString() {
return "TestRandom{" + "value=" + value + '}';
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package io.quarkus.it.smallrye.graphql.metricresources;

import java.util.List;

import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;

import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Mutation;
import org.eclipse.microprofile.graphql.Query;
import org.eclipse.microprofile.graphql.Source;

import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLSchema;
import io.micrometer.core.instrument.Metrics;
import io.smallrye.graphql.api.Context;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.infrastructure.Infrastructure;

/**
* Just a test endpoint
*/
@GraphQLApi
public class TestResource {

public static final double SLEEP_TIME = 0.15;
@Inject
Context context;

@Query
public TestPojo ping() {
return new TestPojo("pong");
}

@Query
public TestPojo foo() {
return new TestPojo("bar");
}

@Query
public TestPojo[] superMetricFoo() throws InterruptedException {
Thread.sleep(sleepTimeInMilliseconds());
return new TestPojo[] { foo(), foo(), foo() };
}

@Query
public TestPojo[] foos() {
return new TestPojo[] { foo() };
}

@Query
public List<TestPojo> batchFoo(@Source List<TestPojo> testPojos) throws InterruptedException {
Thread.sleep(sleepTimeInMilliseconds());
return List.of(new TestPojo("bar1"), new TestPojo("bar2"), new TestPojo("bar3"));
}

@Query
public Uni<List<TestPojo>> asyncBatchFoo(@Source List<TestPojo> testPojos) {
return Uni.createFrom().item(() -> {
try {
Thread.sleep(sleepTimeInMilliseconds());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return List.of(new TestPojo("abar1"), new TestPojo("abar2"), new TestPojo("abar3"));
});
}

@Query("context")
public String getPathFromContext() {
return context.getPath();
}

@Query
public TestPojo systemserror() {
throw new RuntimeException("Some system problem");
}

@Mutation
public TestPojo moo(String name) {
return new TestPojo(name);
}

@Query
public String testCharset(String characters) {
return characters;
}

// <placeholder>
@Query
public Uni<TestPojo[]> asyncSuperMetricFoo() throws InterruptedException {
return Uni.createFrom().item(() -> {
try {
Thread.sleep(sleepTimeInMilliseconds());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new TestPojo[] { new TestPojo("async1"), new TestPojo("async2"), new TestPojo("async3") };
}).runSubscriptionOn(Infrastructure.getDefaultWorkerPool());

}

@Mutation
public void clearMetrics() {
Metrics.globalRegistry.clear();
}

public TestRandom getRandomNumber(@Source TestPojo testPojo) throws InterruptedException {
Thread.sleep(sleepTimeInMilliseconds());
return new TestRandom(123);
}

public Uni<TestRandom> getRandomNumberAsync(@Source TestPojo testPojo) throws InterruptedException {
return Uni.createFrom().item(() -> {
try {
Thread.sleep(sleepTimeInMilliseconds());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return new TestRandom(123);
});
}

public GraphQLSchema.Builder addMyOwnEnum(@Observes GraphQLSchema.Builder builder) {

GraphQLEnumType myOwnEnum = GraphQLEnumType.newEnum()
.name("SomeEnum")
.description("Adding some enum type")
.value("value1")
.value("value2").build();

return builder.additionalType(myOwnEnum);
}

private long sleepTimeInMilliseconds() {
return (long) (SLEEP_TIME * 1000);
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
message=Production
quarkus.smallrye-graphql.show-runtime-exception-message=org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException
quarkus.smallrye-graphql.show-runtime-exception-message=org.eclipse.microprofile.faulttolerance.exceptions.TimeoutException
quarkus.smallrye-graphql.metrics.enabled=true
Loading

0 comments on commit 2e52275

Please sign in to comment.