From 30a7011130472e24dc65680e12174781bf0e5376 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:09:34 -0400 Subject: [PATCH 001/103] enable 4 metrics from GAX --- google-cloud-firestore/pom.xml | 28 +-- .../FirestoreOpenTelemetryOptions.java | 21 +++ .../cloud/firestore/FirestoreOptions.java | 8 + .../firestore/spi/v1/GrpcFirestoreRpc.java | 9 + .../firestore/telemetry/MetricsUtil.java | 168 ++++++++++++++++++ 5 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 48c1ea9ec..d38ac2a43 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -119,6 +119,21 @@ io.opentelemetry.instrumentation opentelemetry-grpc-1.6 + + io.opentelemetry + opentelemetry-sdk + ${opentelemetry.version} + + + com.google.cloud.opentelemetry + exporter-metrics + 0.31.0 + + + io.opentelemetry + opentelemetry-sdk-common + ${opentelemetry.version} + @@ -181,12 +196,6 @@ test - - io.opentelemetry - opentelemetry-sdk - ${opentelemetry.version} - test - io.opentelemetry opentelemetry-sdk-testing @@ -205,12 +214,7 @@ ${opentelemetry.version} test - - io.opentelemetry - opentelemetry-sdk-common - ${opentelemetry.version} - test - + com.google.cloud.opentelemetry exporter-trace diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 2b4606565..752b9480a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -28,10 +28,12 @@ @BetaApi public class FirestoreOpenTelemetryOptions { private final boolean tracingEnabled; + private final boolean metricsEnabled; private final @Nullable OpenTelemetry openTelemetry; FirestoreOpenTelemetryOptions(Builder builder) { this.tracingEnabled = builder.tracingEnabled; + this.metricsEnabled = builder.metricsEnabled; this.openTelemetry = builder.openTelemetry; } @@ -39,6 +41,10 @@ public boolean isTracingEnabled() { return tracingEnabled; } + public boolean isMetricsEnabled() { + return metricsEnabled; + } + public OpenTelemetry getOpenTelemetry() { return openTelemetry; } @@ -57,15 +63,19 @@ public static class Builder { private boolean tracingEnabled; + private boolean metricsEnabled; + @Nullable private OpenTelemetry openTelemetry; private Builder() { tracingEnabled = false; + metricsEnabled = false; openTelemetry = null; } private Builder(FirestoreOpenTelemetryOptions options) { this.tracingEnabled = options.tracingEnabled; + this.metricsEnabled = options.metricsEnabled; this.openTelemetry = options.openTelemetry; } @@ -85,6 +95,17 @@ public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEn return this; } + /** + * Sets whether client side metrics should be enabled. + * + * @param metricsEnabled Whether client side metrics should be enabled. + */ + @Nonnull + public FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEnabled) { + this.metricsEnabled = metricsEnabled; + return this; + } + /** * Sets the {@link OpenTelemetry} to use with this Firestore instance. If telemetry collection * is enabled, but an `OpenTelemetry` is not provided, the Firestore SDK will attempt to use the diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index b80b9dacd..259082a3f 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -30,6 +30,7 @@ import com.google.cloud.TransportOptions; import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.spi.v1.GrpcFirestoreRpc; +import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.v1.FirestoreSettings; import com.google.cloud.grpc.GrpcTransportOptions; import com.google.common.collect.ImmutableMap; @@ -64,6 +65,7 @@ public final class FirestoreOptions extends ServiceOptions PROJECT_ID_KEY = AttributeKey.stringKey("project_id"); + // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); + + // Metric attribute keys for labels + static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language"); + static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name"); + static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); + public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id"); + + static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid"); + + // Metric + static final String OPERATION_LATENCIES_NAME = "operation_latencies"; + static final String OPERATION_COUNT_NAME = "operation_count"; + static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies"; + static final String ATTEMPT_COUNT_NAME = "attempt_count"; + static final String FIRST_RESPONSE_LATENCIES_NAME = "first_response_latencies"; + static final String END_TO_END_LATENCIES_NAME = "end_to_end_latencies"; + static final String TRANSACTION_LATENCIES_NAME = "transaction_latencies"; + static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count"; + + static final String MILLISECOND_UNIT = "ms"; + + static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; + + private final FirestoreOptions firestoreOptions; + private OpenTelemetry openTelemetry; + private ApiTracerFactory otelApiTracerFactory; + + private Meter meter; + private DoubleHistogram endToEndRequestLatency; + private DoubleHistogram firstResponseLatency; + + /** + * Creates and returns an instance of the MetricsUtil class. + * + * @param firestoreOptions The FirestoreOptions object that is requesting an instance of + * MetricsUtil. + * @return An instance of the MetricsUtil class. + */ + public MetricsUtil(FirestoreOptions firestoreOptions) { + this.firestoreOptions = firestoreOptions; + + boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); + + // The environment variable can override options to enable/disable telemetry collection. + String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); + if (enableMetricsEnvVar != null) { + if (enableMetricsEnvVar.equalsIgnoreCase("true") + || enableMetricsEnvVar.equalsIgnoreCase("on")) { + createEnabledInstance = true; + } + if (enableMetricsEnvVar.equalsIgnoreCase("false") + || enableMetricsEnvVar.equalsIgnoreCase("off")) { + createEnabledInstance = false; + } + } + + if (createEnabledInstance) { + createMtericsUtil(); + } + } + + private void createMtericsUtil() { + this.openTelemetry = getDefaultOpenTelemetryInstance(); + + OpenTelemetryMetricsRecorder recorder = + new OpenTelemetryMetricsRecorder(openTelemetry, METRICS_INTERNAL); + + this.otelApiTracerFactory = new MetricsTracerFactory(recorder); + + registerMetrics(); + } + + void registerMetrics() { + Package pkg = this.getClass().getPackage(); + this.meter = + openTelemetry + .meterBuilder(FIRESTORE_LIBRARY_NAME) + .setInstrumentationVersion(pkg.getImplementationVersion()) + .build(); + + this.endToEndRequestLatency = + meter + .histogramBuilder(METRICS_INTERNAL+"/"+END_TO_END_LATENCIES_NAME) + .setDescription("Firestore E2E metrics") + .setUnit(MILLISECOND_UNIT) + .build(); + + this.firstResponseLatency = + meter + .histogramBuilder(METRICS_INTERNAL+"/"+FIRST_RESPONSE_LATENCIES_NAME) + .setDescription("Firestore query first response latency") + .setUnit(MILLISECOND_UNIT) + .build(); + } + + private OpenTelemetry getDefaultOpenTelemetryInstance() { + OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); + + // If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel + // instance. + if (openTelemetry == null) { + MetricExporter metricExporter = + GoogleCloudMetricExporter.createWithConfiguration( + MetricConfiguration.builder() + .setProjectId(firestoreOptions.getProjectId()) + .setPrefix(METRICS_NAMESPACE) + .build()); + + SdkMeterProvider METER_PROVIDER = + SdkMeterProvider.builder() + .registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()) + .build(); + + openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(METER_PROVIDER).build(); + } + return openTelemetry; + } + + public ApiTracerFactory getOpenTelemetryApiTracerFactory() { + return this.otelApiTracerFactory; + } +} From ed85413cce957cb4db62c01938dc84f22e3c7d55 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 16 Aug 2024 12:11:28 -0400 Subject: [PATCH 002/103] format --- .../com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java | 5 ++--- .../com/google/cloud/firestore/telemetry/MetricsUtil.java | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java index 49359d598..9ddc4998b 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java @@ -28,8 +28,6 @@ import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.TransportChannel; import com.google.api.gax.rpc.UnaryCallable; -import com.google.api.gax.tracing.MetricsTracerFactory; -import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.cloud.NoCredentials; import com.google.cloud.ServiceOptions; import com.google.cloud.firestore.FirestoreOptions; @@ -163,7 +161,8 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException { firestoreBuilder.setTracerFactory(options.getApiTracerFactory()); } else if (options.getOpenTelemetryOptions().isMetricsEnabled()) { // TODO: move this logic to Firestore options - firestoreBuilder.setTracerFactory(options.getMetricsUtil().getOpenTelemetryApiTracerFactory()); + firestoreBuilder.setTracerFactory( + options.getMetricsUtil().getOpenTelemetryApiTracerFactory()); } firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build()); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 4a638d9a6..d3e2364fe 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -112,7 +112,7 @@ private void createMtericsUtil() { new OpenTelemetryMetricsRecorder(openTelemetry, METRICS_INTERNAL); this.otelApiTracerFactory = new MetricsTracerFactory(recorder); - + registerMetrics(); } @@ -126,14 +126,14 @@ void registerMetrics() { this.endToEndRequestLatency = meter - .histogramBuilder(METRICS_INTERNAL+"/"+END_TO_END_LATENCIES_NAME) + .histogramBuilder(METRICS_INTERNAL + "/" + END_TO_END_LATENCIES_NAME) .setDescription("Firestore E2E metrics") .setUnit(MILLISECOND_UNIT) .build(); this.firstResponseLatency = meter - .histogramBuilder(METRICS_INTERNAL+"/"+FIRST_RESPONSE_LATENCIES_NAME) + .histogramBuilder(METRICS_INTERNAL + "/" + FIRST_RESPONSE_LATENCIES_NAME) .setDescription("Firestore query first response latency") .setUnit(MILLISECOND_UNIT) .build(); From 57084c4b0b3a87a045f0777553a2efd25fe69603 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 21 Aug 2024 11:33:54 -0400 Subject: [PATCH 003/103] add default otel --- .../cloud/firestore/DocumentReference.java | 8 + .../google/cloud/firestore/FirestoreImpl.java | 24 ++ .../com/google/cloud/firestore/Query.java | 17 ++ .../firestore/telemetry/MetricsUtil.java | 248 ++++++++++++++++-- 4 files changed, 269 insertions(+), 28 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index 57254bb2b..bbad025e7 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -21,11 +21,13 @@ import com.google.api.core.InternalExtensionOnly; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.ApiExceptions; +import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse; import com.google.common.util.concurrent.MoreExecutors; import com.google.firestore.v1.ListCollectionIdsRequest; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -473,8 +475,14 @@ public ApiFuture delete() { @Nonnull public ApiFuture get() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET); + + MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); + double start = System.currentTimeMillis(); try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); + Map attributes = new HashMap<>(); + attributes.put("method_name", "DocumentReference.get"); + util.endAtFuture(result, start, attributes); span.endAtFuture(result); return result; } catch (Exception error) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index 48c691466..dcf8a18cc 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -32,6 +32,7 @@ import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; import com.google.cloud.firestore.spi.v1.FirestoreRpc; +import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -227,6 +228,12 @@ void getAll( // that we receive from the server. final int NUM_RESPONSES_PER_TRACE_EVENT = 100; + MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); + double start = System.currentTimeMillis(); + Map attributes = new HashMap<>(); + String method = transactionId != null ? "Batch.get" : "Transaction.get"; + attributes.put("method_name", method); + ResponseObserver responseObserver = new ResponseObserver() { int numResponses = 0; @@ -254,6 +261,12 @@ public void onResponse(BatchGetDocumentsResponse response) { getTraceUtil() .currentSpan() .addEvent(TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": First response received"); + + double end = System.currentTimeMillis(); + double elapsedTime = end - start; + attributes.put("status", "OK"); + util.firstResponseLatencyRecorder(elapsedTime, attributes); + } else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) { getTraceUtil() .currentSpan() @@ -262,6 +275,11 @@ public void onResponse(BatchGetDocumentsResponse response) { + ": Received " + numResponses + " responses"); + + double end = System.currentTimeMillis(); + double elapsedTime = end - start; + attributes.put("status", "OK"); + util.endToEndRequestLatencyRecorder(elapsedTime, attributes); } switch (response.getResultCase()) { @@ -298,6 +316,12 @@ public void onResponse(BatchGetDocumentsResponse response) { @Override public void onError(Throwable throwable) { getTraceUtil().currentSpan().end(throwable); + + double end = System.currentTimeMillis(); + double elapsedTime = end - start; + attributes.put("status", MetricsUtil.extractErrorStatus(throwable)); + util.endToEndRequestLatencyRecorder(elapsedTime, attributes); + apiStreamObserver.onError(throwable); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index 4721ba93d..58431de14 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -39,6 +39,7 @@ import com.google.auto.value.AutoValue; import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.QueryOptions.Builder; +import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -65,9 +66,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.SortedSet; @@ -1740,6 +1743,12 @@ private void internalStream( final AtomicReference lastReceivedDocument = new AtomicReference<>(); + MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); + long start = System.currentTimeMillis(); + Map attributes = new HashMap<>(); + String method = transactionId != null ? "Query.get" : "Transaction.get"; + attributes.put("method_name", method); + ResponseObserver observer = new ResponseObserver() { Timestamp readTime; @@ -1967,6 +1976,12 @@ ApiFuture get( transactionId == null ? TraceUtil.SPAN_NAME_QUERY_GET : TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY); + + MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); + long start = System.currentTimeMillis(); + Map attributes = new HashMap<>(); + String method = transactionId != null ? "Query.get" : "Transaction.get"; + attributes.put("method_name", method); try (Scope ignored = span.makeCurrent()) { final SettableApiFuture result = SettableApiFuture.create(); internalStream( @@ -2013,6 +2028,8 @@ public void onCompleted() { /* isRetryRequestWithCursor= */ false); span.endAtFuture(result); + util.endAtFuture(result, start, attributes); + return result; } catch (Exception error) { span.end(error); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index d3e2364fe..3f9b44f82 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -16,20 +16,40 @@ package com.google.cloud.firestore.telemetry; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutureCallback; +import com.google.api.core.ApiFutures; +import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.MetricsTracerFactory; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.cloud.firestore.FirestoreException; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; import com.google.cloud.opentelemetry.metric.MetricConfiguration; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.MoreExecutors; +import io.grpc.Status; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.View; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nullable; /** * A utility interface for trace collection. Classes that implement this interface may make their @@ -37,36 +57,64 @@ * they may use a particular tracing framework such as OpenTelemetry. */ public class MetricsUtil { - static final String FIRESTORE_LIBRARY_NAME = "firestore_java"; - static final String METRICS_NAMESPACE = "custom.googleapis.com"; - public static final String METRICS_INTERNAL = "internal"; + static final String FIRESTORE_METER_NAME = "firestore_java"; + + // TODO: change to firestore.googleapis.com + public static final String METER_NAME = "custom.googleapis.com/internal/client"; + public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; // Metric attribute keys for monitored resource static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id"); // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); // Metric attribute keys for labels - static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language"); + // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language"); static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name"); static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id"); - + static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library"); + static final AttributeKey LIBRARY_VERSION_KEY = AttributeKey.stringKey("library_version"); static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid"); + static final AttributeKey Language_KEY = AttributeKey.stringKey("language"); + public static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); // Metric - static final String OPERATION_LATENCIES_NAME = "operation_latencies"; + static final String OPERATION_LATENCY_NAME = "operation_latency"; static final String OPERATION_COUNT_NAME = "operation_count"; - static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies"; + static final String ATTEMPT_LATENCY_NAME = "attempt_latency"; static final String ATTEMPT_COUNT_NAME = "attempt_count"; - static final String FIRST_RESPONSE_LATENCIES_NAME = "first_response_latencies"; - static final String END_TO_END_LATENCIES_NAME = "end_to_end_latencies"; - static final String TRANSACTION_LATENCIES_NAME = "transaction_latencies"; + static final String FIRST_RESPONSE_LATENCY_NAME = "first_response_latency"; + static final String END_TO_END_LATENCY_NAME = "end_to_end_latency"; + static final String TRANSACTION_LATENCY_NAME = "transaction_latency"; static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count"; static final String MILLISECOND_UNIT = "ms"; static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; + public static final Set COMMON_ATTRIBUTES = + ImmutableSet.of( + PROJECT_ID_KEY, + DATABASE_ID_KEY, + CLIENT_UID_KEY, + STATUS_KEY, + CLIENT_LIBRARY_KEY, + LIBRARY_VERSION_KEY); + + public static final Set BUILTIN_METRICS = + ImmutableSet.of( + OPERATION_LATENCY_NAME, + ATTEMPT_LATENCY_NAME, + OPERATION_COUNT_NAME, + ATTEMPT_COUNT_NAME, + FIRST_RESPONSE_LATENCY_NAME, + END_TO_END_LATENCY_NAME, + TRANSACTION_LATENCY_NAME, + TRANSACTION_ATTEMPT_COUNT_NAME) + .stream() + .map(m -> METER_NAME + '/' + m) + .collect(Collectors.toSet()); + private final FirestoreOptions firestoreOptions; private OpenTelemetry openTelemetry; private ApiTracerFactory otelApiTracerFactory; @@ -101,15 +149,15 @@ public MetricsUtil(FirestoreOptions firestoreOptions) { } if (createEnabledInstance) { - createMtericsUtil(); + createMetricsUtil(); } } - private void createMtericsUtil() { + private void createMetricsUtil() { this.openTelemetry = getDefaultOpenTelemetryInstance(); OpenTelemetryMetricsRecorder recorder = - new OpenTelemetryMetricsRecorder(openTelemetry, METRICS_INTERNAL); + new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); this.otelApiTracerFactory = new MetricsTracerFactory(recorder); @@ -117,28 +165,35 @@ private void createMtericsUtil() { } void registerMetrics() { - Package pkg = this.getClass().getPackage(); - this.meter = - openTelemetry - .meterBuilder(FIRESTORE_LIBRARY_NAME) - .setInstrumentationVersion(pkg.getImplementationVersion()) - .build(); + this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); this.endToEndRequestLatency = meter - .histogramBuilder(METRICS_INTERNAL + "/" + END_TO_END_LATENCIES_NAME) + .histogramBuilder(METER_NAME + "/" + END_TO_END_LATENCY_NAME) .setDescription("Firestore E2E metrics") .setUnit(MILLISECOND_UNIT) .build(); this.firstResponseLatency = meter - .histogramBuilder(METRICS_INTERNAL + "/" + FIRST_RESPONSE_LATENCIES_NAME) + .histogramBuilder(METER_NAME + "/" + FIRST_RESPONSE_LATENCY_NAME) .setDescription("Firestore query first response latency") .setUnit(MILLISECOND_UNIT) .build(); } + public void endToEndRequestLatencyRecorder(double latency, Map attributes) { + if (endToEndRequestLatency != null) { + endToEndRequestLatency.record(latency, toOtelAttributes(attributes)); + } + } + + public void firstResponseLatencyRecorder(double latency, Map attributes) { + if (firstResponseLatency != null) { + firstResponseLatency.record(latency, toOtelAttributes(attributes)); + } + } + private OpenTelemetry getDefaultOpenTelemetryInstance() { OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); @@ -147,17 +202,32 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() { if (openTelemetry == null) { MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( - MetricConfiguration.builder() - .setProjectId(firestoreOptions.getProjectId()) - .setPrefix(METRICS_NAMESPACE) - .build()); + MetricConfiguration.builder().setProjectId(firestoreOptions.getProjectId()).build()); + + SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); + + // Remove "Language" attributes from GAX metrics + for (Map.Entry entry : getAllViews().entrySet()) { + sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); + } + + // This adds to Metrics.Resource.Attributes, doesn't show up on GC monitoring dashboard. + Package pkg = this.getClass().getPackage(); + Resource resource = + Resource.getDefault() + .merge( + Resource.builder() + .put(CLIENT_LIBRARY_KEY, FIRESTORE_METER_NAME) + .put(LIBRARY_VERSION_KEY, pkg.getImplementationVersion()) + .build()); + sdkMeterProviderBuilder.setResource(resource); - SdkMeterProvider METER_PROVIDER = - SdkMeterProvider.builder() + SdkMeterProvider meterProvider = + sdkMeterProviderBuilder .registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()) .build(); - openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(METER_PROVIDER).build(); + openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); } return openTelemetry; } @@ -165,4 +235,126 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() { public ApiTracerFactory getOpenTelemetryApiTracerFactory() { return this.otelApiTracerFactory; } + + @VisibleForTesting + Attributes toOtelAttributes(Map attributes) { + AttributesBuilder attributesBuilder = Attributes.builder(); + attributes.forEach(attributesBuilder::put); + return attributesBuilder.build(); + } + + public void endAtFuture( + ApiFuture futureValue, double start, Map attributes) { + ApiFutures.addCallback( + futureValue, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + double end = System.currentTimeMillis(); + double elapsedTime = end - start; + attributes.put("status", extractErrorStatus(t)); + endToEndRequestLatencyRecorder(elapsedTime, attributes); + } + + @Override + public void onSuccess(T result) { + double end = System.currentTimeMillis(); + double elapsedTime = end - start; + attributes.put("status", "OK"); + endToEndRequestLatencyRecorder(elapsedTime, attributes); + } + }, + MoreExecutors.directExecutor()); + } + + public void end(Throwable t, double start, Map attributes) { + attributes.put("status", extractErrorStatus(t)); + double end = System.currentTimeMillis(); + double elapsedTime = end - start; + endToEndRequestLatencyRecorder(elapsedTime, attributes); + } + + /** Function to extract the status of the error as a string */ + public static String extractErrorStatus(@Nullable Throwable throwable) { + if (!(throwable instanceof FirestoreException)) { + return StatusCode.Code.UNKNOWN.toString(); + } + Status status = ((FirestoreException) throwable).getStatus(); + + return status.getCode().name(); + } + + static void defineView( + ImmutableMap.Builder viewMap, + String MeterName, + String id, + Set attributes) { + InstrumentSelector selector = + InstrumentSelector.builder().setName(id).setMeterName(MeterName).build(); + Set attributesFilter = + ImmutableSet.builder() + .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) + .build(); + View view = View.builder().setName(id).setAttributeFilter(attributesFilter).build(); + + viewMap.put(selector, view); + } + + public static Map getAllViews() { + ImmutableMap.Builder views = ImmutableMap.builder(); + + defineView( + views, + GAX_METER_NAME, + OPERATION_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + defineView( + views, + GAX_METER_NAME, + ATTEMPT_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + GAX_METER_NAME, + OPERATION_COUNT_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + GAX_METER_NAME, + ATTEMPT_COUNT_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + FIRESTORE_METER_NAME, + FIRST_RESPONSE_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + FIRESTORE_METER_NAME, + END_TO_END_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + return views.build(); + } } From 571c0956aea852c122ed8149f46670df1fa2c7f5 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:28:13 -0400 Subject: [PATCH 004/103] format --- .../firestore/telemetry/MetricsUtil.java | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 3f9b44f82..2bc1f7aaa 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -97,9 +97,9 @@ public class MetricsUtil { PROJECT_ID_KEY, DATABASE_ID_KEY, CLIENT_UID_KEY, - STATUS_KEY, CLIENT_LIBRARY_KEY, - LIBRARY_VERSION_KEY); + LIBRARY_VERSION_KEY, + STATUS_KEY); public static final Set BUILTIN_METRICS = ImmutableSet.of( @@ -200,35 +200,35 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() { // If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel // instance. if (openTelemetry == null) { + SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); + + // Attach built-in exporter MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder().setProjectId(firestoreOptions.getProjectId()).build()); + sdkMeterProviderBuilder.registerMetricReader( + PeriodicMetricReader.builder(metricExporter).build()); - SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); - - // Remove "Language" attributes from GAX metrics - for (Map.Entry entry : getAllViews().entrySet()) { - sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); - } - - // This adds to Metrics.Resource.Attributes, doesn't show up on GC monitoring dashboard. + // Add static attributes to resource Package pkg = this.getClass().getPackage(); - Resource resource = + sdkMeterProviderBuilder.setResource( Resource.getDefault() .merge( Resource.builder() + .put(DATABASE_ID_KEY, firestoreOptions.getDatabaseId()) .put(CLIENT_LIBRARY_KEY, FIRESTORE_METER_NAME) .put(LIBRARY_VERSION_KEY, pkg.getImplementationVersion()) - .build()); - sdkMeterProviderBuilder.setResource(resource); + .build())); - SdkMeterProvider meterProvider = - sdkMeterProviderBuilder - .registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()) - .build(); + // Filter out attributes that are not defined + for (Map.Entry entry : getAllViews().entrySet()) { + sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); + } - openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); + openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); } + return openTelemetry; } @@ -286,16 +286,15 @@ public static String extractErrorStatus(@Nullable Throwable throwable) { static void defineView( ImmutableMap.Builder viewMap, - String MeterName, String id, Set attributes) { InstrumentSelector selector = - InstrumentSelector.builder().setName(id).setMeterName(MeterName).build(); + InstrumentSelector.builder().setName(METER_NAME + "/" + id).build(); Set attributesFilter = ImmutableSet.builder() .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) .build(); - View view = View.builder().setName(id).setAttributeFilter(attributesFilter).build(); + View view = View.builder().setAttributeFilter(attributesFilter).build(); viewMap.put(selector, view); } @@ -305,7 +304,6 @@ public static Map getAllViews() { defineView( views, - GAX_METER_NAME, OPERATION_LATENCY_NAME, ImmutableSet.builder() .addAll(COMMON_ATTRIBUTES) @@ -313,7 +311,6 @@ public static Map getAllViews() { .build()); defineView( views, - GAX_METER_NAME, ATTEMPT_LATENCY_NAME, ImmutableSet.builder() .addAll(COMMON_ATTRIBUTES) @@ -322,7 +319,6 @@ public static Map getAllViews() { defineView( views, - GAX_METER_NAME, OPERATION_COUNT_NAME, ImmutableSet.builder() .addAll(COMMON_ATTRIBUTES) @@ -331,7 +327,6 @@ public static Map getAllViews() { defineView( views, - GAX_METER_NAME, ATTEMPT_COUNT_NAME, ImmutableSet.builder() .addAll(COMMON_ATTRIBUTES) @@ -340,7 +335,6 @@ public static Map getAllViews() { defineView( views, - FIRESTORE_METER_NAME, FIRST_RESPONSE_LATENCY_NAME, ImmutableSet.builder() .addAll(COMMON_ATTRIBUTES) @@ -349,7 +343,6 @@ public static Map getAllViews() { defineView( views, - FIRESTORE_METER_NAME, END_TO_END_LATENCY_NAME, ImmutableSet.builder() .addAll(COMMON_ATTRIBUTES) From f4b8416aed326d3290aaf6db01cd6cda920440c6 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:35:20 -0400 Subject: [PATCH 005/103] move constants out --- .../telemetry/BuiltinMetricsConstants.java | 139 ++++++++++++++ .../telemetry/BuiltinMetricsView.java | 56 ++++++ .../firestore/telemetry/MetricsUtil.java | 173 ++---------------- 3 files changed, 207 insertions(+), 161 deletions(-) create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java new file mode 100644 index 000000000..181802dc1 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java @@ -0,0 +1,139 @@ +package com.google.cloud.firestore.telemetry; + +import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.View; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class BuiltinMetricsConstants { + + static final String FIRESTORE_METER_NAME = "firestore_java"; + + // TODO: change to firestore.googleapis.com + public static final String METER_NAME = "custom.googleapis.com/internal/client"; + public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; + + // Metric attribute keys for monitored resource + static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id"); + // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); + + // Metric attribute keys for labels + // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language"); + static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name"); + static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); + public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id"); + static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library"); + static final AttributeKey LIBRARY_VERSION_KEY = AttributeKey.stringKey("library_version"); + static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid"); + + static final AttributeKey Language_KEY = AttributeKey.stringKey("language"); + public static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); + // Metric + static final String OPERATION_LATENCY_NAME = "operation_latency"; + static final String OPERATION_COUNT_NAME = "operation_count"; + static final String ATTEMPT_LATENCY_NAME = "attempt_latency"; + static final String ATTEMPT_COUNT_NAME = "attempt_count"; + static final String FIRST_RESPONSE_LATENCY_NAME = "first_response_latency"; + static final String END_TO_END_LATENCY_NAME = "end_to_end_latency"; + static final String TRANSACTION_LATENCY_NAME = "transaction_latency"; + static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count"; + + static final String MILLISECOND_UNIT = "ms"; + + public static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; + + public static final Set COMMON_ATTRIBUTES = + ImmutableSet.of( + PROJECT_ID_KEY, + DATABASE_ID_KEY, + CLIENT_UID_KEY, + CLIENT_LIBRARY_KEY, + LIBRARY_VERSION_KEY, + STATUS_KEY); + + public static final Set BUILTIN_METRICS = + ImmutableSet.of( + OPERATION_LATENCY_NAME, + ATTEMPT_LATENCY_NAME, + OPERATION_COUNT_NAME, + ATTEMPT_COUNT_NAME, + FIRST_RESPONSE_LATENCY_NAME, + END_TO_END_LATENCY_NAME, + TRANSACTION_LATENCY_NAME, + TRANSACTION_ATTEMPT_COUNT_NAME) + .stream() + .map(m -> METER_NAME + '/' + m) + .collect(Collectors.toSet()); + + static void defineView( + ImmutableMap.Builder viewMap, + String id, + Set attributes) { + InstrumentSelector selector = + InstrumentSelector.builder().setName(METER_NAME + "/" + id).build(); + Set attributesFilter = + ImmutableSet.builder() + .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) + .build(); + View view = View.builder().setAttributeFilter(attributesFilter).build(); + + viewMap.put(selector, view); + } + + public static Map getAllViews() { + ImmutableMap.Builder views = ImmutableMap.builder(); + + defineView( + views, + OPERATION_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + defineView( + views, + ATTEMPT_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + OPERATION_COUNT_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + ATTEMPT_COUNT_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + FIRST_RESPONSE_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + + defineView( + views, + END_TO_END_LATENCY_NAME, + ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .add(METHOD_NAME_KEY) + .build()); + return views.build(); + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java new file mode 100644 index 000000000..db9ebb6e4 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java @@ -0,0 +1,56 @@ +package com.google.cloud.firestore.telemetry; + +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; + +import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; +import com.google.cloud.opentelemetry.metric.MetricConfiguration; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.View; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import java.io.IOException; +import java.util.Map; + +public class BuiltinMetricsView { + + private BuiltinMetricsView() {} + + /** Register built-in metrics on the {@link SdkMeterProviderBuilder} with credentials. */ + public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder) + throws IOException { + + // Attach built-in exporter + MetricExporter metricExporter = + GoogleCloudMetricExporter.createWithConfiguration( + MetricConfiguration.builder().setProjectId(projectId).build()); + + builder.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()); + + // Add static attributes to resource + // Package pkg = this.getClass().getPackage(); + + // Filter out attributes that are not defined + for (Map.Entry entry : getAllViews().entrySet()) { + builder.registerView(entry.getKey(), entry.getValue()); + } + + builder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 2bc1f7aaa..e346d7c1a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -16,6 +16,8 @@ package com.google.cloud.firestore.telemetry; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; + import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; @@ -25,30 +27,19 @@ import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.cloud.firestore.FirestoreException; import com.google.cloud.firestore.FirestoreOptions; -import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; -import com.google.cloud.opentelemetry.metric.MetricConfiguration; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Status; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.View; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import io.opentelemetry.sdk.resources.Resource; +import java.io.IOException; import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; import javax.annotation.Nullable; /** @@ -57,63 +48,6 @@ * they may use a particular tracing framework such as OpenTelemetry. */ public class MetricsUtil { - static final String FIRESTORE_METER_NAME = "firestore_java"; - - // TODO: change to firestore.googleapis.com - public static final String METER_NAME = "custom.googleapis.com/internal/client"; - public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; - - // Metric attribute keys for monitored resource - static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id"); - // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); - - // Metric attribute keys for labels - // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language"); - static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name"); - static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); - public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id"); - static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library"); - static final AttributeKey LIBRARY_VERSION_KEY = AttributeKey.stringKey("library_version"); - static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid"); - - static final AttributeKey Language_KEY = AttributeKey.stringKey("language"); - public static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); - // Metric - static final String OPERATION_LATENCY_NAME = "operation_latency"; - static final String OPERATION_COUNT_NAME = "operation_count"; - static final String ATTEMPT_LATENCY_NAME = "attempt_latency"; - static final String ATTEMPT_COUNT_NAME = "attempt_count"; - static final String FIRST_RESPONSE_LATENCY_NAME = "first_response_latency"; - static final String END_TO_END_LATENCY_NAME = "end_to_end_latency"; - static final String TRANSACTION_LATENCY_NAME = "transaction_latency"; - static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count"; - - static final String MILLISECOND_UNIT = "ms"; - - static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; - - public static final Set COMMON_ATTRIBUTES = - ImmutableSet.of( - PROJECT_ID_KEY, - DATABASE_ID_KEY, - CLIENT_UID_KEY, - CLIENT_LIBRARY_KEY, - LIBRARY_VERSION_KEY, - STATUS_KEY); - - public static final Set BUILTIN_METRICS = - ImmutableSet.of( - OPERATION_LATENCY_NAME, - ATTEMPT_LATENCY_NAME, - OPERATION_COUNT_NAME, - ATTEMPT_COUNT_NAME, - FIRST_RESPONSE_LATENCY_NAME, - END_TO_END_LATENCY_NAME, - TRANSACTION_LATENCY_NAME, - TRANSACTION_ATTEMPT_COUNT_NAME) - .stream() - .map(m -> METER_NAME + '/' + m) - .collect(Collectors.toSet()); private final FirestoreOptions firestoreOptions; private OpenTelemetry openTelemetry; @@ -149,11 +83,15 @@ public MetricsUtil(FirestoreOptions firestoreOptions) { } if (createEnabledInstance) { - createMetricsUtil(); + try { + createMetricsUtil(); + } catch (IOException e) { + System.out.println(e); + } } } - private void createMetricsUtil() { + private void createMetricsUtil() throws IOException { this.openTelemetry = getDefaultOpenTelemetryInstance(); OpenTelemetryMetricsRecorder recorder = @@ -194,7 +132,7 @@ public void firstResponseLatencyRecorder(double latency, Map att } } - private OpenTelemetry getDefaultOpenTelemetryInstance() { + private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); // If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel @@ -202,28 +140,8 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() { if (openTelemetry == null) { SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); - // Attach built-in exporter - MetricExporter metricExporter = - GoogleCloudMetricExporter.createWithConfiguration( - MetricConfiguration.builder().setProjectId(firestoreOptions.getProjectId()).build()); - sdkMeterProviderBuilder.registerMetricReader( - PeriodicMetricReader.builder(metricExporter).build()); - - // Add static attributes to resource - Package pkg = this.getClass().getPackage(); - sdkMeterProviderBuilder.setResource( - Resource.getDefault() - .merge( - Resource.builder() - .put(DATABASE_ID_KEY, firestoreOptions.getDatabaseId()) - .put(CLIENT_LIBRARY_KEY, FIRESTORE_METER_NAME) - .put(LIBRARY_VERSION_KEY, pkg.getImplementationVersion()) - .build())); - - // Filter out attributes that are not defined - for (Map.Entry entry : getAllViews().entrySet()) { - sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); - } + BuiltinMetricsView.registerBuiltinMetrics( + firestoreOptions.getProjectId(), sdkMeterProviderBuilder); openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); @@ -283,71 +201,4 @@ public static String extractErrorStatus(@Nullable Throwable throwable) { return status.getCode().name(); } - - static void defineView( - ImmutableMap.Builder viewMap, - String id, - Set attributes) { - InstrumentSelector selector = - InstrumentSelector.builder().setName(METER_NAME + "/" + id).build(); - Set attributesFilter = - ImmutableSet.builder() - .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) - .build(); - View view = View.builder().setAttributeFilter(attributesFilter).build(); - - viewMap.put(selector, view); - } - - public static Map getAllViews() { - ImmutableMap.Builder views = ImmutableMap.builder(); - - defineView( - views, - OPERATION_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - defineView( - views, - ATTEMPT_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - - defineView( - views, - OPERATION_COUNT_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - - defineView( - views, - ATTEMPT_COUNT_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - - defineView( - views, - FIRST_RESPONSE_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - - defineView( - views, - END_TO_END_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - return views.build(); - } } From da0fe82ef5387fe7ee2daf36f632558a966c4a08 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:06:23 -0400 Subject: [PATCH 006/103] format --- .../telemetry/BuiltinMetricsConstants.java | 16 ++++++++++++++++ .../firestore/telemetry/BuiltinMetricsView.java | 3 +-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java index 181802dc1..17a210796 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java @@ -1,3 +1,19 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.cloud.firestore.telemetry; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java index db9ebb6e4..c42f131ef 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java @@ -1,5 +1,3 @@ -package com.google.cloud.firestore.telemetry; - /* * Copyright 2023 Google LLC * @@ -15,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.google.cloud.firestore.telemetry; import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; From 61432eb6efd31720f18a0283e75f23485fec6bdc Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:52:27 -0400 Subject: [PATCH 007/103] add additional attributes --- .../cloud/firestore/spi/v1/GrpcFirestoreRpc.java | 3 ++- .../cloud/firestore/telemetry/MetricsUtil.java | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java index 9ddc4998b..36cd8dac2 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java @@ -160,7 +160,8 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException { if (options.getApiTracerFactory() != null) { firestoreBuilder.setTracerFactory(options.getApiTracerFactory()); } else if (options.getOpenTelemetryOptions().isMetricsEnabled()) { - // TODO: move this logic to Firestore options + // TODO: it should not be determined by isMetricsEnabled. move this whole logic to + // options.getApiTracerFactory() firestoreBuilder.setTracerFactory( options.getMetricsUtil().getOpenTelemetryApiTracerFactory()); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index e346d7c1a..62e159fdf 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -39,6 +39,7 @@ import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; @@ -84,20 +85,26 @@ public MetricsUtil(FirestoreOptions firestoreOptions) { if (createEnabledInstance) { try { - createMetricsUtil(); + createMetricsUtil(firestoreOptions); } catch (IOException e) { System.out.println(e); } } } - private void createMetricsUtil() throws IOException { + private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOException { this.openTelemetry = getDefaultOpenTelemetryInstance(); OpenTelemetryMetricsRecorder recorder = new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); - this.otelApiTracerFactory = new MetricsTracerFactory(recorder); + Map attributes = new HashMap<>(); + attributes.put(DATABASE_ID_KEY.toString(), firestoreOptions.getDatabaseId()); + attributes.put(PROJECT_ID_KEY.toString(), firestoreOptions.getProjectId()); + + System.out.println("====1"); + this.otelApiTracerFactory = new MetricsTracerFactory(recorder, attributes); + System.out.println("====2"); registerMetrics(); } From f0a029d9e73b7225a7008ed59b7b16c17c256dea Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:45:33 -0400 Subject: [PATCH 008/103] add attributes to gax metrics --- .../com/google/cloud/firestore/FirestoreOptions.java | 4 +++- .../firestore/telemetry/BuiltinMetricsConstants.java | 2 +- .../cloud/firestore/telemetry/BuiltinMetricsView.java | 3 --- .../google/cloud/firestore/telemetry/MetricsUtil.java | 11 ++++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index 259082a3f..512b71364 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -326,13 +326,15 @@ protected FirestoreOptions(Builder builder) { ? builder.openTelemetryOptions : FirestoreOpenTelemetryOptions.newBuilder().build(); this.traceUtil = com.google.cloud.firestore.telemetry.TraceUtil.getInstance(this); - this.metricsUtil = new MetricsUtil(this); this.databaseId = builder.databaseId != null ? builder.databaseId : FirestoreDefaults.INSTANCE.getDatabaseId(); + // set metrics util after database ID is set + this.metricsUtil = new MetricsUtil(this); + if (builder.channelProvider == null) { ApiFunction channelConfigurator = this.traceUtil.getChannelConfigurator(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java index 17a210796..093111d06 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java @@ -28,7 +28,7 @@ public class BuiltinMetricsConstants { - static final String FIRESTORE_METER_NAME = "firestore_java"; + static final String FIRESTORE_LIBRARY_NAME = "firestore_java"; // TODO: change to firestore.googleapis.com public static final String METER_NAME = "custom.googleapis.com/internal/client"; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java index c42f131ef..0871471f4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java @@ -42,9 +42,6 @@ public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuil builder.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()); - // Add static attributes to resource - // Package pkg = this.getClass().getPackage(); - // Filter out attributes that are not defined for (Map.Entry entry : getAllViews().entrySet()) { builder.registerView(entry.getKey(), entry.getValue()); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 62e159fdf..a4e851c88 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -100,17 +100,18 @@ private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOExcep Map attributes = new HashMap<>(); attributes.put(DATABASE_ID_KEY.toString(), firestoreOptions.getDatabaseId()); - attributes.put(PROJECT_ID_KEY.toString(), firestoreOptions.getProjectId()); + attributes.put(CLIENT_LIBRARY_KEY.toString(), FIRESTORE_LIBRARY_NAME); + String pkgVersion = this.getClass().getPackage().getImplementationVersion(); + if (pkgVersion != null) { + attributes.put(LIBRARY_VERSION_KEY.toString(), pkgVersion); + } - System.out.println("====1"); this.otelApiTracerFactory = new MetricsTracerFactory(recorder, attributes); - System.out.println("====2"); - registerMetrics(); } void registerMetrics() { - this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); + this.meter = openTelemetry.getMeter(FIRESTORE_LIBRARY_NAME); this.endToEndRequestLatency = meter From 54560387caf541e2ca08cfeaf4279510683b6647 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 4 Sep 2024 11:41:14 -0400 Subject: [PATCH 009/103] Update BuiltinMetricsConstants.java --- .../telemetry/BuiltinMetricsConstants.java | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java index 093111d06..f436cf4fe 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java @@ -32,6 +32,7 @@ public class BuiltinMetricsConstants { // TODO: change to firestore.googleapis.com public static final String METER_NAME = "custom.googleapis.com/internal/client"; + static final String FIRESTORE_METER_NAME = "firestore_java"; public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; // Metric attribute keys for monitored resource @@ -89,9 +90,10 @@ public class BuiltinMetricsConstants { static void defineView( ImmutableMap.Builder viewMap, String id, + String meter, Set attributes) { InstrumentSelector selector = - InstrumentSelector.builder().setName(METER_NAME + "/" + id).build(); + InstrumentSelector.builder().setMeterName(meter).setName(METER_NAME + "/" + id).build(); Set attributesFilter = ImmutableSet.builder() .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) @@ -101,55 +103,52 @@ static void defineView( viewMap.put(selector, view); } + private static Set withAdditionalAttributes(Set attributes) { + return ImmutableSet.builder() + .addAll(COMMON_ATTRIBUTES) + .addAll(attributes) + .build(); + } + public static Map getAllViews() { ImmutableMap.Builder views = ImmutableMap.builder(); + // Define views with COMMON_ATTRIBUTES and METHOD_NAME_KEY defineView( views, OPERATION_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); + GAX_METER_NAME, + withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); defineView( views, ATTEMPT_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - + GAX_METER_NAME, + withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); defineView( views, OPERATION_COUNT_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - + GAX_METER_NAME, + withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); defineView( views, ATTEMPT_COUNT_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - + GAX_METER_NAME, + withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); defineView( views, FIRST_RESPONSE_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); - + FIRESTORE_METER_NAME, + withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); defineView( views, END_TO_END_LATENCY_NAME, - ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .add(METHOD_NAME_KEY) - .build()); + FIRESTORE_METER_NAME, + withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); + + // Define views with only COMMON_ATTRIBUTES + defineView(views, TRANSACTION_LATENCY_NAME, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); + defineView(views, TRANSACTION_ATTEMPT_COUNT_NAME, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); + return views.build(); } } From 5efaba63ed1523d323e5664dfcd6ceacc02eda8d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:18:53 -0400 Subject: [PATCH 010/103] remove current metrics implementation --- .../cloud/firestore/DocumentReference.java | 7 ------ .../google/cloud/firestore/FirestoreImpl.java | 24 ------------------- .../com/google/cloud/firestore/Query.java | 17 ------------- .../telemetry/BuiltinMetricsView.java | 5 +++- 4 files changed, 4 insertions(+), 49 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index bbad025e7..9ecad6e95 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -21,13 +21,11 @@ import com.google.api.core.InternalExtensionOnly; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.ApiExceptions; -import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse; import com.google.common.util.concurrent.MoreExecutors; import com.google.firestore.v1.ListCollectionIdsRequest; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -476,13 +474,8 @@ public ApiFuture delete() { public ApiFuture get() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET); - MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); - double start = System.currentTimeMillis(); try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); - Map attributes = new HashMap<>(); - attributes.put("method_name", "DocumentReference.get"); - util.endAtFuture(result, start, attributes); span.endAtFuture(result); return result; } catch (Exception error) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index dcf8a18cc..48c691466 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -32,7 +32,6 @@ import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; import com.google.cloud.firestore.spi.v1.FirestoreRpc; -import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -228,12 +227,6 @@ void getAll( // that we receive from the server. final int NUM_RESPONSES_PER_TRACE_EVENT = 100; - MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); - double start = System.currentTimeMillis(); - Map attributes = new HashMap<>(); - String method = transactionId != null ? "Batch.get" : "Transaction.get"; - attributes.put("method_name", method); - ResponseObserver responseObserver = new ResponseObserver() { int numResponses = 0; @@ -261,12 +254,6 @@ public void onResponse(BatchGetDocumentsResponse response) { getTraceUtil() .currentSpan() .addEvent(TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": First response received"); - - double end = System.currentTimeMillis(); - double elapsedTime = end - start; - attributes.put("status", "OK"); - util.firstResponseLatencyRecorder(elapsedTime, attributes); - } else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) { getTraceUtil() .currentSpan() @@ -275,11 +262,6 @@ public void onResponse(BatchGetDocumentsResponse response) { + ": Received " + numResponses + " responses"); - - double end = System.currentTimeMillis(); - double elapsedTime = end - start; - attributes.put("status", "OK"); - util.endToEndRequestLatencyRecorder(elapsedTime, attributes); } switch (response.getResultCase()) { @@ -316,12 +298,6 @@ public void onResponse(BatchGetDocumentsResponse response) { @Override public void onError(Throwable throwable) { getTraceUtil().currentSpan().end(throwable); - - double end = System.currentTimeMillis(); - double elapsedTime = end - start; - attributes.put("status", MetricsUtil.extractErrorStatus(throwable)); - util.endToEndRequestLatencyRecorder(elapsedTime, attributes); - apiStreamObserver.onError(throwable); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index 58431de14..b19fe8276 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -39,7 +39,6 @@ import com.google.auto.value.AutoValue; import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.QueryOptions.Builder; -import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -66,11 +65,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.SortedSet; @@ -1742,13 +1739,6 @@ private void internalStream( .build()); final AtomicReference lastReceivedDocument = new AtomicReference<>(); - - MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); - long start = System.currentTimeMillis(); - Map attributes = new HashMap<>(); - String method = transactionId != null ? "Query.get" : "Transaction.get"; - attributes.put("method_name", method); - ResponseObserver observer = new ResponseObserver() { Timestamp readTime; @@ -1977,11 +1967,6 @@ ApiFuture get( ? TraceUtil.SPAN_NAME_QUERY_GET : TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY); - MetricsUtil util = getFirestore().getOptions().getMetricsUtil(); - long start = System.currentTimeMillis(); - Map attributes = new HashMap<>(); - String method = transactionId != null ? "Query.get" : "Transaction.get"; - attributes.put("method_name", method); try (Scope ignored = span.makeCurrent()) { final SettableApiFuture result = SettableApiFuture.create(); internalStream( @@ -2028,8 +2013,6 @@ public void onCompleted() { /* isRetryRequestWithCursor= */ false); span.endAtFuture(result); - util.endAtFuture(result, start, attributes); - return result; } catch (Exception error) { span.end(error); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java index 0871471f4..1a2941b8d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java @@ -38,7 +38,10 @@ public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuil // Attach built-in exporter MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( - MetricConfiguration.builder().setProjectId(projectId).build()); + MetricConfiguration.builder() + .setProjectId(projectId) + .setInstrumentationLibraryLabelsEnabled(false) + .build()); builder.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()); From c0b4cc17ed001432ec98ed85835de72f5946d1ea Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:38:23 -0400 Subject: [PATCH 011/103] add custom exporter, add latency recorder --- .../cloud/firestore/AggregateQuery.java | 25 +++ .../google/cloud/firestore/BulkWriter.java | 11 ++ .../cloud/firestore/CollectionGroup.java | 15 ++ .../cloud/firestore/CollectionReference.java | 26 +++ .../cloud/firestore/DocumentReference.java | 108 +++++++++++ .../com/google/cloud/firestore/Query.java | 33 ++++ .../cloud/firestore/ReadTimeTransaction.java | 31 +++ .../firestore/ServerSideTransaction.java | 41 ++++ .../ServerSideTransactionRunner.java | 11 ++ .../google/cloud/firestore/Transaction.java | 6 + .../google/cloud/firestore/UpdateBuilder.java | 14 ++ .../telemetry/BuiltinMetricsConstants.java | 16 +- .../telemetry/BuiltinMetricsView.java | 13 +- .../FirestoreCloudMonitoringExporter.java | 91 +++++++++ .../firestore/telemetry/MetricsUtil.java | 178 +++++++++++------- 15 files changed, 531 insertions(+), 88 deletions(-) create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 84b4a0478..72fbeda08 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -27,6 +27,8 @@ import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StreamController; import com.google.cloud.Timestamp; +import com.google.cloud.firestore.telemetry.MetricsUtil; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -70,6 +72,11 @@ private TraceUtil getTraceUtil() { return query.getFirestore().getOptions().getTraceUtil(); } + @Nonnull + private MetricsUtil getMetricsUtil() { + return query.getFirestore().getOptions().getMetricsUtil(); + } + /** Returns the query whose aggregations will be calculated by this object. */ @Nonnull public Query getQuery() { @@ -97,6 +104,10 @@ public ApiFuture get() { @Nonnull public ApiFuture> explain(ExplainOptions options) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET); + + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET); + try (Scope ignored = span.makeCurrent()) { AggregateQueryExplainResponseDeliverer responseDeliverer = new AggregateQueryExplainResponseDeliverer( @@ -106,10 +117,14 @@ public ApiFuture> explain(ExplainOptions /* explainOptions= */ options); runQuery(responseDeliverer, /* attempt */ 0); ApiFuture> result = responseDeliverer.getFuture(); + span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -123,6 +138,14 @@ ApiFuture get( transactionId == null ? TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET : TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY); + + MetricsContext metricsContext = + getMetricsUtil() + .createMetricsContext( + transactionId == null + ? TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET + : TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY); + try (Scope ignored = span.makeCurrent()) { AggregateQueryResponseDeliverer responseDeliverer = new AggregateQueryResponseDeliverer( @@ -132,9 +155,11 @@ ApiFuture get( runQuery(responseDeliverer, /* attempt= */ 0); ApiFuture result = responseDeliverer.getFuture(); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java index bc881979c..f12f42cc5 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java @@ -26,6 +26,7 @@ import com.google.api.core.SettableApiFuture; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode.Code; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Context; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -914,6 +915,13 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) { .getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT, traceContext) .setAttribute(ATTRIBUTE_KEY_DOC_COUNT, batch.getMutationsSize()); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT); + try (Scope ignored = span.makeCurrent()) { ApiFuture result = batch.bulkCommit(); result.addListener( @@ -926,8 +934,11 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) { }, bulkWriterExecutor); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } else { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java index 3026e5183..d408194d5 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java @@ -21,6 +21,7 @@ import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.ApiExceptions; import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.PartitionQueryPagedResponse; @@ -110,6 +111,14 @@ public ApiFuture> getPartitions(long desiredPartitionCount) .getOptions() .getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_PARTITION_QUERY); + + MetricsContext metricsContext = + rpcContext + .getFirestore() + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_PARTITION_QUERY); + try (Scope ignored = span.makeCurrent()) { ApiFuture> result = ApiFutures.transform( @@ -127,12 +136,18 @@ public ApiFuture> getPartitions(long desiredPartitionCount) }, MoreExecutors.directExecutor()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (ApiException exception) { span.end(exception); + metricsContext.recordEndToEndLatency(exception); + throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); + metricsContext.recordEndToEndLatency(throwable); + throw throwable; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java index c736d7028..ed84992ab 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java @@ -23,6 +23,7 @@ import com.google.api.gax.rpc.ApiExceptions; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.firestore.spi.v1.FirestoreRpc; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListDocumentsPagedResponse; @@ -134,6 +135,14 @@ public Iterable listDocuments() { .getOptions() .getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS); + + MetricsContext metricsContext = + rpcContext + .getFirestore() + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS); + try (Scope ignored = span.makeCurrent()) { ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder(); request.setParent(options.getParentPath().toString()); @@ -173,12 +182,18 @@ public void remove() { } }; span.end(); + metricsContext.recordEndToEndLatency(); + return result; } catch (ApiException exception) { span.end(exception); + metricsContext.recordEndToEndLatency(exception); + throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); + metricsContext.recordEndToEndLatency(throwable); + throw throwable; } } @@ -200,6 +215,14 @@ public ApiFuture add(@Nonnull final Map field .getOptions() .getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_COL_REF_ADD); + + MetricsContext metricsContext = + rpcContext + .getFirestore() + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_COL_REF_ADD); + try (Scope ignored = span.makeCurrent()) { final DocumentReference documentReference = document(); ApiFuture createFuture = documentReference.create(fields); @@ -207,9 +230,12 @@ public ApiFuture add(@Nonnull final Map field ApiFutures.transform( createFuture, writeResult -> documentReference, MoreExecutors.directExecutor()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index 9ecad6e95..abc68409d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -21,6 +21,8 @@ import com.google.api.core.InternalExtensionOnly; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.ApiExceptions; +import com.google.cloud.firestore.telemetry.MetricsUtil; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse; @@ -139,6 +141,11 @@ private TraceUtil getTraceUtil() { return getFirestore().getOptions().getTraceUtil(); } + @Nonnull + private MetricsUtil getMetricsUtil() { + return getFirestore().getOptions().getMetricsUtil(); + } + /** * Creates a new Document at the DocumentReference's Location. It fails the write if the document * exists. @@ -149,13 +156,20 @@ private TraceUtil getTraceUtil() { @Nonnull public ApiFuture create(@Nonnull Map fields) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, fields).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -170,13 +184,19 @@ public ApiFuture create(@Nonnull Map fields) { @Nonnull public ApiFuture create(@Nonnull Object pojo) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, pojo).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -191,13 +211,19 @@ public ApiFuture create(@Nonnull Object pojo) { @Nonnull public ApiFuture set(@Nonnull Map fields) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -215,13 +241,19 @@ public ApiFuture set(@Nonnull Map fields) { public ApiFuture set( @Nonnull Map fields, @Nonnull SetOptions options) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields, options).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -236,13 +268,19 @@ public ApiFuture set( @Nonnull public ApiFuture set(@Nonnull Object pojo) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -259,13 +297,19 @@ public ApiFuture set(@Nonnull Object pojo) { @Nonnull public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions options) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo, options).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -280,13 +324,19 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti @Nonnull public ApiFuture update(@Nonnull Map fields) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.update(this, fields).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -302,14 +352,20 @@ public ApiFuture update(@Nonnull Map fields) { @Nonnull public ApiFuture update(@Nonnull Map fields, Precondition options) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.update(this, fields, options).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -327,14 +383,20 @@ public ApiFuture update(@Nonnull Map fields, Precon public ApiFuture update( @Nonnull String field, @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.update(this, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -352,14 +414,20 @@ public ApiFuture update( public ApiFuture update( @Nonnull FieldPath fieldPath, @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.update(this, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -381,15 +449,21 @@ public ApiFuture update( @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst( writeBatch.update(this, options, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -411,15 +485,21 @@ public ApiFuture update( @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst( writeBatch.update(this, options, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -433,13 +513,19 @@ public ApiFuture update( @Nonnull public ApiFuture delete(@Nonnull Precondition options) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this, options).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -452,13 +538,19 @@ public ApiFuture delete(@Nonnull Precondition options) { @Nonnull public ApiFuture delete() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this).commit()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -473,13 +565,18 @@ public ApiFuture delete() { @Nonnull public ApiFuture get() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_GET); try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -495,13 +592,19 @@ public ApiFuture get() { @Nonnull public ApiFuture get(FieldMask fieldMask) { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_GET); + try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(new DocumentReference[] {this}, fieldMask)); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -515,6 +618,9 @@ public ApiFuture get(FieldMask fieldMask) { @Nonnull public Iterable listCollections() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS); + MetricsContext metricsContext = + getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS); + try (Scope ignored = span.makeCurrent()) { ListCollectionIdsRequest.Builder request = ListCollectionIdsRequest.newBuilder(); request.setParent(path.toString()); @@ -548,9 +654,11 @@ public void remove() { } }; span.end(); + metricsContext.recordEndToEndLatency(); return result; } catch (ApiException exception) { span.end(exception); + metricsContext.recordEndToEndLatency(exception); throw FirestoreException.forApiException(exception); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index b19fe8276..593071dcf 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -39,6 +39,7 @@ import com.google.auto.value.AutoValue; import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.QueryOptions.Builder; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -1731,6 +1732,14 @@ private void internalStream( } TraceUtil.Span currentSpan = traceUtil.currentSpan(); + + // TODO(mila): record transaction latency, retry counts if applicable + MetricsContext metricsContext = + getFirestore() + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_RUN_QUERY); + currentSpan.addEvent( TraceUtil.SPAN_NAME_RUN_QUERY, new ImmutableMap.Builder() @@ -1757,6 +1766,7 @@ public void onResponse(RunQueryResponse response) { if (!firstResponse) { firstResponse = true; currentSpan.addEvent(TraceUtil.SPAN_NAME_RUN_QUERY + ": First Response"); + metricsContext.recordFirstResponseLatency(); } runQueryResponseObserver.onNext(response); @@ -1777,6 +1787,7 @@ public void onResponse(RunQueryResponse response) { if (response.getDone()) { currentSpan.addEvent( TraceUtil.SPAN_NAME_RUN_QUERY + ": Received RunQueryResponse.Done"); + metricsContext.recordEndToEndLatency(); onComplete(); } } @@ -1784,6 +1795,7 @@ public void onResponse(RunQueryResponse response) { @Override public void onError(Throwable throwable) { QueryDocumentSnapshot cursor = lastReceivedDocument.get(); + // TODO(mila): how do we capture e2e latency when retries are present if (shouldRetry(cursor, throwable)) { currentSpan.addEvent( TraceUtil.SPAN_NAME_RUN_QUERY + ": Retryable Error", @@ -1802,6 +1814,7 @@ public void onError(Throwable throwable) { currentSpan.addEvent( TraceUtil.SPAN_NAME_RUN_QUERY + ": Error", Collections.singletonMap("error.message", throwable.getMessage())); + metricsContext.recordEndToEndLatency(throwable); runQueryResponseObserver.onError(throwable); } } @@ -1861,6 +1874,11 @@ public ApiFuture get() { public ApiFuture> explain(ExplainOptions options) { TraceUtil.Span span = getFirestore().getOptions().getTraceUtil().startSpan(TraceUtil.SPAN_NAME_QUERY_GET); + MetricsContext metricsContext = + getFirestore() + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_QUERY_GET); try (Scope ignored = span.makeCurrent()) { final SettableApiFuture> result = SettableApiFuture.create(); @@ -1925,9 +1943,12 @@ public void onCompleted() { /* isRetryRequestWithCursor= */ false); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -1967,6 +1988,15 @@ ApiFuture get( ? TraceUtil.SPAN_NAME_QUERY_GET : TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY); + MetricsContext metricsContext = + getFirestore() + .getOptions() + .getMetricsUtil() + .createMetricsContext( + transactionId == null + ? TraceUtil.SPAN_NAME_QUERY_GET + : TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY); + try (Scope ignored = span.makeCurrent()) { final SettableApiFuture result = SettableApiFuture.create(); internalStream( @@ -2013,9 +2043,12 @@ public void onCompleted() { /* isRetryRequestWithCursor= */ false); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java index 0c423469a..4a3daa416 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java @@ -18,6 +18,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; @@ -58,6 +59,13 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT); + try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture result = ApiFutures.transform( @@ -66,9 +74,12 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { snapshots -> snapshots.isEmpty() ? null : snapshots.get(0), MoreExecutors.directExecutor()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -80,13 +91,23 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); + try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -98,13 +119,23 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); + try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java index 5d366c965..46223ccd1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java @@ -19,6 +19,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.cloud.firestore.TransactionOptions.TransactionOptionsType; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; @@ -109,6 +110,13 @@ ApiFuture> commit() { ApiFuture rollback() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK, transactionTraceContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK); + try (TraceUtil.Scope ignored = span.makeCurrent()) { RollbackRequest req = RollbackRequest.newBuilder() @@ -135,9 +143,12 @@ ApiFuture rollback() { }, MoreExecutors.directExecutor()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -159,6 +170,13 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT); + try (TraceUtil.Scope ignored = span.makeCurrent()) { Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG); ApiFuture result = @@ -171,9 +189,12 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { snapshots -> snapshots.isEmpty() ? null : snapshots.get(0), MoreExecutors.directExecutor()); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -192,14 +213,24 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); + try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll( documentReferences, /* fieldMask= */ null, transactionId, /* readTime= */ null); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -220,13 +251,23 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); + try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, fieldMask, transactionId, /* readTime= */ null); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index db8ebff63..fb81a6f8e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -26,6 +26,7 @@ import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.rpc.ApiException; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.telemetry.TraceUtil.Span; @@ -120,6 +121,13 @@ ApiFuture run() { ApiFuture begin() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_BEGIN, runTransactionContext); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_BEGIN); + try (Scope ignored = span.makeCurrent()) { ServerSideTransaction previousTransaction = this.transaction; this.transaction = null; @@ -133,9 +141,12 @@ ApiFuture begin() { return serverSideTransaction; }); span.endAtFuture(result); + metricsContext.recordEndToEndLatencyAtFuture(result); + return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java index 04d83a1a1..585734530 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java @@ -18,6 +18,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.InternalExtensionOnly; +import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Context; import java.util.List; @@ -49,6 +50,11 @@ TraceUtil getTraceUtil() { return firestore.getOptions().getTraceUtil(); } + @Nonnull + MetricsUtil getMetricsUtil() { + return firestore.getOptions().getMetricsUtil(); + } + @Nonnull Context setTransactionTraceContext(Context context) { return transactionTraceContext = context; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index e93fe8310..cd9347d5a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -25,6 +25,7 @@ import com.google.api.core.ApiFutures; import com.google.api.core.InternalExtensionOnly; import com.google.cloud.firestore.UserDataConverter.EncodingOptions; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.common.base.Preconditions; @@ -617,6 +618,16 @@ ApiFuture> commit(@Nullable ByteString transactionId) { transactionId == null ? TraceUtil.SPAN_NAME_BATCH_COMMIT : TraceUtil.SPAN_NAME_TRANSACTION_COMMIT); + + MetricsContext metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext( + transactionId == null + ? TraceUtil.SPAN_NAME_BATCH_COMMIT + : TraceUtil.SPAN_NAME_TRANSACTION_COMMIT); + span.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, writes.size()); span.setAttribute(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null); try (Scope ignored = span.makeCurrent()) { @@ -652,9 +663,12 @@ ApiFuture> commit(@Nullable ByteString transactionId) { }, MoreExecutors.directExecutor()); span.endAtFuture(returnValue); + metricsContext.recordEndToEndLatencyAtFuture(returnValue); + return returnValue; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java index f436cf4fe..f54afb670 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java @@ -41,7 +41,7 @@ public class BuiltinMetricsConstants { // Metric attribute keys for labels // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language"); - static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name"); + static final AttributeKey METHOD_KEY = AttributeKey.stringKey("method"); static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id"); static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library"); @@ -113,37 +113,37 @@ private static Set withAdditionalAttributes(Set attr public static Map getAllViews() { ImmutableMap.Builder views = ImmutableMap.builder(); - // Define views with COMMON_ATTRIBUTES and METHOD_NAME_KEY + // Define views with COMMON_ATTRIBUTES and METHOD_KEY defineView( views, OPERATION_LATENCY_NAME, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); + withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); defineView( views, ATTEMPT_LATENCY_NAME, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); + withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); defineView( views, OPERATION_COUNT_NAME, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); + withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); defineView( views, ATTEMPT_COUNT_NAME, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); + withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); defineView( views, FIRST_RESPONSE_LATENCY_NAME, FIRESTORE_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); + withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); defineView( views, END_TO_END_LATENCY_NAME, FIRESTORE_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY))); + withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); // Define views with only COMMON_ATTRIBUTES defineView(views, TRANSACTION_LATENCY_NAME, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java index 1a2941b8d..9e0903b7c 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java @@ -17,8 +17,6 @@ import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; -import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; -import com.google.cloud.opentelemetry.metric.MetricConfiguration; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.View; @@ -35,21 +33,12 @@ private BuiltinMetricsView() {} public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder) throws IOException { - // Attach built-in exporter - MetricExporter metricExporter = - GoogleCloudMetricExporter.createWithConfiguration( - MetricConfiguration.builder() - .setProjectId(projectId) - .setInstrumentationLibraryLabelsEnabled(false) - .build()); - - builder.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()); - // Filter out attributes that are not defined for (Map.Entry entry : getAllViews().entrySet()) { builder.registerView(entry.getKey(), entry.getValue()); } + MetricExporter metricExporter = FirestoreCloudMonitoringExporter.create(projectId); builder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java new file mode 100644 index 000000000..50098187d --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java @@ -0,0 +1,91 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; + +import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; +import com.google.cloud.opentelemetry.metric.MetricConfiguration; +import com.google.common.annotations.VisibleForTesting; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import java.util.Collection; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; + +// import org.slf4j.Logger; +// import org.slf4j.LoggerFactory; + +class FirestoreCloudMonitoringExporter implements MetricExporter { + + // private static final Logger logger = + // LoggerFactory.getLogger(FirestoreCloudMonitoringExporter.class); + private final MetricExporter delegate; + + private FirestoreCloudMonitoringExporter(MetricConfiguration configuration) { + this.delegate = GoogleCloudMetricExporter.createWithConfiguration(configuration); + } + + public static FirestoreCloudMonitoringExporter create(String projectId) { + return new FirestoreCloudMonitoringExporter( + MetricConfiguration.builder() + .setProjectId(projectId) + .setInstrumentationLibraryLabelsEnabled(false) + // .setMonitoredResourceDescription((null)) + // .setUseServiceTimeSeries(false) + .build()); + } + + @VisibleForTesting + FirestoreCloudMonitoringExporter createWithConfiguration(MetricConfiguration configuration) { + return new FirestoreCloudMonitoringExporter(configuration); + } + + @Override + public CompletableResultCode export(@Nonnull Collection metrics) { + // Filter out non built-in metrics + Collection filteredMetrics = + metrics.stream() + .filter(md -> BUILTIN_METRICS.contains(md.getName())) + .collect(Collectors.toList()); + + // Skips exporting if there's none + if (filteredMetrics.isEmpty()) { + return CompletableResultCode.ofSuccess(); + } + + return this.delegate.export(filteredMetrics); + } + + @Override + public CompletableResultCode flush() { + return this.delegate.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return this.delegate.shutdown(); + } + + @Override + public AggregationTemporality getAggregationTemporality(@Nonnull InstrumentType instrumentType) { + return this.delegate.getAggregationTemporality(instrumentType); + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index a4e851c88..c1e472759 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -28,6 +28,7 @@ import com.google.cloud.firestore.FirestoreException; import com.google.cloud.firestore.FirestoreOptions; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Status; import io.opentelemetry.api.OpenTelemetry; @@ -41,6 +42,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; /** @@ -68,6 +70,9 @@ public class MetricsUtil { public MetricsUtil(FirestoreOptions firestoreOptions) { this.firestoreOptions = firestoreOptions; + // TODO(mila): this checking should be done inside FirestoreOptions builder, so that we can + // setApiTracerFactory + // to collect GAX layer metrics boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); // The environment variable can override options to enable/disable telemetry collection. @@ -83,6 +88,7 @@ public MetricsUtil(FirestoreOptions firestoreOptions) { } } + // TODO(mila): re-assess if it should follow tracing's design: enabled/disabled MetricsUtil if (createEnabledInstance) { try { createMetricsUtil(firestoreOptions); @@ -93,21 +99,56 @@ public MetricsUtil(FirestoreOptions firestoreOptions) { } private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOException { + // TODO(mila): re-locate this create otel instance logic to FirestoreOptions, as we might need + // 2 different OTELs based on customers config this.openTelemetry = getDefaultOpenTelemetryInstance(); OpenTelemetryMetricsRecorder recorder = new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); - Map attributes = new HashMap<>(); - attributes.put(DATABASE_ID_KEY.toString(), firestoreOptions.getDatabaseId()); - attributes.put(CLIENT_LIBRARY_KEY.toString(), FIRESTORE_LIBRARY_NAME); + this.otelApiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); + registerMetrics(); + } + + private Map createStaticAttributes() { + Map staticAttributes = new HashMap<>(); + // TODO(mila): add client_uid to static attributes + staticAttributes.put(DATABASE_ID_KEY.toString(), firestoreOptions.getDatabaseId()); + staticAttributes.put(CLIENT_LIBRARY_KEY.toString(), FIRESTORE_LIBRARY_NAME); String pkgVersion = this.getClass().getPackage().getImplementationVersion(); if (pkgVersion != null) { - attributes.put(LIBRARY_VERSION_KEY.toString(), pkgVersion); + staticAttributes.put(LIBRARY_VERSION_KEY.toString(), pkgVersion); } - this.otelApiTracerFactory = new MetricsTracerFactory(recorder, attributes); - registerMetrics(); + return staticAttributes; + } + + @VisibleForTesting + Attributes toOtelAttributes(Map attributes) { + AttributesBuilder attributesBuilder = Attributes.builder(); + attributes.forEach(attributesBuilder::put); + return attributesBuilder.build(); + } + + private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { + OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); + + // If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel + // instance. + if (openTelemetry == null) { + SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); + BuiltinMetricsView.registerBuiltinMetrics( + firestoreOptions.getProjectId(), sdkMeterProviderBuilder); + + openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); + } + + return openTelemetry; + } + + public ApiTracerFactory getOpenTelemetryApiTracerFactory() { + return this.otelApiTracerFactory; } void registerMetrics() { @@ -126,87 +167,88 @@ void registerMetrics() { .setDescription("Firestore query first response latency") .setUnit(MILLISECOND_UNIT) .build(); + // TODO(mila): add transaction latency and retry count metrics } - public void endToEndRequestLatencyRecorder(double latency, Map attributes) { + public void endToEndRequestLatencyRecorder(double latency, Attributes attributes) { if (endToEndRequestLatency != null) { - endToEndRequestLatency.record(latency, toOtelAttributes(attributes)); + endToEndRequestLatency.record(latency, attributes); } } - public void firstResponseLatencyRecorder(double latency, Map attributes) { + public void firstResponseLatencyRecorder(double latency, Attributes attributes) { if (firstResponseLatency != null) { - firstResponseLatency.record(latency, toOtelAttributes(attributes)); + firstResponseLatency.record(latency, attributes); } } - private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { - OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); - - // If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel - // instance. - if (openTelemetry == null) { - SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); + public MetricsContext createMetricsContext(String methodName) { + return new MetricsContext(methodName); + } - BuiltinMetricsView.registerBuiltinMetrics( - firestoreOptions.getProjectId(), sdkMeterProviderBuilder); + public class MetricsContext { + private final Stopwatch stopwatch; + private final String methodName; - openTelemetry = - OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); + public MetricsContext(String methodName) { + this.stopwatch = Stopwatch.createStarted(); + this.methodName = methodName; } - return openTelemetry; - } - - public ApiTracerFactory getOpenTelemetryApiTracerFactory() { - return this.otelApiTracerFactory; - } + public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) { + ApiFutures.addCallback( + futureValue, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + recordEndToEndLatency(t); + } + + @Override + public void onSuccess(T result) { + recordEndToEndLatency(); + } + }, + MoreExecutors.directExecutor()); + } - @VisibleForTesting - Attributes toOtelAttributes(Map attributes) { - AttributesBuilder attributesBuilder = Attributes.builder(); - attributes.forEach(attributesBuilder::put); - return attributesBuilder.build(); - } + public void recordEndToEndLatency() { + recordEndToEndLatency(StatusCode.Code.OK.toString()); + } - public void endAtFuture( - ApiFuture futureValue, double start, Map attributes) { - ApiFutures.addCallback( - futureValue, - new ApiFutureCallback() { - @Override - public void onFailure(Throwable t) { - double end = System.currentTimeMillis(); - double elapsedTime = end - start; - attributes.put("status", extractErrorStatus(t)); - endToEndRequestLatencyRecorder(elapsedTime, attributes); - } - - @Override - public void onSuccess(T result) { - double end = System.currentTimeMillis(); - double elapsedTime = end - start; - attributes.put("status", "OK"); - endToEndRequestLatencyRecorder(elapsedTime, attributes); - } - }, - MoreExecutors.directExecutor()); - } + public void recordEndToEndLatency(Throwable t) { + recordEndToEndLatency(extractErrorStatus(t)); + } - public void end(Throwable t, double start, Map attributes) { - attributes.put("status", extractErrorStatus(t)); - double end = System.currentTimeMillis(); - double elapsedTime = end - start; - endToEndRequestLatencyRecorder(elapsedTime, attributes); - } + private void recordEndToEndLatency(String status) { + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Attributes attributes = + Attributes.builder() + .put(METHOD_KEY, methodName) + .put(STATUS_KEY, status) + .putAll(toOtelAttributes(createStaticAttributes())) + .build(); + endToEndRequestLatencyRecorder(elapsedTime, attributes); + } - /** Function to extract the status of the error as a string */ - public static String extractErrorStatus(@Nullable Throwable throwable) { - if (!(throwable instanceof FirestoreException)) { - return StatusCode.Code.UNKNOWN.toString(); + public void recordFirstResponseLatency() { + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Attributes attributes = + Attributes.builder() + .put(METHOD_KEY, methodName) + .put(STATUS_KEY, StatusCode.Code.OK.toString()) + .putAll(toOtelAttributes(createStaticAttributes())) + .build(); + firstResponseLatencyRecorder(elapsedTime, attributes); } - Status status = ((FirestoreException) throwable).getStatus(); - return status.getCode().name(); + /** Function to extract the status of the error as a string */ + public String extractErrorStatus(@Nullable Throwable throwable) { + if (!(throwable instanceof FirestoreException)) { + return StatusCode.Code.UNKNOWN.toString(); + } + Status status = ((FirestoreException) throwable).getStatus(); + return status.getCode().name(); + } } } From 39793310c179cedb8d7d9238eabc121588d64772 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:53:44 -0400 Subject: [PATCH 012/103] use 2 otel instances --- .../FirestoreOpenTelemetryOptions.java | 14 +- .../firestore/spi/v1/GrpcFirestoreRpc.java | 5 +- .../telemetry/BuiltinMetricsProvider.java | 115 +++++++++++ .../telemetry/BuiltinMetricsView.java | 44 ----- .../FirestoreCloudMonitoringExporter.java | 91 --------- .../firestore/telemetry/MetricsUtil.java | 183 +++++++----------- 6 files changed, 195 insertions(+), 257 deletions(-) create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java delete mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java delete mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 752b9480a..45ab3f28e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -29,12 +29,12 @@ public class FirestoreOpenTelemetryOptions { private final boolean tracingEnabled; private final boolean metricsEnabled; - private final @Nullable OpenTelemetry openTelemetry; + private final @Nullable OpenTelemetry customOpenTelemetry; FirestoreOpenTelemetryOptions(Builder builder) { this.tracingEnabled = builder.tracingEnabled; this.metricsEnabled = builder.metricsEnabled; - this.openTelemetry = builder.openTelemetry; + this.customOpenTelemetry = builder.customOpenTelemetry; } public boolean isTracingEnabled() { @@ -46,7 +46,7 @@ public boolean isMetricsEnabled() { } public OpenTelemetry getOpenTelemetry() { - return openTelemetry; + return customOpenTelemetry; } @Nonnull @@ -65,18 +65,18 @@ public static class Builder { private boolean metricsEnabled; - @Nullable private OpenTelemetry openTelemetry; + @Nullable private OpenTelemetry customOpenTelemetry; private Builder() { tracingEnabled = false; metricsEnabled = false; - openTelemetry = null; + customOpenTelemetry = null; } private Builder(FirestoreOpenTelemetryOptions options) { this.tracingEnabled = options.tracingEnabled; this.metricsEnabled = options.metricsEnabled; - this.openTelemetry = options.openTelemetry; + this.customOpenTelemetry = options.customOpenTelemetry; } @Nonnull @@ -116,7 +116,7 @@ public FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEn @Nonnull public FirestoreOpenTelemetryOptions.Builder setOpenTelemetry( @Nonnull OpenTelemetry openTelemetry) { - this.openTelemetry = openTelemetry; + this.customOpenTelemetry = openTelemetry; return this; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java index 36cd8dac2..780e47afc 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java @@ -163,7 +163,10 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException { // TODO: it should not be determined by isMetricsEnabled. move this whole logic to // options.getApiTracerFactory() firestoreBuilder.setTracerFactory( - options.getMetricsUtil().getOpenTelemetryApiTracerFactory()); + options + .getMetricsUtil() + .getDefaultOpenTelemetryMetricsProvider() + .getOpenTelemetryApiTracerFactory()); } firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build()); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java new file mode 100644 index 000000000..c820cb635 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -0,0 +1,115 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; + +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.api.gax.tracing.MetricsTracerFactory; +import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.common.annotations.VisibleForTesting; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; +import java.util.HashMap; +import java.util.Map; + +public class BuiltinMetricsProvider { + private OpenTelemetry openTelemetry; + private ApiTracerFactory otelApiTracerFactory; + + private Meter meter; + private DoubleHistogram endToEndRequestLatency; + private DoubleHistogram firstResponseLatency; + + public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { + OpenTelemetryMetricsRecorder recorder = + new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); + + this.otelApiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); + + registerMetrics(); + } + + private Map createStaticAttributes() { + Map staticAttributes = new HashMap<>(); + staticAttributes.put(CLIENT_LIBRARY_KEY.toString(), FIRESTORE_LIBRARY_NAME); + String pkgVersion = this.getClass().getPackage().getImplementationVersion(); + if (pkgVersion != null) { + staticAttributes.put(LIBRARY_VERSION_KEY.toString(), pkgVersion); + } + return staticAttributes; + } + + public ApiTracerFactory getOpenTelemetryApiTracerFactory() { + return this.otelApiTracerFactory; + } + + void registerMetrics() { + this.meter = openTelemetry.getMeter(FIRESTORE_LIBRARY_NAME); + + this.endToEndRequestLatency = + meter + .histogramBuilder(METER_NAME + "/" + END_TO_END_LATENCY_NAME) + .setDescription("Firestore E2E metrics") + .setUnit(MILLISECOND_UNIT) + .build(); + + this.firstResponseLatency = + meter + .histogramBuilder(METER_NAME + "/" + FIRST_RESPONSE_LATENCY_NAME) + .setDescription("Firestore query first response latency") + .setUnit(MILLISECOND_UNIT) + .build(); + // TODO(mila): add transaction latency and retry count metrics + } + + // public void endToEndRequestLatencyRecorder(double latency, Attributes attributes) { + // if (endToEndRequestLatency != null) { + // endToEndRequestLatency.record(latency, attributes); + // } + // } + + // public void firstResponseLatencyRecorder(double latency, Attributes attributes) { + // if (firstResponseLatency != null) { + // firstResponseLatency.record(latency, attributes); + // } + // } + + public void endToEndRequestLatencyRecorder(double latency, Map attributes) { + if (endToEndRequestLatency != null) { + attributes.putAll(createStaticAttributes()); + endToEndRequestLatency.record(latency, toOtelAttributes(attributes)); + } + } + + public void firstResponseLatencyRecorder(double latency, Map attributes) { + if (firstResponseLatency != null) { + attributes.putAll(createStaticAttributes()); + firstResponseLatency.record(latency, toOtelAttributes(attributes)); + } + } + + @VisibleForTesting + Attributes toOtelAttributes(Map attributes) { + AttributesBuilder attributesBuilder = Attributes.builder(); + attributes.forEach(attributesBuilder::put); + return attributesBuilder.build(); + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java deleted file mode 100644 index 9e0903b7c..000000000 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.firestore.telemetry; - -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; - -import io.opentelemetry.sdk.metrics.InstrumentSelector; -import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.View; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import java.io.IOException; -import java.util.Map; - -public class BuiltinMetricsView { - - private BuiltinMetricsView() {} - - /** Register built-in metrics on the {@link SdkMeterProviderBuilder} with credentials. */ - public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder) - throws IOException { - - // Filter out attributes that are not defined - for (Map.Entry entry : getAllViews().entrySet()) { - builder.registerView(entry.getKey(), entry.getValue()); - } - - MetricExporter metricExporter = FirestoreCloudMonitoringExporter.create(projectId); - builder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); - } -} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java deleted file mode 100644 index 50098187d..000000000 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.firestore.telemetry; - -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; - -import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; -import com.google.cloud.opentelemetry.metric.MetricConfiguration; -import com.google.common.annotations.VisibleForTesting; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.metrics.InstrumentType; -import io.opentelemetry.sdk.metrics.data.AggregationTemporality; -import io.opentelemetry.sdk.metrics.data.MetricData; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import java.util.Collection; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; - -// import org.slf4j.Logger; -// import org.slf4j.LoggerFactory; - -class FirestoreCloudMonitoringExporter implements MetricExporter { - - // private static final Logger logger = - // LoggerFactory.getLogger(FirestoreCloudMonitoringExporter.class); - private final MetricExporter delegate; - - private FirestoreCloudMonitoringExporter(MetricConfiguration configuration) { - this.delegate = GoogleCloudMetricExporter.createWithConfiguration(configuration); - } - - public static FirestoreCloudMonitoringExporter create(String projectId) { - return new FirestoreCloudMonitoringExporter( - MetricConfiguration.builder() - .setProjectId(projectId) - .setInstrumentationLibraryLabelsEnabled(false) - // .setMonitoredResourceDescription((null)) - // .setUseServiceTimeSeries(false) - .build()); - } - - @VisibleForTesting - FirestoreCloudMonitoringExporter createWithConfiguration(MetricConfiguration configuration) { - return new FirestoreCloudMonitoringExporter(configuration); - } - - @Override - public CompletableResultCode export(@Nonnull Collection metrics) { - // Filter out non built-in metrics - Collection filteredMetrics = - metrics.stream() - .filter(md -> BUILTIN_METRICS.contains(md.getName())) - .collect(Collectors.toList()); - - // Skips exporting if there's none - if (filteredMetrics.isEmpty()) { - return CompletableResultCode.ofSuccess(); - } - - return this.delegate.export(filteredMetrics); - } - - @Override - public CompletableResultCode flush() { - return this.delegate.flush(); - } - - @Override - public CompletableResultCode shutdown() { - return this.delegate.shutdown(); - } - - @Override - public AggregationTemporality getAggregationTemporality(@Nonnull InstrumentType instrumentType) { - return this.delegate.getAggregationTemporality(instrumentType); - } -} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index c1e472759..3f2dd1944 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -22,23 +22,21 @@ import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; import com.google.api.gax.rpc.StatusCode; -import com.google.api.gax.tracing.ApiTracerFactory; -import com.google.api.gax.tracing.MetricsTracerFactory; -import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.cloud.firestore.FirestoreException; import com.google.cloud.firestore.FirestoreOptions; -import com.google.common.annotations.VisibleForTesting; +import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; +import com.google.cloud.opentelemetry.metric.MetricConfiguration; import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Status; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; -import io.opentelemetry.api.metrics.DoubleHistogram; -import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.View; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -53,12 +51,9 @@ public class MetricsUtil { private final FirestoreOptions firestoreOptions; - private OpenTelemetry openTelemetry; - private ApiTracerFactory otelApiTracerFactory; - private Meter meter; - private DoubleHistogram endToEndRequestLatency; - private DoubleHistogram firstResponseLatency; + BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; + BuiltinMetricsProvider customOpenTelemetryMetricsProvider; /** * Creates and returns an instance of the MetricsUtil class. @@ -70,12 +65,21 @@ public class MetricsUtil { public MetricsUtil(FirestoreOptions firestoreOptions) { this.firestoreOptions = firestoreOptions; - // TODO(mila): this checking should be done inside FirestoreOptions builder, so that we can - // setApiTracerFactory - // to collect GAX layer metrics + // TODO(mila): re-assess if it should follow tracing's design: enabled/disabled MetricsUtil + if (createEnabledInstance()) { + try { + createMetricsUtil(); + } catch (IOException e) { + System.out.println(e); + } + } + } + + private boolean createEnabledInstance() { + // Start with the value from FirestoreOptions boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); - // The environment variable can override options to enable/disable telemetry collection. + // Override based on the environment variable String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { if (enableMetricsEnvVar.equalsIgnoreCase("true") @@ -88,98 +92,36 @@ public MetricsUtil(FirestoreOptions firestoreOptions) { } } - // TODO(mila): re-assess if it should follow tracing's design: enabled/disabled MetricsUtil - if (createEnabledInstance) { - try { - createMetricsUtil(firestoreOptions); - } catch (IOException e) { - System.out.println(e); - } - } - } - - private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOException { - // TODO(mila): re-locate this create otel instance logic to FirestoreOptions, as we might need - // 2 different OTELs based on customers config - this.openTelemetry = getDefaultOpenTelemetryInstance(); - - OpenTelemetryMetricsRecorder recorder = - new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); - - this.otelApiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); - registerMetrics(); + return createEnabledInstance; } - private Map createStaticAttributes() { - Map staticAttributes = new HashMap<>(); - // TODO(mila): add client_uid to static attributes - staticAttributes.put(DATABASE_ID_KEY.toString(), firestoreOptions.getDatabaseId()); - staticAttributes.put(CLIENT_LIBRARY_KEY.toString(), FIRESTORE_LIBRARY_NAME); - String pkgVersion = this.getClass().getPackage().getImplementationVersion(); - if (pkgVersion != null) { - staticAttributes.put(LIBRARY_VERSION_KEY.toString(), pkgVersion); - } - - return staticAttributes; - } - - @VisibleForTesting - Attributes toOtelAttributes(Map attributes) { - AttributesBuilder attributesBuilder = Attributes.builder(); - attributes.forEach(attributesBuilder::put); - return attributesBuilder.build(); + private void createMetricsUtil() throws IOException { + this.defaultOpenTelemetryMetricsProvider = + new BuiltinMetricsProvider(getDefaultOpenTelemetryInstance()); + this.customOpenTelemetryMetricsProvider = + new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); } private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { - OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); - // If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel - // instance. - if (openTelemetry == null) { - SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); - BuiltinMetricsView.registerBuiltinMetrics( - firestoreOptions.getProjectId(), sdkMeterProviderBuilder); + SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); - openTelemetry = - OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); + // Filter out attributes that are not defined + for (Map.Entry entry : getAllViews().entrySet()) { + sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); } - return openTelemetry; - } - - public ApiTracerFactory getOpenTelemetryApiTracerFactory() { - return this.otelApiTracerFactory; - } - - void registerMetrics() { - this.meter = openTelemetry.getMeter(FIRESTORE_LIBRARY_NAME); - - this.endToEndRequestLatency = - meter - .histogramBuilder(METER_NAME + "/" + END_TO_END_LATENCY_NAME) - .setDescription("Firestore E2E metrics") - .setUnit(MILLISECOND_UNIT) - .build(); - - this.firstResponseLatency = - meter - .histogramBuilder(METER_NAME + "/" + FIRST_RESPONSE_LATENCY_NAME) - .setDescription("Firestore query first response latency") - .setUnit(MILLISECOND_UNIT) - .build(); - // TODO(mila): add transaction latency and retry count metrics - } - - public void endToEndRequestLatencyRecorder(double latency, Attributes attributes) { - if (endToEndRequestLatency != null) { - endToEndRequestLatency.record(latency, attributes); - } - } - - public void firstResponseLatencyRecorder(double latency, Attributes attributes) { - if (firstResponseLatency != null) { - firstResponseLatency.record(latency, attributes); - } + MetricExporter metricExporter = + GoogleCloudMetricExporter.createWithConfiguration( + MetricConfiguration.builder() + .setProjectId(firestoreOptions.getProjectId()) + .setInstrumentationLibraryLabelsEnabled(false) + // .setMonitoredResourceDescription((null)) + // .setUseServiceTimeSeries(false) + .build()); + + sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); + return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); } public MetricsContext createMetricsContext(String methodName) { @@ -221,25 +163,28 @@ public void recordEndToEndLatency(Throwable t) { } private void recordEndToEndLatency(String status) { + if (defaultOpenTelemetryMetricsProvider == null) + return; double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Attributes attributes = - Attributes.builder() - .put(METHOD_KEY, methodName) - .put(STATUS_KEY, status) - .putAll(toOtelAttributes(createStaticAttributes())) - .build(); - endToEndRequestLatencyRecorder(elapsedTime, attributes); + Map attributes = new HashMap<>(); + attributes.put(METHOD_KEY.toString(), methodName); + attributes.put(STATUS_KEY.toString(), status); + + defaultOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + customOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); } public void recordFirstResponseLatency() { + if (defaultOpenTelemetryMetricsProvider == null) + return; + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Attributes attributes = - Attributes.builder() - .put(METHOD_KEY, methodName) - .put(STATUS_KEY, StatusCode.Code.OK.toString()) - .putAll(toOtelAttributes(createStaticAttributes())) - .build(); - firstResponseLatencyRecorder(elapsedTime, attributes); + Map attributes = new HashMap<>(); + attributes.put(METHOD_KEY.toString(), methodName); + attributes.put(STATUS_KEY.toString(), StatusCode.Code.OK.toString()); + + defaultOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); + customOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); } /** Function to extract the status of the error as a string */ @@ -251,4 +196,14 @@ public String extractErrorStatus(@Nullable Throwable throwable) { return status.getCode().name(); } } + + public BuiltinMetricsProvider getDefaultOpenTelemetryMetricsProvider() { + // TODO Auto-generated method stub + return this.defaultOpenTelemetryMetricsProvider; + } + + public BuiltinMetricsProvider getCustomOpenTelemetryMetricsProvider() { + // TODO Auto-generated method stub + return this.customOpenTelemetryMetricsProvider; + } } From a9d08b586c260a9f0c77d3ad0aea08f22552723e Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:14:42 -0400 Subject: [PATCH 013/103] set custom apiTracerFactory to gax --- .../firestore/CompositeApiTracerFactory.java | 44 ++++ .../cloud/firestore/CompositeTracer.java | 189 ++++++++++++++++++ .../cloud/firestore/FirestoreOptions.java | 31 +++ .../firestore/spi/v1/GrpcFirestoreRpc.java | 8 - .../telemetry/BuiltinMetricsProvider.java | 24 +-- .../firestore/telemetry/MetricsUtil.java | 16 +- 6 files changed, 281 insertions(+), 31 deletions(-) create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeApiTracerFactory.java create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeApiTracerFactory.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeApiTracerFactory.java new file mode 100644 index 000000000..a30857e66 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeApiTracerFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore; + +import com.google.api.gax.tracing.ApiTracer; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.api.gax.tracing.BaseApiTracerFactory; +import com.google.api.gax.tracing.SpanName; +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.List; + +class CompositeApiTracerFactory extends BaseApiTracerFactory { + + private final List apiTracerFactories; + + public CompositeApiTracerFactory(List apiTracerFactories) { + this.apiTracerFactories = ImmutableList.copyOf(apiTracerFactories); + } + + @Override + public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) { + List children = new ArrayList<>(apiTracerFactories.size()); + + for (ApiTracerFactory factory : apiTracerFactories) { + children.add(factory.newTracer(parent, spanName, operationType)); + } + return new CompositeTracer(children); + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java new file mode 100644 index 000000000..223f36bb9 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java @@ -0,0 +1,189 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore; + +import com.google.api.gax.tracing.ApiTracer; +import com.google.api.gax.tracing.BaseApiTracer; +import com.google.api.gax.tracing.MetricsTracer; +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.threeten.bp.Duration; + +class CompositeTracer extends BaseApiTracer { + private final List children; + + public CompositeTracer(List children) { + this.children = ImmutableList.copyOf(children); + } + + @Override + public Scope inScope() { + final List childScopes = new ArrayList<>(children.size()); + + for (ApiTracer child : children) { + childScopes.add(child.inScope()); + } + + return new Scope() { + @Override + public void close() { + for (Scope childScope : childScopes) { + childScope.close(); + } + } + }; + } + + @Override + public void operationSucceeded() { + for (ApiTracer child : children) { + child.operationSucceeded(); + } + } + + @Override + public void operationCancelled() { + for (ApiTracer child : children) { + child.operationCancelled(); + } + } + + @Override + public void operationFailed(Throwable error) { + for (ApiTracer child : children) { + child.operationFailed(error); + } + } + + @Override + public void connectionSelected(String id) { + for (ApiTracer child : children) { + child.connectionSelected(id); + } + } + + @Override + public void attemptStarted(int attemptNumber) { + for (ApiTracer child : children) { + child.attemptStarted(null, attemptNumber); + } + } + + @Override + public void attemptStarted(Object request, int attemptNumber) { + for (ApiTracer child : children) { + child.attemptStarted(request, attemptNumber); + } + } + + @Override + public void attemptSucceeded() { + for (ApiTracer child : children) { + child.attemptSucceeded(); + } + } + + @Override + public void attemptCancelled() { + for (ApiTracer child : children) { + child.attemptCancelled(); + } + } + + @Override + public void attemptFailed(Throwable error, Duration delay) { + for (ApiTracer child : children) { + child.attemptFailed(error, delay); + } + } + + @Override + public void attemptFailedDuration(Throwable error, java.time.Duration delay) { + for (ApiTracer child : children) { + child.attemptFailedDuration(error, delay); + } + } + + @Override + public void attemptFailedRetriesExhausted(Throwable error) { + for (ApiTracer child : children) { + child.attemptFailedRetriesExhausted(error); + } + } + + @Override + public void attemptPermanentFailure(Throwable error) { + for (ApiTracer child : children) { + child.attemptPermanentFailure(error); + } + } + + @Override + public void lroStartFailed(Throwable error) { + for (ApiTracer child : children) { + child.lroStartFailed(error); + } + } + + @Override + public void lroStartSucceeded() { + for (ApiTracer child : children) { + child.lroStartSucceeded(); + } + } + + @Override + public void responseReceived() { + for (ApiTracer child : children) { + child.responseReceived(); + } + } + + @Override + public void requestSent() { + for (ApiTracer child : children) { + child.requestSent(); + } + } + + @Override + public void batchRequestSent(long elementCount, long requestSize) { + for (ApiTracer child : children) { + child.batchRequestSent(elementCount, requestSize); + } + } + + public void addAttributes(String key, String value) { + for (ApiTracer child : children) { + if (child instanceof MetricsTracer) { + MetricsTracer metricsTracer = (MetricsTracer) child; + metricsTracer.addAttributes(key, value); + } + } + } + + public void addAttributes(Map attributes) { + for (ApiTracer child : children) { + if (child instanceof MetricsTracer) { + MetricsTracer metricsTracer = (MetricsTracer) child; + attributes.forEach((key, value) -> metricsTracer.addAttributes(key, value)); + } + } + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index 512b71364..0cb238258 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -24,6 +24,7 @@ import com.google.api.gax.core.GoogleCredentialsProvider; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider; import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.api.gax.tracing.ApiTracerFactory; import com.google.auth.Credentials; import com.google.cloud.ServiceDefaults; import com.google.cloud.ServiceOptions; @@ -38,6 +39,7 @@ import io.grpc.ManagedChannelBuilder; import java.io.IOException; import java.net.URI; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -108,6 +110,35 @@ protected String getDefaultHost() { return FirestoreDefaults.INSTANCE.getHost(); } + @Override + public ApiTracerFactory getApiTracerFactory() { + return createApiTracerFactory(); + } + + private ApiTracerFactory createApiTracerFactory() { + List apiTracerFactories = new ArrayList<>(); + // Prefer any direct ApiTracerFactory that might have been set on the builder. + if (super.getApiTracerFactory() != null) { + apiTracerFactories.add(super.getApiTracerFactory()); + } + // Add Metrics Tracer factory if built in metrics are enabled and if the client is data client + // and if emulator is not enabled. + if (metricsUtil.isBuiltInMetricsEnabled()) { + ApiTracerFactory defaultMetricsTracerFactory = + metricsUtil.getDefaultOpenTelemetryMetricsProvider().getOpenTelemetryApiTracerFactory(); + ApiTracerFactory customMetricsTracerFactory = + metricsUtil.getCustomOpenTelemetryMetricsProvider().getOpenTelemetryApiTracerFactory(); + if (defaultMetricsTracerFactory != null) { + apiTracerFactories.add(defaultMetricsTracerFactory); + } + if (customMetricsTracerFactory != null) { + apiTracerFactories.add(customMetricsTracerFactory); + } + } + + return new CompositeApiTracerFactory(apiTracerFactories); + } + public String getDatabaseId() { return databaseId; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java index 780e47afc..89873dd8f 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java @@ -159,14 +159,6 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException { if (options.getApiTracerFactory() != null) { firestoreBuilder.setTracerFactory(options.getApiTracerFactory()); - } else if (options.getOpenTelemetryOptions().isMetricsEnabled()) { - // TODO: it should not be determined by isMetricsEnabled. move this whole logic to - // options.getApiTracerFactory() - firestoreBuilder.setTracerFactory( - options - .getMetricsUtil() - .getDefaultOpenTelemetryMetricsProvider() - .getOpenTelemetryApiTracerFactory()); } firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build()); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index c820cb635..25b1072c7 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -39,12 +39,16 @@ public class BuiltinMetricsProvider { private DoubleHistogram firstResponseLatency; public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { - OpenTelemetryMetricsRecorder recorder = - new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); - this.otelApiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); + this.openTelemetry = openTelemetry; + if (openTelemetry != null) { + OpenTelemetryMetricsRecorder recorder = + new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); - registerMetrics(); + this.otelApiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); + + registerMetrics(); + } } private Map createStaticAttributes() { @@ -80,18 +84,6 @@ void registerMetrics() { // TODO(mila): add transaction latency and retry count metrics } - // public void endToEndRequestLatencyRecorder(double latency, Attributes attributes) { - // if (endToEndRequestLatency != null) { - // endToEndRequestLatency.record(latency, attributes); - // } - // } - - // public void firstResponseLatencyRecorder(double latency, Attributes attributes) { - // if (firstResponseLatency != null) { - // firstResponseLatency.record(latency, attributes); - // } - // } - public void endToEndRequestLatencyRecorder(double latency, Map attributes) { if (endToEndRequestLatency != null) { attributes.putAll(createStaticAttributes()); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 3f2dd1944..fadc6d7bb 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -51,6 +51,7 @@ public class MetricsUtil { private final FirestoreOptions firestoreOptions; + private final boolean isBuiltInMetricsEnabled; BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; BuiltinMetricsProvider customOpenTelemetryMetricsProvider; @@ -64,9 +65,10 @@ public class MetricsUtil { */ public MetricsUtil(FirestoreOptions firestoreOptions) { this.firestoreOptions = firestoreOptions; + this.isBuiltInMetricsEnabled = createEnabledInstance(); // TODO(mila): re-assess if it should follow tracing's design: enabled/disabled MetricsUtil - if (createEnabledInstance()) { + if (isBuiltInMetricsEnabled) { try { createMetricsUtil(); } catch (IOException e) { @@ -75,6 +77,10 @@ public MetricsUtil(FirestoreOptions firestoreOptions) { } } + public boolean isBuiltInMetricsEnabled() { + return isBuiltInMetricsEnabled; + } + private boolean createEnabledInstance() { // Start with the value from FirestoreOptions boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); @@ -163,8 +169,7 @@ public void recordEndToEndLatency(Throwable t) { } private void recordEndToEndLatency(String status) { - if (defaultOpenTelemetryMetricsProvider == null) - return; + if (defaultOpenTelemetryMetricsProvider == null) return; double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); Map attributes = new HashMap<>(); attributes.put(METHOD_KEY.toString(), methodName); @@ -175,8 +180,7 @@ private void recordEndToEndLatency(String status) { } public void recordFirstResponseLatency() { - if (defaultOpenTelemetryMetricsProvider == null) - return; + if (defaultOpenTelemetryMetricsProvider == null) return; double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); Map attributes = new HashMap<>(); @@ -198,12 +202,10 @@ public String extractErrorStatus(@Nullable Throwable throwable) { } public BuiltinMetricsProvider getDefaultOpenTelemetryMetricsProvider() { - // TODO Auto-generated method stub return this.defaultOpenTelemetryMetricsProvider; } public BuiltinMetricsProvider getCustomOpenTelemetryMetricsProvider() { - // TODO Auto-generated method stub return this.customOpenTelemetryMetricsProvider; } } From 50e05fbe30d5217899b539c53b423a48fe07c2da Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:02:38 -0400 Subject: [PATCH 014/103] fix opentelemtry dependency version conflict issue --- google-cloud-firestore/pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 4bf5ba575..f2c4dd277 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -122,7 +122,6 @@ io.opentelemetry opentelemetry-sdk - ${opentelemetry.version} com.google.cloud.opentelemetry @@ -132,7 +131,6 @@ io.opentelemetry opentelemetry-sdk-common - ${opentelemetry.version} From 53cd48a8ebf8a079baf385580c84016a96c8d58a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 23 Sep 2024 22:26:52 -0400 Subject: [PATCH 015/103] use enabled/disabled style --- .../cloud/firestore/FirestoreOptions.java | 15 +- .../telemetry/DisabledMetricsUtil.java | 47 +++++ .../telemetry/EnabledMetricsUtil.java | 180 ++++++++++++++++++ .../firestore/telemetry/MetricsUtil.java | 177 +++-------------- 4 files changed, 254 insertions(+), 165 deletions(-) create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index 0cb238258..91f45c1d5 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -123,18 +123,7 @@ private ApiTracerFactory createApiTracerFactory() { } // Add Metrics Tracer factory if built in metrics are enabled and if the client is data client // and if emulator is not enabled. - if (metricsUtil.isBuiltInMetricsEnabled()) { - ApiTracerFactory defaultMetricsTracerFactory = - metricsUtil.getDefaultOpenTelemetryMetricsProvider().getOpenTelemetryApiTracerFactory(); - ApiTracerFactory customMetricsTracerFactory = - metricsUtil.getCustomOpenTelemetryMetricsProvider().getOpenTelemetryApiTracerFactory(); - if (defaultMetricsTracerFactory != null) { - apiTracerFactories.add(defaultMetricsTracerFactory); - } - if (customMetricsTracerFactory != null) { - apiTracerFactories.add(customMetricsTracerFactory); - } - } + metricsUtil.addMetricsTracerFactory(apiTracerFactories); return new CompositeApiTracerFactory(apiTracerFactories); } @@ -364,7 +353,7 @@ protected FirestoreOptions(Builder builder) { : FirestoreDefaults.INSTANCE.getDatabaseId(); // set metrics util after database ID is set - this.metricsUtil = new MetricsUtil(this); + this.metricsUtil = MetricsUtil.getInstance(this); if (builder.channelProvider == null) { ApiFunction channelConfigurator = diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java new file mode 100644 index 000000000..6db3961e0 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +import com.google.api.core.ApiFuture; +import com.google.api.gax.tracing.ApiTracerFactory; +import java.util.List; + +public class DisabledMetricsUtil implements MetricsUtil { + + static class MetricsContext implements MetricsUtil.MetricsContext { + + @Override + public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) {} + + @Override + public void recordEndToEndLatency() {} + + @Override + public void recordEndToEndLatency(Throwable t) {} + + @Override + public void recordFirstResponseLatency() {} + } + + @Override + public MetricsContext createMetricsContext(String methodName) { + return new MetricsContext(); + } + + @Override + public void addMetricsTracerFactory(List apiTracerFactories) {} +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java new file mode 100644 index 000000000..717983ca0 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -0,0 +1,180 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; + +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutureCallback; +import com.google.api.core.ApiFutures; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.cloud.firestore.FirestoreException; +import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; +import com.google.cloud.opentelemetry.metric.MetricConfiguration; +import com.google.common.base.Stopwatch; +import com.google.common.util.concurrent.MoreExecutors; +import io.grpc.Status; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.View; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + +/** + * A utility interface for trace collection. Classes that implement this interface may make their + * own design choices for how they approach trace collection. For instance, they may be no-op, or + * they may use a particular tracing framework such as OpenTelemetry. + */ +public class EnabledMetricsUtil implements MetricsUtil { + + private final FirestoreOptions firestoreOptions; + + private static BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; + private static BuiltinMetricsProvider customOpenTelemetryMetricsProvider; + + EnabledMetricsUtil(FirestoreOptions firestoreOptions) { + this.firestoreOptions = firestoreOptions; + try { + createMetricsUtil(); + } catch (IOException e) { + // TODO: Handle the exception appropriately (e.g., logging) + } + } + + private void createMetricsUtil() throws IOException { + EnabledMetricsUtil.defaultOpenTelemetryMetricsProvider = + new BuiltinMetricsProvider(getDefaultOpenTelemetryInstance()); + EnabledMetricsUtil.customOpenTelemetryMetricsProvider = + new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); + } + + private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { + + SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); + + // Filter out attributes that are not defined + for (Map.Entry entry : getAllViews().entrySet()) { + sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); + } + + MetricExporter metricExporter = + GoogleCloudMetricExporter.createWithConfiguration( + MetricConfiguration.builder() + .setProjectId(firestoreOptions.getProjectId()) + .setInstrumentationLibraryLabelsEnabled(false) + // .setMonitoredResourceDescription((null)) + // .setUseServiceTimeSeries(false) + .build()); + + sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); + return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); + } + + @Override + public MetricsContext createMetricsContext(String methodName) { + return new MetricsContext(methodName); + } + + @Override + public void addMetricsTracerFactory(List apiTracerFactories) { + ApiTracerFactory defaultMetricsTracerFactory = + defaultOpenTelemetryMetricsProvider.getOpenTelemetryApiTracerFactory(); + ApiTracerFactory customMetricsTracerFactory = + customOpenTelemetryMetricsProvider.getOpenTelemetryApiTracerFactory(); + if (defaultMetricsTracerFactory != null) { + apiTracerFactories.add(defaultMetricsTracerFactory); + } + if (customMetricsTracerFactory != null) { + apiTracerFactories.add(customMetricsTracerFactory); + } + } + + static class MetricsContext implements MetricsUtil.MetricsContext { + private final Stopwatch stopwatch; + private final String methodName; + + public MetricsContext(String methodName) { + this.stopwatch = Stopwatch.createStarted(); + this.methodName = methodName; + } + + public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) { + ApiFutures.addCallback( + futureValue, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + recordEndToEndLatency(t); + } + + @Override + public void onSuccess(T result) { + recordEndToEndLatency(); + } + }, + MoreExecutors.directExecutor()); + } + + public void recordEndToEndLatency() { + recordEndToEndLatency(StatusCode.Code.OK.toString()); + } + + public void recordEndToEndLatency(Throwable t) { + recordEndToEndLatency(extractErrorStatus(t)); + } + + private void recordEndToEndLatency(String status) { + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Map attributes = new HashMap<>(); + attributes.put(METHOD_KEY.toString(), methodName); + attributes.put(STATUS_KEY.toString(), status); + + defaultOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + customOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + } + + public void recordFirstResponseLatency() { + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Map attributes = new HashMap<>(); + attributes.put(METHOD_KEY.toString(), methodName); + attributes.put(STATUS_KEY.toString(), StatusCode.Code.OK.toString()); + + defaultOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); + customOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); + } + + /** Function to extract the status of the error as a string */ + public String extractErrorStatus(@Nullable Throwable throwable) { + if (!(throwable instanceof FirestoreException)) { + return StatusCode.Code.UNKNOWN.toString(); + } + Status status = ((FirestoreException) throwable).getStatus(); + return status.getCode().name(); + } + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index fadc6d7bb..0c15e9884 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -19,69 +19,29 @@ import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutureCallback; -import com.google.api.core.ApiFutures; -import com.google.api.gax.rpc.StatusCode; -import com.google.cloud.firestore.FirestoreException; +import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreOptions; -import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; -import com.google.cloud.opentelemetry.metric.MetricConfiguration; -import com.google.common.base.Stopwatch; -import com.google.common.util.concurrent.MoreExecutors; -import io.grpc.Status; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.InstrumentSelector; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.View; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; +import java.util.List; +import javax.annotation.Nonnull; /** * A utility interface for trace collection. Classes that implement this interface may make their * own design choices for how they approach trace collection. For instance, they may be no-op, or * they may use a particular tracing framework such as OpenTelemetry. */ -public class MetricsUtil { - - private final FirestoreOptions firestoreOptions; - private final boolean isBuiltInMetricsEnabled; - - BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; - BuiltinMetricsProvider customOpenTelemetryMetricsProvider; - - /** - * Creates and returns an instance of the MetricsUtil class. - * - * @param firestoreOptions The FirestoreOptions object that is requesting an instance of - * MetricsUtil. - * @return An instance of the MetricsUtil class. - */ - public MetricsUtil(FirestoreOptions firestoreOptions) { - this.firestoreOptions = firestoreOptions; - this.isBuiltInMetricsEnabled = createEnabledInstance(); - - // TODO(mila): re-assess if it should follow tracing's design: enabled/disabled MetricsUtil +public interface MetricsUtil { + + static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { + boolean isBuiltInMetricsEnabled = createEnabledInstance(firestoreOptions); + if (isBuiltInMetricsEnabled) { - try { - createMetricsUtil(); - } catch (IOException e) { - System.out.println(e); - } + return new EnabledMetricsUtil(firestoreOptions); + } else { + return new DisabledMetricsUtil(); } } - public boolean isBuiltInMetricsEnabled() { - return isBuiltInMetricsEnabled; - } - - private boolean createEnabledInstance() { + static boolean createEnabledInstance(FirestoreOptions firestoreOptions) { // Start with the value from FirestoreOptions boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); @@ -101,111 +61,24 @@ private boolean createEnabledInstance() { return createEnabledInstance; } - private void createMetricsUtil() throws IOException { - this.defaultOpenTelemetryMetricsProvider = - new BuiltinMetricsProvider(getDefaultOpenTelemetryInstance()); - this.customOpenTelemetryMetricsProvider = - new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); - } - - private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { - - SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); + abstract MetricsContext createMetricsContext(String methodName); - // Filter out attributes that are not defined - for (Map.Entry entry : getAllViews().entrySet()) { - sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); - } - - MetricExporter metricExporter = - GoogleCloudMetricExporter.createWithConfiguration( - MetricConfiguration.builder() - .setProjectId(firestoreOptions.getProjectId()) - .setInstrumentationLibraryLabelsEnabled(false) - // .setMonitoredResourceDescription((null)) - // .setUseServiceTimeSeries(false) - .build()); - - sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); - return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); - } - - public MetricsContext createMetricsContext(String methodName) { - return new MetricsContext(methodName); - } + abstract void addMetricsTracerFactory(List apiTracerFactories); - public class MetricsContext { - private final Stopwatch stopwatch; - private final String methodName; + public interface MetricsContext { - public MetricsContext(String methodName) { - this.stopwatch = Stopwatch.createStarted(); - this.methodName = methodName; - } + /** + * If an operation ends in the future, its relevant metrics should be recorded _after_ the + * future has been completed. This method "appends" the metrics recording code at the completion + * of the given future. + */ + void recordEndToEndLatencyAtFuture(ApiFuture futureValue); - public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) { - ApiFutures.addCallback( - futureValue, - new ApiFutureCallback() { - @Override - public void onFailure(Throwable t) { - recordEndToEndLatency(t); - } - - @Override - public void onSuccess(T result) { - recordEndToEndLatency(); - } - }, - MoreExecutors.directExecutor()); - } + void recordEndToEndLatency(); - public void recordEndToEndLatency() { - recordEndToEndLatency(StatusCode.Code.OK.toString()); - } - - public void recordEndToEndLatency(Throwable t) { - recordEndToEndLatency(extractErrorStatus(t)); - } - - private void recordEndToEndLatency(String status) { - if (defaultOpenTelemetryMetricsProvider == null) return; - double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = new HashMap<>(); - attributes.put(METHOD_KEY.toString(), methodName); - attributes.put(STATUS_KEY.toString(), status); - - defaultOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); - customOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); - } - - public void recordFirstResponseLatency() { - if (defaultOpenTelemetryMetricsProvider == null) return; - - double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = new HashMap<>(); - attributes.put(METHOD_KEY.toString(), methodName); - attributes.put(STATUS_KEY.toString(), StatusCode.Code.OK.toString()); - - defaultOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); - customOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); - } - - /** Function to extract the status of the error as a string */ - public String extractErrorStatus(@Nullable Throwable throwable) { - if (!(throwable instanceof FirestoreException)) { - return StatusCode.Code.UNKNOWN.toString(); - } - Status status = ((FirestoreException) throwable).getStatus(); - return status.getCode().name(); - } - } - - public BuiltinMetricsProvider getDefaultOpenTelemetryMetricsProvider() { - return this.defaultOpenTelemetryMetricsProvider; - } + void recordEndToEndLatency(Throwable t); - public BuiltinMetricsProvider getCustomOpenTelemetryMetricsProvider() { - return this.customOpenTelemetryMetricsProvider; + /** Records first response latency for the current operation. */ + void recordFirstResponseLatency(); } } From 7adb9d60a1771a5cbb4aa3c85ac342f83b0df8e9 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:05:01 -0400 Subject: [PATCH 016/103] modify access modifiers --- .../telemetry/DisabledMetricsUtil.java | 4 +-- .../telemetry/EnabledMetricsUtil.java | 33 ++++++++----------- .../firestore/telemetry/MetricsUtil.java | 4 ++- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index 6db3961e0..7a669806f 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -20,9 +20,9 @@ import com.google.api.gax.tracing.ApiTracerFactory; import java.util.List; -public class DisabledMetricsUtil implements MetricsUtil { +class DisabledMetricsUtil implements MetricsUtil { - static class MetricsContext implements MetricsUtil.MetricsContext { + class MetricsContext implements MetricsUtil.MetricsContext { @Override public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) {} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 717983ca0..eaf9a3a08 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -45,35 +45,28 @@ import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; -/** - * A utility interface for trace collection. Classes that implement this interface may make their - * own design choices for how they approach trace collection. For instance, they may be no-op, or - * they may use a particular tracing framework such as OpenTelemetry. - */ -public class EnabledMetricsUtil implements MetricsUtil { - - private final FirestoreOptions firestoreOptions; +class EnabledMetricsUtil implements MetricsUtil { - private static BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; - private static BuiltinMetricsProvider customOpenTelemetryMetricsProvider; + private BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; + private BuiltinMetricsProvider customOpenTelemetryMetricsProvider; EnabledMetricsUtil(FirestoreOptions firestoreOptions) { - this.firestoreOptions = firestoreOptions; try { - createMetricsUtil(); + createMetricsUtil(firestoreOptions); } catch (IOException e) { // TODO: Handle the exception appropriately (e.g., logging) } } - private void createMetricsUtil() throws IOException { - EnabledMetricsUtil.defaultOpenTelemetryMetricsProvider = - new BuiltinMetricsProvider(getDefaultOpenTelemetryInstance()); - EnabledMetricsUtil.customOpenTelemetryMetricsProvider = + private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOException { + this.defaultOpenTelemetryMetricsProvider = + new BuiltinMetricsProvider( + getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId())); + this.customOpenTelemetryMetricsProvider = new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); } - private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { + private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws IOException { SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); @@ -85,7 +78,7 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder() - .setProjectId(firestoreOptions.getProjectId()) + .setProjectId(projectId) .setInstrumentationLibraryLabelsEnabled(false) // .setMonitoredResourceDescription((null)) // .setUseServiceTimeSeries(false) @@ -114,7 +107,7 @@ public void addMetricsTracerFactory(List apiTracerFactories) { } } - static class MetricsContext implements MetricsUtil.MetricsContext { + class MetricsContext implements MetricsUtil.MetricsContext { private final Stopwatch stopwatch; private final String methodName; @@ -169,7 +162,7 @@ public void recordFirstResponseLatency() { } /** Function to extract the status of the error as a string */ - public String extractErrorStatus(@Nullable Throwable throwable) { + private String extractErrorStatus(@Nullable Throwable throwable) { if (!(throwable instanceof FirestoreException)) { return StatusCode.Code.UNKNOWN.toString(); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 0c15e9884..e4c5006f2 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -19,6 +19,7 @@ import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; import com.google.api.core.ApiFuture; +import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreOptions; import java.util.List; @@ -29,6 +30,7 @@ * own design choices for how they approach trace collection. For instance, they may be no-op, or * they may use a particular tracing framework such as OpenTelemetry. */ +@InternalApi public interface MetricsUtil { static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { @@ -65,7 +67,7 @@ static boolean createEnabledInstance(FirestoreOptions firestoreOptions) { abstract void addMetricsTracerFactory(List apiTracerFactories); - public interface MetricsContext { + interface MetricsContext { /** * If an operation ends in the future, its relevant metrics should be recorded _after_ the From 690e4ee9e7d85de044723945512ef15613fbb0f4 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:02:31 -0400 Subject: [PATCH 017/103] format --- google-cloud-firestore/pom.xml | 1 - .../cloud/firestore/FirestoreOptions.java | 4 +--- .../telemetry/EnabledMetricsUtil.java | 20 +++++++---------- .../firestore/telemetry/MetricsUtil.java | 22 +++++++++---------- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index f2c4dd277..294e42ebc 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -212,7 +212,6 @@ ${opentelemetry.version} test - com.google.cloud.opentelemetry exporter-trace diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index 91f45c1d5..b892655b0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -121,10 +121,8 @@ private ApiTracerFactory createApiTracerFactory() { if (super.getApiTracerFactory() != null) { apiTracerFactories.add(super.getApiTracerFactory()); } - // Add Metrics Tracer factory if built in metrics are enabled and if the client is data client - // and if emulator is not enabled. + // Add Metrics Tracer factory if built in metrics are enabled. metricsUtil.addMetricsTracerFactory(apiTracerFactories); - return new CompositeApiTracerFactory(apiTracerFactories); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index eaf9a3a08..527499a3e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -52,22 +52,17 @@ class EnabledMetricsUtil implements MetricsUtil { EnabledMetricsUtil(FirestoreOptions firestoreOptions) { try { - createMetricsUtil(firestoreOptions); + this.defaultOpenTelemetryMetricsProvider = + new BuiltinMetricsProvider( + getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId())); + this.customOpenTelemetryMetricsProvider = + new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); } catch (IOException e) { // TODO: Handle the exception appropriately (e.g., logging) } } - private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOException { - this.defaultOpenTelemetryMetricsProvider = - new BuiltinMetricsProvider( - getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId())); - this.customOpenTelemetryMetricsProvider = - new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); - } - private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws IOException { - SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); // Filter out attributes that are not defined @@ -97,11 +92,12 @@ public MetricsContext createMetricsContext(String methodName) { public void addMetricsTracerFactory(List apiTracerFactories) { ApiTracerFactory defaultMetricsTracerFactory = defaultOpenTelemetryMetricsProvider.getOpenTelemetryApiTracerFactory(); - ApiTracerFactory customMetricsTracerFactory = - customOpenTelemetryMetricsProvider.getOpenTelemetryApiTracerFactory(); if (defaultMetricsTracerFactory != null) { apiTracerFactories.add(defaultMetricsTracerFactory); } + + ApiTracerFactory customMetricsTracerFactory = + customOpenTelemetryMetricsProvider.getOpenTelemetryApiTracerFactory(); if (customMetricsTracerFactory != null) { apiTracerFactories.add(customMetricsTracerFactory); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index e4c5006f2..a4d53f01e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -34,29 +34,29 @@ public interface MetricsUtil { static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { - boolean isBuiltInMetricsEnabled = createEnabledInstance(firestoreOptions); - - if (isBuiltInMetricsEnabled) { + if (isBuiltInMetricsEnabled(firestoreOptions)) { return new EnabledMetricsUtil(firestoreOptions); } else { return new DisabledMetricsUtil(); } } - static boolean createEnabledInstance(FirestoreOptions firestoreOptions) { + static boolean isBuiltInMetricsEnabled(FirestoreOptions firestoreOptions) { // Start with the value from FirestoreOptions boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); // Override based on the environment variable String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { - if (enableMetricsEnvVar.equalsIgnoreCase("true") - || enableMetricsEnvVar.equalsIgnoreCase("on")) { - createEnabledInstance = true; - } - if (enableMetricsEnvVar.equalsIgnoreCase("false") - || enableMetricsEnvVar.equalsIgnoreCase("off")) { - createEnabledInstance = false; + switch (enableMetricsEnvVar.toLowerCase()) { + case "true": + case "on": + createEnabledInstance = true; + break; + case "false": + case "off": + createEnabledInstance = false; + break; } } From e6f8ba617e65beaf3940d5b586709aed8f833734 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:23:39 -0400 Subject: [PATCH 018/103] rename boolean --- .../firestore/FirestoreOpenTelemetryOptions.java | 14 +++++++------- .../cloud/firestore/telemetry/MetricsUtil.java | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 45ab3f28e..752b9480a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -29,12 +29,12 @@ public class FirestoreOpenTelemetryOptions { private final boolean tracingEnabled; private final boolean metricsEnabled; - private final @Nullable OpenTelemetry customOpenTelemetry; + private final @Nullable OpenTelemetry openTelemetry; FirestoreOpenTelemetryOptions(Builder builder) { this.tracingEnabled = builder.tracingEnabled; this.metricsEnabled = builder.metricsEnabled; - this.customOpenTelemetry = builder.customOpenTelemetry; + this.openTelemetry = builder.openTelemetry; } public boolean isTracingEnabled() { @@ -46,7 +46,7 @@ public boolean isMetricsEnabled() { } public OpenTelemetry getOpenTelemetry() { - return customOpenTelemetry; + return openTelemetry; } @Nonnull @@ -65,18 +65,18 @@ public static class Builder { private boolean metricsEnabled; - @Nullable private OpenTelemetry customOpenTelemetry; + @Nullable private OpenTelemetry openTelemetry; private Builder() { tracingEnabled = false; metricsEnabled = false; - customOpenTelemetry = null; + openTelemetry = null; } private Builder(FirestoreOpenTelemetryOptions options) { this.tracingEnabled = options.tracingEnabled; this.metricsEnabled = options.metricsEnabled; - this.customOpenTelemetry = options.customOpenTelemetry; + this.openTelemetry = options.openTelemetry; } @Nonnull @@ -116,7 +116,7 @@ public FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEn @Nonnull public FirestoreOpenTelemetryOptions.Builder setOpenTelemetry( @Nonnull OpenTelemetry openTelemetry) { - this.customOpenTelemetry = openTelemetry; + this.openTelemetry = openTelemetry; return this; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index a4d53f01e..93525c4e0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -34,14 +34,14 @@ public interface MetricsUtil { static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { - if (isBuiltInMetricsEnabled(firestoreOptions)) { + if (createEnabledInstance(firestoreOptions)) { return new EnabledMetricsUtil(firestoreOptions); } else { return new DisabledMetricsUtil(); } } - static boolean isBuiltInMetricsEnabled(FirestoreOptions firestoreOptions) { + static boolean createEnabledInstance(FirestoreOptions firestoreOptions) { // Start with the value from FirestoreOptions boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); From 887221d0fbc2c226851ee0e5fdc41ab2ec4e88a3 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Fri, 27 Sep 2024 19:27:33 +0000 Subject: [PATCH 019/103] chore: generate libraries at Fri Sep 27 19:25:02 UTC 2024 --- README.md | 6 +++--- .../src/main/java/com/google/cloud/firestore/Query.java | 2 +- .../main/java/com/google/cloud/firestore/UpdateBuilder.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e1131e121..c68c27fde 100644 --- a/README.md +++ b/README.md @@ -57,13 +57,13 @@ implementation 'com.google.cloud:google-cloud-firestore' If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-firestore:3.26.3' +implementation 'com.google.cloud:google-cloud-firestore:3.26.4' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.26.3" +libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.26.4" ``` @@ -221,7 +221,7 @@ Java is a registered trademark of Oracle and/or its affiliates. [kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-firestore/java11.html [stability-image]: https://img.shields.io/badge/stability-stable-green [maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-firestore.svg -[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-firestore/3.26.3 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-firestore/3.26.4 [authentication]: https://github.com/googleapis/google-cloud-java#authentication [auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes [predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index 01cac0295..339a774e4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -39,8 +39,8 @@ import com.google.auto.value.AutoValue; import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.QueryOptions.Builder; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.encoding.CustomClassMapper; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index fd457bfb5..f4c96fa15 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -25,8 +25,8 @@ import com.google.api.core.ApiFutures; import com.google.api.core.InternalExtensionOnly; import com.google.cloud.firestore.UserDataConverter.EncodingOptions; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.encoding.CustomClassMapper; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.common.base.Preconditions; From a2c715adcb058c885cf1a79742e7d2afeb891a38 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:56:08 -0400 Subject: [PATCH 020/103] Update pom.xml --- google-cloud-firestore/pom.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index f79b28e4f..f8f889a2f 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -128,10 +128,6 @@ exporter-metrics 0.31.0 - - io.opentelemetry - opentelemetry-sdk-common - @@ -211,6 +207,12 @@ opentelemetry-sdk-trace ${opentelemetry.version} test + + + io.opentelemetry + opentelemetry-sdk-common + ${opentelemetry.version} + test com.google.cloud.opentelemetry From ed78182ac6e6f3f2afb75dec4c99448f1f2b4f61 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:02:45 -0400 Subject: [PATCH 021/103] resolve comments from proto --- .../FirestoreOpenTelemetryOptions.java | 2 +- .../telemetry/BuiltinMetricsConstants.java | 110 +++++++++--------- .../telemetry/BuiltinMetricsProvider.java | 17 ++- .../telemetry/EnabledMetricsUtil.java | 15 ++- .../firestore/telemetry/MetricsUtil.java | 2 +- 5 files changed, 77 insertions(+), 69 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 752b9480a..bc611fbb6 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -69,7 +69,7 @@ public static class Builder { private Builder() { tracingEnabled = false; - metricsEnabled = false; + metricsEnabled = true; openTelemetry = null; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java index f54afb670..70bc3dd00 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java @@ -28,66 +28,56 @@ public class BuiltinMetricsConstants { - static final String FIRESTORE_LIBRARY_NAME = "firestore_java"; - // TODO: change to firestore.googleapis.com public static final String METER_NAME = "custom.googleapis.com/internal/client"; - static final String FIRESTORE_METER_NAME = "firestore_java"; + public static final String FIRESTORE_METER_NAME = "java_firestore"; public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; // Metric attribute keys for monitored resource - static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id"); - // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance"); + public static final AttributeKey METRIC_KEY_PROJECT_ID = + AttributeKey.stringKey("project_id"); + public static final AttributeKey METRIC_KEY_DATABASE_ID = + AttributeKey.stringKey("database_id"); // Metric attribute keys for labels - // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language"); - static final AttributeKey METHOD_KEY = AttributeKey.stringKey("method"); - static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status"); - public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id"); - static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library"); - static final AttributeKey LIBRARY_VERSION_KEY = AttributeKey.stringKey("library_version"); - static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid"); - - static final AttributeKey Language_KEY = AttributeKey.stringKey("language"); - public static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name"); + public static final AttributeKey METRIC_KEY_METHOD = AttributeKey.stringKey("method"); + public static final AttributeKey METRIC_KEY_STATUS = AttributeKey.stringKey("status"); + public static final AttributeKey METRIC_KEY_LIBRARY_NAME = + AttributeKey.stringKey("library_name"); + public static final AttributeKey METRIC_KEY_LIBRARY_VERSION = + AttributeKey.stringKey("library_version"); + public static final AttributeKey METRIC_KEY_CLIENT_UID = + AttributeKey.stringKey("client_uid"); + // Metric - static final String OPERATION_LATENCY_NAME = "operation_latency"; - static final String OPERATION_COUNT_NAME = "operation_count"; - static final String ATTEMPT_LATENCY_NAME = "attempt_latency"; - static final String ATTEMPT_COUNT_NAME = "attempt_count"; - static final String FIRST_RESPONSE_LATENCY_NAME = "first_response_latency"; - static final String END_TO_END_LATENCY_NAME = "end_to_end_latency"; - static final String TRANSACTION_LATENCY_NAME = "transaction_latency"; - static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count"; + public static final String METRIC_NAME_OPERATION_LATENCY = "operation_latency"; + public static final String METRIC_NAME_OPERATION_COUNT = "operation_count"; + public static final String METRIC_NAME_ATTEMPT_LATENCY = "attempt_latency"; + public static final String METRIC_NAME_ATTEMPT_COUNT = "attempt_count"; + public static final String METRIC_NAME_FIRST_RESPONSE_LATENCY = "first_response_latency"; + public static final String METRIC_NAME_END_TO_END_LATENCY = "end_to_end_latency"; + public static final String METRIC_NAME_TRANSACTION_LATENCY = "transaction_latency"; + public static final String METRIC_NAME_TRANSACTION_ATTEMPT_COUNT = "transaction_attempt_count"; - static final String MILLISECOND_UNIT = "ms"; + public static final String MILLISECOND_UNIT = "ms"; public static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; - public static final Set COMMON_ATTRIBUTES = - ImmutableSet.of( - PROJECT_ID_KEY, - DATABASE_ID_KEY, - CLIENT_UID_KEY, - CLIENT_LIBRARY_KEY, - LIBRARY_VERSION_KEY, - STATUS_KEY); - public static final Set BUILTIN_METRICS = ImmutableSet.of( - OPERATION_LATENCY_NAME, - ATTEMPT_LATENCY_NAME, - OPERATION_COUNT_NAME, - ATTEMPT_COUNT_NAME, - FIRST_RESPONSE_LATENCY_NAME, - END_TO_END_LATENCY_NAME, - TRANSACTION_LATENCY_NAME, - TRANSACTION_ATTEMPT_COUNT_NAME) + METRIC_NAME_OPERATION_LATENCY, + METRIC_NAME_ATTEMPT_LATENCY, + METRIC_NAME_OPERATION_COUNT, + METRIC_NAME_ATTEMPT_COUNT, + METRIC_NAME_FIRST_RESPONSE_LATENCY, + METRIC_NAME_END_TO_END_LATENCY, + METRIC_NAME_TRANSACTION_LATENCY, + METRIC_NAME_TRANSACTION_ATTEMPT_COUNT) .stream() .map(m -> METER_NAME + '/' + m) .collect(Collectors.toSet()); - static void defineView( + public static void defineView( ImmutableMap.Builder viewMap, String id, String meter, @@ -103,6 +93,13 @@ static void defineView( viewMap.put(selector, view); } + public static final Set COMMON_ATTRIBUTES = + ImmutableSet.of( + METRIC_KEY_CLIENT_UID, + METRIC_KEY_LIBRARY_NAME, + METRIC_KEY_LIBRARY_VERSION, + METRIC_KEY_STATUS); + private static Set withAdditionalAttributes(Set attributes) { return ImmutableSet.builder() .addAll(COMMON_ATTRIBUTES) @@ -116,38 +113,39 @@ public static Map getAllViews() { // Define views with COMMON_ATTRIBUTES and METHOD_KEY defineView( views, - OPERATION_LATENCY_NAME, + METRIC_NAME_OPERATION_LATENCY, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); + withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); defineView( views, - ATTEMPT_LATENCY_NAME, + METRIC_NAME_ATTEMPT_LATENCY, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); + withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); defineView( views, - OPERATION_COUNT_NAME, + METRIC_NAME_OPERATION_COUNT, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); + withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); defineView( views, - ATTEMPT_COUNT_NAME, + METRIC_NAME_ATTEMPT_COUNT, GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); + withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); defineView( views, - FIRST_RESPONSE_LATENCY_NAME, + METRIC_NAME_FIRST_RESPONSE_LATENCY, FIRESTORE_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); + withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); defineView( views, - END_TO_END_LATENCY_NAME, + METRIC_NAME_END_TO_END_LATENCY, FIRESTORE_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METHOD_KEY))); + withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); // Define views with only COMMON_ATTRIBUTES - defineView(views, TRANSACTION_LATENCY_NAME, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); - defineView(views, TRANSACTION_ATTEMPT_COUNT_NAME, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); + defineView(views, METRIC_NAME_TRANSACTION_LATENCY, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); + defineView( + views, METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); return views.build(); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 25b1072c7..4a422bd37 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -16,7 +16,12 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METER_NAME; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_LIBRARY_NAME; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_LIBRARY_VERSION; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_NAME_END_TO_END_LATENCY; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.MILLISECOND_UNIT; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.MetricsTracerFactory; @@ -38,6 +43,8 @@ public class BuiltinMetricsProvider { private DoubleHistogram endToEndRequestLatency; private DoubleHistogram firstResponseLatency; + static final String FIRESTORE_LIBRARY_NAME = "java_firestore"; + public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -53,10 +60,10 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); - staticAttributes.put(CLIENT_LIBRARY_KEY.toString(), FIRESTORE_LIBRARY_NAME); + staticAttributes.put(METRIC_KEY_LIBRARY_NAME.toString(), FIRESTORE_LIBRARY_NAME); String pkgVersion = this.getClass().getPackage().getImplementationVersion(); if (pkgVersion != null) { - staticAttributes.put(LIBRARY_VERSION_KEY.toString(), pkgVersion); + staticAttributes.put(METRIC_KEY_LIBRARY_VERSION.toString(), pkgVersion); } return staticAttributes; } @@ -70,14 +77,14 @@ void registerMetrics() { this.endToEndRequestLatency = meter - .histogramBuilder(METER_NAME + "/" + END_TO_END_LATENCY_NAME) + .histogramBuilder(METER_NAME + "/" + METRIC_NAME_END_TO_END_LATENCY) .setDescription("Firestore E2E metrics") .setUnit(MILLISECOND_UNIT) .build(); this.firstResponseLatency = meter - .histogramBuilder(METER_NAME + "/" + FIRST_RESPONSE_LATENCY_NAME) + .histogramBuilder(METER_NAME + "/" + METRIC_NAME_FIRST_RESPONSE_LATENCY) .setDescription("Firestore query first response latency") .setUnit(MILLISECOND_UNIT) .build(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 527499a3e..015d323ac 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -16,7 +16,9 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_METHOD; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_STATUS; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.getAllViews; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; @@ -55,10 +57,11 @@ class EnabledMetricsUtil implements MetricsUtil { this.defaultOpenTelemetryMetricsProvider = new BuiltinMetricsProvider( getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId())); + // TODO(metrics): decide if we want to fall back to global opentelemetry this.customOpenTelemetryMetricsProvider = new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); } catch (IOException e) { - // TODO: Handle the exception appropriately (e.g., logging) + // TODO(metrics): Handle the exception appropriately (e.g., logging) } } @@ -140,8 +143,8 @@ public void recordEndToEndLatency(Throwable t) { private void recordEndToEndLatency(String status) { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); Map attributes = new HashMap<>(); - attributes.put(METHOD_KEY.toString(), methodName); - attributes.put(STATUS_KEY.toString(), status); + attributes.put(METRIC_KEY_METHOD.toString(), methodName); + attributes.put(METRIC_KEY_STATUS.toString(), status); defaultOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); customOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); @@ -150,8 +153,8 @@ private void recordEndToEndLatency(String status) { public void recordFirstResponseLatency() { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); Map attributes = new HashMap<>(); - attributes.put(METHOD_KEY.toString(), methodName); - attributes.put(STATUS_KEY.toString(), StatusCode.Code.OK.toString()); + attributes.put(METRIC_KEY_METHOD.toString(), methodName); + attributes.put(METRIC_KEY_STATUS.toString(), StatusCode.Code.OK.toString()); defaultOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); customOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 93525c4e0..faee622c9 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -16,7 +16,7 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.ENABLE_METRICS_ENV_VAR; import com.google.api.core.ApiFuture; import com.google.api.core.InternalApi; From e0d46bee90de6300e2dba2cce78ba9bc047b4372 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:13:20 -0400 Subject: [PATCH 022/103] e2E latency for runTransaction --- .../google/cloud/firestore/FirestoreImpl.java | 36 +++++++++--- .../com/google/cloud/firestore/Query.java | 9 ++- .../cloud/firestore/ReadTimeTransaction.java | 28 --------- .../firestore/ServerSideTransaction.java | 35 ----------- .../ServerSideTransactionRunner.java | 11 +--- .../google/cloud/firestore/Transaction.java | 1 + .../google/cloud/firestore/UpdateBuilder.java | 13 ----- .../telemetry/BuiltinMetricsConstants.java | 58 +++++++++++++++++-- .../telemetry/BuiltinMetricsProvider.java | 8 ++- .../telemetry/EnabledMetricsUtil.java | 29 ++++++++-- 10 files changed, 117 insertions(+), 111 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index 48c691466..aa00fbec0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -32,6 +32,7 @@ import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; import com.google.cloud.firestore.spi.v1.FirestoreRpc; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -249,6 +250,9 @@ public void onResponse(BatchGetDocumentsResponse response) { DocumentReference documentReference; DocumentSnapshot documentSnapshot; + // TODO(metrics): record first_response_latency here. Need to come up with a way to + // access the method and stopWatch. Maybe share the Context and Scope used by tracing + numResponses++; if (numResponses == 1) { getTraceUtil() @@ -427,16 +431,30 @@ public ApiFuture runAsyncTransaction( @Nonnull final Transaction.AsyncFunction updateFunction, @Nonnull TransactionOptions transactionOptions) { - if (transactionOptions.getReadTime() != null) { - // READ_ONLY transactions with readTime have no retry, nor transaction state, so we don't need - // a runner. - return updateFunction.updateCallback( - new ReadTimeTransaction(this, transactionOptions.getReadTime())); - } else { - // For READ_ONLY transactions without readTime, there is still strong consistency applied, - // that cannot be tracked client side. - return new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run(); + MetricsContext metricsContext = + getOptions().getMetricsUtil().createMetricsContext("RunTransaction"); + + ApiFuture result; + try { + if (transactionOptions.getReadTime() != null) { + // READ_ONLY transactions with readTime have no retry, nor transaction state, so we don't + // need a runner. + result = + updateFunction.updateCallback( + new ReadTimeTransaction(this, transactionOptions.getReadTime())); + metricsContext.recordEndToEndLatencyAtFuture(result); + } else { + // For READ_ONLY transactions without readTime, there is still strong consistency applied, + // that cannot be tracked client side. + result = new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run(); + metricsContext.recordEndToEndLatencyAtFuture(result); + } + } catch (Exception error) { + metricsContext.recordEndToEndLatency(error); + throw error; } + + return result; } @Nonnull diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index 339a774e4..bbb3db793 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -1734,12 +1734,15 @@ private void internalStream( TraceUtil.Span currentSpan = traceUtil.currentSpan(); - // TODO(mila): record transaction latency, retry counts if applicable + String method = + transactionId != null + ? ".Transaction" + : explainOptions != null ? ".ExplainQuery" : ".Query"; MetricsContext metricsContext = getFirestore() .getOptions() .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_RUN_QUERY); + .createMetricsContext(TraceUtil.SPAN_NAME_RUN_QUERY + method); currentSpan.addEvent( TraceUtil.SPAN_NAME_RUN_QUERY, @@ -1796,7 +1799,7 @@ public void onResponse(RunQueryResponse response) { @Override public void onError(Throwable throwable) { QueryDocumentSnapshot cursor = lastReceivedDocument.get(); - // TODO(mila): how do we capture e2e latency when retries are present + // TODO(metrics): record transaction latency, retry counts if applicable if (shouldRetry(cursor, throwable)) { currentSpan.addEvent( TraceUtil.SPAN_NAME_RUN_QUERY + ": Retryable Error", diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java index 4a3daa416..f6715e219 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java @@ -18,7 +18,6 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; @@ -60,12 +59,6 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture result = ApiFutures.transform( @@ -74,12 +67,9 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { snapshots -> snapshots.isEmpty() ? null : snapshots.get(0), MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } @@ -92,22 +82,13 @@ public ApiFuture> getAll( getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } @@ -120,22 +101,13 @@ public ApiFuture> getAll( getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java index 46223ccd1..cb4b8bdba 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java @@ -19,7 +19,6 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.cloud.firestore.TransactionOptions.TransactionOptionsType; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; @@ -111,12 +110,6 @@ ApiFuture rollback() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK, transactionTraceContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK); - try (TraceUtil.Scope ignored = span.makeCurrent()) { RollbackRequest req = RollbackRequest.newBuilder() @@ -143,12 +136,10 @@ ApiFuture rollback() { }, MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } @@ -171,12 +162,6 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT); - try (TraceUtil.Scope ignored = span.makeCurrent()) { Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG); ApiFuture result = @@ -189,12 +174,10 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { snapshots -> snapshots.isEmpty() ? null : snapshots.get(0), MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } @@ -214,23 +197,14 @@ public ApiFuture> getAll( getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll( documentReferences, /* fieldMask= */ null, transactionId, /* readTime= */ null); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } @@ -252,22 +226,13 @@ public ApiFuture> getAll( getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, fieldMask, transactionId, /* readTime= */ null); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index fb81a6f8e..f287389f6 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -26,7 +26,6 @@ import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.rpc.ApiException; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.telemetry.TraceUtil.Span; @@ -95,6 +94,7 @@ private TraceUtil getTraceUtil() { ApiFuture run() { runTransactionSpan = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_RUN); + runTransactionSpan.setAttribute( ATTRIBUTE_KEY_TRANSACTION_TYPE, transactionOptions.getType().name()); runTransactionSpan.setAttribute( @@ -122,12 +122,6 @@ ApiFuture begin() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_BEGIN, runTransactionContext); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_BEGIN); - try (Scope ignored = span.makeCurrent()) { ServerSideTransaction previousTransaction = this.transaction; this.transaction = null; @@ -141,12 +135,9 @@ ApiFuture begin() { return serverSideTransaction; }); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java index 585734530..ff1b7e2d6 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java @@ -50,6 +50,7 @@ TraceUtil getTraceUtil() { return firestore.getOptions().getTraceUtil(); } + // TODO(Metrics): implement transaction latency and attempt count metrics @Nonnull MetricsUtil getMetricsUtil() { return firestore.getOptions().getMetricsUtil(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index f4c96fa15..c3bb00311 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -26,7 +26,6 @@ import com.google.api.core.InternalExtensionOnly; import com.google.cloud.firestore.UserDataConverter.EncodingOptions; import com.google.cloud.firestore.encoding.CustomClassMapper; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.common.base.Preconditions; @@ -620,15 +619,6 @@ ApiFuture> commit(@Nullable ByteString transactionId) { ? TraceUtil.SPAN_NAME_BATCH_COMMIT : TraceUtil.SPAN_NAME_TRANSACTION_COMMIT); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext( - transactionId == null - ? TraceUtil.SPAN_NAME_BATCH_COMMIT - : TraceUtil.SPAN_NAME_TRANSACTION_COMMIT); - span.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, writes.size()); span.setAttribute(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null); try (Scope ignored = span.makeCurrent()) { @@ -664,12 +654,9 @@ ApiFuture> commit(@Nullable ByteString transactionId) { }, MoreExecutors.directExecutor()); span.endAtFuture(returnValue); - metricsContext.recordEndToEndLatencyAtFuture(returnValue); - return returnValue; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java index 70bc3dd00..72a5442c0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java @@ -22,22 +22,28 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.View; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; public class BuiltinMetricsConstants { + public static final String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw"; + // TODO: change to firestore.googleapis.com public static final String METER_NAME = "custom.googleapis.com/internal/client"; public static final String FIRESTORE_METER_NAME = "java_firestore"; public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; - // Metric attribute keys for monitored resource - public static final AttributeKey METRIC_KEY_PROJECT_ID = - AttributeKey.stringKey("project_id"); - public static final AttributeKey METRIC_KEY_DATABASE_ID = - AttributeKey.stringKey("database_id"); + // Monitored resource labels + static final String RESOURCE_KEY_RESOURCE_CONTAINER = "resource_container"; + static final String RESOURCE_KEY_LOCATION = "location"; + static final String RESOURCE_KEY_DATABASE_ID = "database_id"; // Metric attribute keys for labels public static final AttributeKey METRIC_KEY_METHOD = AttributeKey.stringKey("method"); @@ -59,10 +65,17 @@ public class BuiltinMetricsConstants { public static final String METRIC_NAME_TRANSACTION_LATENCY = "transaction_latency"; public static final String METRIC_NAME_TRANSACTION_ATTEMPT_COUNT = "transaction_attempt_count"; + // TODO(metrics): an app should have one client_uid, and it is contant. should this be hold here? + private static String CLIENT_UID; + public static final String MILLISECOND_UNIT = "ms"; public static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; + static final Set FIRESTORE_RESOURCE_LABELS = + ImmutableSet.of( + RESOURCE_KEY_RESOURCE_CONTAINER, RESOURCE_KEY_LOCATION, RESOURCE_KEY_DATABASE_ID); + public static final Set BUILTIN_METRICS = ImmutableSet.of( METRIC_NAME_OPERATION_LATENCY, @@ -149,4 +162,39 @@ public static Map getAllViews() { return views.build(); } + + public static String getClientUid() { + if (CLIENT_UID == null) { + String identifier = UUID.randomUUID().toString(); + String pid = getProcessId(); + + try { + String hostname = InetAddress.getLocalHost().getHostName(); + CLIENT_UID = identifier + "@" + pid + "@" + hostname; + } catch (UnknownHostException e) { + CLIENT_UID = identifier + "@" + pid + "@localhost"; + } + } + return CLIENT_UID; + } + + private static String getProcessId() { + try { + // Check if Java 9+ and ProcessHandle class is available + Class processHandleClass = Class.forName("java.lang.ProcessHandle"); + Method currentMethod = processHandleClass.getMethod("current"); + Object processHandleInstance = currentMethod.invoke(null); + Method pidMethod = processHandleClass.getMethod("pid"); + long pid = (long) pidMethod.invoke(processHandleInstance); + return Long.toString(pid); + } catch (Exception e) { + // Fallback to Java 8 method + final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + if (jvmName != null && jvmName.contains("@")) { + return jvmName.split("@")[0]; + } else { + return "unknown"; + } + } + } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 4a422bd37..424c0bb70 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -16,12 +16,15 @@ package com.google.cloud.firestore.telemetry; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.FIRESTORE_METER_NAME; import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METER_NAME; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_CLIENT_UID; import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_LIBRARY_NAME; import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_LIBRARY_VERSION; import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_NAME_END_TO_END_LATENCY; import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.MILLISECOND_UNIT; +import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.getClientUid; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.MetricsTracerFactory; @@ -60,6 +63,7 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); + staticAttributes.put(METRIC_KEY_CLIENT_UID.toString(), getClientUid()); staticAttributes.put(METRIC_KEY_LIBRARY_NAME.toString(), FIRESTORE_LIBRARY_NAME); String pkgVersion = this.getClass().getPackage().getImplementationVersion(); if (pkgVersion != null) { @@ -73,7 +77,7 @@ public ApiTracerFactory getOpenTelemetryApiTracerFactory() { } void registerMetrics() { - this.meter = openTelemetry.getMeter(FIRESTORE_LIBRARY_NAME); + this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); this.endToEndRequestLatency = meter @@ -88,7 +92,7 @@ void registerMetrics() { .setDescription("Firestore query first response latency") .setUnit(MILLISECOND_UNIT) .build(); - // TODO(mila): add transaction latency and retry count metrics + // TODO(metrics): add transaction latency and retry count metrics } public void endToEndRequestLatencyRecorder(double latency, Map attributes) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 015d323ac..38a0d5072 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -32,6 +32,7 @@ import com.google.common.base.Stopwatch; import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Status; +import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.InstrumentSelector; @@ -45,6 +46,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.annotation.Nullable; class EnabledMetricsUtil implements MetricsUtil { @@ -52,16 +55,25 @@ class EnabledMetricsUtil implements MetricsUtil { private BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; private BuiltinMetricsProvider customOpenTelemetryMetricsProvider; + private static final Logger logger = Logger.getLogger(EnabledMetricsUtil.class.getName()); + EnabledMetricsUtil(FirestoreOptions firestoreOptions) { try { this.defaultOpenTelemetryMetricsProvider = new BuiltinMetricsProvider( getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId())); - // TODO(metrics): decide if we want to fall back to global opentelemetry - this.customOpenTelemetryMetricsProvider = - new BuiltinMetricsProvider(firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry()); + + OpenTelemetry customOpenTelemetry = + firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); + if (customOpenTelemetry == null) { + customOpenTelemetry = GlobalOpenTelemetry.get(); + } + this.customOpenTelemetryMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry); } catch (IOException e) { - // TODO(metrics): Handle the exception appropriately (e.g., logging) + logger.log( + Level.WARNING, + "Unable to get create MetricsUtil object for client side metrics, will skip exporting client side metrics", + e); } } @@ -73,13 +85,18 @@ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws I sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); } + // MonitoredResourceDescription monitoredResourceMapping = + // new MonitoredResourceDescription(FIRESTORE_RESOURCE_TYPE, FIRESTORE_RESOURCE_LABELS); + MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder() .setProjectId(projectId) + // Ignore library info as it is collected by the metric attributes as well .setInstrumentationLibraryLabelsEnabled(false) - // .setMonitoredResourceDescription((null)) - // .setUseServiceTimeSeries(false) + // TODO(metrics): enable the configuration below when backend is ready + // .setMonitoredResourceDescription(monitoredResourceMapping) + // .setUseServiceTimeSeries(true) .build()); sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); From 92c83c11edb3b2027fa5047236ee5f2e02996c64 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Fri, 4 Oct 2024 14:46:59 +0000 Subject: [PATCH 023/103] chore: generate libraries at Fri Oct 4 14:44:19 UTC 2024 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cdf2fa4c8..a2308f34e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file: com.google.cloud libraries-bom - 26.47.0 + 26.48.0 pom import @@ -42,7 +42,7 @@ If you are using Maven without the BOM, add this to your dependencies: com.google.cloud google-cloud-firestore - 3.26.5 + 3.27.0 ``` From c5137c136bb3ad5d6def98e9f3730fd8c20142bb Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:04:09 -0400 Subject: [PATCH 024/103] remove noise --- google-cloud-firestore/pom.xml | 2 +- .../cloud/firestore/FirestoreOpenTelemetryOptions.java | 3 ++- .../com/google/cloud/firestore/ReadTimeTransaction.java | 3 --- .../com/google/cloud/firestore/ServerSideTransaction.java | 6 ------ .../google/cloud/firestore/ServerSideTransactionRunner.java | 2 -- .../main/java/com/google/cloud/firestore/UpdateBuilder.java | 1 - 6 files changed, 3 insertions(+), 14 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 8961bef6e..128cc20fe 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -208,7 +208,7 @@ ${opentelemetry.version} test - + io.opentelemetry opentelemetry-sdk-common ${opentelemetry.version} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index bc611fbb6..171c33d9a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -69,7 +69,8 @@ public static class Builder { private Builder() { tracingEnabled = false; - metricsEnabled = true; + // TODO(metrics): set this to default on + metricsEnabled = false; openTelemetry = null; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java index f6715e219..0c423469a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java @@ -58,7 +58,6 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture result = ApiFutures.transform( @@ -81,7 +80,6 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); @@ -100,7 +98,6 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java index cb4b8bdba..5d366c965 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java @@ -109,7 +109,6 @@ ApiFuture> commit() { ApiFuture rollback() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK, transactionTraceContext); - try (TraceUtil.Scope ignored = span.makeCurrent()) { RollbackRequest req = RollbackRequest.newBuilder() @@ -136,7 +135,6 @@ ApiFuture rollback() { }, MoreExecutors.directExecutor()); span.endAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -161,7 +159,6 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); - try (TraceUtil.Scope ignored = span.makeCurrent()) { Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG); ApiFuture result = @@ -174,7 +171,6 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) { snapshots -> snapshots.isEmpty() ? null : snapshots.get(0), MoreExecutors.directExecutor()); span.endAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -196,7 +192,6 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll( @@ -225,7 +220,6 @@ public ApiFuture> getAll( TraceUtil.Span span = getTraceUtil() .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); - try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, fieldMask, transactionId, /* readTime= */ null); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index f287389f6..db8ebff63 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -94,7 +94,6 @@ private TraceUtil getTraceUtil() { ApiFuture run() { runTransactionSpan = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_RUN); - runTransactionSpan.setAttribute( ATTRIBUTE_KEY_TRANSACTION_TYPE, transactionOptions.getType().name()); runTransactionSpan.setAttribute( @@ -121,7 +120,6 @@ ApiFuture run() { ApiFuture begin() { TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_BEGIN, runTransactionContext); - try (Scope ignored = span.makeCurrent()) { ServerSideTransaction previousTransaction = this.transaction; this.transaction = null; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index c3bb00311..31434667b 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -618,7 +618,6 @@ ApiFuture> commit(@Nullable ByteString transactionId) { transactionId == null ? TraceUtil.SPAN_NAME_BATCH_COMMIT : TraceUtil.SPAN_NAME_TRANSACTION_COMMIT); - span.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, writes.size()); span.setAttribute(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null); try (Scope ignored = span.makeCurrent()) { From 63b5bd35d045f3bae4a3ced0878309606a4be880 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:02:49 -0400 Subject: [PATCH 025/103] reformat code --- .../cloud/firestore/AggregateQuery.java | 26 +-- .../google/cloud/firestore/BulkWriter.java | 5 +- .../cloud/firestore/CollectionGroup.java | 5 +- .../cloud/firestore/CollectionReference.java | 9 +- .../cloud/firestore/DocumentReference.java | 71 +++++---- .../google/cloud/firestore/FirestoreImpl.java | 26 ++- .../FirestoreOpenTelemetryOptions.java | 2 +- .../cloud/firestore/ReadTimeTransaction.java | 10 +- .../firestore/ServerSideTransaction.java | 14 +- .../ServerSideTransactionRunner.java | 6 +- .../cloud/firestore/StreamableQuery.java | 42 +++-- .../google/cloud/firestore/UpdateBuilder.java | 5 +- .../telemetry/TelemetryConstants.java | 49 ++++++ .../cloud/firestore/telemetry/TraceUtil.java | 24 --- .../cloud/firestore/it/ITE2ETracingTest.java | 150 +++++++++--------- .../cloud/firestore/it/ITTracingTest.java | 87 +++++----- 16 files changed, 307 insertions(+), 224 deletions(-) create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 72fbeda08..032ce8572 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -16,8 +16,8 @@ package com.google.cloud.firestore; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY; import static com.google.cloud.firestore.telemetry.TraceUtil.ATTRIBUTE_KEY_ATTEMPT; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_RUN_AGGREGATION_QUERY; import com.google.api.core.ApiFuture; import com.google.api.core.InternalExtensionOnly; @@ -29,6 +29,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -103,10 +104,13 @@ public ApiFuture get() { */ @Nonnull public ApiFuture> explain(ExplainOptions options) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET); + TraceUtil.Span span = + getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET); + getMetricsUtil() + .createMetricsContext( + TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN_AGGREGATION_QUERY); try (Scope ignored = span.makeCurrent()) { AggregateQueryExplainResponseDeliverer responseDeliverer = @@ -136,15 +140,15 @@ ApiFuture get( getTraceUtil() .startSpan( transactionId == null - ? TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET - : TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY); + ? TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET + : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY); MetricsContext metricsContext = getMetricsUtil() .createMetricsContext( transactionId == null - ? TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET - : TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY); + ? TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET + : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY); try (Scope ignored = span.makeCurrent()) { AggregateQueryResponseDeliverer responseDeliverer = @@ -327,7 +331,7 @@ private boolean isExplainQuery() { public void onStart(StreamController streamController) { getTraceUtil() .currentSpan() - .addEvent(SPAN_NAME_RUN_AGGREGATION_QUERY + " Stream started.", getAttemptAttributes()); + .addEvent(METHOD_NAME_RUN_AGGREGATION_QUERY + " Stream started.", getAttemptAttributes()); } @Override @@ -335,7 +339,7 @@ public void onResponse(RunAggregationQueryResponse response) { getTraceUtil() .currentSpan() .addEvent( - SPAN_NAME_RUN_AGGREGATION_QUERY + " Response Received.", getAttemptAttributes()); + METHOD_NAME_RUN_AGGREGATION_QUERY + " Response Received.", getAttemptAttributes()); if (response.hasReadTime()) { readTime = Timestamp.fromProto(response.getReadTime()); } @@ -364,7 +368,7 @@ public void onError(Throwable throwable) { getTraceUtil() .currentSpan() .addEvent( - SPAN_NAME_RUN_AGGREGATION_QUERY + ": Retryable Error", + METHOD_NAME_RUN_AGGREGATION_QUERY + ": Retryable Error", Collections.singletonMap("error.message", throwable.getMessage())); runQuery(responseDeliverer, attempt + 1); @@ -372,7 +376,7 @@ public void onError(Throwable throwable) { getTraceUtil() .currentSpan() .addEvent( - SPAN_NAME_RUN_AGGREGATION_QUERY + ": Error", + METHOD_NAME_RUN_AGGREGATION_QUERY + ": Error", Collections.singletonMap("error.message", throwable.getMessage())); responseDeliverer.deliverError(throwable); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java index f12f42cc5..3d15aa41b 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java @@ -27,6 +27,7 @@ import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode.Code; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Context; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -913,14 +914,14 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) { firestore .getOptions() .getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT, traceContext) + .startSpan(TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT, traceContext) .setAttribute(ATTRIBUTE_KEY_DOC_COUNT, batch.getMutationsSize()); MetricsContext metricsContext = firestore .getOptions() .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT); + .createMetricsContext(TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT); try (Scope ignored = span.makeCurrent()) { ApiFuture result = batch.bulkCommit(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java index d408194d5..33577ea32 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java @@ -22,6 +22,7 @@ import com.google.api.gax.rpc.ApiExceptions; import com.google.api.gax.rpc.ApiStreamObserver; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.PartitionQueryPagedResponse; @@ -110,14 +111,14 @@ public ApiFuture> getPartitions(long desiredPartitionCount) .getFirestore() .getOptions() .getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_PARTITION_QUERY); + .startSpan(TelemetryConstants.METHOD_NAME_PARTITION_QUERY); MetricsContext metricsContext = rpcContext .getFirestore() .getOptions() .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_PARTITION_QUERY); + .createMetricsContext(TelemetryConstants.METHOD_NAME_PARTITION_QUERY); try (Scope ignored = span.makeCurrent()) { ApiFuture> result = diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java index 7f2610b11..9271888f8 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java @@ -25,6 +25,7 @@ import com.google.cloud.firestore.encoding.CustomClassMapper; import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListDocumentsPagedResponse; @@ -135,14 +136,14 @@ public Iterable listDocuments() { .getFirestore() .getOptions() .getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS); + .startSpan(TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS); MetricsContext metricsContext = rpcContext .getFirestore() .getOptions() .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS); + .createMetricsContext(TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS); try (Scope ignored = span.makeCurrent()) { ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder(); @@ -215,14 +216,14 @@ public ApiFuture add(@Nonnull final Map field .getFirestore() .getOptions() .getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_COL_REF_ADD); + .startSpan(TelemetryConstants.METHOD_NAME_COL_REF_ADD); MetricsContext metricsContext = rpcContext .getFirestore() .getOptions() .getMetricsUtil() - .createMetricsContext(TraceUtil.SPAN_NAME_COL_REF_ADD); + .createMetricsContext(TelemetryConstants.METHOD_NAME_COL_REF_ADD); try (Scope ignored = span.makeCurrent()) { final DocumentReference documentReference = document(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index abc68409d..bfa4d27f6 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -23,6 +23,7 @@ import com.google.api.gax.rpc.ApiExceptions; import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse; @@ -155,10 +156,10 @@ private MetricsUtil getMetricsUtil() { */ @Nonnull public ApiFuture create(@Nonnull Map fields) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -183,9 +184,9 @@ public ApiFuture create(@Nonnull Map fields) { */ @Nonnull public ApiFuture create(@Nonnull Object pojo) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_CREATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -210,9 +211,9 @@ public ApiFuture create(@Nonnull Object pojo) { */ @Nonnull public ApiFuture set(@Nonnull Map fields) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -240,9 +241,9 @@ public ApiFuture set(@Nonnull Map fields) { @Nonnull public ApiFuture set( @Nonnull Map fields, @Nonnull SetOptions options) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -267,9 +268,9 @@ public ApiFuture set( */ @Nonnull public ApiFuture set(@Nonnull Object pojo) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -296,9 +297,9 @@ public ApiFuture set(@Nonnull Object pojo) { */ @Nonnull public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions options) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -323,9 +324,9 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti */ @Nonnull public ApiFuture update(@Nonnull Map fields) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -351,9 +352,9 @@ public ApiFuture update(@Nonnull Map fields) { */ @Nonnull public ApiFuture update(@Nonnull Map fields, Precondition options) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -382,9 +383,9 @@ public ApiFuture update(@Nonnull Map fields, Precon @Nonnull public ApiFuture update( @Nonnull String field, @Nullable Object value, Object... moreFieldsAndValues) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -413,9 +414,9 @@ public ApiFuture update( @Nonnull public ApiFuture update( @Nonnull FieldPath fieldPath, @Nullable Object value, Object... moreFieldsAndValues) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -448,9 +449,9 @@ public ApiFuture update( @Nonnull String field, @Nullable Object value, Object... moreFieldsAndValues) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -484,9 +485,9 @@ public ApiFuture update( @Nonnull FieldPath fieldPath, @Nullable Object value, Object... moreFieldsAndValues) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -512,9 +513,9 @@ public ApiFuture update( */ @Nonnull public ApiFuture delete(@Nonnull Precondition options) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -537,9 +538,9 @@ public ApiFuture delete(@Nonnull Precondition options) { */ @Nonnull public ApiFuture delete() { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_DELETE); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -564,9 +565,9 @@ public ApiFuture delete() { */ @Nonnull public ApiFuture get() { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_GET); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_GET); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_GET); try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); @@ -591,9 +592,9 @@ public ApiFuture get() { */ @Nonnull public ApiFuture get(FieldMask fieldMask) { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET); + TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_GET); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_GET); + getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_GET); try (Scope ignored = span.makeCurrent()) { ApiFuture result = @@ -617,9 +618,11 @@ public ApiFuture get(FieldMask fieldMask) { */ @Nonnull public Iterable listCollections() { - TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS); + TraceUtil.Span span = + getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS); MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS); + getMetricsUtil() + .createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS); try (Scope ignored = span.makeCurrent()) { ListCollectionIdsRequest.Builder request = ListCollectionIdsRequest.newBuilder(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index aa00fbec0..cc51d87f9 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -33,6 +33,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -228,6 +229,11 @@ void getAll( // that we receive from the server. final int NUM_RESPONSES_PER_TRACE_EVENT = 100; + MetricsContext metricsContext = + getOptions() + .getMetricsUtil() + .createMetricsContext(TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS); + ResponseObserver responseObserver = new ResponseObserver() { int numResponses = 0; @@ -238,7 +244,7 @@ public void onStart(StreamController streamController) { getTraceUtil() .currentSpan() .addEvent( - TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": Start", + TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + ": Start", new ImmutableMap.Builder() .put(ATTRIBUTE_KEY_DOC_COUNT, documentReferences.length) .put(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null) @@ -250,19 +256,19 @@ public void onResponse(BatchGetDocumentsResponse response) { DocumentReference documentReference; DocumentSnapshot documentSnapshot; - // TODO(metrics): record first_response_latency here. Need to come up with a way to - // access the method and stopWatch. Maybe share the Context and Scope used by tracing - numResponses++; if (numResponses == 1) { getTraceUtil() .currentSpan() - .addEvent(TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": First response received"); + .addEvent( + TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + + ": First response received"); + metricsContext.recordFirstResponseLatency(); } else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) { getTraceUtil() .currentSpan() .addEvent( - TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + ": Received " + numResponses + " responses"); @@ -302,6 +308,7 @@ public void onResponse(BatchGetDocumentsResponse response) { @Override public void onError(Throwable throwable) { getTraceUtil().currentSpan().end(throwable); + metricsContext.recordEndToEndLatency(throwable); apiStreamObserver.onError(throwable); } @@ -312,11 +319,12 @@ public void onComplete() { getTraceUtil() .currentSpan() .addEvent( - TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + ": Completed with " + numResponses + " responses.", Collections.singletonMap(ATTRIBUTE_KEY_NUM_RESPONSES, numResponses)); + metricsContext.recordEndToEndLatency(); apiStreamObserver.onCompleted(); } }; @@ -432,7 +440,9 @@ public ApiFuture runAsyncTransaction( @Nonnull TransactionOptions transactionOptions) { MetricsContext metricsContext = - getOptions().getMetricsUtil().createMetricsContext("RunTransaction"); + getOptions() + .getMetricsUtil() + .createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_TRANSACTION); ApiFuture result; try { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 171c33d9a..c72e5a1f1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -102,7 +102,7 @@ public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEn * @param metricsEnabled Whether client side metrics should be enabled. */ @Nonnull - public FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEnabled) { + private FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEnabled) { this.metricsEnabled = metricsEnabled; return this; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java index 0c423469a..8780e9c63 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java @@ -18,6 +18,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; @@ -57,7 +58,8 @@ public boolean hasTransactionId() { public ApiFuture get(@Nonnull DocumentReference documentRef) { TraceUtil.Span span = getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); + .startSpan( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture result = ApiFutures.transform( @@ -79,7 +81,8 @@ public ApiFuture> getAll( @Nonnull DocumentReference... documentReferences) { TraceUtil.Span span = getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + .startSpan( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); @@ -97,7 +100,8 @@ public ApiFuture> getAll( @Nonnull DocumentReference[] documentReferences, @Nullable FieldMask fieldMask) { TraceUtil.Span span = getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + .startSpan( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, /* fieldMask= */ null, readTime); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java index 5d366c965..40dd64ce8 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java @@ -19,6 +19,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.cloud.firestore.TransactionOptions.TransactionOptionsType; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.MoreExecutors; @@ -108,7 +109,9 @@ ApiFuture> commit() { /** Rolls a transaction back and releases all read locks. */ ApiFuture rollback() { TraceUtil.Span span = - getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK, transactionTraceContext); + getTraceUtil() + .startSpan( + TelemetryConstants.METHOD_NAME_TRANSACTION_ROLLBACK, transactionTraceContext); try (TraceUtil.Scope ignored = span.makeCurrent()) { RollbackRequest req = RollbackRequest.newBuilder() @@ -158,7 +161,8 @@ public boolean hasTransactionId() { public ApiFuture get(@Nonnull DocumentReference documentRef) { TraceUtil.Span span = getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); + .startSpan( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext); try (TraceUtil.Scope ignored = span.makeCurrent()) { Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG); ApiFuture result = @@ -191,7 +195,8 @@ public ApiFuture> getAll( Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG); TraceUtil.Span span = getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + .startSpan( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll( @@ -219,7 +224,8 @@ public ApiFuture> getAll( Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG); TraceUtil.Span span = getTraceUtil() - .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); + .startSpan( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext); try (TraceUtil.Scope ignored = span.makeCurrent()) { ApiFuture> result = firestore.getAll(documentReferences, fieldMask, transactionId, /* readTime= */ null); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index db8ebff63..2edd7e71e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -26,6 +26,7 @@ import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.rpc.ApiException; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.telemetry.TraceUtil.Span; @@ -93,7 +94,7 @@ private TraceUtil getTraceUtil() { } ApiFuture run() { - runTransactionSpan = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_RUN); + runTransactionSpan = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN); runTransactionSpan.setAttribute( ATTRIBUTE_KEY_TRANSACTION_TYPE, transactionOptions.getType().name()); runTransactionSpan.setAttribute( @@ -119,7 +120,8 @@ ApiFuture run() { ApiFuture begin() { TraceUtil.Span span = - getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_BEGIN, runTransactionContext); + getTraceUtil() + .startSpan(TelemetryConstants.METHOD_NAME_TRANSACTION_BEGIN, runTransactionContext); try (Scope ignored = span.makeCurrent()) { ServerSideTransaction previousTransaction = this.transaction; this.transaction = null; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java index 24be9e69c..15938e63c 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java @@ -26,7 +26,10 @@ import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StreamController; import com.google.cloud.Timestamp; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; +import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; import com.google.common.collect.ImmutableMap; import com.google.firestore.v1.Document; @@ -100,8 +103,8 @@ ApiFuture get( .getTraceUtil() .startSpan( transactionId == null - ? TraceUtil.SPAN_NAME_QUERY_GET - : TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY); + ? TelemetryConstants.METHOD_NAME_QUERY_GET + : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY); try (Scope ignored = span.makeCurrent()) { final SettableApiFuture result = SettableApiFuture.create(); internalStream( @@ -165,7 +168,10 @@ public void onCompleted() { @Nonnull public ApiFuture> explain(ExplainOptions options) { TraceUtil.Span span = - getFirestore().getOptions().getTraceUtil().startSpan(TraceUtil.SPAN_NAME_QUERY_GET); + getFirestore() + .getOptions() + .getTraceUtil() + .startSpan(TelemetryConstants.METHOD_NAME_QUERY_GET); try (Scope ignored = span.makeCurrent()) { final SettableApiFuture> result = SettableApiFuture.create(); @@ -245,18 +251,28 @@ protected void internalStream( @Nullable final ExplainOptions explainOptions, final boolean isRetryRequestWithCursor) { TraceUtil traceUtil = getFirestore().getOptions().getTraceUtil(); + // To reduce the size of traces, we only register one event for every 100 responses // that we receive from the server. final int NUM_RESPONSES_PER_TRACE_EVENT = 100; TraceUtil.Span currentSpan = traceUtil.currentSpan(); currentSpan.addEvent( - TraceUtil.SPAN_NAME_RUN_QUERY, + TelemetryConstants.METHOD_NAME_RUN_QUERY, new ImmutableMap.Builder() .put(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null) .put(ATTRIBUTE_KEY_IS_RETRY_WITH_CURSOR, isRetryRequestWithCursor) .build()); + String method = + transactionId != null + ? TelemetryConstants.METHOD_NAME_RUN_QUERY_TRANSACTION + : explainOptions != null + ? TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN_QUERY + : TelemetryConstants.METHOD_NAME_RUN_QUERY_QUERY; + MetricsContext metricsContext = + getFirestore().getOptions().getMetricsUtil().createMetricsContext(method); + final AtomicReference lastReceivedDocument = new AtomicReference<>(); ResponseObserver observer = @@ -276,7 +292,8 @@ public void onStart(StreamController streamController) {} public void onResponse(RunQueryResponse response) { if (!firstResponse) { firstResponse = true; - currentSpan.addEvent(TraceUtil.SPAN_NAME_RUN_QUERY + ": First Response"); + currentSpan.addEvent(TelemetryConstants.METHOD_NAME_RUN_QUERY + ": First Response"); + metricsContext.recordFirstResponseLatency(); } runQueryResponseObserver.onNext(response); @@ -285,7 +302,10 @@ public void onResponse(RunQueryResponse response) { numDocuments++; if (numDocuments % NUM_RESPONSES_PER_TRACE_EVENT == 0) { currentSpan.addEvent( - TraceUtil.SPAN_NAME_RUN_QUERY + ": Received " + numDocuments + " documents"); + TelemetryConstants.METHOD_NAME_RUN_QUERY + + ": Received " + + numDocuments + + " documents"); } Document document = response.getDocument(); QueryDocumentSnapshot documentSnapshot = @@ -296,7 +316,7 @@ public void onResponse(RunQueryResponse response) { if (response.getDone()) { currentSpan.addEvent( - TraceUtil.SPAN_NAME_RUN_QUERY + ": Received RunQueryResponse.Done"); + TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Received RunQueryResponse.Done"); onComplete(); } } @@ -306,7 +326,7 @@ public void onError(Throwable throwable) { QueryDocumentSnapshot cursor = lastReceivedDocument.get(); if (isRetryableWithCursor() && shouldRetry(cursor, throwable)) { currentSpan.addEvent( - TraceUtil.SPAN_NAME_RUN_QUERY + ": Retryable Error", + TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Retryable Error", Collections.singletonMap("error.message", throwable.getMessage())); startAfter(cursor) @@ -319,8 +339,9 @@ public void onError(Throwable throwable) { /* isRetryRequestWithCursor= */ true); } else { currentSpan.addEvent( - TraceUtil.SPAN_NAME_RUN_QUERY + ": Error", + TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Error", Collections.singletonMap("error.message", throwable.getMessage())); + metricsContext.recordEndToEndLatency(throwable); runQueryResponseObserver.onError(throwable); } } @@ -330,8 +351,9 @@ public void onComplete() { if (hasCompleted) return; hasCompleted = true; currentSpan.addEvent( - TraceUtil.SPAN_NAME_RUN_QUERY + ": Completed", + TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Completed", Collections.singletonMap(ATTRIBUTE_KEY_DOC_COUNT, numDocuments)); + metricsContext.recordEndToEndLatency(); runQueryResponseObserver.onCompleted(); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index 31434667b..cfa852ce4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -26,6 +26,7 @@ import com.google.api.core.InternalExtensionOnly; import com.google.cloud.firestore.UserDataConverter.EncodingOptions; import com.google.cloud.firestore.encoding.CustomClassMapper; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.common.base.Preconditions; @@ -616,8 +617,8 @@ ApiFuture> commit(@Nullable ByteString transactionId) { .getTraceUtil() .startSpan( transactionId == null - ? TraceUtil.SPAN_NAME_BATCH_COMMIT - : TraceUtil.SPAN_NAME_TRANSACTION_COMMIT); + ? TelemetryConstants.METHOD_NAME_BATCH_COMMIT + : TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT); span.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, writes.size()); span.setAttribute(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null); try (Scope ignored = span.makeCurrent()) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java new file mode 100644 index 000000000..64d51bd48 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -0,0 +1,49 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +public interface TelemetryConstants { + String METHOD_NAME_DOC_REF_CREATE = "DocumentReference.Create"; + String METHOD_NAME_DOC_REF_SET = "DocumentReference.Set"; + String METHOD_NAME_DOC_REF_UPDATE = "DocumentReference.Update"; + String METHOD_NAME_DOC_REF_DELETE = "DocumentReference.Delete"; + String METHOD_NAME_DOC_REF_GET = "DocumentReference.Get"; + String METHOD_NAME_DOC_REF_LIST_COLLECTIONS = "DocumentReference.ListCollections"; + String METHOD_NAME_COL_REF_ADD = "CollectionReference.Add"; + String METHOD_NAME_COL_REF_LIST_DOCUMENTS = "CollectionReference.ListDocuments"; + String METHOD_NAME_QUERY_GET = "Query.Get"; + String METHOD_NAME_AGGREGATION_QUERY_GET = "AggregationQuery.Get"; + String METHOD_NAME_RUN_QUERY = "RunQuery"; + String METHOD_NAME_RUN_QUERY_TRANSACTION = "RunQuery.Transaction"; + String METHOD_NAME_RUN_QUERY_EXPLAIN_QUERY = "RunQuery.ExplainQuery"; + String METHOD_NAME_RUN_QUERY_EXPLAIN_AGGREGATION_QUERY = "RunQuery.ExplainAggregationQuery"; + String METHOD_NAME_RUN_QUERY_QUERY = "RunQuery.Query"; + String METHOD_NAME_RUN_AGGREGATION_QUERY = "RunAggregationQuery"; + String METHOD_NAME_BATCH_GET_DOCUMENTS = "BatchGetDocuments"; + String METHOD_NAME_TRANSACTION_RUN = "Transaction.Run"; + String METHOD_NAME_TRANSACTION_BEGIN = "Transaction.Begin"; + String METHOD_NAME_TRANSACTION_GET_QUERY = "Transaction.Get.Query"; + String METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY = "Transaction.Get.AggregationQuery"; + String METHOD_NAME_TRANSACTION_GET_DOCUMENT = "Transaction.Get.Document"; + String METHOD_NAME_TRANSACTION_GET_DOCUMENTS = "Transaction.Get.Documents"; + String METHOD_NAME_TRANSACTION_ROLLBACK = "Transaction.Rollback"; + String METHOD_NAME_BATCH_COMMIT = "Batch.Commit"; + String METHOD_NAME_TRANSACTION_COMMIT = "Transaction.Commit"; + String METHOD_NAME_PARTITION_QUERY = "PartitionQuery"; + String METHOD_NAME_BULK_WRITER_COMMIT = "BulkWriter.Commit"; + String METHOD_NAME_RUN_TRANSACTION = "RunTransaction"; +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java index 22dfb3ed0..9dea95f28 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java @@ -31,30 +31,6 @@ */ public interface TraceUtil { String ATTRIBUTE_SERVICE_PREFIX = "gcp.firestore."; - String SPAN_NAME_DOC_REF_CREATE = "DocumentReference.Create"; - String SPAN_NAME_DOC_REF_SET = "DocumentReference.Set"; - String SPAN_NAME_DOC_REF_UPDATE = "DocumentReference.Update"; - String SPAN_NAME_DOC_REF_DELETE = "DocumentReference.Delete"; - String SPAN_NAME_DOC_REF_GET = "DocumentReference.Get"; - String SPAN_NAME_DOC_REF_LIST_COLLECTIONS = "DocumentReference.ListCollections"; - String SPAN_NAME_COL_REF_ADD = "CollectionReference.Add"; - String SPAN_NAME_COL_REF_LIST_DOCUMENTS = "CollectionReference.ListDocuments"; - String SPAN_NAME_QUERY_GET = "Query.Get"; - String SPAN_NAME_AGGREGATION_QUERY_GET = "AggregationQuery.Get"; - String SPAN_NAME_RUN_QUERY = "RunQuery"; - String SPAN_NAME_RUN_AGGREGATION_QUERY = "RunAggregationQuery"; - String SPAN_NAME_BATCH_GET_DOCUMENTS = "BatchGetDocuments"; - String SPAN_NAME_TRANSACTION_RUN = "Transaction.Run"; - String SPAN_NAME_TRANSACTION_BEGIN = "Transaction.Begin"; - String SPAN_NAME_TRANSACTION_GET_QUERY = "Transaction.Get.Query"; - String SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY = "Transaction.Get.AggregationQuery"; - String SPAN_NAME_TRANSACTION_GET_DOCUMENT = "Transaction.Get.Document"; - String SPAN_NAME_TRANSACTION_GET_DOCUMENTS = "Transaction.Get.Documents"; - String SPAN_NAME_TRANSACTION_ROLLBACK = "Transaction.Rollback"; - String SPAN_NAME_BATCH_COMMIT = "Batch.Commit"; - String SPAN_NAME_TRANSACTION_COMMIT = "Transaction.Commit"; - String SPAN_NAME_PARTITION_QUERY = "PartitionQuery"; - String SPAN_NAME_BULK_WRITER_COMMIT = "BulkWriter.Commit"; String ATTRIBUTE_KEY_ATTEMPT = "attempt"; String ATTRIBUTE_KEY_DOC_COUNT = "doc_count"; String ATTRIBUTE_KEY_IS_TRANSACTIONAL = "transactional"; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java index 3d385e344..105cfe03a 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java @@ -16,24 +16,24 @@ package com.google.cloud.firestore.it; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_BATCH_COMMIT; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_CREATE; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_DELETE; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_GET; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_SET; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_UPDATE; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_PARTITION_QUERY; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_QUERY_GET; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_BEGIN; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_COMMIT; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK; -import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_RUN; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_BATCH_COMMIT; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_CREATE; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_DELETE; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_GET; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_SET; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_PARTITION_QUERY; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_QUERY_GET; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_BEGIN; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_ROLLBACK; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_RUN; import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -559,18 +559,18 @@ public void traceContainerTest() throws Exception { // Contains exact path assertTrue( traceCont.containsCallStack( - rootSpanName, SPAN_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME))); + rootSpanName, METHOD_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME))); // Top-level mismatch - assertFalse(traceCont.containsCallStack(SPAN_NAME_QUERY_GET, RUN_QUERY_RPC_NAME)); + assertFalse(traceCont.containsCallStack(METHOD_NAME_QUERY_GET, RUN_QUERY_RPC_NAME)); // Mid-level match - assertFalse(traceCont.containsCallStack(rootSpanName, SPAN_NAME_QUERY_GET)); + assertFalse(traceCont.containsCallStack(rootSpanName, METHOD_NAME_QUERY_GET)); // Leaf-level mismatch/missing assertFalse( traceCont.containsCallStack( - rootSpanName, SPAN_NAME_QUERY_GET, RUN_AGGREGATION_QUERY_RPC_NAME)); + rootSpanName, METHOD_NAME_QUERY_GET, RUN_AGGREGATION_QUERY_RPC_NAME)); } @Test @@ -621,7 +621,7 @@ public void bulkWriterCommitTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_BULK_WRITER_COMMIT, + METHOD_NAME_BULK_WRITER_COMMIT, grpcSpanName(BATCH_WRITE_RPC_NAME)); } @@ -643,8 +643,8 @@ public void partitionQueryTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_PARTITION_QUERY, - grpcSpanName(SPAN_NAME_PARTITION_QUERY)); + METHOD_NAME_PARTITION_QUERY, + grpcSpanName(METHOD_NAME_PARTITION_QUERY)); } @Test @@ -664,7 +664,7 @@ public void collectionListDocumentsTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_COL_REF_LIST_DOCUMENTS, + METHOD_NAME_COL_REF_LIST_DOCUMENTS, grpcSpanName(LIST_DOCUMENTS_RPC_NAME)); } @@ -685,8 +685,8 @@ public void docRefCreateTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_CREATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_CREATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -707,8 +707,8 @@ public void docRefCreate2TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_CREATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_CREATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -729,8 +729,8 @@ public void docRefSetTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_SET, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_SET, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -755,8 +755,8 @@ public void docRefSet2TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_SET, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_SET, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -777,8 +777,8 @@ public void docRefSet3TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_SET, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_SET, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -799,8 +799,8 @@ public void docRefSet4TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_SET, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_SET, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -825,8 +825,8 @@ public void docRefUpdateTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_UPDATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_UPDATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -851,8 +851,8 @@ public void docRefUpdate2TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_UPDATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_UPDATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -873,8 +873,8 @@ public void docRefUpdate3TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_UPDATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_UPDATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -899,8 +899,8 @@ public void docRefUpdate4TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_UPDATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_UPDATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -925,8 +925,8 @@ public void docRefUpdate5TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_UPDATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_UPDATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -951,8 +951,8 @@ public void docRefUpdate6TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_UPDATE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_UPDATE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -973,8 +973,8 @@ public void docRefDeleteTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_DELETE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_DELETE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -995,8 +995,8 @@ public void docRefDelete2TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_DELETE, - SPAN_NAME_BATCH_COMMIT, + METHOD_NAME_DOC_REF_DELETE, + METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @@ -1017,7 +1017,7 @@ public void docRefGetTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_GET, + METHOD_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)); } @@ -1038,7 +1038,7 @@ public void docRefGet2TraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_GET, + METHOD_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)); } @@ -1059,7 +1059,7 @@ public void docListCollectionsTraceTest() throws Exception { // Read and validate traces fetchAndValidateTrace( customSpanContext.getTraceId(), - SPAN_NAME_DOC_REF_LIST_COLLECTIONS, + METHOD_NAME_DOC_REF_LIST_COLLECTIONS, grpcSpanName(LIST_COLLECTIONS_RPC_NAME)); } @@ -1099,7 +1099,7 @@ public void queryGetTraceTest() throws Exception { waitForTracesToComplete(); fetchAndValidateTrace( - customSpanContext.getTraceId(), SPAN_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME)); + customSpanContext.getTraceId(), METHOD_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME)); } @Test @@ -1144,28 +1144,28 @@ public void transactionTraceTest() throws Exception { /*numExpectedSpans=*/ 11, Arrays.asList( Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_BEGIN, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_BEGIN, grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)), Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_BEGIN, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_BEGIN, grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)), Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_GET_QUERY, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_GET_QUERY, grpcSpanName(RUN_QUERY_RPC_NAME)), Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, grpcSpanName(RUN_AGGREGATION_QUERY_RPC_NAME)), Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_GET_DOCUMENTS, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_GET_DOCUMENTS, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)), Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_COMMIT, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_COMMIT, grpcSpanName(COMMIT_RPC_NAME)))); } @@ -1199,12 +1199,12 @@ public void transactionRollbackTraceTest() throws Exception { /*numExpectedSpans=*/ 5, Arrays.asList( Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_BEGIN, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_BEGIN, grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)), Arrays.asList( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_ROLLBACK, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_ROLLBACK, grpcSpanName(ROLLBACK_RPC_NAME)))); } @@ -1228,6 +1228,6 @@ public void writeBatchTraceTest() throws Exception { waitForTracesToComplete(); fetchAndValidateTrace( - customSpanContext.getTraceId(), SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + customSpanContext.getTraceId(), METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java index 46e8294e1..5844d7353 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java @@ -365,8 +365,8 @@ public void aggregateQueryGet() throws Exception { List spans = inMemorySpanExporter.getFinishedSpanItems(); buildSpanMaps(spans); assertEquals(2, spans.size()); - SpanData getSpan = getSpanByName(SPAN_NAME_AGGREGATION_QUERY_GET); - SpanData grpcSpan = getGrpcSpanByName(SPAN_NAME_RUN_AGGREGATION_QUERY); + SpanData getSpan = getSpanByName(METHOD_NAME_AGGREGATION_QUERY_GET); + SpanData grpcSpan = getGrpcSpanByName(METHOD_NAME_RUN_AGGREGATION_QUERY); assertNotNull(getSpan); assertNotNull(grpcSpan); assertEquals(grpcSpan.getParentSpanId(), getSpan.getSpanId()); @@ -394,7 +394,7 @@ public void bulkWriterCommit() throws Exception { List spans = prepareSpans(); assertEquals(2, spans.size()); - assertSpanHierarchy(SPAN_NAME_BULK_WRITER_COMMIT, grpcSpanName(BATCH_WRITE_RPC_NAME)); + assertSpanHierarchy(METHOD_NAME_BULK_WRITER_COMMIT, grpcSpanName(BATCH_WRITE_RPC_NAME)); } @Test @@ -404,7 +404,7 @@ public void partitionQuery() throws Exception { List spans = prepareSpans(); assertEquals(2, spans.size()); - assertSpanHierarchy(SPAN_NAME_PARTITION_QUERY, grpcSpanName(SPAN_NAME_PARTITION_QUERY)); + assertSpanHierarchy(METHOD_NAME_PARTITION_QUERY, grpcSpanName(METHOD_NAME_PARTITION_QUERY)); } @Test @@ -413,7 +413,7 @@ public void collectionListDocuments() throws Exception { List spans = prepareSpans(); assertEquals(2, spans.size()); - assertSpanHierarchy(SPAN_NAME_COL_REF_LIST_DOCUMENTS, grpcSpanName(LIST_DOCUMENTS_RPC_NAME)); + assertSpanHierarchy(METHOD_NAME_COL_REF_LIST_DOCUMENTS, grpcSpanName(LIST_DOCUMENTS_RPC_NAME)); } @Test @@ -423,7 +423,7 @@ public void docRefCreate() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_CREATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_CREATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -433,7 +433,7 @@ public void docRefCreate2() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_CREATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_CREATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -443,7 +443,7 @@ public void docRefSet() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -457,7 +457,7 @@ public void docRefSet2() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -467,7 +467,7 @@ public void docRefSet3() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -477,7 +477,7 @@ public void docRefSet4() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -491,7 +491,7 @@ public void docRefUpdate() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -505,7 +505,7 @@ public void docRefUpdate2() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -515,7 +515,7 @@ public void docRefUpdate3() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -529,7 +529,7 @@ public void docRefUpdate4() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -543,7 +543,7 @@ public void docRefUpdate5() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -557,7 +557,7 @@ public void docRefUpdate6() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -567,7 +567,7 @@ public void docRefDelete() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_DELETE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_DELETE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -577,7 +577,7 @@ public void docRefDelete2() throws Exception { List spans = prepareSpans(); assertEquals(3, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_DELETE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_DOC_REF_DELETE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); } @Test @@ -586,7 +586,7 @@ public void docRefGet() throws Exception { List spans = prepareSpans(); assertEquals(2, spans.size()); - assertSpanHierarchy(SPAN_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)); + assertSpanHierarchy(METHOD_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)); } @Test @@ -595,7 +595,7 @@ public void docRefGet2() throws Exception { List spans = prepareSpans(); assertEquals(2, spans.size()); - assertSpanHierarchy(SPAN_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)); + assertSpanHierarchy(METHOD_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)); } @Test @@ -605,7 +605,7 @@ public void docListCollections() throws Exception { List spans = prepareSpans(); assertEquals(2, spans.size()); assertSpanHierarchy( - SPAN_NAME_DOC_REF_LIST_COLLECTIONS, grpcSpanName(LIST_COLLECTIONS_RPC_NAME)); + METHOD_NAME_DOC_REF_LIST_COLLECTIONS, grpcSpanName(LIST_COLLECTIONS_RPC_NAME)); } @Test @@ -630,8 +630,8 @@ public void queryGet() throws Exception { firestore.collection("col").whereEqualTo("foo", "my_non_existent_value").get().get(); List spans = prepareSpans(); assertEquals(2, spans.size()); - assertSpanHierarchy(SPAN_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME)); - SpanData span = getSpanByName(SPAN_NAME_QUERY_GET); + assertSpanHierarchy(METHOD_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME)); + SpanData span = getSpanByName(METHOD_NAME_QUERY_GET); assertTrue( hasEvent( span, @@ -687,32 +687,33 @@ public void transaction() throws Exception { List spans = prepareSpans(); assertEquals(11, spans.size()); assertSpanHierarchy( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_BEGIN, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_BEGIN, grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)); assertSpanHierarchy( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_GET_QUERY, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_GET_QUERY, grpcSpanName(RUN_QUERY_RPC_NAME)); assertSpanHierarchy( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, grpcSpanName(RUN_AGGREGATION_QUERY_RPC_NAME)); assertSpanHierarchy( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_GET_DOCUMENTS, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_GET_DOCUMENTS, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)); assertSpanHierarchy( - SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + METHOD_NAME_TRANSACTION_RUN, METHOD_NAME_TRANSACTION_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); - Attributes commitAttributes = getSpanByName(SPAN_NAME_TRANSACTION_COMMIT).getAttributes(); + Attributes commitAttributes = getSpanByName(METHOD_NAME_TRANSACTION_COMMIT).getAttributes(); assertEquals( 2L, commitAttributes .get(AttributeKey.longKey("gcp.firestore." + ATTRIBUTE_KEY_DOC_COUNT)) .longValue()); - Attributes runTransactionAttributes = getSpanByName(SPAN_NAME_TRANSACTION_RUN).getAttributes(); + Attributes runTransactionAttributes = + getSpanByName(METHOD_NAME_TRANSACTION_RUN).getAttributes(); assertEquals( 5L, runTransactionAttributes @@ -754,13 +755,15 @@ public void transactionRollback() throws Exception { List spans = prepareSpans(); assertEquals(5, spans.size()); assertSpanHierarchy( - SPAN_NAME_TRANSACTION_RUN, - SPAN_NAME_TRANSACTION_BEGIN, + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_BEGIN, grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)); assertSpanHierarchy( - SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_ROLLBACK, grpcSpanName(ROLLBACK_RPC_NAME)); + METHOD_NAME_TRANSACTION_RUN, + METHOD_NAME_TRANSACTION_ROLLBACK, + grpcSpanName(ROLLBACK_RPC_NAME)); - SpanData runTransactionSpanData = getSpanByName(SPAN_NAME_TRANSACTION_RUN); + SpanData runTransactionSpanData = getSpanByName(METHOD_NAME_TRANSACTION_RUN); assertEquals(StatusCode.ERROR, runTransactionSpanData.getStatus().getStatusCode()); assertEquals(1, runTransactionSpanData.getEvents().size()); assertEquals( @@ -797,16 +800,16 @@ public void writeBatch() throws Exception { List spans = prepareSpans(); assertEquals(2, spans.size()); - assertSpanHierarchy(SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); + assertSpanHierarchy(METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME)); assertEquals( false, - getSpanByName(SPAN_NAME_BATCH_COMMIT) + getSpanByName(METHOD_NAME_BATCH_COMMIT) .getAttributes() .get(AttributeKey.booleanKey("gcp.firestore." + ATTRIBUTE_KEY_IS_TRANSACTIONAL)) .booleanValue()); assertEquals( 3L, - getSpanByName(SPAN_NAME_BATCH_COMMIT) + getSpanByName(METHOD_NAME_BATCH_COMMIT) .getAttributes() .get(AttributeKey.longKey("gcp.firestore." + ATTRIBUTE_KEY_DOC_COUNT)) .longValue()); From ac424a60609da1ce72f39b406a132e6728bf5298 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:58:01 -0400 Subject: [PATCH 026/103] re-format code --- .../FirestoreOpenTelemetryOptions.java | 1 + .../telemetry/BuiltinMetricsConstants.java | 200 ------------------ .../telemetry/BuiltinMetricsProvider.java | 69 +++--- .../telemetry/EnabledMetricsUtil.java | 86 ++++---- .../firestore/telemetry/MetricsUtil.java | 22 +- .../telemetry/TelemetryConstants.java | 59 ++++++ .../firestore/telemetry/TelemetryHelper.java | 87 ++++++++ .../cloud/firestore/it/ITTracingTest.java | 1 + 8 files changed, 241 insertions(+), 284 deletions(-) delete mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryHelper.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index c72e5a1f1..8db5acec1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -101,6 +101,7 @@ public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEn * * @param metricsEnabled Whether client side metrics should be enabled. */ + // TODO: change this to public when the feature is ready @Nonnull private FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEnabled) { this.metricsEnabled = metricsEnabled; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java deleted file mode 100644 index 72a5442c0..000000000 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.firestore.telemetry; - -import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.sdk.metrics.InstrumentSelector; -import io.opentelemetry.sdk.metrics.View; -import java.lang.management.ManagementFactory; -import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -public class BuiltinMetricsConstants { - - public static final String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw"; - - // TODO: change to firestore.googleapis.com - public static final String METER_NAME = "custom.googleapis.com/internal/client"; - public static final String FIRESTORE_METER_NAME = "java_firestore"; - public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; - - // Monitored resource labels - static final String RESOURCE_KEY_RESOURCE_CONTAINER = "resource_container"; - static final String RESOURCE_KEY_LOCATION = "location"; - static final String RESOURCE_KEY_DATABASE_ID = "database_id"; - - // Metric attribute keys for labels - public static final AttributeKey METRIC_KEY_METHOD = AttributeKey.stringKey("method"); - public static final AttributeKey METRIC_KEY_STATUS = AttributeKey.stringKey("status"); - public static final AttributeKey METRIC_KEY_LIBRARY_NAME = - AttributeKey.stringKey("library_name"); - public static final AttributeKey METRIC_KEY_LIBRARY_VERSION = - AttributeKey.stringKey("library_version"); - public static final AttributeKey METRIC_KEY_CLIENT_UID = - AttributeKey.stringKey("client_uid"); - - // Metric - public static final String METRIC_NAME_OPERATION_LATENCY = "operation_latency"; - public static final String METRIC_NAME_OPERATION_COUNT = "operation_count"; - public static final String METRIC_NAME_ATTEMPT_LATENCY = "attempt_latency"; - public static final String METRIC_NAME_ATTEMPT_COUNT = "attempt_count"; - public static final String METRIC_NAME_FIRST_RESPONSE_LATENCY = "first_response_latency"; - public static final String METRIC_NAME_END_TO_END_LATENCY = "end_to_end_latency"; - public static final String METRIC_NAME_TRANSACTION_LATENCY = "transaction_latency"; - public static final String METRIC_NAME_TRANSACTION_ATTEMPT_COUNT = "transaction_attempt_count"; - - // TODO(metrics): an app should have one client_uid, and it is contant. should this be hold here? - private static String CLIENT_UID; - - public static final String MILLISECOND_UNIT = "ms"; - - public static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; - - static final Set FIRESTORE_RESOURCE_LABELS = - ImmutableSet.of( - RESOURCE_KEY_RESOURCE_CONTAINER, RESOURCE_KEY_LOCATION, RESOURCE_KEY_DATABASE_ID); - - public static final Set BUILTIN_METRICS = - ImmutableSet.of( - METRIC_NAME_OPERATION_LATENCY, - METRIC_NAME_ATTEMPT_LATENCY, - METRIC_NAME_OPERATION_COUNT, - METRIC_NAME_ATTEMPT_COUNT, - METRIC_NAME_FIRST_RESPONSE_LATENCY, - METRIC_NAME_END_TO_END_LATENCY, - METRIC_NAME_TRANSACTION_LATENCY, - METRIC_NAME_TRANSACTION_ATTEMPT_COUNT) - .stream() - .map(m -> METER_NAME + '/' + m) - .collect(Collectors.toSet()); - - public static void defineView( - ImmutableMap.Builder viewMap, - String id, - String meter, - Set attributes) { - InstrumentSelector selector = - InstrumentSelector.builder().setMeterName(meter).setName(METER_NAME + "/" + id).build(); - Set attributesFilter = - ImmutableSet.builder() - .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) - .build(); - View view = View.builder().setAttributeFilter(attributesFilter).build(); - - viewMap.put(selector, view); - } - - public static final Set COMMON_ATTRIBUTES = - ImmutableSet.of( - METRIC_KEY_CLIENT_UID, - METRIC_KEY_LIBRARY_NAME, - METRIC_KEY_LIBRARY_VERSION, - METRIC_KEY_STATUS); - - private static Set withAdditionalAttributes(Set attributes) { - return ImmutableSet.builder() - .addAll(COMMON_ATTRIBUTES) - .addAll(attributes) - .build(); - } - - public static Map getAllViews() { - ImmutableMap.Builder views = ImmutableMap.builder(); - - // Define views with COMMON_ATTRIBUTES and METHOD_KEY - defineView( - views, - METRIC_NAME_OPERATION_LATENCY, - GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); - defineView( - views, - METRIC_NAME_ATTEMPT_LATENCY, - GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); - defineView( - views, - METRIC_NAME_OPERATION_COUNT, - GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); - defineView( - views, - METRIC_NAME_ATTEMPT_COUNT, - GAX_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); - defineView( - views, - METRIC_NAME_FIRST_RESPONSE_LATENCY, - FIRESTORE_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); - defineView( - views, - METRIC_NAME_END_TO_END_LATENCY, - FIRESTORE_METER_NAME, - withAdditionalAttributes(ImmutableSet.of(METRIC_KEY_METHOD))); - - // Define views with only COMMON_ATTRIBUTES - defineView(views, METRIC_NAME_TRANSACTION_LATENCY, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); - defineView( - views, METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES); - - return views.build(); - } - - public static String getClientUid() { - if (CLIENT_UID == null) { - String identifier = UUID.randomUUID().toString(); - String pid = getProcessId(); - - try { - String hostname = InetAddress.getLocalHost().getHostName(); - CLIENT_UID = identifier + "@" + pid + "@" + hostname; - } catch (UnknownHostException e) { - CLIENT_UID = identifier + "@" + pid + "@localhost"; - } - } - return CLIENT_UID; - } - - private static String getProcessId() { - try { - // Check if Java 9+ and ProcessHandle class is available - Class processHandleClass = Class.forName("java.lang.ProcessHandle"); - Method currentMethod = processHandleClass.getMethod("current"); - Object processHandleInstance = currentMethod.invoke(null); - Method pidMethod = processHandleClass.getMethod("pid"); - long pid = (long) pidMethod.invoke(processHandleInstance); - return Long.toString(pid); - } catch (Exception e) { - // Fallback to Java 8 method - final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); - if (jvmName != null && jvmName.contains("@")) { - return jvmName.split("@")[0]; - } else { - return "unknown"; - } - } - } -} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 424c0bb70..2dab2d7fa 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -16,20 +16,17 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.FIRESTORE_METER_NAME; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METER_NAME; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_CLIENT_UID; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_LIBRARY_NAME; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_LIBRARY_VERSION; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_NAME_END_TO_END_LATENCY; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.MILLISECOND_UNIT; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.getClientUid; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METER_NAME; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_CLIENT_UID; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_LIBRARY_NAME; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_LIBRARY_VERSION; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.MetricsTracerFactory; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; -import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -40,55 +37,47 @@ public class BuiltinMetricsProvider { private OpenTelemetry openTelemetry; - private ApiTracerFactory otelApiTracerFactory; + private ApiTracerFactory apiTracerFactory; private Meter meter; private DoubleHistogram endToEndRequestLatency; private DoubleHistogram firstResponseLatency; - static final String FIRESTORE_LIBRARY_NAME = "java_firestore"; + private static final String MILLISECOND_UNIT = "ms"; + private static final String FIRESTORE_LIBRARY_NAME = "java_firestore"; public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { - this.openTelemetry = openTelemetry; - if (openTelemetry != null) { - OpenTelemetryMetricsRecorder recorder = - new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME); - - this.otelApiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); - registerMetrics(); + if (openTelemetry != null) { + configureGaxLayerMetrics(); + configureFirestoreLayerMetrics(); } } - private Map createStaticAttributes() { - Map staticAttributes = new HashMap<>(); - staticAttributes.put(METRIC_KEY_CLIENT_UID.toString(), getClientUid()); - staticAttributes.put(METRIC_KEY_LIBRARY_NAME.toString(), FIRESTORE_LIBRARY_NAME); - String pkgVersion = this.getClass().getPackage().getImplementationVersion(); - if (pkgVersion != null) { - staticAttributes.put(METRIC_KEY_LIBRARY_VERSION.toString(), pkgVersion); - } - return staticAttributes; + public ApiTracerFactory getApiTracerFactory() { + return this.apiTracerFactory; } - public ApiTracerFactory getOpenTelemetryApiTracerFactory() { - return this.otelApiTracerFactory; + void configureGaxLayerMetrics() { + OpenTelemetryMetricsRecorder recorder = + new OpenTelemetryMetricsRecorder(openTelemetry, METRIC_PREFIX); + this.apiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); } - void registerMetrics() { + void configureFirestoreLayerMetrics() { this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); this.endToEndRequestLatency = meter - .histogramBuilder(METER_NAME + "/" + METRIC_NAME_END_TO_END_LATENCY) + .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_END_TO_END_LATENCY) .setDescription("Firestore E2E metrics") .setUnit(MILLISECOND_UNIT) .build(); this.firstResponseLatency = meter - .histogramBuilder(METER_NAME + "/" + METRIC_NAME_FIRST_RESPONSE_LATENCY) + .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_FIRST_RESPONSE_LATENCY) .setDescription("Firestore query first response latency") .setUnit(MILLISECOND_UNIT) .build(); @@ -109,8 +98,18 @@ public void firstResponseLatencyRecorder(double latency, Map att } } - @VisibleForTesting - Attributes toOtelAttributes(Map attributes) { + private Map createStaticAttributes() { + Map staticAttributes = new HashMap<>(); + staticAttributes.put(METRIC_KEY_CLIENT_UID.toString(), TelemetryHelper.getClientUid()); + staticAttributes.put(METRIC_KEY_LIBRARY_NAME.toString(), FIRESTORE_LIBRARY_NAME); + String pkgVersion = this.getClass().getPackage().getImplementationVersion(); + if (pkgVersion != null) { + staticAttributes.put(METRIC_KEY_LIBRARY_VERSION.toString(), pkgVersion); + } + return staticAttributes; + } + + private Attributes toOtelAttributes(Map attributes) { AttributesBuilder attributesBuilder = Attributes.builder(); attributes.forEach(attributesBuilder::put); return attributesBuilder.build(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 38a0d5072..dd5e8ab83 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -16,9 +16,8 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_METHOD; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.METRIC_KEY_STATUS; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.getAllViews; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_METHOD; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_STATUS; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; @@ -46,34 +45,31 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; class EnabledMetricsUtil implements MetricsUtil { - - private BuiltinMetricsProvider defaultOpenTelemetryMetricsProvider; - private BuiltinMetricsProvider customOpenTelemetryMetricsProvider; + private BuiltinMetricsProvider defaultMetricsProvider; + private BuiltinMetricsProvider customMetricsProvider; private static final Logger logger = Logger.getLogger(EnabledMetricsUtil.class.getName()); EnabledMetricsUtil(FirestoreOptions firestoreOptions) { try { - this.defaultOpenTelemetryMetricsProvider = - new BuiltinMetricsProvider( - getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId())); + OpenTelemetry defaultOpenTelemetry = + getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId()); + this.defaultMetricsProvider = new BuiltinMetricsProvider(defaultOpenTelemetry); OpenTelemetry customOpenTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); if (customOpenTelemetry == null) { customOpenTelemetry = GlobalOpenTelemetry.get(); } - this.customOpenTelemetryMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry); + this.customMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry); } catch (IOException e) { - logger.log( - Level.WARNING, - "Unable to get create MetricsUtil object for client side metrics, will skip exporting client side metrics", - e); + logger.warning( + "Unable to get create MetricsUtil object for client side metrics, will skip exporting client side metrics" + + e); } } @@ -81,24 +77,32 @@ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws I SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); // Filter out attributes that are not defined - for (Map.Entry entry : getAllViews().entrySet()) { + for (Map.Entry entry : TelemetryHelper.getAllViews().entrySet()) { sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); } - // MonitoredResourceDescription monitoredResourceMapping = - // new MonitoredResourceDescription(FIRESTORE_RESOURCE_TYPE, FIRESTORE_RESOURCE_LABELS); - MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder() .setProjectId(projectId) // Ignore library info as it is collected by the metric attributes as well .setInstrumentationLibraryLabelsEnabled(false) - // TODO(metrics): enable the configuration below when backend is ready - // .setMonitoredResourceDescription(monitoredResourceMapping) - // .setUseServiceTimeSeries(true) .build()); + // TODO: utlize the new features on GoogleCloudMetricExporter when backend is ready + // MonitoredResourceDescription monitoredResourceMapping = + // new MonitoredResourceDescription(FIRESTORE_RESOURCE_TYPE, FIRESTORE_RESOURCE_LABELS); + + // MetricExporter metricExporter = + // GoogleCloudMetricExporter.createWithConfiguration( + // MetricConfiguration.builder() + // .setProjectId(projectId) + // // Ignore library info as it is collected by the metric attributes as well + // .setInstrumentationLibraryLabelsEnabled(false) + // .setMonitoredResourceDescription(monitoredResourceMapping) + // .setUseServiceTimeSeries(true) + // .build()); + sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); } @@ -110,16 +114,15 @@ public MetricsContext createMetricsContext(String methodName) { @Override public void addMetricsTracerFactory(List apiTracerFactories) { - ApiTracerFactory defaultMetricsTracerFactory = - defaultOpenTelemetryMetricsProvider.getOpenTelemetryApiTracerFactory(); - if (defaultMetricsTracerFactory != null) { - apiTracerFactories.add(defaultMetricsTracerFactory); - } + addTracerFactory(apiTracerFactories, defaultMetricsProvider); + addTracerFactory(apiTracerFactories, customMetricsProvider); + } - ApiTracerFactory customMetricsTracerFactory = - customOpenTelemetryMetricsProvider.getOpenTelemetryApiTracerFactory(); - if (customMetricsTracerFactory != null) { - apiTracerFactories.add(customMetricsTracerFactory); + private void addTracerFactory( + List apiTracerFactories, BuiltinMetricsProvider metricsProvider) { + ApiTracerFactory tracerFactory = metricsProvider.getApiTracerFactory(); + if (tracerFactory != null) { + apiTracerFactories.add(tracerFactory); } } @@ -159,22 +162,23 @@ public void recordEndToEndLatency(Throwable t) { private void recordEndToEndLatency(String status) { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = new HashMap<>(); - attributes.put(METRIC_KEY_METHOD.toString(), methodName); - attributes.put(METRIC_KEY_STATUS.toString(), status); - - defaultOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); - customOpenTelemetryMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + Map attributes = createAttributes(status); + defaultMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); } public void recordFirstResponseLatency() { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Map attributes = createAttributes(StatusCode.Code.OK.toString()); + defaultMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); + customMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); + } + + private Map createAttributes(String status) { Map attributes = new HashMap<>(); attributes.put(METRIC_KEY_METHOD.toString(), methodName); - attributes.put(METRIC_KEY_STATUS.toString(), StatusCode.Code.OK.toString()); - - defaultOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); - customOpenTelemetryMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); + attributes.put(METRIC_KEY_STATUS.toString(), status); + return attributes; } /** Function to extract the status of the error as a string */ diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index faee622c9..9ad4465a0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -16,13 +16,12 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.ENABLE_METRICS_ENV_VAR; - import com.google.api.core.ApiFuture; import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreOptions; import java.util.List; +import java.util.logging.Logger; import javax.annotation.Nonnull; /** @@ -32,18 +31,23 @@ */ @InternalApi public interface MetricsUtil { + // TODO: rename the env based on OTEL convention + static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_METRICS"; + + final Logger logger = Logger.getLogger(MetricsUtil.class.getName()); static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { - if (createEnabledInstance(firestoreOptions)) { + if (shouldCreateEnabledInstance(firestoreOptions)) { return new EnabledMetricsUtil(firestoreOptions); } else { return new DisabledMetricsUtil(); } } - static boolean createEnabledInstance(FirestoreOptions firestoreOptions) { + static boolean shouldCreateEnabledInstance(FirestoreOptions firestoreOptions) { // Start with the value from FirestoreOptions - boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); + boolean shouldCreateEnabledInstance = + firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); // Override based on the environment variable String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); @@ -51,16 +55,18 @@ static boolean createEnabledInstance(FirestoreOptions firestoreOptions) { switch (enableMetricsEnvVar.toLowerCase()) { case "true": case "on": - createEnabledInstance = true; + shouldCreateEnabledInstance = true; break; case "false": case "off": - createEnabledInstance = false; + shouldCreateEnabledInstance = false; break; + default: + logger.warning("Invalid value for ENABLE_METRICS_ENV_VAR: " + enableMetricsEnvVar); } } - return createEnabledInstance; + return shouldCreateEnabledInstance; } abstract MetricsContext createMetricsContext(String methodName); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index 64d51bd48..b9b5d100e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -16,6 +16,11 @@ package com.google.cloud.firestore.telemetry; +import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.common.collect.ImmutableSet; +import io.opentelemetry.api.common.AttributeKey; +import java.util.Set; + public interface TelemetryConstants { String METHOD_NAME_DOC_REF_CREATE = "DocumentReference.Create"; String METHOD_NAME_DOC_REF_SET = "DocumentReference.Set"; @@ -46,4 +51,58 @@ public interface TelemetryConstants { String METHOD_NAME_PARTITION_QUERY = "PartitionQuery"; String METHOD_NAME_BULK_WRITER_COMMIT = "BulkWriter.Commit"; String METHOD_NAME_RUN_TRANSACTION = "RunTransaction"; + + // OpenTelemetry built-in metrics constants + String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw"; + String METRIC_PREFIX = + "custom.googleapis.com/internal/client"; // TODO: change to firestore.googleapis.com + String FIRESTORE_METER_NAME = "java_firestore"; + String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; + + // Monitored resource keys for labels + String RESOURCE_KEY_RESOURCE_CONTAINER = "resource_container"; + String RESOURCE_KEY_LOCATION = "location"; + String RESOURCE_KEY_DATABASE_ID = "database_id"; + Set FIRESTORE_RESOURCE_LABELS = + ImmutableSet.of( + RESOURCE_KEY_RESOURCE_CONTAINER, RESOURCE_KEY_LOCATION, RESOURCE_KEY_DATABASE_ID); + + // Metric attribute keys for labels + AttributeKey METRIC_KEY_METHOD = AttributeKey.stringKey("method"); + AttributeKey METRIC_KEY_STATUS = AttributeKey.stringKey("status"); + AttributeKey METRIC_KEY_LIBRARY_NAME = AttributeKey.stringKey("library_name"); + AttributeKey METRIC_KEY_LIBRARY_VERSION = AttributeKey.stringKey("library_version"); + AttributeKey METRIC_KEY_CLIENT_UID = AttributeKey.stringKey("client_uid"); + Set COMMON_ATTRIBUTES = + ImmutableSet.of( + METRIC_KEY_CLIENT_UID, + METRIC_KEY_LIBRARY_NAME, + METRIC_KEY_LIBRARY_VERSION, + METRIC_KEY_STATUS, + METRIC_KEY_METHOD); + + // Metric + String METRIC_NAME_OPERATION_LATENCY = "operation_latency"; + String METRIC_NAME_OPERATION_COUNT = "operation_count"; + String METRIC_NAME_ATTEMPT_LATENCY = "attempt_latency"; + String METRIC_NAME_ATTEMPT_COUNT = "attempt_count"; + String METRIC_NAME_FIRST_RESPONSE_LATENCY = "first_response_latency"; + String METRIC_NAME_END_TO_END_LATENCY = "end_to_end_latency"; + String METRIC_NAME_TRANSACTION_LATENCY = "transaction_latency"; + String METRIC_NAME_TRANSACTION_ATTEMPT_COUNT = "transaction_attempt_count"; + + // Define views for GAX and Firestore meters + Set gaxMetrics = + ImmutableSet.of( + METRIC_NAME_OPERATION_LATENCY, + METRIC_NAME_ATTEMPT_LATENCY, + METRIC_NAME_OPERATION_COUNT, + METRIC_NAME_ATTEMPT_COUNT); + + Set firestoreMetrics = + ImmutableSet.of( + METRIC_NAME_FIRST_RESPONSE_LATENCY, + METRIC_NAME_END_TO_END_LATENCY, + METRIC_NAME_TRANSACTION_LATENCY, + METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryHelper.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryHelper.java new file mode 100644 index 000000000..bf4e4e82d --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryHelper.java @@ -0,0 +1,87 @@ +package com.google.cloud.firestore.telemetry; + +import static com.google.cloud.firestore.telemetry.TelemetryConstants.*; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.View; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public final class TelemetryHelper { + + private TelemetryHelper() {} // Private constructor to prevent instantiation + + private static String CLIENT_UID; + + public static Map getAllViews() { + ImmutableMap.Builder views = ImmutableMap.builder(); + gaxMetrics.forEach(metric -> defineView(views, metric, GAX_METER_NAME)); + firestoreMetrics.forEach(metric -> defineView(views, metric, FIRESTORE_METER_NAME)); + return views.build(); + } + + public static void defineView( + ImmutableMap.Builder viewMap, String id, String meter) { + InstrumentSelector selector = + InstrumentSelector.builder().setMeterName(meter).setName(METRIC_PREFIX + "/" + id).build(); + Set attributesFilter = + ImmutableSet.builder() + .addAll( + COMMON_ATTRIBUTES.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) + .build(); + View view = View.builder().setAttributeFilter(attributesFilter).build(); + + viewMap.put(selector, view); + } + + public static String getClientUid() { + if (CLIENT_UID == null) { + CLIENT_UID = generateClientUid(); + } + return CLIENT_UID; + } + + private static String generateClientUid() { + String identifier = UUID.randomUUID().toString(); + String pid = getProcessId(); + String hostname = getHostName(); + return String.format("%s@%s@%s", identifier, pid, hostname); + } + + private static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + return "localhost"; + } + } + + private static String getProcessId() { + try { + // Check if Java 9+ and ProcessHandle class is available + Class processHandleClass = Class.forName("java.lang.ProcessHandle"); + Method currentMethod = processHandleClass.getMethod("current"); + Object processHandleInstance = currentMethod.invoke(null); + Method pidMethod = processHandleClass.getMethod("pid"); + long pid = (long) pidMethod.invoke(processHandleInstance); + return Long.toString(pid); + } catch (Exception e) { + // Fallback to Java 8 method + final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + if (jvmName != null && jvmName.contains("@")) { + return jvmName.split("@")[0]; + } else { + return "unknown"; + } + } + } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java index 5844d7353..46fbc9f17 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java @@ -16,6 +16,7 @@ package com.google.cloud.firestore.it; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.*; import static com.google.cloud.firestore.telemetry.TraceUtil.*; import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; import static org.junit.Assert.assertEquals; From 52f428fa6529f38b6d5cdabe82b715249ab3f4bd Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:07:08 -0400 Subject: [PATCH 027/103] add documentations/comments --- .../clirr-ignored-differences.xml | 7 ++ .../cloud/firestore/FirestoreOptions.java | 4 + .../firestore/spi/v1/GrpcFirestoreRpc.java | 6 +- .../telemetry/BuiltinMetricsProvider.java | 63 +++++++++------- ...metryHelper.java => ClientIdentifier.java} | 57 ++++++-------- .../telemetry/DisabledMetricsUtil.java | 4 + .../telemetry/DisabledTraceUtil.java | 2 +- .../telemetry/EnabledMetricsUtil.java | 75 ++++++++++++++----- .../firestore/telemetry/MetricsUtil.java | 37 +++++++-- .../telemetry/TelemetryConstants.java | 16 ++-- 10 files changed, 178 insertions(+), 93 deletions(-) rename google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/{TelemetryHelper.java => ClientIdentifier.java} (52%) diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index fd6e9e5b0..06edc17b0 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -314,4 +314,11 @@ com.google.cloud.firestore.Query getQuery() + + + 6011 + com/google/cloud/firestore/telemetry/TraceUtil + SPAN_NAME_* + + diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index b892655b0..be935e815 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -123,6 +123,10 @@ private ApiTracerFactory createApiTracerFactory() { } // Add Metrics Tracer factory if built in metrics are enabled. metricsUtil.addMetricsTracerFactory(apiTracerFactories); + + if (apiTracerFactories.isEmpty()) { + return null; + } return new CompositeApiTracerFactory(apiTracerFactories); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java index 89873dd8f..9668fbeb0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java @@ -28,6 +28,7 @@ import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.TransportChannel; import com.google.api.gax.rpc.UnaryCallable; +import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.NoCredentials; import com.google.cloud.ServiceOptions; import com.google.cloud.firestore.FirestoreOptions; @@ -157,8 +158,9 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException { firestoreBuilder.batchGetDocumentsSettings().setRetrySettings(retrySettings); } - if (options.getApiTracerFactory() != null) { - firestoreBuilder.setTracerFactory(options.getApiTracerFactory()); + ApiTracerFactory apiTracerFactory = options.getApiTracerFactory(); + if (apiTracerFactory != null) { + firestoreBuilder.setTracerFactory(apiTracerFactory); } firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build()); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 2dab2d7fa..2d6e1850d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -32,26 +32,33 @@ import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterProvider; import java.util.HashMap; import java.util.Map; -public class BuiltinMetricsProvider { +/** + * A provider for built-in metrics. This class is responsible for storing OpenTelemetry metrics + * configuration and recording built-in metrics for the Firestore SDK. + */ +class BuiltinMetricsProvider { private OpenTelemetry openTelemetry; - private ApiTracerFactory apiTracerFactory; - private Meter meter; private DoubleHistogram endToEndRequestLatency; private DoubleHistogram firstResponseLatency; + private ApiTracerFactory apiTracerFactory; + private final Map staticAttributes; + private static final String MILLISECOND_UNIT = "ms"; private static final String FIRESTORE_LIBRARY_NAME = "java_firestore"; public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; + this.staticAttributes = createStaticAttributes(); - if (openTelemetry != null) { - configureGaxLayerMetrics(); - configureFirestoreLayerMetrics(); + if (openTelemetry != null && openTelemetry.getMeterProvider() != MeterProvider.noop()) { + configureRPCLayerMetrics(); + configureSDKLayerMetrics(); } } @@ -59,13 +66,31 @@ public ApiTracerFactory getApiTracerFactory() { return this.apiTracerFactory; } - void configureGaxLayerMetrics() { + public void endToEndRequestLatencyRecorder(double latency, Map attributes) { + recordLatency(endToEndRequestLatency, latency, attributes); + } + + public void firstResponseLatencyRecorder(double latency, Map attributes) { + recordLatency(firstResponseLatency, latency, attributes); + } + + private void recordLatency( + DoubleHistogram latencyHistogram, double latency, Map attributes) { + if (latencyHistogram != null) { + attributes.putAll(staticAttributes); + latencyHistogram.record(latency, toOtelAttributes(attributes)); + } + } + + /** Creates an ApiTracerFactory to be passed into GAX library and collect RPC layer metrics. */ + private void configureRPCLayerMetrics() { OpenTelemetryMetricsRecorder recorder = new OpenTelemetryMetricsRecorder(openTelemetry, METRIC_PREFIX); - this.apiTracerFactory = new MetricsTracerFactory(recorder, createStaticAttributes()); + this.apiTracerFactory = new MetricsTracerFactory(recorder, staticAttributes); } - void configureFirestoreLayerMetrics() { + /** Registers metrics to be collected at the Firestore SDK layer */ + private void configureSDKLayerMetrics() { this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); this.endToEndRequestLatency = @@ -84,27 +109,13 @@ void configureFirestoreLayerMetrics() { // TODO(metrics): add transaction latency and retry count metrics } - public void endToEndRequestLatencyRecorder(double latency, Map attributes) { - if (endToEndRequestLatency != null) { - attributes.putAll(createStaticAttributes()); - endToEndRequestLatency.record(latency, toOtelAttributes(attributes)); - } - } - - public void firstResponseLatencyRecorder(double latency, Map attributes) { - if (firstResponseLatency != null) { - attributes.putAll(createStaticAttributes()); - firstResponseLatency.record(latency, toOtelAttributes(attributes)); - } - } - private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); - staticAttributes.put(METRIC_KEY_CLIENT_UID.toString(), TelemetryHelper.getClientUid()); - staticAttributes.put(METRIC_KEY_LIBRARY_NAME.toString(), FIRESTORE_LIBRARY_NAME); + staticAttributes.put(METRIC_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); + staticAttributes.put(METRIC_KEY_LIBRARY_NAME.getKey(), FIRESTORE_LIBRARY_NAME); String pkgVersion = this.getClass().getPackage().getImplementationVersion(); if (pkgVersion != null) { - staticAttributes.put(METRIC_KEY_LIBRARY_VERSION.toString(), pkgVersion); + staticAttributes.put(METRIC_KEY_LIBRARY_VERSION.getKey(), pkgVersion); } return staticAttributes; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryHelper.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java similarity index 52% rename from google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryHelper.java rename to google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java index bf4e4e82d..02d9f1c13 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryHelper.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java @@ -1,48 +1,35 @@ -package com.google.cloud.firestore.telemetry; +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import static com.google.cloud.firestore.telemetry.TelemetryConstants.*; +package com.google.cloud.firestore.telemetry; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.sdk.metrics.InstrumentSelector; -import io.opentelemetry.sdk.metrics.View; import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.Map; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; -public final class TelemetryHelper { +/** A utility class for retrieving a unique client identifier (CLIENT_UID) */ +final class ClientIdentifier { - private TelemetryHelper() {} // Private constructor to prevent instantiation + private ClientIdentifier() {} private static String CLIENT_UID; - public static Map getAllViews() { - ImmutableMap.Builder views = ImmutableMap.builder(); - gaxMetrics.forEach(metric -> defineView(views, metric, GAX_METER_NAME)); - firestoreMetrics.forEach(metric -> defineView(views, metric, FIRESTORE_METER_NAME)); - return views.build(); - } - - public static void defineView( - ImmutableMap.Builder viewMap, String id, String meter) { - InstrumentSelector selector = - InstrumentSelector.builder().setMeterName(meter).setName(METRIC_PREFIX + "/" + id).build(); - Set attributesFilter = - ImmutableSet.builder() - .addAll( - COMMON_ATTRIBUTES.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) - .build(); - View view = View.builder().setAttributeFilter(attributesFilter).build(); - - viewMap.put(selector, view); - } - + /** Gets the unique identifier for the client. */ public static String getClientUid() { if (CLIENT_UID == null) { CLIENT_UID = generateClientUid(); @@ -50,6 +37,10 @@ public static String getClientUid() { return CLIENT_UID; } + /** + * Generates a unique identifier for the client that is composed of a random UUID, the process ID, + * and the hostname of the machine. + */ private static String generateClientUid() { String identifier = UUID.randomUUID().toString(); String pid = getProcessId(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index 7a669806f..9c7e4d40a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -20,6 +20,10 @@ import com.google.api.gax.tracing.ApiTracerFactory; import java.util.List; +/** + * A fully disabled (No-op) MetricsUtil class that does not perform any metrics collection actions + * and has near-zero overhead. + */ class DisabledMetricsUtil implements MetricsUtil { class MetricsContext implements MetricsUtil.MetricsContext { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java index c2b47d536..5115ac7b7 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java @@ -24,7 +24,7 @@ import javax.annotation.Nullable; /** - * A fully disabled (No-op) tracing utility class that does not perform any tracing actions and has + * A no-op implementation of {@link MetricsUtil} that does not collect or export any metrics and has * near-zero overhead. */ public class DisabledTraceUtil implements TraceUtil { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index dd5e8ab83..613b819bd 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -16,8 +16,14 @@ package com.google.cloud.firestore.telemetry; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.COMMON_ATTRIBUTES; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METER_NAME; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METRICS; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METER_NAME; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METRICS; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_METHOD; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_STATUS; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; @@ -29,10 +35,13 @@ import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; import com.google.cloud.opentelemetry.metric.MetricConfiguration; import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.MoreExecutors; import io.grpc.Status; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -44,10 +53,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.annotation.Nullable; +/** + * An implementation of {@link MetricsUtil} that uses OpenTelemetry to collect and export metrics. + * `FirestoreOpenTelemetryOptions` in `FirestoreOptions` can be used to configure its behavior. + */ class EnabledMetricsUtil implements MetricsUtil { private BuiltinMetricsProvider defaultMetricsProvider; private BuiltinMetricsProvider customMetricsProvider; @@ -73,11 +88,26 @@ class EnabledMetricsUtil implements MetricsUtil { } } + @Override + public MetricsContext createMetricsContext(String methodName) { + return new MetricsContext(methodName); + } + + @Override + public void addMetricsTracerFactory(List apiTracerFactories) { + addTracerFactory(apiTracerFactories, defaultMetricsProvider); + addTracerFactory(apiTracerFactories, customMetricsProvider); + } + + /** + * Creates a default {@link OpenTelemetry} instance to collect and export built-in client side + * metrics to Google Cloud Monitoring. + */ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws IOException { SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); // Filter out attributes that are not defined - for (Map.Entry entry : TelemetryHelper.getAllViews().entrySet()) { + for (Map.Entry entry : getAllViews().entrySet()) { sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); } @@ -107,15 +137,25 @@ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws I return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); } - @Override - public MetricsContext createMetricsContext(String methodName) { - return new MetricsContext(methodName); + private static Map getAllViews() { + ImmutableMap.Builder views = ImmutableMap.builder(); + GAX_METRICS.forEach(metric -> defineView(views, metric, GAX_METER_NAME)); + FIRESTORE_METRICS.forEach(metric -> defineView(views, metric, FIRESTORE_METER_NAME)); + return views.build(); } - @Override - public void addMetricsTracerFactory(List apiTracerFactories) { - addTracerFactory(apiTracerFactories, defaultMetricsProvider); - addTracerFactory(apiTracerFactories, customMetricsProvider); + private static void defineView( + ImmutableMap.Builder viewMap, String id, String meter) { + InstrumentSelector selector = + InstrumentSelector.builder().setMeterName(meter).setName(METRIC_PREFIX + "/" + id).build(); + Set attributesFilter = + ImmutableSet.builder() + .addAll( + COMMON_ATTRIBUTES.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) + .build(); + View view = View.builder().setAttributeFilter(attributesFilter).build(); + + viewMap.put(selector, view); } private void addTracerFactory( @@ -160,13 +200,6 @@ public void recordEndToEndLatency(Throwable t) { recordEndToEndLatency(extractErrorStatus(t)); } - private void recordEndToEndLatency(String status) { - double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(status); - defaultMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); - } - public void recordFirstResponseLatency() { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); Map attributes = createAttributes(StatusCode.Code.OK.toString()); @@ -174,14 +207,20 @@ public void recordFirstResponseLatency() { customMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); } + private void recordEndToEndLatency(String status) { + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Map attributes = createAttributes(status); + defaultMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + } + private Map createAttributes(String status) { Map attributes = new HashMap<>(); - attributes.put(METRIC_KEY_METHOD.toString(), methodName); - attributes.put(METRIC_KEY_STATUS.toString(), status); + attributes.put(METRIC_KEY_METHOD.getKey(), methodName); + attributes.put(METRIC_KEY_STATUS.getKey(), status); return attributes; } - /** Function to extract the status of the error as a string */ private String extractErrorStatus(@Nullable Throwable throwable) { if (!(throwable instanceof FirestoreException)) { return StatusCode.Code.UNKNOWN.toString(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 9ad4465a0..56aeb88c9 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -25,21 +25,30 @@ import javax.annotation.Nonnull; /** - * A utility interface for trace collection. Classes that implement this interface may make their - * own design choices for how they approach trace collection. For instance, they may be no-op, or - * they may use a particular tracing framework such as OpenTelemetry. + * A utility interface for metrics collection. Classes that implement this interface may make their + * own design choices for how they approach metrics collection. For instance, they may be no-op, or + * they may use a particular metrics framework such as OpenTelemetry. */ @InternalApi public interface MetricsUtil { - // TODO: rename the env based on OTEL convention - static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_METRICS"; - final Logger logger = Logger.getLogger(MetricsUtil.class.getName()); + static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_METRICS"; + + /** + * Creates an instance of {@code MetricsUtil}. If the environment variable + * `FIRESTORE_ENABLE_METRICS` is set to false or off, an instance of {@link DisabledMetricsUtil} + * will be returned. Otherwise, an instance of {@link EnabledMetricsUtil} will be returned. + * + * @param firestoreOptions The Firestore options that configures client side metrics. + * @return An instance of {@code MetricsUtil}. + */ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { if (shouldCreateEnabledInstance(firestoreOptions)) { + logger.info("Client side metrics is enabled"); return new EnabledMetricsUtil(firestoreOptions); } else { + logger.info("Client side metrics is disabled"); return new DisabledMetricsUtil(); } } @@ -69,21 +78,35 @@ static boolean shouldCreateEnabledInstance(FirestoreOptions firestoreOptions) { return shouldCreateEnabledInstance; } + /** + * Creates a new {@code MetricsContext} for the given method and starts timing. + * + * @param methodName The name of the method. + * @return A new {@code MetricsContext}. + */ abstract MetricsContext createMetricsContext(String methodName); + /** + * Adds a metrics tracer factory to the given list of API tracer factories. + * + * @param apiTracerFactories The list of API tracer factories. + */ abstract void addMetricsTracerFactory(List apiTracerFactories); + /** A context for recording metrics. */ interface MetricsContext { /** - * If an operation ends in the future, its relevant metrics should be recorded _after_ the + * If the operation ends in the future, its relevant metrics should be recorded _after_ the * future has been completed. This method "appends" the metrics recording code at the completion * of the given future. */ void recordEndToEndLatencyAtFuture(ApiFuture futureValue); + /** Records end-to-end latency for the current operation. */ void recordEndToEndLatency(); + /** Records end-to-end latency for the current operation, which ended with a throwable. */ void recordEndToEndLatency(Throwable t); /** Records first response latency for the current operation. */ diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index b9b5d100e..2de4fb739 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -16,12 +16,16 @@ package com.google.cloud.firestore.telemetry; +import com.google.api.core.InternalApi; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.common.collect.ImmutableSet; import io.opentelemetry.api.common.AttributeKey; import java.util.Set; +/** Constants used for telemetry in the Firestore SDK. */ +@InternalApi public interface TelemetryConstants { + // Method names for Firestore operations String METHOD_NAME_DOC_REF_CREATE = "DocumentReference.Create"; String METHOD_NAME_DOC_REF_SET = "DocumentReference.Set"; String METHOD_NAME_DOC_REF_UPDATE = "DocumentReference.Update"; @@ -54,8 +58,8 @@ public interface TelemetryConstants { // OpenTelemetry built-in metrics constants String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw"; - String METRIC_PREFIX = - "custom.googleapis.com/internal/client"; // TODO: change to firestore.googleapis.com + // TODO: change to firestore.googleapis.com + String METRIC_PREFIX = "custom.googleapis.com/internal/client"; String FIRESTORE_METER_NAME = "java_firestore"; String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; @@ -81,7 +85,7 @@ public interface TelemetryConstants { METRIC_KEY_STATUS, METRIC_KEY_METHOD); - // Metric + // Metric names String METRIC_NAME_OPERATION_LATENCY = "operation_latency"; String METRIC_NAME_OPERATION_COUNT = "operation_count"; String METRIC_NAME_ATTEMPT_LATENCY = "attempt_latency"; @@ -91,15 +95,15 @@ public interface TelemetryConstants { String METRIC_NAME_TRANSACTION_LATENCY = "transaction_latency"; String METRIC_NAME_TRANSACTION_ATTEMPT_COUNT = "transaction_attempt_count"; - // Define views for GAX and Firestore meters - Set gaxMetrics = + // Metrics collected on GAX and Firestore SDK layer + Set GAX_METRICS = ImmutableSet.of( METRIC_NAME_OPERATION_LATENCY, METRIC_NAME_ATTEMPT_LATENCY, METRIC_NAME_OPERATION_COUNT, METRIC_NAME_ATTEMPT_COUNT); - Set firestoreMetrics = + Set FIRESTORE_METRICS = ImmutableSet.of( METRIC_NAME_FIRST_RESPONSE_LATENCY, METRIC_NAME_END_TO_END_LATENCY, From c06b2cbacfac98cd15c0f97746202c9259f3ef44 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:18:12 -0400 Subject: [PATCH 028/103] remove logger.info --- .../java/com/google/cloud/firestore/telemetry/MetricsUtil.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 56aeb88c9..3826d3caf 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -44,11 +44,10 @@ public interface MetricsUtil { * @return An instance of {@code MetricsUtil}. */ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { + // TODO(metrics): add debug level logging if possible if (shouldCreateEnabledInstance(firestoreOptions)) { - logger.info("Client side metrics is enabled"); return new EnabledMetricsUtil(firestoreOptions); } else { - logger.info("Client side metrics is disabled"); return new DisabledMetricsUtil(); } } From daf9fbe6e4f14952fd10742cb391431389660f8b Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:57:38 -0400 Subject: [PATCH 029/103] record AggregationQuery firstResponseLatency --- .../cloud/firestore/AggregateQuery.java | 37 +++++++++---------- .../google/cloud/firestore/FirestoreImpl.java | 5 ++- .../cloud/firestore/StreamableQuery.java | 6 +-- .../telemetry/TelemetryConstants.java | 12 ++++-- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 032ce8572..d7f00b9dc 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -107,11 +107,6 @@ public ApiFuture> explain(ExplainOptions TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET); - MetricsContext metricsContext = - getMetricsUtil() - .createMetricsContext( - TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN_AGGREGATION_QUERY); - try (Scope ignored = span.makeCurrent()) { AggregateQueryExplainResponseDeliverer responseDeliverer = new AggregateQueryExplainResponseDeliverer( @@ -121,14 +116,10 @@ public ApiFuture> explain(ExplainOptions /* explainOptions= */ options); runQuery(responseDeliverer, /* attempt */ 0); ApiFuture> result = responseDeliverer.getFuture(); - span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } @@ -143,13 +134,6 @@ ApiFuture get( ? TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY); - MetricsContext metricsContext = - getMetricsUtil() - .createMetricsContext( - transactionId == null - ? TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET - : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY); - try (Scope ignored = span.makeCurrent()) { AggregateQueryResponseDeliverer responseDeliverer = new AggregateQueryResponseDeliverer( @@ -159,11 +143,9 @@ ApiFuture get( runQuery(responseDeliverer, /* attempt= */ 0); ApiFuture result = responseDeliverer.getFuture(); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); throw error; } } @@ -313,10 +295,21 @@ private final class AggregateQueryResponseObserver @Nullable private Map aggregateFieldsMap = null; @Nullable private ExplainMetrics metrics = null; private int attempt; + private boolean firstResponse = false; + private MetricsContext metricsContext; AggregateQueryResponseObserver(ResponseDeliverer responseDeliverer, int attempt) { this.responseDeliverer = responseDeliverer; this.attempt = attempt; + + String method = + responseDeliverer.getTransactionId() != null + ? TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL + : responseDeliverer.getExplainOptions() != null + ? TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN + : TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_GET; + + this.metricsContext = getMetricsUtil().createMetricsContext(method); } Map getAttemptAttributes() { @@ -336,6 +329,11 @@ public void onStart(StreamController streamController) { @Override public void onResponse(RunAggregationQueryResponse response) { + if (!firstResponse) { + firstResponse = true; + metricsContext.recordFirstResponseLatency(); + } + getTraceUtil() .currentSpan() .addEvent( @@ -370,7 +368,6 @@ public void onError(Throwable throwable) { .addEvent( METHOD_NAME_RUN_AGGREGATION_QUERY + ": Retryable Error", Collections.singletonMap("error.message", throwable.getMessage())); - runQuery(responseDeliverer, attempt + 1); } else { getTraceUtil() @@ -379,6 +376,7 @@ public void onError(Throwable throwable) { METHOD_NAME_RUN_AGGREGATION_QUERY + ": Error", Collections.singletonMap("error.message", throwable.getMessage())); responseDeliverer.deliverError(throwable); + metricsContext.recordEndToEndLatency(throwable); } } @@ -402,6 +400,7 @@ private boolean shouldRetry(Throwable throwable) { @Override public void onComplete() { responseDeliverer.deliverResult(aggregateFieldsMap, readTime, metrics); + metricsContext.recordEndToEndLatency(); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index cc51d87f9..5207c476b 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -232,7 +232,10 @@ void getAll( MetricsContext metricsContext = getOptions() .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS); + .createMetricsContext( + transactionId == null + ? TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_GET_ALL + : TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_TRANSACTIONAL); ResponseObserver responseObserver = new ResponseObserver() { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java index 15938e63c..50c446da1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java @@ -266,10 +266,10 @@ protected void internalStream( String method = transactionId != null - ? TelemetryConstants.METHOD_NAME_RUN_QUERY_TRANSACTION + ? TelemetryConstants.METHOD_NAME_RUN_QUERY_TRANSACTIONAL : explainOptions != null - ? TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN_QUERY - : TelemetryConstants.METHOD_NAME_RUN_QUERY_QUERY; + ? TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN + : TelemetryConstants.METHOD_NAME_RUN_QUERY_GET; MetricsContext metricsContext = getFirestore().getOptions().getMetricsUtil().createMetricsContext(method); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index 2de4fb739..f744557a2 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -37,12 +37,16 @@ public interface TelemetryConstants { String METHOD_NAME_QUERY_GET = "Query.Get"; String METHOD_NAME_AGGREGATION_QUERY_GET = "AggregationQuery.Get"; String METHOD_NAME_RUN_QUERY = "RunQuery"; - String METHOD_NAME_RUN_QUERY_TRANSACTION = "RunQuery.Transaction"; - String METHOD_NAME_RUN_QUERY_EXPLAIN_QUERY = "RunQuery.ExplainQuery"; - String METHOD_NAME_RUN_QUERY_EXPLAIN_AGGREGATION_QUERY = "RunQuery.ExplainAggregationQuery"; - String METHOD_NAME_RUN_QUERY_QUERY = "RunQuery.Query"; + String METHOD_NAME_RUN_QUERY_EXPLAIN = "RunQuery.Explain"; + String METHOD_NAME_RUN_QUERY_GET = "RunQuery.Get"; + String METHOD_NAME_RUN_QUERY_TRANSACTIONAL = "RunQuery.Transactional"; String METHOD_NAME_RUN_AGGREGATION_QUERY = "RunAggregationQuery"; + String METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN = "RunAggregationQuery.Explain"; + String METHOD_NAME_RUN_AGGREGATION_QUERY_GET = "RunAggregationQuery.Get"; + String METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL = "RunAggregationQuery.Transactional"; String METHOD_NAME_BATCH_GET_DOCUMENTS = "BatchGetDocuments"; + String METHOD_NAME_BATCH_GET_DOCUMENTS_GET_ALL = "BatchGetDocuments.GetAll"; + String METHOD_NAME_BATCH_GET_DOCUMENTS_TRANSACTIONAL = "BatchGetDocuments.Transactional"; String METHOD_NAME_TRANSACTION_RUN = "Transaction.Run"; String METHOD_NAME_TRANSACTION_BEGIN = "Transaction.Begin"; String METHOD_NAME_TRANSACTION_GET_QUERY = "Transaction.Get.Query"; From 1999ff71fd77b7edd113d58ccbeb96e965c97f76 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:39:38 -0400 Subject: [PATCH 030/103] Update BuiltinMetricsProvider.java --- .../cloud/firestore/telemetry/BuiltinMetricsProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 2d6e1850d..2661f7ff1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -96,14 +96,14 @@ private void configureSDKLayerMetrics() { this.endToEndRequestLatency = meter .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_END_TO_END_LATENCY) - .setDescription("Firestore E2E metrics") + .setDescription("Firestore operations' end-to-end latency") .setUnit(MILLISECOND_UNIT) .build(); this.firstResponseLatency = meter .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_FIRST_RESPONSE_LATENCY) - .setDescription("Firestore query first response latency") + .setDescription("Firestore streaming operations' first response latency") .setUnit(MILLISECOND_UNIT) .build(); // TODO(metrics): add transaction latency and retry count metrics From 6aa816b038d728a5de80d0f8c93fc1c629ee5192 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:13:00 -0400 Subject: [PATCH 031/103] add 2 new metrics --- .../telemetry/BuiltinMetricsProvider.java | 76 +++++++++++++------ 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 2d6e1850d..a66efa870 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -22,6 +22,8 @@ import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_LIBRARY_VERSION; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX; import com.google.api.gax.tracing.ApiTracerFactory; @@ -31,6 +33,7 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterProvider; import java.util.HashMap; @@ -45,11 +48,14 @@ class BuiltinMetricsProvider { private Meter meter; private DoubleHistogram endToEndRequestLatency; private DoubleHistogram firstResponseLatency; + private DoubleHistogram transactionLatency; + private LongCounter transactionAttemptCount; private ApiTracerFactory apiTracerFactory; private final Map staticAttributes; private static final String MILLISECOND_UNIT = "ms"; + private static final String INTEGER_UNIT = "1"; private static final String FIRESTORE_LIBRARY_NAME = "java_firestore"; public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { @@ -62,26 +68,6 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { } } - public ApiTracerFactory getApiTracerFactory() { - return this.apiTracerFactory; - } - - public void endToEndRequestLatencyRecorder(double latency, Map attributes) { - recordLatency(endToEndRequestLatency, latency, attributes); - } - - public void firstResponseLatencyRecorder(double latency, Map attributes) { - recordLatency(firstResponseLatency, latency, attributes); - } - - private void recordLatency( - DoubleHistogram latencyHistogram, double latency, Map attributes) { - if (latencyHistogram != null) { - attributes.putAll(staticAttributes); - latencyHistogram.record(latency, toOtelAttributes(attributes)); - } - } - /** Creates an ApiTracerFactory to be passed into GAX library and collect RPC layer metrics. */ private void configureRPCLayerMetrics() { OpenTelemetryMetricsRecorder recorder = @@ -96,17 +82,61 @@ private void configureSDKLayerMetrics() { this.endToEndRequestLatency = meter .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_END_TO_END_LATENCY) - .setDescription("Firestore E2E metrics") + .setDescription("Firestore operations' end-to-end latency") .setUnit(MILLISECOND_UNIT) .build(); this.firstResponseLatency = meter .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_FIRST_RESPONSE_LATENCY) - .setDescription("Firestore query first response latency") + .setDescription("Firestore streaming operations' first response latency") .setUnit(MILLISECOND_UNIT) .build(); - // TODO(metrics): add transaction latency and retry count metrics + + this.transactionLatency = + meter + .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_TRANSACTION_LATENCY) + .setDescription("Firestore transactions' end-to-end latency") + .setUnit(MILLISECOND_UNIT) + .build(); + + this.transactionAttemptCount = + meter + .counterBuilder(METRIC_PREFIX + "/" + METRIC_NAME_TRANSACTION_ATTEMPT_COUNT) + .setDescription("Number of Firestore transaction attempts including retries") + .setUnit(INTEGER_UNIT) + .build(); + } + + public ApiTracerFactory getApiTracerFactory() { + return this.apiTracerFactory; + } + + public void endToEndRequestLatencyRecorder(double latency, Map attributes) { + recordLatency(endToEndRequestLatency, latency, attributes); + } + + public void firstResponseLatencyRecorder(double latency, Map attributes) { + recordLatency(firstResponseLatency, latency, attributes); + } + + public void transactionLatencyRecorder(double latency, Map attributes) { + recordLatency(transactionLatency, latency, attributes); + } + + private void recordLatency( + DoubleHistogram latencyHistogram, double latency, Map attributes) { + if (latencyHistogram != null) { + attributes.putAll(staticAttributes); + latencyHistogram.record(latency, toOtelAttributes(attributes)); + } + } + + public void transactionAttemptCountRecorder(long count, Map attributes) { + if (transactionAttemptCount != null) { + attributes.putAll(staticAttributes); + transactionAttemptCount.add(count, toOtelAttributes(attributes)); + } } private Map createStaticAttributes() { From f4e36d964f6e4296aff09e0c4fc92cf909e8b144 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:38:31 -0400 Subject: [PATCH 032/103] resolve comments --- .../cloud/firestore/CompositeTracer.java | 189 ------------------ .../FirestoreOpenTelemetryOptions.java | 23 --- .../cloud/firestore/FirestoreOptions.java | 12 +- .../telemetry/BuiltinMetricsProvider.java | 14 +- .../firestore/telemetry/ClientIdentifier.java | 3 +- .../telemetry/CompositeApiTracer.java | 129 ++++++++++++ .../CompositeApiTracerFactory.java | 9 +- .../telemetry/EnabledMetricsUtil.java | 29 +-- .../firestore/telemetry/MetricsUtil.java | 17 +- .../telemetry/TelemetryConstants.java | 23 ++- 10 files changed, 177 insertions(+), 271 deletions(-) delete mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java rename google-cloud-firestore/src/main/java/com/google/cloud/firestore/{ => telemetry}/CompositeApiTracerFactory.java (82%) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java deleted file mode 100644 index 223f36bb9..000000000 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeTracer.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.firestore; - -import com.google.api.gax.tracing.ApiTracer; -import com.google.api.gax.tracing.BaseApiTracer; -import com.google.api.gax.tracing.MetricsTracer; -import com.google.common.collect.ImmutableList; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import org.threeten.bp.Duration; - -class CompositeTracer extends BaseApiTracer { - private final List children; - - public CompositeTracer(List children) { - this.children = ImmutableList.copyOf(children); - } - - @Override - public Scope inScope() { - final List childScopes = new ArrayList<>(children.size()); - - for (ApiTracer child : children) { - childScopes.add(child.inScope()); - } - - return new Scope() { - @Override - public void close() { - for (Scope childScope : childScopes) { - childScope.close(); - } - } - }; - } - - @Override - public void operationSucceeded() { - for (ApiTracer child : children) { - child.operationSucceeded(); - } - } - - @Override - public void operationCancelled() { - for (ApiTracer child : children) { - child.operationCancelled(); - } - } - - @Override - public void operationFailed(Throwable error) { - for (ApiTracer child : children) { - child.operationFailed(error); - } - } - - @Override - public void connectionSelected(String id) { - for (ApiTracer child : children) { - child.connectionSelected(id); - } - } - - @Override - public void attemptStarted(int attemptNumber) { - for (ApiTracer child : children) { - child.attemptStarted(null, attemptNumber); - } - } - - @Override - public void attemptStarted(Object request, int attemptNumber) { - for (ApiTracer child : children) { - child.attemptStarted(request, attemptNumber); - } - } - - @Override - public void attemptSucceeded() { - for (ApiTracer child : children) { - child.attemptSucceeded(); - } - } - - @Override - public void attemptCancelled() { - for (ApiTracer child : children) { - child.attemptCancelled(); - } - } - - @Override - public void attemptFailed(Throwable error, Duration delay) { - for (ApiTracer child : children) { - child.attemptFailed(error, delay); - } - } - - @Override - public void attemptFailedDuration(Throwable error, java.time.Duration delay) { - for (ApiTracer child : children) { - child.attemptFailedDuration(error, delay); - } - } - - @Override - public void attemptFailedRetriesExhausted(Throwable error) { - for (ApiTracer child : children) { - child.attemptFailedRetriesExhausted(error); - } - } - - @Override - public void attemptPermanentFailure(Throwable error) { - for (ApiTracer child : children) { - child.attemptPermanentFailure(error); - } - } - - @Override - public void lroStartFailed(Throwable error) { - for (ApiTracer child : children) { - child.lroStartFailed(error); - } - } - - @Override - public void lroStartSucceeded() { - for (ApiTracer child : children) { - child.lroStartSucceeded(); - } - } - - @Override - public void responseReceived() { - for (ApiTracer child : children) { - child.responseReceived(); - } - } - - @Override - public void requestSent() { - for (ApiTracer child : children) { - child.requestSent(); - } - } - - @Override - public void batchRequestSent(long elementCount, long requestSize) { - for (ApiTracer child : children) { - child.batchRequestSent(elementCount, requestSize); - } - } - - public void addAttributes(String key, String value) { - for (ApiTracer child : children) { - if (child instanceof MetricsTracer) { - MetricsTracer metricsTracer = (MetricsTracer) child; - metricsTracer.addAttributes(key, value); - } - } - } - - public void addAttributes(Map attributes) { - for (ApiTracer child : children) { - if (child instanceof MetricsTracer) { - MetricsTracer metricsTracer = (MetricsTracer) child; - attributes.forEach((key, value) -> metricsTracer.addAttributes(key, value)); - } - } - } -} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 8db5acec1..2b4606565 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -28,12 +28,10 @@ @BetaApi public class FirestoreOpenTelemetryOptions { private final boolean tracingEnabled; - private final boolean metricsEnabled; private final @Nullable OpenTelemetry openTelemetry; FirestoreOpenTelemetryOptions(Builder builder) { this.tracingEnabled = builder.tracingEnabled; - this.metricsEnabled = builder.metricsEnabled; this.openTelemetry = builder.openTelemetry; } @@ -41,10 +39,6 @@ public boolean isTracingEnabled() { return tracingEnabled; } - public boolean isMetricsEnabled() { - return metricsEnabled; - } - public OpenTelemetry getOpenTelemetry() { return openTelemetry; } @@ -63,20 +57,15 @@ public static class Builder { private boolean tracingEnabled; - private boolean metricsEnabled; - @Nullable private OpenTelemetry openTelemetry; private Builder() { tracingEnabled = false; - // TODO(metrics): set this to default on - metricsEnabled = false; openTelemetry = null; } private Builder(FirestoreOpenTelemetryOptions options) { this.tracingEnabled = options.tracingEnabled; - this.metricsEnabled = options.metricsEnabled; this.openTelemetry = options.openTelemetry; } @@ -96,18 +85,6 @@ public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEn return this; } - /** - * Sets whether client side metrics should be enabled. - * - * @param metricsEnabled Whether client side metrics should be enabled. - */ - // TODO: change this to public when the feature is ready - @Nonnull - private FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEnabled) { - this.metricsEnabled = metricsEnabled; - return this; - } - /** * Sets the {@link OpenTelemetry} to use with this Firestore instance. If telemetry collection * is enabled, but an `OpenTelemetry` is not provided, the Firestore SDK will attempt to use the diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index be935e815..b0887f602 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -31,6 +31,7 @@ import com.google.cloud.TransportOptions; import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.spi.v1.GrpcFirestoreRpc; +import com.google.cloud.firestore.telemetry.CompositeApiTracerFactory; import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.v1.FirestoreSettings; import com.google.cloud.grpc.GrpcTransportOptions; @@ -110,18 +111,15 @@ protected String getDefaultHost() { return FirestoreDefaults.INSTANCE.getHost(); } + @InternalApi @Override public ApiTracerFactory getApiTracerFactory() { - return createApiTracerFactory(); - } - - private ApiTracerFactory createApiTracerFactory() { List apiTracerFactories = new ArrayList<>(); // Prefer any direct ApiTracerFactory that might have been set on the builder. if (super.getApiTracerFactory() != null) { apiTracerFactories.add(super.getApiTracerFactory()); } - // Add Metrics Tracer factory if built in metrics are enabled. + // Add Metrics Tracer factory if built-in metrics are enabled. metricsUtil.addMetricsTracerFactory(apiTracerFactories); if (apiTracerFactories.isEmpty()) { @@ -152,7 +150,7 @@ com.google.cloud.firestore.telemetry.TraceUtil getTraceUtil() { } @Nonnull - public com.google.cloud.firestore.telemetry.MetricsUtil getMetricsUtil() { + com.google.cloud.firestore.telemetry.MetricsUtil getMetricsUtil() { return metricsUtil; } @@ -354,7 +352,7 @@ protected FirestoreOptions(Builder builder) { ? builder.databaseId : FirestoreDefaults.INSTANCE.getDatabaseId(); - // set metrics util after database ID is set + // Set up the `MetricsUtil` instance after the database ID has been set. this.metricsUtil = MetricsUtil.getInstance(this); if (builder.channelProvider == null) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 2661f7ff1..b07b2b03b 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -17,9 +17,9 @@ package com.google.cloud.firestore.telemetry; import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METER_NAME; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_CLIENT_UID; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_LIBRARY_NAME; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_LIBRARY_VERSION; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX; @@ -50,7 +50,7 @@ class BuiltinMetricsProvider { private final Map staticAttributes; private static final String MILLISECOND_UNIT = "ms"; - private static final String FIRESTORE_LIBRARY_NAME = "java_firestore"; + private static final String FIRESTORE_LIBRARY_NAME = "com.google.cloud.firestore"; public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -111,11 +111,11 @@ private void configureSDKLayerMetrics() { private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); - staticAttributes.put(METRIC_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); - staticAttributes.put(METRIC_KEY_LIBRARY_NAME.getKey(), FIRESTORE_LIBRARY_NAME); + staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); + staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), FIRESTORE_LIBRARY_NAME); String pkgVersion = this.getClass().getPackage().getImplementationVersion(); if (pkgVersion != null) { - staticAttributes.put(METRIC_KEY_LIBRARY_VERSION.getKey(), pkgVersion); + staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), pkgVersion); } return staticAttributes; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java index 02d9f1c13..dc4a3eae0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java @@ -19,7 +19,6 @@ import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.net.InetAddress; -import java.net.UnknownHostException; import java.util.UUID; /** A utility class for retrieving a unique client identifier (CLIENT_UID) */ @@ -51,7 +50,7 @@ private static String generateClientUid() { private static String getHostName() { try { return InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { + } catch (Exception e) { return "localhost"; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java new file mode 100644 index 000000000..31dbddbbf --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java @@ -0,0 +1,129 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +import com.google.api.gax.tracing.ApiTracer; +import com.google.api.gax.tracing.BaseApiTracer; +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.threeten.bp.Duration; + +/** Combines multiple {@link ApiTracer}s into a single {@link ApiTracer}. */ +class CompositeApiTracer extends BaseApiTracer { + private final List children; + + public CompositeApiTracer(List children) { + this.children = ImmutableList.copyOf(children); + } + + @Override + public Scope inScope() { + List childScopes = + children.stream() + .map(ApiTracer::inScope) + .collect(Collectors.toCollection(() -> new ArrayList<>(children.size()))); + + return () -> childScopes.forEach(Scope::close); + } + + @Override + public void operationSucceeded() { + children.forEach(ApiTracer::operationSucceeded); + } + + @Override + public void operationCancelled() { + children.forEach(ApiTracer::operationCancelled); + } + + @Override + public void operationFailed(Throwable error) { + children.forEach(child -> child.operationFailed(error)); + } + + @Override + public void connectionSelected(String id) { + children.forEach(child -> child.connectionSelected(id)); + } + + @Override + public void attemptStarted(int attemptNumber) { + children.forEach(child -> child.attemptStarted(null, attemptNumber)); + } + + @Override + public void attemptStarted(Object request, int attemptNumber) { + children.forEach(child -> child.attemptStarted(request, attemptNumber)); + } + + @Override + public void attemptSucceeded() { + children.forEach(ApiTracer::attemptSucceeded); + } + + @Override + public void attemptCancelled() { + children.forEach(ApiTracer::attemptCancelled); + } + + @Override + public void attemptFailed(Throwable error, Duration delay) { + children.forEach(child -> child.attemptFailed(error, delay)); + } + + @Override + public void attemptFailedDuration(Throwable error, java.time.Duration delay) { + children.forEach(child -> child.attemptFailedDuration(error, delay)); + } + + @Override + public void attemptFailedRetriesExhausted(Throwable error) { + children.forEach(child -> child.attemptFailedRetriesExhausted(error)); + } + + @Override + public void attemptPermanentFailure(Throwable error) { + children.forEach(child -> child.attemptPermanentFailure(error)); + } + + @Override + public void lroStartFailed(Throwable error) { + children.forEach(child -> child.lroStartFailed(error)); + } + + @Override + public void lroStartSucceeded() { + children.forEach(ApiTracer::lroStartSucceeded); + } + + @Override + public void responseReceived() { + children.forEach(ApiTracer::responseReceived); + } + + @Override + public void requestSent() { + children.forEach(ApiTracer::requestSent); + } + + @Override + public void batchRequestSent(long elementCount, long requestSize) { + children.forEach(child -> child.batchRequestSent(elementCount, requestSize)); + } +} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeApiTracerFactory.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.java similarity index 82% rename from google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeApiTracerFactory.java rename to google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.java index a30857e66..ddd0f226d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CompositeApiTracerFactory.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.google.cloud.firestore; +package com.google.cloud.firestore.telemetry; +import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.BaseApiTracerFactory; @@ -24,7 +25,9 @@ import java.util.ArrayList; import java.util.List; -class CompositeApiTracerFactory extends BaseApiTracerFactory { +/** Combines multiple {@link ApiTracerFactory} into a single {@link ApiTracerFactory}. */ +@InternalApi +public class CompositeApiTracerFactory extends BaseApiTracerFactory { private final List apiTracerFactories; @@ -39,6 +42,6 @@ public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType op for (ApiTracerFactory factory : apiTracerFactories) { children.add(factory.newTracer(parent, spanName, operationType)); } - return new CompositeTracer(children); + return new CompositeApiTracer(children); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 613b819bd..7ac7ecaab 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -21,8 +21,8 @@ import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METRICS; import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METER_NAME; import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METRICS; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_METHOD; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_KEY_STATUS; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX; import com.google.api.core.ApiFuture; @@ -83,7 +83,7 @@ class EnabledMetricsUtil implements MetricsUtil { this.customMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry); } catch (IOException e) { logger.warning( - "Unable to get create MetricsUtil object for client side metrics, will skip exporting client side metrics" + "Unable to create MetricsUtil object for client side metrics, will skip exporting client side metrics" + e); } } @@ -118,22 +118,8 @@ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws I // Ignore library info as it is collected by the metric attributes as well .setInstrumentationLibraryLabelsEnabled(false) .build()); - - // TODO: utlize the new features on GoogleCloudMetricExporter when backend is ready - // MonitoredResourceDescription monitoredResourceMapping = - // new MonitoredResourceDescription(FIRESTORE_RESOURCE_TYPE, FIRESTORE_RESOURCE_LABELS); - - // MetricExporter metricExporter = - // GoogleCloudMetricExporter.createWithConfiguration( - // MetricConfiguration.builder() - // .setProjectId(projectId) - // // Ignore library info as it is collected by the metric attributes as well - // .setInstrumentationLibraryLabelsEnabled(false) - // .setMonitoredResourceDescription(monitoredResourceMapping) - // .setUseServiceTimeSeries(true) - // .build()); - sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); + return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); } @@ -216,8 +202,8 @@ private void recordEndToEndLatency(String status) { private Map createAttributes(String status) { Map attributes = new HashMap<>(); - attributes.put(METRIC_KEY_METHOD.getKey(), methodName); - attributes.put(METRIC_KEY_STATUS.getKey(), status); + attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName); + attributes.put(METRIC_ATTRIBUTE_KEY_STATUS.getKey(), status); return attributes; } @@ -226,6 +212,9 @@ private String extractErrorStatus(@Nullable Throwable throwable) { return StatusCode.Code.UNKNOWN.toString(); } Status status = ((FirestoreException) throwable).getStatus(); + if (status == null) { + return StatusCode.Code.UNKNOWN.toString(); + } return status.getCode().name(); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 3826d3caf..c43c2db3a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -44,33 +44,32 @@ public interface MetricsUtil { * @return An instance of {@code MetricsUtil}. */ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { - // TODO(metrics): add debug level logging if possible - if (shouldCreateEnabledInstance(firestoreOptions)) { + if (shouldCreateEnabledInstance()) { return new EnabledMetricsUtil(firestoreOptions); } else { return new DisabledMetricsUtil(); } } - static boolean shouldCreateEnabledInstance(FirestoreOptions firestoreOptions) { - // Start with the value from FirestoreOptions - boolean shouldCreateEnabledInstance = - firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled(); + static boolean shouldCreateEnabledInstance() { + // Client side metrics feature is default on unless it is manually turned off by + // environment variables + // TODO(metrics): The feature is disabled before it is ready for general release. + boolean shouldCreateEnabledInstance = false; - // Override based on the environment variable String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { switch (enableMetricsEnvVar.toLowerCase()) { case "true": case "on": - shouldCreateEnabledInstance = true; + // The feature is default on. break; case "false": case "off": shouldCreateEnabledInstance = false; break; default: - logger.warning("Invalid value for ENABLE_METRICS_ENV_VAR: " + enableMetricsEnvVar); + logger.warning("Invalid value for FIRESTORE_ENABLE_METRICS: " + enableMetricsEnvVar); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index f744557a2..b9e69d8e1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -62,7 +62,7 @@ public interface TelemetryConstants { // OpenTelemetry built-in metrics constants String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw"; - // TODO: change to firestore.googleapis.com + // TODO(metrics): change to firestore.googleapis.com String METRIC_PREFIX = "custom.googleapis.com/internal/client"; String FIRESTORE_METER_NAME = "java_firestore"; String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; @@ -76,18 +76,19 @@ public interface TelemetryConstants { RESOURCE_KEY_RESOURCE_CONTAINER, RESOURCE_KEY_LOCATION, RESOURCE_KEY_DATABASE_ID); // Metric attribute keys for labels - AttributeKey METRIC_KEY_METHOD = AttributeKey.stringKey("method"); - AttributeKey METRIC_KEY_STATUS = AttributeKey.stringKey("status"); - AttributeKey METRIC_KEY_LIBRARY_NAME = AttributeKey.stringKey("library_name"); - AttributeKey METRIC_KEY_LIBRARY_VERSION = AttributeKey.stringKey("library_version"); - AttributeKey METRIC_KEY_CLIENT_UID = AttributeKey.stringKey("client_uid"); + AttributeKey METRIC_ATTRIBUTE_KEY_METHOD = AttributeKey.stringKey("method"); + AttributeKey METRIC_ATTRIBUTE_KEY_STATUS = AttributeKey.stringKey("status"); + AttributeKey METRIC_ATTRIBUTE_KEY_LIBRARY_NAME = AttributeKey.stringKey("library_name"); + AttributeKey METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION = + AttributeKey.stringKey("library_version"); + AttributeKey METRIC_ATTRIBUTE_KEY_CLIENT_UID = AttributeKey.stringKey("client_uid"); Set COMMON_ATTRIBUTES = ImmutableSet.of( - METRIC_KEY_CLIENT_UID, - METRIC_KEY_LIBRARY_NAME, - METRIC_KEY_LIBRARY_VERSION, - METRIC_KEY_STATUS, - METRIC_KEY_METHOD); + METRIC_ATTRIBUTE_KEY_CLIENT_UID, + METRIC_ATTRIBUTE_KEY_LIBRARY_NAME, + METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION, + METRIC_ATTRIBUTE_KEY_STATUS, + METRIC_ATTRIBUTE_KEY_METHOD); // Metric names String METRIC_NAME_OPERATION_LATENCY = "operation_latency"; From 05e8efbd5ac2ae805408fb0e2272425ab896a704 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:30:45 -0400 Subject: [PATCH 033/103] Update pom.xml --- google-cloud-firestore/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index fe13ddd6f..8e2739eb8 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -126,7 +126,6 @@ com.google.cloud.opentelemetry exporter-metrics - 0.31.0 From b0b1687643938281d3ca1935681d87e22beca539 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:56:28 -0400 Subject: [PATCH 034/103] remove duplicated tracing dependency --- google-cloud-firestore/pom.xml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 8e2739eb8..cf6d08a39 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -227,20 +227,6 @@ 2.52.0 test - - com.google.cloud.opentelemetry - exporter-trace - 0.33.0 - test - - - - - com.google.api.grpc - proto-google-cloud-trace-v1 - 2.52.0 - test - com.google.cloud google-cloud-trace From 4abbcf0bf936dcaf0c1c4591ba388119e6301db3 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:32:11 -0400 Subject: [PATCH 035/103] add opentelemetry-sdk-metrics dependency --- google-cloud-firestore/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index cf6d08a39..f9bb321fc 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -123,6 +123,10 @@ io.opentelemetry opentelemetry-sdk + + io.opentelemetry + opentelemetry-sdk-metrics + com.google.cloud.opentelemetry exporter-metrics From 328855d28b401bdcc32af4fe5a237dc9f6f49f94 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 18 Oct 2024 21:52:06 -0400 Subject: [PATCH 036/103] adjust end-to-end latency in streaming queries --- .../cloud/firestore/AggregateQuery.java | 90 +++++++++++-------- .../google/cloud/firestore/BulkWriter.java | 1 - .../cloud/firestore/CollectionGroup.java | 3 - .../cloud/firestore/CollectionReference.java | 4 - .../cloud/firestore/DocumentReference.java | 16 ---- .../com/google/cloud/firestore/Query.java | 23 ++++- .../cloud/firestore/StreamableQuery.java | 88 +++++++++++++----- 7 files changed, 138 insertions(+), 87 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index d7f00b9dc..60395c352 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -27,7 +27,6 @@ import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StreamController; import com.google.cloud.Timestamp; -import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; @@ -74,8 +73,8 @@ private TraceUtil getTraceUtil() { } @Nonnull - private MetricsUtil getMetricsUtil() { - return query.getFirestore().getOptions().getMetricsUtil(); + private MetricsContext createMetricsContext(String method) { + return query.getFirestore().getOptions().getMetricsUtil().createMetricsContext(method); } /** Returns the query whose aggregations will be calculated by this object. */ @@ -107,13 +106,17 @@ public ApiFuture> explain(ExplainOptions TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET); + MetricsContext metricsContext = + createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN); + try (Scope ignored = span.makeCurrent()) { AggregateQueryExplainResponseDeliverer responseDeliverer = new AggregateQueryExplainResponseDeliverer( /* transactionId= */ null, /* readTime= */ null, /* startTimeNanos= */ query.rpcContext.getClock().nanoTime(), - /* explainOptions= */ options); + /* explainOptions= */ options, + metricsContext); runQuery(responseDeliverer, /* attempt */ 0); ApiFuture> result = responseDeliverer.getFuture(); span.endAtFuture(result); @@ -134,12 +137,19 @@ ApiFuture get( ? TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY); + MetricsContext metricsContext = + createMetricsContext( + transactionId == null + ? TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_GET + : TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL); + try (Scope ignored = span.makeCurrent()) { AggregateQueryResponseDeliverer responseDeliverer = new AggregateQueryResponseDeliverer( transactionId, readTime, - /* startTimeNanos= */ query.rpcContext.getClock().nanoTime()); + /* startTimeNanos= */ query.rpcContext.getClock().nanoTime(), + metricsContext); runQuery(responseDeliverer, /* attempt= */ 0); ApiFuture result = responseDeliverer.getFuture(); span.endAtFuture(result); @@ -176,14 +186,17 @@ private abstract static class ResponseDeliverer { private final @Nullable com.google.protobuf.Timestamp readTime; private final long startTimeNanos; private final SettableApiFuture future = SettableApiFuture.create(); + private MetricsContext metricsContext; ResponseDeliverer( @Nullable ByteString transactionId, @Nullable com.google.protobuf.Timestamp readTime, - long startTimeNanos) { + long startTimeNanos, + MetricsContext metricsContext) { this.transactionId = transactionId; this.readTime = readTime; this.startTimeNanos = startTimeNanos; + this.metricsContext = metricsContext; } @Nullable @@ -209,15 +222,29 @@ ApiFuture getFuture() { return future; } - protected void setFuture(T value) { - future.set(value); + void deliverFirstResponse() { + metricsContext.recordFirstResponseLatency(); } void deliverError(Throwable throwable) { future.setException(throwable); + metricsContext.recordEndToEndLatency(throwable); } - abstract void deliverResult( + void deliverResult( + @Nullable Map serverData, + Timestamp readTime, + @Nullable ExplainMetrics metrics) { + try { + T result = processResult(serverData, readTime, metrics); + future.set(result); + metricsContext.recordEndToEndLatency(); + } catch (Exception error) { + deliverError(error); + } + } + + abstract T processResult( @Nullable Map serverData, Timestamp readTime, @Nullable ExplainMetrics metrics); @@ -227,24 +254,23 @@ private class AggregateQueryResponseDeliverer extends ResponseDeliverer serverData, Timestamp readTime, @Nullable ExplainMetrics metrics) { if (serverData == null) { - deliverError(new RuntimeException("Did not receive any aggregate query results.")); - return; + throw new RuntimeException("Did not receive any aggregate query results."); } - setFuture( - new AggregateQuerySnapshot( - AggregateQuery.this, - readTime, - convertServerAggregateFieldsMapToClientAggregateFieldsMap(serverData))); + return new AggregateQuerySnapshot( + AggregateQuery.this, + readTime, + convertServerAggregateFieldsMapToClientAggregateFieldsMap(serverData)); } } @@ -256,8 +282,9 @@ private final class AggregateQueryExplainResponseDeliverer @Nullable ByteString transactionId, @Nullable com.google.protobuf.Timestamp readTime, long startTimeNanos, - @Nullable ExplainOptions explainOptions) { - super(transactionId, readTime, startTimeNanos); + @Nullable ExplainOptions explainOptions, + MetricsContext metricsContext) { + super(transactionId, readTime, startTimeNanos, metricsContext); this.explainOptions = explainOptions; } @@ -268,14 +295,13 @@ ExplainOptions getExplainOptions() { } @Override - void deliverResult( + ExplainResults processResult( @Nullable Map serverData, Timestamp readTime, @Nullable ExplainMetrics metrics) { // The server is required to provide ExplainMetrics for explain queries. if (metrics == null) { - deliverError(new RuntimeException("Did not receive any metrics for explain query.")); - return; + throw new RuntimeException("Did not receive any metrics for explain query."); } AggregateQuerySnapshot snapshot = serverData == null @@ -284,7 +310,7 @@ void deliverResult( AggregateQuery.this, readTime, convertServerAggregateFieldsMapToClientAggregateFieldsMap(serverData)); - setFuture(new ExplainResults<>(metrics, snapshot)); + return new ExplainResults<>(metrics, snapshot); } } @@ -296,20 +322,10 @@ private final class AggregateQueryResponseObserver @Nullable private ExplainMetrics metrics = null; private int attempt; private boolean firstResponse = false; - private MetricsContext metricsContext; AggregateQueryResponseObserver(ResponseDeliverer responseDeliverer, int attempt) { this.responseDeliverer = responseDeliverer; this.attempt = attempt; - - String method = - responseDeliverer.getTransactionId() != null - ? TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL - : responseDeliverer.getExplainOptions() != null - ? TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN - : TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_GET; - - this.metricsContext = getMetricsUtil().createMetricsContext(method); } Map getAttemptAttributes() { @@ -331,7 +347,7 @@ public void onStart(StreamController streamController) { public void onResponse(RunAggregationQueryResponse response) { if (!firstResponse) { firstResponse = true; - metricsContext.recordFirstResponseLatency(); + responseDeliverer.deliverFirstResponse(); } getTraceUtil() @@ -376,7 +392,6 @@ public void onError(Throwable throwable) { METHOD_NAME_RUN_AGGREGATION_QUERY + ": Error", Collections.singletonMap("error.message", throwable.getMessage())); responseDeliverer.deliverError(throwable); - metricsContext.recordEndToEndLatency(throwable); } } @@ -400,7 +415,6 @@ private boolean shouldRetry(Throwable throwable) { @Override public void onComplete() { responseDeliverer.deliverResult(aggregateFieldsMap, readTime, metrics); - metricsContext.recordEndToEndLatency(); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java index 3d15aa41b..65cb946d2 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java @@ -936,7 +936,6 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) { bulkWriterExecutor); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - } catch (Exception error) { span.end(error); metricsContext.recordEndToEndLatency(error); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java index 33577ea32..182e41ef2 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java @@ -138,17 +138,14 @@ public ApiFuture> getPartitions(long desiredPartitionCount) MoreExecutors.directExecutor()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (ApiException exception) { span.end(exception); metricsContext.recordEndToEndLatency(exception); - throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); metricsContext.recordEndToEndLatency(throwable); - throw throwable; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java index 9271888f8..a2e8b3f55 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java @@ -185,17 +185,14 @@ public void remove() { }; span.end(); metricsContext.recordEndToEndLatency(); - return result; } catch (ApiException exception) { span.end(exception); metricsContext.recordEndToEndLatency(exception); - throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); metricsContext.recordEndToEndLatency(throwable); - throw throwable; } } @@ -233,7 +230,6 @@ public ApiFuture add(@Nonnull final Map field createFuture, writeResult -> documentReference, MoreExecutors.directExecutor()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index bfa4d27f6..25f1eadcd 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -166,7 +166,6 @@ public ApiFuture create(@Nonnull Map fields) { ApiFuture result = extractFirst(writeBatch.create(this, fields).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -193,7 +192,6 @@ public ApiFuture create(@Nonnull Object pojo) { ApiFuture result = extractFirst(writeBatch.create(this, pojo).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -220,7 +218,6 @@ public ApiFuture set(@Nonnull Map fields) { ApiFuture result = extractFirst(writeBatch.set(this, fields).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -250,7 +247,6 @@ public ApiFuture set( ApiFuture result = extractFirst(writeBatch.set(this, fields, options).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -277,7 +273,6 @@ public ApiFuture set(@Nonnull Object pojo) { ApiFuture result = extractFirst(writeBatch.set(this, pojo).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -306,7 +301,6 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti ApiFuture result = extractFirst(writeBatch.set(this, pojo, options).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -333,7 +327,6 @@ public ApiFuture update(@Nonnull Map fields) { ApiFuture result = extractFirst(writeBatch.update(this, fields).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -362,7 +355,6 @@ public ApiFuture update(@Nonnull Map fields, Precon extractFirst(writeBatch.update(this, fields, options).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -393,7 +385,6 @@ public ApiFuture update( extractFirst(writeBatch.update(this, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -424,7 +415,6 @@ public ApiFuture update( extractFirst(writeBatch.update(this, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -460,7 +450,6 @@ public ApiFuture update( writeBatch.update(this, options, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -496,7 +485,6 @@ public ApiFuture update( writeBatch.update(this, options, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -522,7 +510,6 @@ public ApiFuture delete(@Nonnull Precondition options) { ApiFuture result = extractFirst(writeBatch.delete(this, options).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -547,7 +534,6 @@ public ApiFuture delete() { ApiFuture result = extractFirst(writeBatch.delete(this).commit()); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -573,7 +559,6 @@ public ApiFuture get() { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); @@ -601,7 +586,6 @@ public ApiFuture get(FieldMask fieldMask) { extractFirst(rpcContext.getFirestore().getAll(new DocumentReference[] {this}, fieldMask)); span.endAtFuture(result); metricsContext.recordEndToEndLatencyAtFuture(result); - return result; } catch (Exception error) { span.end(error); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index e66393b84..f93ec2c29 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -35,6 +35,8 @@ import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.QueryOptions.Builder; import com.google.cloud.firestore.encoding.CustomClassMapper; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.firestore.bundle.BundledQuery; @@ -1487,7 +1489,10 @@ public void stream(@Nonnull final ApiStreamObserver responseOb "Query results for queries that include limitToLast() constraints cannot be streamed. " + "Use Query.get() instead."); - internalStream( + MetricsContext metricsContext = + createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_GET); + + ApiStreamObserver observer = new ApiStreamObserver() { @Override public void onNext(RunQueryResponse runQueryResponse) { @@ -1509,7 +1514,10 @@ public void onError(Throwable throwable) { public void onCompleted() { responseObserver.onCompleted(); } - }, + }; + + internalStream( + new MonitoredStreamResponseObserver(observer, metricsContext), /* startTimeNanos= */ rpcContext.getClock().nanoTime(), /* transactionId= */ null, /* readTime= */ null, @@ -1533,8 +1541,12 @@ public ApiFuture explainStream( "Query results for queries that include limitToLast() constraints cannot be streamed. " + "Use Query.explain() instead."); + MetricsContext metricsContext = + createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN); + final SettableApiFuture metricsFuture = SettableApiFuture.create(); - internalStream( + + ApiStreamObserver observer = new ApiStreamObserver() { @Override public void onNext(RunQueryResponse runQueryResponse) { @@ -1566,7 +1578,10 @@ public void onCompleted() { new RuntimeException("Did not receive any explain results.")); } } - }, + }; + + internalStream( + new MonitoredStreamResponseObserver(observer, metricsContext), /* startTimeNanos= */ rpcContext.getClock().nanoTime(), /* transactionId= */ null, /* readTime= */ null, diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java index 50c446da1..081567cee 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java @@ -82,6 +82,10 @@ abstract SnapshotType createSnaphot( public Firestore getFirestore() { return rpcContext.getFirestore(); } + + protected MetricsContext createMetricsContext(String methodName) { + return getFirestore().getOptions().getMetricsUtil().createMetricsContext(methodName); + } /** * Executes the query and returns the results as QuerySnapshot. * @@ -105,9 +109,17 @@ ApiFuture get( transactionId == null ? TelemetryConstants.METHOD_NAME_QUERY_GET : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY); + + MetricsContext metricsContext = + createMetricsContext( + transactionId != null + ? TelemetryConstants.METHOD_NAME_RUN_QUERY_TRANSACTIONAL + : TelemetryConstants.METHOD_NAME_RUN_QUERY_GET); + try (Scope ignored = span.makeCurrent()) { final SettableApiFuture result = SettableApiFuture.create(); - internalStream( + + ApiStreamObserver observer = new ApiStreamObserver() { final List documentSnapshots = new ArrayList<>(); Timestamp responseReadTime; @@ -142,7 +154,10 @@ public void onCompleted() { SnapshotType querySnapshot = createSnaphot(responseReadTime, resultView); result.set(querySnapshot); } - }, + }; + + internalStream( + new MonitoredStreamResponseObserver(observer, metricsContext), /* startTimeNanos= */ rpcContext.getClock().nanoTime(), transactionId, /* readTime= */ requestReadTime, @@ -153,6 +168,7 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } @@ -173,9 +189,13 @@ public ApiFuture> explain(ExplainOptions options) { .getTraceUtil() .startSpan(TelemetryConstants.METHOD_NAME_QUERY_GET); + MetricsContext metricsContext = + createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN); + try (Scope ignored = span.makeCurrent()) { final SettableApiFuture> result = SettableApiFuture.create(); - internalStream( + + ApiStreamObserver observer = new ApiStreamObserver() { @Nullable List documentSnapshots = null; Timestamp readTime; @@ -228,7 +248,10 @@ public void onCompleted() { } result.set(new ExplainResults<>(metrics, snapshot)); } - }, + }; + + internalStream( + new MonitoredStreamResponseObserver(observer, metricsContext), /* startTimeNanos= */ rpcContext.getClock().nanoTime(), /* transactionId= */ null, /* readTime= */ null, @@ -239,12 +262,47 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); + metricsContext.recordEndToEndLatency(error); throw error; } } + class MonitoredStreamResponseObserver implements ApiStreamObserver { + private final ApiStreamObserver observer; + private final MetricsContext metricsContext; + private boolean receivedFirstResponse = false; + + // Constructor to initialize with the delegate and MetricsContext + public MonitoredStreamResponseObserver( + ApiStreamObserver observer, MetricsContext metricsContext) { + this.observer = observer; + this.metricsContext = metricsContext; + } + + @Override + public void onNext(RunQueryResponse value) { + if (!receivedFirstResponse) { + receivedFirstResponse = true; + metricsContext.recordFirstResponseLatency(); + } + observer.onNext(value); + } + + @Override + public void onError(Throwable t) { + metricsContext.recordEndToEndLatency(t); + observer.onError(t); + } + + @Override + public void onCompleted() { + metricsContext.recordEndToEndLatency(); + observer.onCompleted(); + } + } + protected void internalStream( - final ApiStreamObserver runQueryResponseObserver, + final MonitoredStreamResponseObserver streamResponseObserver, final long startTimeNanos, @Nullable final ByteString transactionId, @Nullable final Timestamp readTime, @@ -264,15 +322,6 @@ protected void internalStream( .put(ATTRIBUTE_KEY_IS_RETRY_WITH_CURSOR, isRetryRequestWithCursor) .build()); - String method = - transactionId != null - ? TelemetryConstants.METHOD_NAME_RUN_QUERY_TRANSACTIONAL - : explainOptions != null - ? TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN - : TelemetryConstants.METHOD_NAME_RUN_QUERY_GET; - MetricsContext metricsContext = - getFirestore().getOptions().getMetricsUtil().createMetricsContext(method); - final AtomicReference lastReceivedDocument = new AtomicReference<>(); ResponseObserver observer = @@ -293,10 +342,9 @@ public void onResponse(RunQueryResponse response) { if (!firstResponse) { firstResponse = true; currentSpan.addEvent(TelemetryConstants.METHOD_NAME_RUN_QUERY + ": First Response"); - metricsContext.recordFirstResponseLatency(); } - runQueryResponseObserver.onNext(response); + streamResponseObserver.onNext(response); if (response.hasDocument()) { numDocuments++; @@ -331,7 +379,7 @@ public void onError(Throwable throwable) { startAfter(cursor) .internalStream( - runQueryResponseObserver, + streamResponseObserver, startTimeNanos, /* transactionId= */ null, options.getRequireConsistency() ? cursor.getReadTime() : null, @@ -341,8 +389,7 @@ public void onError(Throwable throwable) { currentSpan.addEvent( TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Error", Collections.singletonMap("error.message", throwable.getMessage())); - metricsContext.recordEndToEndLatency(throwable); - runQueryResponseObserver.onError(throwable); + streamResponseObserver.onError(throwable); } } @@ -353,8 +400,7 @@ public void onComplete() { currentSpan.addEvent( TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Completed", Collections.singletonMap(ATTRIBUTE_KEY_DOC_COUNT, numDocuments)); - metricsContext.recordEndToEndLatency(); - runQueryResponseObserver.onCompleted(); + streamResponseObserver.onCompleted(); } boolean shouldRetry(DocumentSnapshot lastDocument, Throwable t) { From 5e1fb4b8aa86378814206e76ae5e1d1fc258ea3c Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:45:27 -0400 Subject: [PATCH 037/103] fix clirr error --- google-cloud-firestore/clirr-ignored-differences.xml | 8 ++++++++ .../java/com/google/cloud/firestore/FirestoreImpl.java | 6 ++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index 06edc17b0..8a10f3899 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -321,4 +321,12 @@ SPAN_NAME_* + + + 7005 + com/google/cloud/firestore/StreamableQuery + * internalStream(com.google.api.gax.rpc.ApiStreamObserver, long, com.google.protobuf.ByteString, com.google.cloud.Timestamp, com.google.cloud.firestore.ExplainOptions, boolean) + * + + diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index 5207c476b..93acd29ef 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -446,8 +446,8 @@ public ApiFuture runAsyncTransaction( getOptions() .getMetricsUtil() .createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_TRANSACTION); - ApiFuture result; + try { if (transactionOptions.getReadTime() != null) { // READ_ONLY transactions with readTime have no retry, nor transaction state, so we don't @@ -455,18 +455,16 @@ public ApiFuture runAsyncTransaction( result = updateFunction.updateCallback( new ReadTimeTransaction(this, transactionOptions.getReadTime())); - metricsContext.recordEndToEndLatencyAtFuture(result); } else { // For READ_ONLY transactions without readTime, there is still strong consistency applied, // that cannot be tracked client side. result = new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run(); - metricsContext.recordEndToEndLatencyAtFuture(result); } + metricsContext.recordEndToEndLatencyAtFuture(result); } catch (Exception error) { metricsContext.recordEndToEndLatency(error); throw error; } - return result; } From 47f6b3714d82c0505bb7c864f1d6edd5f9590f3d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:04:53 -0400 Subject: [PATCH 038/103] record serverSicdeTransaction --- google-cloud-firestore/pom.xml | 15 ++++++ .../ServerSideTransactionRunner.java | 17 ++++++- .../telemetry/DisabledMetricsUtil.java | 8 +++ .../telemetry/EnabledMetricsUtil.java | 51 +++++++++++++++++++ .../firestore/telemetry/MetricsUtil.java | 13 ++++- 5 files changed, 102 insertions(+), 2 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index f9bb321fc..b6beffd6a 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -134,6 +134,21 @@ + + + org.slf4j + slf4j-api + 2.0.7 + test + + + + org.slf4j + slf4j-simple + 2.0.7 + test + + junit junit diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index 2edd7e71e..a1f715fec 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -26,6 +26,7 @@ import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.rpc.ApiException; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -60,6 +61,7 @@ final class ServerSideTransactionRunner { private int attemptsRemaining; private Span runTransactionSpan; private TraceUtil.Context runTransactionContext; + private MetricsContext metricsContext; /** * @param firestore The active Firestore instance @@ -86,6 +88,8 @@ final class ServerSideTransactionRunner { new ExponentialRetryAlgorithm( firestore.getOptions().getRetrySettings(), CurrentMillisClock.getDefaultClock()); this.nextBackoffAttempt = backoffAlgorithm.createFirstAttempt(); + this.metricsContext = + firestore.getOptions().getMetricsUtil().createMetricsContext("ServerSideTransaction"); } @Nonnull @@ -93,7 +97,18 @@ private TraceUtil getTraceUtil() { return firestore.getOptions().getTraceUtil(); } + private int attemptsMade() { + return transactionOptions.getNumberOfAttempts() - this.attemptsRemaining; + } + ApiFuture run() { + ApiFuture result = runInternally(); + metricsContext.recordTransactionLatencyAtFuture(result); + metricsContext.recordTransactionAttemptsAtFuture(result, () -> this.attemptsMade()); + return result; + } + + ApiFuture runInternally() { runTransactionSpan = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN); runTransactionSpan.setAttribute( ATTRIBUTE_KEY_TRANSACTION_TYPE, transactionOptions.getType().name()); @@ -237,7 +252,7 @@ private ApiFuture restartTransactionCallback(Throwable throwable) { getTraceUtil() .currentSpan() .addEvent("Initiating transaction retry. Attempts remaining: " + attemptsRemaining); - return run(); + return runInternally(); } else { final FirestoreException firestoreException = FirestoreException.forApiException( diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index 9c7e4d40a..15f66a4d3 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -19,6 +19,7 @@ import com.google.api.core.ApiFuture; import com.google.api.gax.tracing.ApiTracerFactory; import java.util.List; +import java.util.function.Supplier; /** * A fully disabled (No-op) MetricsUtil class that does not perform any metrics collection actions @@ -39,6 +40,13 @@ public void recordEndToEndLatency(Throwable t) {} @Override public void recordFirstResponseLatency() {} + + @Override + public void recordTransactionLatencyAtFuture(ApiFuture futureValue) {} + + @Override + public void recordTransactionAttemptsAtFuture( + ApiFuture futureValue, Supplier attemptsSupplier) {} } @Override diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 7ac7ecaab..38f74b064 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -55,6 +55,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -200,6 +201,55 @@ private void recordEndToEndLatency(String status) { customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); } + public void recordTransactionLatencyAtFuture(ApiFuture futureValue) { + ApiFutures.addCallback( + futureValue, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + recordTransactionLatency(extractErrorStatus(t)); + } + + @Override + public void onSuccess(T result) { + + recordTransactionLatency(StatusCode.Code.OK.toString()); + } + }, + MoreExecutors.directExecutor()); + } + + private void recordTransactionLatency(String status) { + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Map attributes = createAttributes(status); + defaultMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes); + customMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes); + } + + public void recordTransactionAttemptsAtFuture( + ApiFuture futureValue, Supplier attemptsSupplier) { + ApiFutures.addCallback( + futureValue, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + recordTransactionAttempts(extractErrorStatus(t), attemptsSupplier.get()); + } + + @Override + public void onSuccess(T result) { + recordTransactionAttempts(StatusCode.Code.OK.toString(), attemptsSupplier.get()); + } + }, + MoreExecutors.directExecutor()); + } + + private void recordTransactionAttempts(String status, int attempts) { + Map attributes = createAttributes(status); + defaultMetricsProvider.transactionAttemptCountRecorder((long) attempts, attributes); + customMetricsProvider.transactionAttemptCountRecorder((long) attempts, attributes); + } + private Map createAttributes(String status) { Map attributes = new HashMap<>(); attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName); @@ -211,6 +261,7 @@ private String extractErrorStatus(@Nullable Throwable throwable) { if (!(throwable instanceof FirestoreException)) { return StatusCode.Code.UNKNOWN.toString(); } + Status status = ((FirestoreException) throwable).getStatus(); if (status == null) { return StatusCode.Code.UNKNOWN.toString(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index c43c2db3a..c47386d5d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -21,6 +21,7 @@ import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreOptions; import java.util.List; +import java.util.function.Supplier; import java.util.logging.Logger; import javax.annotation.Nonnull; @@ -55,7 +56,7 @@ static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables // TODO(metrics): The feature is disabled before it is ready for general release. - boolean shouldCreateEnabledInstance = false; + boolean shouldCreateEnabledInstance = true; String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { @@ -109,5 +110,15 @@ interface MetricsContext { /** Records first response latency for the current operation. */ void recordFirstResponseLatency(); + + /** + * Transaction latency should be recorded _after_ all the operations, including the retires has + * been completed. This method "appends" the metrics recording code at the completion of the + * given future. + */ + void recordTransactionLatencyAtFuture(ApiFuture futureValue); + + void recordTransactionAttemptsAtFuture( + ApiFuture futureValue, Supplier attemptsSupplier); } } From 4fa8874849155d31c62d0cc9bd8683b5b6a98d3f Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:50:21 -0400 Subject: [PATCH 039/103] add exportBuiltinMetricsToGoogleCloudMonitoring flag --- .../FirestoreOpenTelemetryOptions.java | 27 ++++++++++++- .../telemetry/BuiltinMetricsProvider.java | 4 +- .../telemetry/EnabledMetricsUtil.java | 39 +++++++++++++------ 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 2b4606565..a10478491 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -28,10 +28,13 @@ @BetaApi public class FirestoreOpenTelemetryOptions { private final boolean tracingEnabled; + private final boolean exportBuiltinMetricsToGoogleCloudMonitoring; private final @Nullable OpenTelemetry openTelemetry; FirestoreOpenTelemetryOptions(Builder builder) { this.tracingEnabled = builder.tracingEnabled; + this.exportBuiltinMetricsToGoogleCloudMonitoring = + builder.exportBuiltinMetricsToGoogleCloudMonitoring; this.openTelemetry = builder.openTelemetry; } @@ -39,6 +42,10 @@ public boolean isTracingEnabled() { return tracingEnabled; } + public boolean exportBuiltinMetricsToGoogleCloudMonitoring() { + return exportBuiltinMetricsToGoogleCloudMonitoring; + } + public OpenTelemetry getOpenTelemetry() { return openTelemetry; } @@ -56,16 +63,20 @@ public static FirestoreOpenTelemetryOptions.Builder newBuilder() { public static class Builder { private boolean tracingEnabled; - + private boolean exportBuiltinMetricsToGoogleCloudMonitoring; @Nullable private OpenTelemetry openTelemetry; private Builder() { tracingEnabled = false; + // TODO(metrics): default this to true when feature is ready + exportBuiltinMetricsToGoogleCloudMonitoring = false; openTelemetry = null; } private Builder(FirestoreOpenTelemetryOptions options) { this.tracingEnabled = options.tracingEnabled; + this.exportBuiltinMetricsToGoogleCloudMonitoring = + options.exportBuiltinMetricsToGoogleCloudMonitoring; this.openTelemetry = options.openTelemetry; } @@ -85,6 +96,20 @@ public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEn return this; } + // TODO(metrics): make this public when feature is ready. + /** + * Sets whether built-in metrics should be exported to Google Cloud Monitoring + * + * @param exportBuiltinMetrics Whether built-in metrics should be exported to Google Cloud + * Monitoring. + */ + @Nonnull + private FirestoreOpenTelemetryOptions.Builder exportBuiltinMetricsToGoogleCloudMonitoring( + boolean exportBuiltinMetrics) { + this.exportBuiltinMetricsToGoogleCloudMonitoring = exportBuiltinMetrics; + return this; + } + /** * Sets the {@link OpenTelemetry} to use with this Firestore instance. If telemetry collection * is enabled, but an `OpenTelemetry` is not provided, the Firestore SDK will attempt to use the diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index b07b2b03b..c940feb5a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -56,7 +56,7 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; this.staticAttributes = createStaticAttributes(); - if (openTelemetry != null && openTelemetry.getMeterProvider() != MeterProvider.noop()) { + if (openTelemetry.getMeterProvider() != MeterProvider.noop()) { configureRPCLayerMetrics(); configureSDKLayerMetrics(); } @@ -66,7 +66,7 @@ public ApiTracerFactory getApiTracerFactory() { return this.apiTracerFactory; } - public void endToEndRequestLatencyRecorder(double latency, Map attributes) { + public void endToEndLatencyRecorder(double latency, Map attributes) { recordLatency(endToEndRequestLatency, latency, attributes); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 7ac7ecaab..5f15204e3 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -71,16 +71,8 @@ class EnabledMetricsUtil implements MetricsUtil { EnabledMetricsUtil(FirestoreOptions firestoreOptions) { try { - OpenTelemetry defaultOpenTelemetry = - getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId()); - this.defaultMetricsProvider = new BuiltinMetricsProvider(defaultOpenTelemetry); - - OpenTelemetry customOpenTelemetry = - firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); - if (customOpenTelemetry == null) { - customOpenTelemetry = GlobalOpenTelemetry.get(); - } - this.customMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry); + configureDefaultMetricsProvider(firestoreOptions); + configureCustomMetricsProvider(firestoreOptions); } catch (IOException e) { logger.warning( "Unable to create MetricsUtil object for client side metrics, will skip exporting client side metrics" @@ -88,6 +80,29 @@ class EnabledMetricsUtil implements MetricsUtil { } } + private void configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) + throws IOException { + OpenTelemetry defaultOpenTelemetry; + boolean exportBuiltinMetricsToGoogleCloudMonitoring = + firestoreOptions.getOpenTelemetryOptions().exportBuiltinMetricsToGoogleCloudMonitoring(); + if (exportBuiltinMetricsToGoogleCloudMonitoring) { + defaultOpenTelemetry = getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId()); + } else { + defaultOpenTelemetry = OpenTelemetry.noop(); + } + this.defaultMetricsProvider = new BuiltinMetricsProvider(defaultOpenTelemetry); + } + + private void configureCustomMetricsProvider(FirestoreOptions firestoreOptions) + throws IOException { + OpenTelemetry customOpenTelemetry = + firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); + if (customOpenTelemetry == null) { + customOpenTelemetry = GlobalOpenTelemetry.get(); + } + this.customMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry); + } + @Override public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(methodName); @@ -196,8 +211,8 @@ public void recordFirstResponseLatency() { private void recordEndToEndLatency(String status) { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); Map attributes = createAttributes(status); - defaultMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + defaultMetricsProvider.endToEndLatencyRecorder(elapsedTime, attributes); + customMetricsProvider.endToEndLatencyRecorder(elapsedTime, attributes); } private Map createAttributes(String status) { From a21feac4b5cdfa597d05e369cae5dc4ec3a482de Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:05:01 -0400 Subject: [PATCH 040/103] include system-rules for trsting --- google-cloud-firestore/pom.xml | 5 ++ .../FirestoreOpenTelemetryOptions.java | 2 +- .../firestore/telemetry/MetricsUtil.java | 2 +- .../firestore/telemetry/MetricsUtilTest.java | 57 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index f9bb321fc..5f96f9216 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -150,6 +150,11 @@ 4.11.0 test + + com.github.stefanbirkner + system-rules + 1.19.0 test + com.google.api diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index a10478491..39671499a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -69,7 +69,7 @@ public static class Builder { private Builder() { tracingEnabled = false; // TODO(metrics): default this to true when feature is ready - exportBuiltinMetricsToGoogleCloudMonitoring = false; + exportBuiltinMetricsToGoogleCloudMonitoring = true; openTelemetry = null; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index c43c2db3a..a88cc6ae0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -55,7 +55,7 @@ static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables // TODO(metrics): The feature is disabled before it is ready for general release. - boolean shouldCreateEnabledInstance = false; + boolean shouldCreateEnabledInstance = true; String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java new file mode 100644 index 000000000..8273f898c --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.firestore.FirestoreOptions; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; + +public class MetricsUtilTest { + + @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + @Test + public void defaultOptionsUseEnabledMetricsUtil() { + + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + } + + @Test + public void environmentVariableCanSetToUseDisabledMetricsUtil() { + + environmentVariables.set("FIRESTORE_ENABLE_METRICS", "off"); + + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof DisabledTraceUtil).isTrue(); + } +} From ba1f0c9053958b10399b66e4e9d0d6f32187e790 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:14:17 -0400 Subject: [PATCH 041/103] fix typo --- .../com/google/cloud/firestore/telemetry/MetricsUtilTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 8273f898c..b670be8ab 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -52,6 +52,6 @@ public void environmentVariableCanSetToUseDisabledMetricsUtil() { .setDatabaseId("(default)") .build()); - assertThat(util instanceof DisabledTraceUtil).isTrue(); + assertThat(util instanceof DisabledMetricsUtil).isTrue(); } } From 72bb99ffbbe97b9e4a614ff44c47dbec04c7b413 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:22:27 -0400 Subject: [PATCH 042/103] debug --- .../java/com/google/cloud/firestore/telemetry/MetricsUtil.java | 3 +++ .../com/google/cloud/firestore/telemetry/MetricsUtilTest.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index a88cc6ae0..dd533fccc 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -57,7 +57,10 @@ static boolean shouldCreateEnabledInstance() { // TODO(metrics): The feature is disabled before it is ready for general release. boolean shouldCreateEnabledInstance = true; + System.out.println("============1"); String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); + System.out.println("============2:"+enableMetricsEnvVar); + if (enableMetricsEnvVar != null) { switch (enableMetricsEnvVar.toLowerCase()) { case "true": diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index b670be8ab..196db486a 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -42,8 +42,10 @@ public void defaultOptionsUseEnabledMetricsUtil() { @Test public void environmentVariableCanSetToUseDisabledMetricsUtil() { + System.out.println("============start"); environmentVariables.set("FIRESTORE_ENABLE_METRICS", "off"); + System.out.println("============set"); MetricsUtil util = MetricsUtil.getInstance( From b74623171a6444cf8ca51744fb4b3466ba44f6f4 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:31:46 -0400 Subject: [PATCH 043/103] remove system-rules lib --- google-cloud-firestore/pom.xml | 5 --- .../firestore/telemetry/MetricsUtil.java | 2 +- .../firestore/telemetry/MetricsUtilTest.java | 32 +++++++++---------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 5f96f9216..f9bb321fc 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -150,11 +150,6 @@ 4.11.0 test - - com.github.stefanbirkner - system-rules - 1.19.0 test - com.google.api diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index dd533fccc..dd299d4f7 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -59,7 +59,7 @@ static boolean shouldCreateEnabledInstance() { System.out.println("============1"); String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); - System.out.println("============2:"+enableMetricsEnvVar); + System.out.println("============2:" + enableMetricsEnvVar); if (enableMetricsEnvVar != null) { switch (enableMetricsEnvVar.toLowerCase()) { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 196db486a..a003c7d0d 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -19,13 +19,13 @@ import static com.google.common.truth.Truth.assertThat; import com.google.cloud.firestore.FirestoreOptions; -import org.junit.Rule; +// import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; +// import org.junit.contrib.java.lang.system.EnvironmentVariables; public class MetricsUtilTest { - @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + // @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Test public void defaultOptionsUseEnabledMetricsUtil() { @@ -40,20 +40,20 @@ public void defaultOptionsUseEnabledMetricsUtil() { assertThat(util instanceof EnabledMetricsUtil).isTrue(); } - @Test - public void environmentVariableCanSetToUseDisabledMetricsUtil() { - System.out.println("============start"); + // @Test + // public void environmentVariableCanSetToUseDisabledMetricsUtil() { + // System.out.println("============start"); - environmentVariables.set("FIRESTORE_ENABLE_METRICS", "off"); - System.out.println("============set"); + // environmentVariables.set("FIRESTORE_ENABLE_METRICS", "off"); + // System.out.println("============set"); - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); + // MetricsUtil util = + // MetricsUtil.getInstance( + // FirestoreOptions.newBuilder() + // .setProjectId("test-project") + // .setDatabaseId("(default)") + // .build()); - assertThat(util instanceof DisabledMetricsUtil).isTrue(); - } + // assertThat(util instanceof DisabledMetricsUtil).isTrue(); + // } } From 6f3cd579ca4c2f8ba6f2ca62cefdbd6f3b012c43 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:36:43 -0400 Subject: [PATCH 044/103] add the lib --- google-cloud-firestore/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index f9bb321fc..d18e2d9fd 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -150,6 +150,12 @@ 4.11.0 test + + com.github.stefanbirkner + system-rules + 1.19.0 + test + com.google.api From 9ce225c86677163a923138d9098749b337d1ad4b Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:46:40 -0400 Subject: [PATCH 045/103] Update MetricsUtilTest.java --- .../com/google/cloud/firestore/telemetry/MetricsUtilTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index a003c7d0d..c891099c7 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -19,9 +19,9 @@ import static com.google.common.truth.Truth.assertThat; import com.google.cloud.firestore.FirestoreOptions; -// import org.junit.Rule; +import org.junit.Rule; import org.junit.Test; -// import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.junit.contrib.java.lang.system.EnvironmentVariables; public class MetricsUtilTest { From 936936ddcaf4819ce0140c490a57066cae048d64 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:49:08 -0400 Subject: [PATCH 046/103] Update MetricsUtilTest.java --- .../com/google/cloud/firestore/telemetry/MetricsUtilTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index c891099c7..099e8e9f2 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -25,7 +25,8 @@ public class MetricsUtilTest { - // @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + @Rule + public EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Test public void defaultOptionsUseEnabledMetricsUtil() { From 6f8b06bd1c2401d58d5ac316e0e16dc4fe5e2e9a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:03:57 -0400 Subject: [PATCH 047/103] Update MetricsUtilTest.java --- .../cloud/firestore/telemetry/MetricsUtilTest.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 099e8e9f2..87975beae 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -25,12 +25,17 @@ public class MetricsUtilTest { - @Rule - public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + public static void main(String[] args) { + // This is just for demonstration purposes to run the tests with the VM argument + String[] arg = new String[] {"--add-opens", "java.base/java.util=ALL-UNNAMED"}; + org.junit.runner.JUnitCore.main(arg); + } @Test public void defaultOptionsUseEnabledMetricsUtil() { - + System.out.println("============start"); MetricsUtil util = MetricsUtil.getInstance( FirestoreOptions.newBuilder() From 03f17c5604ec08de8940e6e2b19e15e93ddde28b Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:15:24 -0400 Subject: [PATCH 048/103] use system lambda instead --- google-cloud-firestore/pom.xml | 4 +- .../firestore/telemetry/MetricsUtilTest.java | 46 ++++++++----------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index d18e2d9fd..699c945e8 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -152,8 +152,8 @@ com.github.stefanbirkner - system-rules - 1.19.0 + system-lambda + 1.2.1 test diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 87975beae..d08ef098a 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -16,23 +16,13 @@ package com.google.cloud.firestore.telemetry; +import static com.github.stefanbirkner.systemlambda.SystemLambda.*; import static com.google.common.truth.Truth.assertThat; import com.google.cloud.firestore.FirestoreOptions; -import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; public class MetricsUtilTest { - - @Rule public EnvironmentVariables environmentVariables = new EnvironmentVariables(); - - public static void main(String[] args) { - // This is just for demonstration purposes to run the tests with the VM argument - String[] arg = new String[] {"--add-opens", "java.base/java.util=ALL-UNNAMED"}; - org.junit.runner.JUnitCore.main(arg); - } - @Test public void defaultOptionsUseEnabledMetricsUtil() { System.out.println("============start"); @@ -46,20 +36,22 @@ public void defaultOptionsUseEnabledMetricsUtil() { assertThat(util instanceof EnabledMetricsUtil).isTrue(); } - // @Test - // public void environmentVariableCanSetToUseDisabledMetricsUtil() { - // System.out.println("============start"); - - // environmentVariables.set("FIRESTORE_ENABLE_METRICS", "off"); - // System.out.println("============set"); - - // MetricsUtil util = - // MetricsUtil.getInstance( - // FirestoreOptions.newBuilder() - // .setProjectId("test-project") - // .setDatabaseId("(default)") - // .build()); - - // assertThat(util instanceof DisabledMetricsUtil).isTrue(); - // } + @Test + public void testFirestoreWithMetricsEnabled() throws Exception { + System.out.println("============start2"); + + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "true") + .execute( + () -> { + System.out.println("============call"); + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof DisabledMetricsUtil).isTrue(); + }); + } } From 89d7e8d64041f9ed73c15f43e0e88e0024e0da8a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:23:49 -0400 Subject: [PATCH 049/103] fix typo --- .../google/cloud/firestore/telemetry/MetricsUtil.java | 3 --- .../cloud/firestore/telemetry/MetricsUtilTest.java | 9 ++------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index dd299d4f7..a88cc6ae0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -57,10 +57,7 @@ static boolean shouldCreateEnabledInstance() { // TODO(metrics): The feature is disabled before it is ready for general release. boolean shouldCreateEnabledInstance = true; - System.out.println("============1"); String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); - System.out.println("============2:" + enableMetricsEnvVar); - if (enableMetricsEnvVar != null) { switch (enableMetricsEnvVar.toLowerCase()) { case "true": diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index d08ef098a..bd71e161d 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -25,7 +25,6 @@ public class MetricsUtilTest { @Test public void defaultOptionsUseEnabledMetricsUtil() { - System.out.println("============start"); MetricsUtil util = MetricsUtil.getInstance( FirestoreOptions.newBuilder() @@ -37,20 +36,16 @@ public void defaultOptionsUseEnabledMetricsUtil() { } @Test - public void testFirestoreWithMetricsEnabled() throws Exception { - System.out.println("============start2"); - - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "true") + public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") .execute( () -> { - System.out.println("============call"); MetricsUtil util = MetricsUtil.getInstance( FirestoreOptions.newBuilder() .setProjectId("test-project") .setDatabaseId("(default)") .build()); - assertThat(util instanceof DisabledMetricsUtil).isTrue(); }); } From 3fb7cae98e24b7c37456e2f5213d56b4257da0fd Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Oct 2024 12:11:06 -0400 Subject: [PATCH 050/103] use system-stubs-junit4 --- google-cloud-firestore/pom.xml | 13 ++++-- .../firestore/telemetry/MetricsUtilTest.java | 41 +++++++++++++------ 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 699c945e8..e9174685b 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -150,10 +150,15 @@ 4.11.0 test - - com.github.stefanbirkner - system-lambda - 1.2.1 + + + + + + + uk.org.webcompere + system-stubs-junit4 + 2.1.7 test diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index bd71e161d..25329b6d5 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -16,13 +16,17 @@ package com.google.cloud.firestore.telemetry; -import static com.github.stefanbirkner.systemlambda.SystemLambda.*; +// import static com.github.stefanbirkner.systemlambda.SystemLambda.*; import static com.google.common.truth.Truth.assertThat; import com.google.cloud.firestore.FirestoreOptions; +import org.junit.Rule; import org.junit.Test; +import uk.org.webcompere.systemstubs.rules.EnvironmentVariablesRule; public class MetricsUtilTest { + @Rule public EnvironmentVariablesRule rule = new EnvironmentVariablesRule(); + @Test public void defaultOptionsUseEnabledMetricsUtil() { MetricsUtil util = @@ -35,18 +39,31 @@ public void defaultOptionsUseEnabledMetricsUtil() { assertThat(util instanceof EnabledMetricsUtil).isTrue(); } + // @Test + // public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { + // withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") + // .execute( + // () -> { + // MetricsUtil util = + // MetricsUtil.getInstance( + // FirestoreOptions.newBuilder() + // .setProjectId("test-project") + // .setDatabaseId("(default)") + // .build()); + // assertThat(util instanceof DisabledMetricsUtil).isTrue(); + // }); + // } + @Test public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - assertThat(util instanceof DisabledMetricsUtil).isTrue(); - }); + rule.set("FIRESTORE_ENABLE_METRICS", "off"); + + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + assertThat(util instanceof DisabledMetricsUtil).isTrue(); } } From be3559f64bb134399b8501606d2fda3a3e5194e9 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:21:14 -0400 Subject: [PATCH 051/103] add system-stubs-core --- google-cloud-firestore/pom.xml | 6 +++ .../firestore/telemetry/MetricsUtilTest.java | 42 ++++++------------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index e9174685b..3593c2f33 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -155,6 +155,12 @@ + + uk.org.webcompere + system-stubs-core + 2.1.7 + test + uk.org.webcompere system-stubs-junit4 diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 25329b6d5..be5f65b55 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -16,17 +16,13 @@ package com.google.cloud.firestore.telemetry; -// import static com.github.stefanbirkner.systemlambda.SystemLambda.*; +import static com.github.stefanbirkner.systemlambda.SystemLambda.*; import static com.google.common.truth.Truth.assertThat; import com.google.cloud.firestore.FirestoreOptions; -import org.junit.Rule; import org.junit.Test; -import uk.org.webcompere.systemstubs.rules.EnvironmentVariablesRule; public class MetricsUtilTest { - @Rule public EnvironmentVariablesRule rule = new EnvironmentVariablesRule(); - @Test public void defaultOptionsUseEnabledMetricsUtil() { MetricsUtil util = @@ -39,31 +35,19 @@ public void defaultOptionsUseEnabledMetricsUtil() { assertThat(util instanceof EnabledMetricsUtil).isTrue(); } - // @Test - // public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { - // withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") - // .execute( - // () -> { - // MetricsUtil util = - // MetricsUtil.getInstance( - // FirestoreOptions.newBuilder() - // .setProjectId("test-project") - // .setDatabaseId("(default)") - // .build()); - // assertThat(util instanceof DisabledMetricsUtil).isTrue(); - // }); - // } - @Test public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { - rule.set("FIRESTORE_ENABLE_METRICS", "off"); - - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - assertThat(util instanceof DisabledMetricsUtil).isTrue(); + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof DisabledMetricsUtil).isTrue(); + }); } } From 08df5b0a7997fdf5a32254ac3c39e396783f95b9 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:23:18 -0400 Subject: [PATCH 052/103] Update MetricsUtilTest.java --- .../firestore/telemetry/MetricsUtilTest.java | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index be5f65b55..03cd55a17 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -16,13 +16,18 @@ package com.google.cloud.firestore.telemetry; -import static com.github.stefanbirkner.systemlambda.SystemLambda.*; +// import static com.github.stefanbirkner.systemlambda.SystemLambda.*; import static com.google.common.truth.Truth.assertThat; import com.google.cloud.firestore.FirestoreOptions; +import org.junit.Rule; import org.junit.Test; +import uk.org.webcompere.systemstubs.rules.EnvironmentVariablesRule; public class MetricsUtilTest { + + @Rule public EnvironmentVariablesRule rule = new EnvironmentVariablesRule(); + @Test public void defaultOptionsUseEnabledMetricsUtil() { MetricsUtil util = @@ -35,19 +40,32 @@ public void defaultOptionsUseEnabledMetricsUtil() { assertThat(util instanceof EnabledMetricsUtil).isTrue(); } + // @Test + // public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { + // withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") + // .execute( + // () -> { + // MetricsUtil util = + // MetricsUtil.getInstance( + // FirestoreOptions.newBuilder() + // .setProjectId("test-project") + // .setDatabaseId("(default)") + // .build()); + // + // assertThat(util instanceof DisabledMetricsUtil).isTrue(); + // }); + // } + @Test public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - - assertThat(util instanceof DisabledMetricsUtil).isTrue(); - }); + rule.set("FIRESTORE_ENABLE_METRICS", "off"); + + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + assertThat(util instanceof DisabledMetricsUtil).isTrue(); } } From f30e21f125622f5f6d620d8a0cbb1659ffb0d928 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:34:49 -0400 Subject: [PATCH 053/103] revert back to system lambda --- google-cloud-firestore/pom.xml | 18 ++------ .../firestore/telemetry/MetricsUtilTest.java | 46 ++++++------------- 2 files changed, 17 insertions(+), 47 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 3593c2f33..35b01c17d 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -150,22 +150,10 @@ 4.11.0 test - - - - - - - uk.org.webcompere - system-stubs-core - 2.1.7 - test - - uk.org.webcompere - system-stubs-junit4 - 2.1.7 - test + com.github.stefanbirkner + system-lambda + 1.2.1 diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 03cd55a17..4db65d68f 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -16,18 +16,13 @@ package com.google.cloud.firestore.telemetry; -// import static com.github.stefanbirkner.systemlambda.SystemLambda.*; +import static com.github.stefanbirkner.systemlambda.SystemLambda.*; import static com.google.common.truth.Truth.assertThat; import com.google.cloud.firestore.FirestoreOptions; -import org.junit.Rule; import org.junit.Test; -import uk.org.webcompere.systemstubs.rules.EnvironmentVariablesRule; public class MetricsUtilTest { - - @Rule public EnvironmentVariablesRule rule = new EnvironmentVariablesRule(); - @Test public void defaultOptionsUseEnabledMetricsUtil() { MetricsUtil util = @@ -40,32 +35,19 @@ public void defaultOptionsUseEnabledMetricsUtil() { assertThat(util instanceof EnabledMetricsUtil).isTrue(); } - // @Test - // public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { - // withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") - // .execute( - // () -> { - // MetricsUtil util = - // MetricsUtil.getInstance( - // FirestoreOptions.newBuilder() - // .setProjectId("test-project") - // .setDatabaseId("(default)") - // .build()); - // - // assertThat(util instanceof DisabledMetricsUtil).isTrue(); - // }); - // } - @Test - public void disabledMetricsUtilWhenEnvVarIsOff() throws Exception { - rule.set("FIRESTORE_ENABLE_METRICS", "off"); - - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - assertThat(util instanceof DisabledMetricsUtil).isTrue(); + public void testFirestoreWithMetricsEnabled() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof DisabledMetricsUtil).isTrue(); + }); } } From 11a3345a8f00fa13bfad5531b540b637cd2f40c6 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:42:04 -0400 Subject: [PATCH 054/103] add vm options to test profile --- google-cloud-firestore/pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 35b01c17d..607c91dc1 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -317,6 +317,18 @@ javax.annotation-api + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + --add-opens java.base/java.util=ALL-UNNAMED + + + + From d491de5cbe92dd3a05374f49a08853aa74ca2056 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:44:43 -0400 Subject: [PATCH 055/103] add test scope to system lambda --- google-cloud-firestore/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 607c91dc1..823f294bf 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -153,6 +153,7 @@ com.github.stefanbirkner system-lambda + test 1.2.1 From cc3834d83907a659c8e20a95da7f3f8b7801dbac Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:23:06 -0400 Subject: [PATCH 056/103] create TransactionMetricsContext --- google-cloud-firestore/pom.xml | 28 ++++--- .../ServerSideTransactionRunner.java | 14 ++-- .../telemetry/DisabledMetricsUtil.java | 15 +++- .../telemetry/EnabledMetricsUtil.java | 75 ++++++++++++------- .../firestore/telemetry/MetricsUtil.java | 18 ++++- .../cloud/firestore/TransactionTest.java | 1 + 6 files changed, 92 insertions(+), 59 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index b6beffd6a..ae7c3df0a 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -134,21 +134,19 @@ - - - org.slf4j - slf4j-api - 2.0.7 - test - - - - org.slf4j - slf4j-simple - 2.0.7 - test - - + + + org.slf4j + slf4j-api + 2.0.7 + test + + + org.slf4j + slf4j-simple + 2.0.7 + test + junit junit diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index a1f715fec..187213857 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -26,7 +26,7 @@ import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.rpc.ApiException; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.MetricsUtil.TransactionMetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -61,7 +61,7 @@ final class ServerSideTransactionRunner { private int attemptsRemaining; private Span runTransactionSpan; private TraceUtil.Context runTransactionContext; - private MetricsContext metricsContext; + private TransactionMetricsContext metricsContext; /** * @param firestore The active Firestore instance @@ -88,8 +88,7 @@ final class ServerSideTransactionRunner { new ExponentialRetryAlgorithm( firestore.getOptions().getRetrySettings(), CurrentMillisClock.getDefaultClock()); this.nextBackoffAttempt = backoffAlgorithm.createFirstAttempt(); - this.metricsContext = - firestore.getOptions().getMetricsUtil().createMetricsContext("ServerSideTransaction"); + this.metricsContext = firestore.getOptions().getMetricsUtil().createTransactionMetricsContext(); } @Nonnull @@ -97,14 +96,10 @@ private TraceUtil getTraceUtil() { return firestore.getOptions().getTraceUtil(); } - private int attemptsMade() { - return transactionOptions.getNumberOfAttempts() - this.attemptsRemaining; - } - ApiFuture run() { ApiFuture result = runInternally(); metricsContext.recordTransactionLatencyAtFuture(result); - metricsContext.recordTransactionAttemptsAtFuture(result, () -> this.attemptsMade()); + metricsContext.recordTransactionAttemptsAtFuture(result); return result; } @@ -118,6 +113,7 @@ ApiFuture runInternally() { try (Scope ignored = runTransactionSpan.makeCurrent()) { runTransactionContext = getTraceUtil().currentContext(); --attemptsRemaining; + metricsContext.incrementAttemptsCount(); ApiFuture result = ApiFutures.catchingAsync( ApiFutures.transformAsync( diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index 15f66a4d3..40ff41665 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -19,7 +19,6 @@ import com.google.api.core.ApiFuture; import com.google.api.gax.tracing.ApiTracerFactory; import java.util.List; -import java.util.function.Supplier; /** * A fully disabled (No-op) MetricsUtil class that does not perform any metrics collection actions @@ -40,13 +39,18 @@ public void recordEndToEndLatency(Throwable t) {} @Override public void recordFirstResponseLatency() {} + } + + class TransactionMetricsContext implements MetricsUtil.TransactionMetricsContext { @Override public void recordTransactionLatencyAtFuture(ApiFuture futureValue) {} @Override - public void recordTransactionAttemptsAtFuture( - ApiFuture futureValue, Supplier attemptsSupplier) {} + public void recordTransactionAttemptsAtFuture(ApiFuture futureValue) {} + + @Override + public void incrementAttemptsCount() {}; } @Override @@ -54,6 +58,11 @@ public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(); } + @Override + public TransactionMetricsContext createTransactionMetricsContext() { + return new TransactionMetricsContext(); + } + @Override public void addMetricsTracerFactory(List apiTracerFactories) {} } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 38aec3cee..26075b804 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -55,7 +55,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -109,6 +108,11 @@ public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(methodName); } + @Override + public TransactionMetricsContext createTransactionMetricsContext() { + return new TransactionMetricsContext(); + } + @Override public void addMetricsTracerFactory(List apiTracerFactories) { addTracerFactory(apiTracerFactories, defaultMetricsProvider); @@ -204,16 +208,30 @@ public void recordEndToEndLatency(Throwable t) { public void recordFirstResponseLatency() { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(StatusCode.Code.OK.toString()); + Map attributes = createAttributes(StatusCode.Code.OK.toString(), methodName); defaultMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); customMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); } private void recordEndToEndLatency(String status) { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(status); - defaultMetricsProvider.endToEndLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.endToEndLatencyRecorder(elapsedTime, attributes); + Map attributes = createAttributes(status, methodName); + defaultMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + } + } + + class TransactionMetricsContext implements MetricsUtil.TransactionMetricsContext { + private final Stopwatch stopwatch; + private int attemptsMade = 0; + private final String METHOD_NAME = "ServerSideTransaction"; + + public TransactionMetricsContext() { + this.stopwatch = Stopwatch.createStarted(); + } + + public void incrementAttemptsCount() { + attemptsMade++; } public void recordTransactionLatencyAtFuture(ApiFuture futureValue) { @@ -236,52 +254,51 @@ public void onSuccess(T result) { private void recordTransactionLatency(String status) { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(status); + Map attributes = createAttributes(status, METHOD_NAME); defaultMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes); customMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes); } - public void recordTransactionAttemptsAtFuture( - ApiFuture futureValue, Supplier attemptsSupplier) { + public void recordTransactionAttemptsAtFuture(ApiFuture futureValue) { ApiFutures.addCallback( futureValue, new ApiFutureCallback() { @Override public void onFailure(Throwable t) { - recordTransactionAttempts(extractErrorStatus(t), attemptsSupplier.get()); + recordTransactionAttempts(extractErrorStatus(t)); } @Override public void onSuccess(T result) { - recordTransactionAttempts(StatusCode.Code.OK.toString(), attemptsSupplier.get()); + recordTransactionAttempts(StatusCode.Code.OK.toString()); } }, MoreExecutors.directExecutor()); } - private void recordTransactionAttempts(String status, int attempts) { - Map attributes = createAttributes(status); - defaultMetricsProvider.transactionAttemptCountRecorder((long) attempts, attributes); - customMetricsProvider.transactionAttemptCountRecorder((long) attempts, attributes); + private void recordTransactionAttempts(String status) { + Map attributes = createAttributes(status, METHOD_NAME); + defaultMetricsProvider.transactionAttemptCountRecorder((long) attemptsMade, attributes); + customMetricsProvider.transactionAttemptCountRecorder((long) attemptsMade, attributes); } + } - private Map createAttributes(String status) { - Map attributes = new HashMap<>(); - attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName); - attributes.put(METRIC_ATTRIBUTE_KEY_STATUS.getKey(), status); - return attributes; - } + private Map createAttributes(String status, String methodName) { + Map attributes = new HashMap<>(); + attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName); + attributes.put(METRIC_ATTRIBUTE_KEY_STATUS.getKey(), status); + return attributes; + } - private String extractErrorStatus(@Nullable Throwable throwable) { - if (!(throwable instanceof FirestoreException)) { - return StatusCode.Code.UNKNOWN.toString(); - } + private String extractErrorStatus(@Nullable Throwable throwable) { + if (!(throwable instanceof FirestoreException)) { + return StatusCode.Code.UNKNOWN.toString(); + } - Status status = ((FirestoreException) throwable).getStatus(); - if (status == null) { - return StatusCode.Code.UNKNOWN.toString(); - } - return status.getCode().name(); + Status status = ((FirestoreException) throwable).getStatus(); + if (status == null) { + return StatusCode.Code.UNKNOWN.toString(); } + return status.getCode().name(); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index c47386d5d..c12541409 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -21,7 +21,6 @@ import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreOptions; import java.util.List; -import java.util.function.Supplier; import java.util.logging.Logger; import javax.annotation.Nonnull; @@ -85,6 +84,14 @@ static boolean shouldCreateEnabledInstance() { */ abstract MetricsContext createMetricsContext(String methodName); + /** + * Creates a new {@code TransactionMetricsContext} for the given server side transaction and + * starts timing. + * + * @return A new {@code MetricsContext}. + */ + abstract TransactionMetricsContext createTransactionMetricsContext(); + /** * Adds a metrics tracer factory to the given list of API tracer factories. * @@ -110,6 +117,10 @@ interface MetricsContext { /** Records first response latency for the current operation. */ void recordFirstResponseLatency(); + } + + /** InnerMetricsUtil */ + public interface TransactionMetricsContext { /** * Transaction latency should be recorded _after_ all the operations, including the retires has @@ -118,7 +129,8 @@ interface MetricsContext { */ void recordTransactionLatencyAtFuture(ApiFuture futureValue); - void recordTransactionAttemptsAtFuture( - ApiFuture futureValue, Supplier attemptsSupplier); + void recordTransactionAttemptsAtFuture(ApiFuture futureValue); + + void incrementAttemptsCount(); } } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java index 97bd6a640..12a1d2dda 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java @@ -483,6 +483,7 @@ private void verifyRetries( stubber.verifyAllRequestsSent(); assertEquals(1, attempts[0]); } + Thread.sleep(1000); } } From 39e5293619a1cf7d697687e5abc275db28ada30d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:42:11 -0400 Subject: [PATCH 057/103] refactor the metrics recorders to use enum --- .../cloud/firestore/AggregateQuery.java | 7 +- .../google/cloud/firestore/BulkWriter.java | 5 +- .../cloud/firestore/CollectionGroup.java | 7 +- .../cloud/firestore/CollectionReference.java | 11 +-- .../cloud/firestore/DocumentReference.java | 69 ++++++++-------- .../google/cloud/firestore/FirestoreImpl.java | 11 +-- .../ServerSideTransactionRunner.java | 5 +- .../cloud/firestore/StreamableQuery.java | 11 +-- .../telemetry/BuiltinMetricsProvider.java | 80 ++++++++++++------- .../telemetry/DisabledMetricsUtil.java | 20 ++--- .../telemetry/EnabledMetricsUtil.java | 76 ++++++------------ .../firestore/telemetry/MetricsUtil.java | 23 ++---- .../telemetry/TelemetryConstants.java | 17 ++++ .../cloud/firestore/TransactionTest.java | 1 - 14 files changed, 174 insertions(+), 169 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 60395c352..c055b559e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -29,6 +29,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -223,12 +224,12 @@ ApiFuture getFuture() { } void deliverFirstResponse() { - metricsContext.recordFirstResponseLatency(); + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } void deliverError(Throwable throwable) { future.setException(throwable); - metricsContext.recordEndToEndLatency(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); } void deliverResult( @@ -238,7 +239,7 @@ void deliverResult( try { T result = processResult(serverData, readTime, metrics); future.set(result); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); } catch (Exception error) { deliverError(error); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java index 65cb946d2..0a713fe40 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java @@ -28,6 +28,7 @@ import com.google.api.gax.rpc.StatusCode.Code; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Context; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -935,10 +936,10 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) { }, bulkWriterExecutor); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } else { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java index 182e41ef2..98b069b34 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java @@ -23,6 +23,7 @@ import com.google.api.gax.rpc.ApiStreamObserver; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.PartitionQueryPagedResponse; @@ -137,15 +138,15 @@ public ApiFuture> getPartitions(long desiredPartitionCount) }, MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordEndToEndLatency(exception); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); - metricsContext.recordEndToEndLatency(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); throw throwable; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java index a2e8b3f55..75954d82d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java @@ -26,6 +26,7 @@ import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListDocumentsPagedResponse; @@ -184,15 +185,15 @@ public void remove() { } }; span.end(); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordEndToEndLatency(exception); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); - metricsContext.recordEndToEndLatency(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); throw throwable; } } @@ -229,11 +230,11 @@ public ApiFuture add(@Nonnull final Map field ApiFutures.transform( createFuture, writeResult -> documentReference, MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index 25f1eadcd..2b0cc1ddc 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -24,6 +24,7 @@ import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse; @@ -165,11 +166,11 @@ public ApiFuture create(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -191,11 +192,11 @@ public ApiFuture create(@Nonnull Object pojo) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, pojo).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -217,11 +218,11 @@ public ApiFuture set(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -246,11 +247,11 @@ public ApiFuture set( WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -272,11 +273,11 @@ public ApiFuture set(@Nonnull Object pojo) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -300,11 +301,11 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -326,11 +327,11 @@ public ApiFuture update(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.update(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -354,11 +355,11 @@ public ApiFuture update(@Nonnull Map fields, Precon ApiFuture result = extractFirst(writeBatch.update(this, fields, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -384,11 +385,11 @@ public ApiFuture update( ApiFuture result = extractFirst(writeBatch.update(this, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -414,11 +415,11 @@ public ApiFuture update( ApiFuture result = extractFirst(writeBatch.update(this, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -449,11 +450,11 @@ public ApiFuture update( extractFirst( writeBatch.update(this, options, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -484,11 +485,11 @@ public ApiFuture update( extractFirst( writeBatch.update(this, options, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -509,11 +510,11 @@ public ApiFuture delete(@Nonnull Precondition options) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -533,11 +534,11 @@ public ApiFuture delete() { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -558,11 +559,11 @@ public ApiFuture get() { try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -585,11 +586,11 @@ public ApiFuture get(FieldMask fieldMask) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(new DocumentReference[] {this}, fieldMask)); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -641,11 +642,11 @@ public void remove() { } }; span.end(); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordEndToEndLatency(exception); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index 93acd29ef..5c7217141 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -34,6 +34,7 @@ import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -266,7 +267,7 @@ public void onResponse(BatchGetDocumentsResponse response) { .addEvent( TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + ": First response received"); - metricsContext.recordFirstResponseLatency(); + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) { getTraceUtil() .currentSpan() @@ -311,7 +312,7 @@ public void onResponse(BatchGetDocumentsResponse response) { @Override public void onError(Throwable throwable) { getTraceUtil().currentSpan().end(throwable); - metricsContext.recordEndToEndLatency(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); apiStreamObserver.onError(throwable); } @@ -327,7 +328,7 @@ public void onComplete() { + numResponses + " responses.", Collections.singletonMap(ATTRIBUTE_KEY_NUM_RESPONSES, numResponses)); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); apiStreamObserver.onCompleted(); } }; @@ -460,9 +461,9 @@ public ApiFuture runAsyncTransaction( // that cannot be tracked client side. result = new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run(); } - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); } catch (Exception error) { - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } return result; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index 187213857..10e736db2 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -28,6 +28,7 @@ import com.google.api.gax.rpc.ApiException; import com.google.cloud.firestore.telemetry.MetricsUtil.TransactionMetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.telemetry.TraceUtil.Span; @@ -98,8 +99,8 @@ private TraceUtil getTraceUtil() { ApiFuture run() { ApiFuture result = runInternally(); - metricsContext.recordTransactionLatencyAtFuture(result); - metricsContext.recordTransactionAttemptsAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.TRANSACTION_LATENCY, result); + metricsContext.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT, result); return result; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java index 081567cee..62803d376 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java @@ -28,6 +28,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -168,7 +169,7 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -262,7 +263,7 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -283,20 +284,20 @@ public MonitoredStreamResponseObserver( public void onNext(RunQueryResponse value) { if (!receivedFirstResponse) { receivedFirstResponse = true; - metricsContext.recordFirstResponseLatency(); + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } observer.onNext(value); } @Override public void onError(Throwable t) { - metricsContext.recordEndToEndLatency(t); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, t); observer.onError(t); } @Override public void onCompleted() { - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); observer.onCompleted(); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 478db2949..f5733af21 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -29,6 +29,7 @@ import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.MetricsTracerFactory; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -38,14 +39,17 @@ import io.opentelemetry.api.metrics.MeterProvider; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * A provider for built-in metrics. This class is responsible for storing OpenTelemetry metrics * configuration and recording built-in metrics for the Firestore SDK. */ class BuiltinMetricsProvider { + private static final Logger logger = Logger.getLogger(BuiltinMetricsProvider.class.getName()); + private OpenTelemetry openTelemetry; - private Meter meter; private DoubleHistogram endToEndRequestLatency; private DoubleHistogram firstResponseLatency; private DoubleHistogram transactionLatency; @@ -68,6 +72,17 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { } } + private Map createStaticAttributes() { + Map staticAttributes = new HashMap<>(); + staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); + staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), FIRESTORE_LIBRARY_NAME); + String pkgVersion = this.getClass().getPackage().getImplementationVersion(); + if (pkgVersion != null) { + staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), pkgVersion); + } + return staticAttributes; + } + /** Creates an ApiTracerFactory to be passed into GAX library and collect RPC layer metrics. */ private void configureRPCLayerMetrics() { OpenTelemetryMetricsRecorder recorder = @@ -77,7 +92,7 @@ private void configureRPCLayerMetrics() { /** Registers metrics to be collected at the Firestore SDK layer */ private void configureSDKLayerMetrics() { - this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); + Meter meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); this.endToEndRequestLatency = meter @@ -112,42 +127,51 @@ public ApiTracerFactory getApiTracerFactory() { return this.apiTracerFactory; } - public void endToEndRequestLatencyRecorder(double latency, Map attributes) { - recordLatency(endToEndRequestLatency, latency, attributes); - } - - public void firstResponseLatencyRecorder(double latency, Map attributes) { - recordLatency(firstResponseLatency, latency, attributes); - } - - public void transactionLatencyRecorder(double latency, Map attributes) { - recordLatency(transactionLatency, latency, attributes); + public void latencyRecorder( + MetricType metricType, double latency, Map attributes) { + DoubleHistogram histogram = getHistogram(metricType); + if (histogram != null) { + attributes.putAll(staticAttributes); + try { + histogram.record(latency, toOtelAttributes(attributes)); + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to record latency metric: " + e.getMessage(), e); + } + } } - private void recordLatency( - DoubleHistogram latencyHistogram, double latency, Map attributes) { - if (latencyHistogram != null) { + public void counterRecorder(MetricType metricType, long count, Map attributes) { + LongCounter counter = getCounter(metricType); + if (counter != null) { attributes.putAll(staticAttributes); - latencyHistogram.record(latency, toOtelAttributes(attributes)); + try { + counter.add(count, toOtelAttributes(attributes)); + } catch (Exception e) { + logger.log( + Level.WARNING, "Failed to record transaction attempt count: " + e.getMessage(), e); + } } } - public void transactionAttemptCountRecorder(long count, Map attributes) { - if (transactionAttemptCount != null) { - attributes.putAll(staticAttributes); - transactionAttemptCount.add(count, toOtelAttributes(attributes)); + public DoubleHistogram getHistogram(MetricType metricType) { + switch (metricType) { + case END_TO_END_LATENCY: + return endToEndRequestLatency; + case FIRST_RESPONSE_LATENCY: + return firstResponseLatency; + case TRANSACTION_LATENCY: + return transactionLatency; + default: + throw new IllegalArgumentException("Unknown latency MetricType: " + metricType); } } - private Map createStaticAttributes() { - Map staticAttributes = new HashMap<>(); - staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); - staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), FIRESTORE_LIBRARY_NAME); - String pkgVersion = this.getClass().getPackage().getImplementationVersion(); - if (pkgVersion != null) { - staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), pkgVersion); + public LongCounter getCounter(MetricType metricType) { + if (metricType == MetricType.TRANSACTION_ATTEMPT) { + return transactionAttemptCount; + } else { + throw new IllegalArgumentException("Unknown counter MetricType: " + metricType); } - return staticAttributes; } private Attributes toOtelAttributes(Map attributes) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index 40ff41665..bebe611c7 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -18,6 +18,7 @@ import com.google.api.core.ApiFuture; import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import java.util.List; /** @@ -29,28 +30,23 @@ class DisabledMetricsUtil implements MetricsUtil { class MetricsContext implements MetricsUtil.MetricsContext { @Override - public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) {} + public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) {} @Override - public void recordEndToEndLatency() {} + public void recordLatency(MetricType metric) {} @Override - public void recordEndToEndLatency(Throwable t) {} - - @Override - public void recordFirstResponseLatency() {} + public void recordLatency(MetricType metric, Throwable t) {} } - class TransactionMetricsContext implements MetricsUtil.TransactionMetricsContext { - - @Override - public void recordTransactionLatencyAtFuture(ApiFuture futureValue) {} + class TransactionMetricsContext extends MetricsContext + implements MetricsUtil.TransactionMetricsContext { @Override - public void recordTransactionAttemptsAtFuture(ApiFuture futureValue) {} + public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) {} @Override - public void incrementAttemptsCount() {}; + public void incrementAttemptsCount() {} } @Override diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 26075b804..e4c79cc3f 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -32,6 +32,7 @@ import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreException; import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; import com.google.cloud.opentelemetry.metric.MetricConfiguration; import com.google.common.base.Stopwatch; @@ -181,105 +182,76 @@ public MetricsContext(String methodName) { this.methodName = methodName; } - public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) { + public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) { ApiFutures.addCallback( futureValue, new ApiFutureCallback() { @Override public void onFailure(Throwable t) { - recordEndToEndLatency(t); + recordLatency(metric, t); } @Override public void onSuccess(T result) { - recordEndToEndLatency(); + recordLatency(metric); } }, MoreExecutors.directExecutor()); } - public void recordEndToEndLatency() { - recordEndToEndLatency(StatusCode.Code.OK.toString()); + public void recordLatency(MetricType metric) { + recordLatency(metric, StatusCode.Code.OK.toString()); } - public void recordEndToEndLatency(Throwable t) { - recordEndToEndLatency(extractErrorStatus(t)); + public void recordLatency(MetricType metric, Throwable t) { + recordLatency(metric, extractErrorStatus(t)); } - public void recordFirstResponseLatency() { - double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(StatusCode.Code.OK.toString(), methodName); - defaultMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); - } - - private void recordEndToEndLatency(String status) { + private void recordLatency(MetricType metric, String status) { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); Map attributes = createAttributes(status, methodName); - defaultMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.endToEndRequestLatencyRecorder(elapsedTime, attributes); + defaultMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); + customMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); } } - class TransactionMetricsContext implements MetricsUtil.TransactionMetricsContext { - private final Stopwatch stopwatch; - private int attemptsMade = 0; + class TransactionMetricsContext extends MetricsContext + implements MetricsUtil.TransactionMetricsContext { + private int attemptsMade; private final String METHOD_NAME = "ServerSideTransaction"; public TransactionMetricsContext() { - this.stopwatch = Stopwatch.createStarted(); + super("ServerSideTransaction"); + this.attemptsMade = 0; } public void incrementAttemptsCount() { attemptsMade++; } - public void recordTransactionLatencyAtFuture(ApiFuture futureValue) { - ApiFutures.addCallback( - futureValue, - new ApiFutureCallback() { - @Override - public void onFailure(Throwable t) { - recordTransactionLatency(extractErrorStatus(t)); - } - - @Override - public void onSuccess(T result) { - - recordTransactionLatency(StatusCode.Code.OK.toString()); - } - }, - MoreExecutors.directExecutor()); - } - - private void recordTransactionLatency(String status) { - double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(status, METHOD_NAME); - defaultMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.transactionLatencyRecorder(elapsedTime, attributes); - } - - public void recordTransactionAttemptsAtFuture(ApiFuture futureValue) { + public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) { ApiFutures.addCallback( futureValue, new ApiFutureCallback() { @Override public void onFailure(Throwable t) { - recordTransactionAttempts(extractErrorStatus(t)); + recordCounter(metric, extractErrorStatus(t)); } @Override public void onSuccess(T result) { - recordTransactionAttempts(StatusCode.Code.OK.toString()); + recordCounter(metric, StatusCode.Code.OK.toString()); } }, MoreExecutors.directExecutor()); } - private void recordTransactionAttempts(String status) { + private void recordCounter(MetricType metric, String status) { Map attributes = createAttributes(status, METHOD_NAME); - defaultMetricsProvider.transactionAttemptCountRecorder((long) attemptsMade, attributes); - customMetricsProvider.transactionAttemptCountRecorder((long) attemptsMade, attributes); + defaultMetricsProvider.counterRecorder( + MetricType.TRANSACTION_ATTEMPT, (long) attemptsMade, attributes); + customMetricsProvider.counterRecorder( + MetricType.TRANSACTION_ATTEMPT, (long) attemptsMade, attributes); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index c12541409..41c6240ce 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -20,6 +20,7 @@ import com.google.api.core.InternalApi; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import java.util.List; import java.util.logging.Logger; import javax.annotation.Nonnull; @@ -101,35 +102,23 @@ static boolean shouldCreateEnabledInstance() { /** A context for recording metrics. */ interface MetricsContext { - /** * If the operation ends in the future, its relevant metrics should be recorded _after_ the * future has been completed. This method "appends" the metrics recording code at the completion * of the given future. */ - void recordEndToEndLatencyAtFuture(ApiFuture futureValue); + void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue); /** Records end-to-end latency for the current operation. */ - void recordEndToEndLatency(); + void recordLatency(MetricType metric); /** Records end-to-end latency for the current operation, which ended with a throwable. */ - void recordEndToEndLatency(Throwable t); - - /** Records first response latency for the current operation. */ - void recordFirstResponseLatency(); + void recordLatency(MetricType metric, Throwable t); } /** InnerMetricsUtil */ - public interface TransactionMetricsContext { - - /** - * Transaction latency should be recorded _after_ all the operations, including the retires has - * been completed. This method "appends" the metrics recording code at the completion of the - * given future. - */ - void recordTransactionLatencyAtFuture(ApiFuture futureValue); - - void recordTransactionAttemptsAtFuture(ApiFuture futureValue); + interface TransactionMetricsContext extends MetricsContext { + void recordCounterAtFuture(MetricType metric, ApiFuture futureValue); void incrementAttemptsCount(); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index b9e69d8e1..d041e0441 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -114,4 +114,21 @@ public interface TelemetryConstants { METRIC_NAME_END_TO_END_LATENCY, METRIC_NAME_TRANSACTION_LATENCY, METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); + + public enum MetricType { + END_TO_END_LATENCY("endToEndRequestLatency"), + FIRST_RESPONSE_LATENCY("firstResponseLatency"), + TRANSACTION_LATENCY("transactionLatency"), + TRANSACTION_ATTEMPT("transactionAttemptCount"); + + private final String metricName; + + MetricType(String metricName) { + this.metricName = metricName; + } + + public String getMetricName() { + return metricName; + } + } } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java index 12a1d2dda..97bd6a640 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java @@ -483,7 +483,6 @@ private void verifyRetries( stubber.verifyAllRequestsSent(); assertEquals(1, attempts[0]); } - Thread.sleep(1000); } } From f17a62b38f333914df0c89be551c1e39cf77c3c9 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:08:53 -0400 Subject: [PATCH 058/103] remove TransactionMetricsContext --- .../ServerSideTransactionRunner.java | 12 +++++--- .../google/cloud/firestore/Transaction.java | 7 ----- .../telemetry/DisabledMetricsUtil.java | 11 +------ .../telemetry/EnabledMetricsUtil.java | 30 +++++-------------- .../firestore/telemetry/MetricsUtil.java | 26 +++++++--------- .../telemetry/TelemetryConstants.java | 1 + 6 files changed, 28 insertions(+), 59 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index 10e736db2..0c7ba5c1e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -26,7 +26,7 @@ import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.rpc.ApiException; -import com.google.cloud.firestore.telemetry.MetricsUtil.TransactionMetricsContext; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; @@ -62,7 +62,7 @@ final class ServerSideTransactionRunner { private int attemptsRemaining; private Span runTransactionSpan; private TraceUtil.Context runTransactionContext; - private TransactionMetricsContext metricsContext; + private MetricsContext metricsContext; /** * @param firestore The active Firestore instance @@ -89,7 +89,11 @@ final class ServerSideTransactionRunner { new ExponentialRetryAlgorithm( firestore.getOptions().getRetrySettings(), CurrentMillisClock.getDefaultClock()); this.nextBackoffAttempt = backoffAlgorithm.createFirstAttempt(); - this.metricsContext = firestore.getOptions().getMetricsUtil().createTransactionMetricsContext(); + this.metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TelemetryConstants.METHOD_NAME_SERVER_SIDE_TRANSACTION); } @Nonnull @@ -114,7 +118,7 @@ ApiFuture runInternally() { try (Scope ignored = runTransactionSpan.makeCurrent()) { runTransactionContext = getTraceUtil().currentContext(); --attemptsRemaining; - metricsContext.incrementAttemptsCount(); + metricsContext.incrementCounter(); ApiFuture result = ApiFutures.catchingAsync( ApiFutures.transformAsync( diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java index ff1b7e2d6..04d83a1a1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java @@ -18,7 +18,6 @@ import com.google.api.core.ApiFuture; import com.google.api.core.InternalExtensionOnly; -import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Context; import java.util.List; @@ -50,12 +49,6 @@ TraceUtil getTraceUtil() { return firestore.getOptions().getTraceUtil(); } - // TODO(Metrics): implement transaction latency and attempt count metrics - @Nonnull - MetricsUtil getMetricsUtil() { - return firestore.getOptions().getMetricsUtil(); - } - @Nonnull Context setTransactionTraceContext(Context context) { return transactionTraceContext = context; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index bebe611c7..0642033c0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -37,16 +37,12 @@ public void recordLatency(MetricType metric) {} @Override public void recordLatency(MetricType metric, Throwable t) {} - } - - class TransactionMetricsContext extends MetricsContext - implements MetricsUtil.TransactionMetricsContext { @Override public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) {} @Override - public void incrementAttemptsCount() {} + public void incrementCounter() {} } @Override @@ -54,11 +50,6 @@ public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(); } - @Override - public TransactionMetricsContext createTransactionMetricsContext() { - return new TransactionMetricsContext(); - } - @Override public void addMetricsTracerFactory(List apiTracerFactories) {} } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index e4c79cc3f..49a4b7829 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -109,11 +109,6 @@ public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(methodName); } - @Override - public TransactionMetricsContext createTransactionMetricsContext() { - return new TransactionMetricsContext(); - } - @Override public void addMetricsTracerFactory(List apiTracerFactories) { addTracerFactory(apiTracerFactories, defaultMetricsProvider); @@ -175,11 +170,13 @@ private void addTracerFactory( class MetricsContext implements MetricsUtil.MetricsContext { private final Stopwatch stopwatch; - private final String methodName; + private int counter; + protected final String methodName; public MetricsContext(String methodName) { this.stopwatch = Stopwatch.createStarted(); this.methodName = methodName; + this.counter = 0; } public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) { @@ -213,20 +210,9 @@ private void recordLatency(MetricType metric, String status) { defaultMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); customMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); } - } - - class TransactionMetricsContext extends MetricsContext - implements MetricsUtil.TransactionMetricsContext { - private int attemptsMade; - private final String METHOD_NAME = "ServerSideTransaction"; - - public TransactionMetricsContext() { - super("ServerSideTransaction"); - this.attemptsMade = 0; - } - public void incrementAttemptsCount() { - attemptsMade++; + public void incrementCounter() { + counter++; } public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) { @@ -247,11 +233,11 @@ public void onSuccess(T result) { } private void recordCounter(MetricType metric, String status) { - Map attributes = createAttributes(status, METHOD_NAME); + Map attributes = createAttributes(status, methodName); defaultMetricsProvider.counterRecorder( - MetricType.TRANSACTION_ATTEMPT, (long) attemptsMade, attributes); + MetricType.TRANSACTION_ATTEMPT, (long) counter, attributes); customMetricsProvider.counterRecorder( - MetricType.TRANSACTION_ATTEMPT, (long) attemptsMade, attributes); + MetricType.TRANSACTION_ATTEMPT, (long) counter, attributes); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 41c6240ce..02ac3a26b 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -56,7 +56,7 @@ static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables // TODO(metrics): The feature is disabled before it is ready for general release. - boolean shouldCreateEnabledInstance = true; + boolean shouldCreateEnabledInstance = false; String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { @@ -85,14 +85,6 @@ static boolean shouldCreateEnabledInstance() { */ abstract MetricsContext createMetricsContext(String methodName); - /** - * Creates a new {@code TransactionMetricsContext} for the given server side transaction and - * starts timing. - * - * @return A new {@code MetricsContext}. - */ - abstract TransactionMetricsContext createTransactionMetricsContext(); - /** * Adds a metrics tracer factory to the given list of API tracer factories. * @@ -100,7 +92,7 @@ static boolean shouldCreateEnabledInstance() { */ abstract void addMetricsTracerFactory(List apiTracerFactories); - /** A context for recording metrics. */ + /** A context for recording metrics in the Firestore SDK. */ interface MetricsContext { /** * If the operation ends in the future, its relevant metrics should be recorded _after_ the @@ -109,17 +101,19 @@ interface MetricsContext { */ void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue); - /** Records end-to-end latency for the current operation. */ + /** Records specific type of latency for the current operation. */ void recordLatency(MetricType metric); - /** Records end-to-end latency for the current operation, which ended with a throwable. */ + /** Records specific type of latency for the current operation, which ended with a throwable. */ void recordLatency(MetricType metric, Throwable t); - } - /** InnerMetricsUtil */ - interface TransactionMetricsContext extends MetricsContext { + /** + * Records the counter value for a metric type _after_ the future has been completed. This + * method "appends" the metrics recording code at the completion of the given future. + */ void recordCounterAtFuture(MetricType metric, ApiFuture futureValue); - void incrementAttemptsCount(); + /** Increments the counter tracked inside the MetricsContext. */ + void incrementCounter(); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index d041e0441..3704ba2b8 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -59,6 +59,7 @@ public interface TelemetryConstants { String METHOD_NAME_PARTITION_QUERY = "PartitionQuery"; String METHOD_NAME_BULK_WRITER_COMMIT = "BulkWriter.Commit"; String METHOD_NAME_RUN_TRANSACTION = "RunTransaction"; + String METHOD_NAME_SERVER_SIDE_TRANSACTION = "ServerSideTransaction"; // OpenTelemetry built-in metrics constants String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw"; From 0662da50dbfc8ee7ced979eb19a50563e28f39bb Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:55:55 -0400 Subject: [PATCH 059/103] Update pom.xml --- google-cloud-firestore/pom.xml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index ae7c3df0a..f9bb321fc 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -134,19 +134,6 @@ - - - org.slf4j - slf4j-api - 2.0.7 - test - - - org.slf4j - slf4j-simple - 2.0.7 - test - junit junit From 2c43cd3a2b2a2924c71974e7738567d5eb10db64 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:40:25 -0400 Subject: [PATCH 060/103] add disabled/enabled MetricsUtil test --- .../FirestoreOpenTelemetryOptions.java | 2 +- .../telemetry/BuiltinMetricsProvider.java | 6 + .../telemetry/EnabledMetricsUtil.java | 27 +- .../telemetry/DisabledMetricsUtilTest.java | 71 ++++++ .../telemetry/EnabledMetricsUtilTest.java | 230 ++++++++++++++++++ .../firestore/telemetry/MetricsUtilTest.java | 85 ++++++- 6 files changed, 410 insertions(+), 11 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index e7aae906e..6b70bed72 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -117,7 +117,7 @@ public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEn * Monitoring. */ @Nonnull - private FirestoreOpenTelemetryOptions.Builder exportBuiltinMetricsToGoogleCloudMonitoring( + public FirestoreOpenTelemetryOptions.Builder exportBuiltinMetricsToGoogleCloudMonitoring( boolean exportBuiltinMetrics) { this.exportBuiltinMetricsToGoogleCloudMonitoring = exportBuiltinMetrics; return this; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index f5733af21..4ba8abe55 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -30,6 +30,7 @@ import com.google.api.gax.tracing.MetricsTracerFactory; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; +import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -72,6 +73,11 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { } } + @VisibleForTesting + public OpenTelemetry getOpenTelemetry() { + return openTelemetry; + } + private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 49a4b7829..23274973c 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -35,6 +35,7 @@ import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; import com.google.cloud.opentelemetry.metric.MetricConfiguration; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -72,8 +73,8 @@ class EnabledMetricsUtil implements MetricsUtil { EnabledMetricsUtil(FirestoreOptions firestoreOptions) { try { - configureDefaultMetricsProvider(firestoreOptions); - configureCustomMetricsProvider(firestoreOptions); + this.defaultMetricsProvider = configureDefaultMetricsProvider(firestoreOptions); + this.customMetricsProvider = configureCustomMetricsProvider(firestoreOptions); } catch (IOException e) { logger.warning( "Unable to create MetricsUtil object for client side metrics, will skip exporting client side metrics" @@ -81,7 +82,18 @@ class EnabledMetricsUtil implements MetricsUtil { } } - private void configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) + @VisibleForTesting + public BuiltinMetricsProvider getCustomMetricsProvider() { + return customMetricsProvider; + } + + @VisibleForTesting + public BuiltinMetricsProvider getDefaultMetricsProvider() { + return defaultMetricsProvider; + } + + @VisibleForTesting + public BuiltinMetricsProvider configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) throws IOException { OpenTelemetry defaultOpenTelemetry; boolean exportBuiltinMetricsToGoogleCloudMonitoring = @@ -91,17 +103,17 @@ private void configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) } else { defaultOpenTelemetry = OpenTelemetry.noop(); } - this.defaultMetricsProvider = new BuiltinMetricsProvider(defaultOpenTelemetry); + return new BuiltinMetricsProvider(defaultOpenTelemetry); } - private void configureCustomMetricsProvider(FirestoreOptions firestoreOptions) + private BuiltinMetricsProvider configureCustomMetricsProvider(FirestoreOptions firestoreOptions) throws IOException { OpenTelemetry customOpenTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); if (customOpenTelemetry == null) { customOpenTelemetry = GlobalOpenTelemetry.get(); } - this.customMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry); + return new BuiltinMetricsProvider(customOpenTelemetry); } @Override @@ -248,7 +260,8 @@ private Map createAttributes(String status, String methodName) { return attributes; } - private String extractErrorStatus(@Nullable Throwable throwable) { + @VisibleForTesting + public String extractErrorStatus(@Nullable Throwable throwable) { if (!(throwable instanceof FirestoreException)) { return StatusCode.Code.UNKNOWN.toString(); } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java new file mode 100644 index 000000000..4d3374c3f --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.firestore.telemetry; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.cloud.firestore.telemetry.DisabledMetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; + +public class DisabledMetricsUtilTest { + private DisabledMetricsUtil disabledMetricsUtil; + + @Before + public void setUp() { + disabledMetricsUtil = new DisabledMetricsUtil(); + } + + @Test + public void createMetricsContextShouldReturnNonNullContext() { + assertThat(disabledMetricsUtil.createMetricsContext("testMethod")).isNotNull(); + assertThat( + disabledMetricsUtil.createMetricsContext("testMethod") + instanceof DisabledMetricsUtil.MetricsContext) + .isTrue(); + } + + @Test + public void shouldNotAddMetricsTracerFactory() { + List factories = new ArrayList<>(); + disabledMetricsUtil.addMetricsTracerFactory(factories); + assertThat(factories).isEmpty(); + } + + @Test + public void metricsContextShouldNotThrow() { + MetricsContext context = disabledMetricsUtil.createMetricsContext("testMethod"); + + // Ensure no exceptions are thrown by no-op methods + try { + context.recordLatency(MetricType.END_TO_END_LATENCY); + context.recordLatency(MetricType.END_TO_END_LATENCY, new Exception("test")); + context.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); + context.incrementCounter(); + + ApiFuture future = ApiFutures.immediateFuture("test"); + context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + } catch (Exception e) { + assertThat(e).isNull(); // Fail the test if any exception is thrown + } + } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java new file mode 100644 index 000000000..2900e918b --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -0,0 +1,230 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.firestore.telemetry; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.*; + +import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.rpc.ApiException; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.cloud.firestore.FirestoreException; +import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; +import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.telemetry.EnabledMetricsUtil.MetricsContext; +import io.grpc.Status; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; + +public class EnabledMetricsUtilTest { + @Before + public void setUp() { + GlobalOpenTelemetry.resetForTest(); + } + + FirestoreOptions.Builder getBaseOptions() { + return FirestoreOptions.newBuilder().setProjectId("test-project").setDatabaseId("(default)"); + } + + EnabledMetricsUtil newEnabledMetricsUtil() { + return new EnabledMetricsUtil(getBaseOptions().build()); + } + + @Test + public void createsDefaultBuiltinMetricsProviderWithBuiltinOpenTelemetryInstance() { + FirestoreOptions firestoreOptions = getBaseOptions().build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); + assertThat(metricsProvider).isNotNull(); + assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); + // The default OpenTelemetry MeterProvider has registered GoogleCloudMonitoringExporter. + assertThat(metricsProvider.getOpenTelemetry().getMeterProvider()) + .isNotEqualTo(MeterProvider.noop()); + } + + @Test + public void canDisableBuiltinMetricsProviderWithFirestoreOpenTelemetryOptions() { + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); + assertThat(metricsProvider).isNotNull(); + assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); + + // Metrics collection is "disabled" with OpenTelemetry No-op meter provider instance + assertThat(metricsProvider.getOpenTelemetry().getMeterProvider()) + .isEqualTo(MeterProvider.noop()); + } + + @Test + public void usesCustomOpenTelemetryFromOptions() { + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); + assertThat(customMetricsProvider).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(openTelemetry); + } + + @Test + public void usesGlobalOpenTelemetryIfCustomOpenTelemetryInstanceNotProvided() { + OpenTelemetrySdk.builder().buildAndRegisterGlobal(); + FirestoreOptions firestoreOptions = getBaseOptions().build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); + assertThat(customMetricsProvider).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get()); + } + + @Test + public void globalOpenTelemetryDefaultsToNoopIfNotRegistered() { + FirestoreOptions firestoreOptions = getBaseOptions().build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); + assertThat(customMetricsProvider).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get()); + assertThat(customMetricsProvider.getOpenTelemetry().getMeterProvider()) + .isEqualTo(MeterProvider.noop()); + } + + @Test + public void usesIndependentOpenTelemetryInstanceInDefaultAndCustomMetricsProvider() { + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + BuiltinMetricsProvider defaultMetricsProvider = metricsUtil.getDefaultMetricsProvider(); + BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); + assertThat(defaultMetricsProvider).isNotNull(); + assertThat(customMetricsProvider).isNotNull(); + assertThat(defaultMetricsProvider).isNotEqualTo(customMetricsProvider); + assertThat(defaultMetricsProvider.getOpenTelemetry()) + .isNotEqualTo(customMetricsProvider.getOpenTelemetry()); + } + + @Test + public void canCreateMetricsContext() { + MetricsContext context = newEnabledMetricsUtil().createMetricsContext("testMethod"); + + assertThat(context).isNotNull(); + assertThat(context instanceof EnabledMetricsUtil.MetricsContext).isTrue(); + assertThat(context.methodName).isEqualTo("testMethod"); + } + + @Test + public void addsMetricsTracerFactoryForDefaultMetricsProvider() { + List factories = new ArrayList<>(); + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + metricsUtil.addMetricsTracerFactory(factories); + // Add tracer factory for default metrics provider only as the custom metrics provider is not + // enabled. + assertThat(factories.size()).isEqualTo(1); + } + + @Test + public void addsMetricsTracerFactoriesForBothMetricsProvider() { + List factories = new ArrayList<>(); + + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + metricsUtil.addMetricsTracerFactory(factories); + // One from each provider + assertThat(factories.size()).isEqualTo(2); + } + + @Test + public void addsMetricsTracerFactoriesIndependentlyForMetricsProviders() { + List factories = new ArrayList<>(); + + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .setOpenTelemetry(openTelemetry) + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + metricsUtil.addMetricsTracerFactory(factories); + // Add tracer factory for custom metrics provider only as the default metrics provider is not + // enabled. + assertThat(factories.size()).isEqualTo(1); + } + + @Test + public void testExtractErrorStatus_firestoreExceptionWithStatus() { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + + FirestoreException firestoreException = + FirestoreException.forApiException( + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false)); + + String errorStatus = metricsUtil.extractErrorStatus(firestoreException); + // TODO(b/305998085):Change this to correct status code + assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); + } + + // @Test + // public void testRecordLatencyAtFutureFailure() { + // EnabledMetricsUtil.MetricsContext context = + // enabledMetricsUtil.createMetricsContext("testMethod"); + // + // FirestoreException exception = new FirestoreException("Test Exception", + // StatusCode.Code.INTERNAL); + // ApiFuture future = ApiFutures.immediateFailedFuture(exception); + // + // context.recordLatencyAtFuture(MetricType.REQUEST_LATENCY, future); + // + // + // } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 4db65d68f..6efddb332 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -24,7 +24,7 @@ public class MetricsUtilTest { @Test - public void defaultOptionsUseEnabledMetricsUtil() { + public void defaultOptionsCreateEnabledMetricsUtil() { MetricsUtil util = MetricsUtil.getInstance( FirestoreOptions.newBuilder() @@ -36,8 +36,40 @@ public void defaultOptionsUseEnabledMetricsUtil() { } @Test - public void testFirestoreWithMetricsEnabled() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "off") + public void createEnabledMetricsUtilWithOnEnv() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "ON") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } + + @Test + public void createEnabledMetricsUtilWithTrueEnv() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "True") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } + + @Test + public void createDisabledMetricsUtilWithOffEnv() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "OFF") .execute( () -> { MetricsUtil util = @@ -50,4 +82,51 @@ public void testFirestoreWithMetricsEnabled() throws Exception { assertThat(util instanceof DisabledMetricsUtil).isTrue(); }); } + + @Test + public void createDisabledMetricsUtilWithFalseEnv() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "false") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof DisabledMetricsUtil).isTrue(); + }); + } + + @Test + public void invalidEnvironmentVariableDefaultsToEnabledMetricsUtil() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "Invalid") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } + + @Test + public void nullEnvironmentVariableDefaultsToEnabledMetricsUtil() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", null) + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } } From 7f5744fbafd80e0090d090dcf66a8c084707d759 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:27:55 -0400 Subject: [PATCH 061/103] add more tests --- .../telemetry/BuiltinMetricsProvider.java | 3 +- .../telemetry/EnabledMetricsUtil.java | 14 +- .../telemetry/ClientIdentifierTest.java | 43 +++++ .../telemetry/EnabledMetricsUtilTest.java | 149 ++++++++++++++++-- 4 files changed, 192 insertions(+), 17 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/ClientIdentifierTest.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 4ba8abe55..a07be4dc4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -180,7 +180,8 @@ public LongCounter getCounter(MetricType metricType) { } } - private Attributes toOtelAttributes(Map attributes) { + @VisibleForTesting + public Attributes toOtelAttributes(Map attributes) { AttributesBuilder attributesBuilder = Attributes.builder(); attributes.forEach(attributesBuilder::put); return attributesBuilder.build(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 23274973c..7b78397a8 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -92,6 +92,16 @@ public BuiltinMetricsProvider getDefaultMetricsProvider() { return defaultMetricsProvider; } + @VisibleForTesting + public void setDefaultMetricsProvider(BuiltinMetricsProvider provider) { + this.defaultMetricsProvider = provider; + } + + @VisibleForTesting + public void setCustomMetricsProvider(BuiltinMetricsProvider provider) { + this.customMetricsProvider = provider; + } + @VisibleForTesting public BuiltinMetricsProvider configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) throws IOException { @@ -263,12 +273,12 @@ private Map createAttributes(String status, String methodName) { @VisibleForTesting public String extractErrorStatus(@Nullable Throwable throwable) { if (!(throwable instanceof FirestoreException)) { - return StatusCode.Code.UNKNOWN.toString(); + return Status.Code.UNKNOWN.toString(); } Status status = ((FirestoreException) throwable).getStatus(); if (status == null) { - return StatusCode.Code.UNKNOWN.toString(); + return Status.Code.UNKNOWN.toString(); } return status.getCode().name(); } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/ClientIdentifierTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/ClientIdentifierTest.java new file mode 100644 index 000000000..ca9418f94 --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/ClientIdentifierTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.firestore.telemetry; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public final class ClientIdentifierTest { + + @Test + public void getClientUidGeneratesUidOnlyOnce() { + String firstUid = ClientIdentifier.getClientUid(); + String secondUid = ClientIdentifier.getClientUid(); + assertEquals(firstUid, secondUid); + } + + @Test + public void generateClientUidHasExpectedFormat() { + String clientUid = ClientIdentifier.getClientUid(); + + String expectedPattern = + String.format( + "%s@%s@%s", + "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", "\\d+", "[\\w.-]+"); + + assertTrue(clientUid.matches(expectedPattern)); + } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java index 2900e918b..08be02fcd 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -16,8 +16,9 @@ package com.google.cloud.firestore.telemetry; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.*; +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode; @@ -26,6 +27,7 @@ import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.telemetry.EnabledMetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import io.grpc.Status; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; @@ -35,6 +37,7 @@ import java.util.List; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; public class EnabledMetricsUtilTest { @Before @@ -198,6 +201,20 @@ public void addsMetricsTracerFactoriesIndependentlyForMetricsProviders() { assertThat(factories.size()).isEqualTo(1); } + @Test + public void extractErrorStatusShouldNotThrowOnNonFirestoreException() { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + + ApiException apiException = + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false); + + String errorStatus = metricsUtil.extractErrorStatus(apiException); + assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); + } + @Test public void testExtractErrorStatus_firestoreExceptionWithStatus() { EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); @@ -214,17 +231,121 @@ public void testExtractErrorStatus_firestoreExceptionWithStatus() { assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); } - // @Test - // public void testRecordLatencyAtFutureFailure() { - // EnabledMetricsUtil.MetricsContext context = - // enabledMetricsUtil.createMetricsContext("testMethod"); - // - // FirestoreException exception = new FirestoreException("Test Exception", - // StatusCode.Code.INTERNAL); - // ApiFuture future = ApiFutures.immediateFailedFuture(exception); - // - // context.recordLatencyAtFuture(MetricType.REQUEST_LATENCY, future); - // - // - // } + @Test + public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() { + EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); + BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); + BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); + enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); + enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); + + MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); + ApiFuture future = ApiFutures.immediateFuture("success"); + + context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + Mockito.verify(mockDefaultProvider) + .latencyRecorder( + Mockito.eq(MetricType.END_TO_END_LATENCY), Mockito.anyDouble(), Mockito.anyMap()); + Mockito.verify(mockCustomProvider) + .latencyRecorder( + Mockito.eq(MetricType.END_TO_END_LATENCY), Mockito.anyDouble(), Mockito.anyMap()); + } + + @Test + public void recordLatencyCalledWhenFutureIsCompletedWithError() { + EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); + BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); + BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); + enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); + enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); + + MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); + FirestoreException firestoreException = + FirestoreException.forApiException( + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false)); + ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); + context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + // TODO(b/305998085):Change this to correct status code + Mockito.verify(mockDefaultProvider) + .latencyRecorder( + Mockito.eq(MetricType.END_TO_END_LATENCY), + Mockito.anyDouble(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + Mockito.verify(mockCustomProvider) + .latencyRecorder( + Mockito.eq(MetricType.END_TO_END_LATENCY), + Mockito.anyDouble(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + } + + @Test + public void recordCounterCalledWhenFutureIsCompletedWithSuccess() { + EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); + BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); + BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); + enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); + enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); + + MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); + ApiFuture future = ApiFutures.immediateFuture("success"); + + context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT, future); + + Mockito.verify(mockDefaultProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT), Mockito.anyLong(), Mockito.anyMap()); + Mockito.verify(mockCustomProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT), Mockito.anyLong(), Mockito.anyMap()); + } + + @Test + public void recordCounterCalledWhenFutureIsCompletedWithError() { + EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); + BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); + BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); + enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); + enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); + + MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); + FirestoreException firestoreException = + FirestoreException.forApiException( + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false)); + ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); + context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT, future); + + // TODO(b/305998085):Change this to correct status code + Mockito.verify(mockDefaultProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT), + Mockito.anyLong(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + Mockito.verify(mockCustomProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT), + Mockito.anyLong(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + } } From e6694c69f7da4e03130ba00cf715def64e6de13c Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:05:08 -0500 Subject: [PATCH 062/103] add unit tests for BuiltInMetricsProvider --- .../telemetry/BuiltinMetricsProvider.java | 8 +- .../telemetry/EnabledMetricsUtil.java | 19 +- .../telemetry/BuiltinMetricsProviderTest.java | 234 +++++++++++++++++ .../telemetry/CompositeApiTracerTest.java | 237 ++++++++++++++++++ .../telemetry/EnabledMetricsUtilTest.java | 126 +++++----- 5 files changed, 546 insertions(+), 78 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 2ec63a648..73887338c 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -31,7 +31,6 @@ import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.common.annotations.VisibleForTesting; - import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -69,13 +68,13 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { this.staticAttributes = createStaticAttributes(); if (openTelemetry.getMeterProvider() != MeterProvider.noop()) { - configureRPCLayerMetrics(); configureSDKLayerMetrics(); + configureRPCLayerMetrics(); } } @VisibleForTesting - public OpenTelemetry getOpenTelemetry() { + OpenTelemetry getOpenTelemetry() { return openTelemetry; } @@ -180,7 +179,8 @@ public LongCounter getCounter(MetricType metricType) { } } - private Attributes toOtelAttributes(Map attributes) { + @VisibleForTesting + Attributes toOtelAttributes(Map attributes) { AttributesBuilder attributesBuilder = Attributes.builder(); attributes.forEach(attributesBuilder::put); return attributesBuilder.build(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index b24899680..5e9f4ff7a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -83,27 +83,16 @@ class EnabledMetricsUtil implements MetricsUtil { } @VisibleForTesting - public BuiltinMetricsProvider getCustomMetricsProvider() { + BuiltinMetricsProvider getCustomMetricsProvider() { return customMetricsProvider; } @VisibleForTesting - public BuiltinMetricsProvider getDefaultMetricsProvider() { + BuiltinMetricsProvider getDefaultMetricsProvider() { return defaultMetricsProvider; } - @VisibleForTesting - public void setDefaultMetricsProvider(BuiltinMetricsProvider provider) { - this.defaultMetricsProvider = provider; - } - - @VisibleForTesting - public void setCustomMetricsProvider(BuiltinMetricsProvider provider) { - this.customMetricsProvider = provider; - } - - @VisibleForTesting - public BuiltinMetricsProvider configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) + private BuiltinMetricsProvider configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) throws IOException { OpenTelemetry defaultOpenTelemetry; boolean exportBuiltinMetricsToGoogleCloudMonitoring = @@ -271,7 +260,7 @@ private Map createAttributes(String status, String methodName) { } @VisibleForTesting - public String extractErrorStatus(@Nullable Throwable throwable) { + String extractErrorStatus(@Nullable Throwable throwable) { if (!(throwable instanceof FirestoreException)) { return Status.Code.UNKNOWN.toString(); } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java new file mode 100644 index 000000000..1f019cac4 --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java @@ -0,0 +1,234 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.firestore.telemetry; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.*; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class BuiltinMetricsProviderTest { + private OpenTelemetry mockOpenTelemetry; + private Meter mockMeter; + private DoubleHistogram mockEndToEndLatency; + private DoubleHistogram mockFirstResponseLatency; + private DoubleHistogram mockTransactionLatency; + private LongCounter mockTransactionAttemptCount; + private BuiltinMetricsProvider metricsProvider; + + @Before + public void setUp() { + // Mock OpenTelemetry components + mockOpenTelemetry = mock(OpenTelemetry.class); + MeterProvider mockMeterProvider = mock(MeterProvider.class); + when(mockOpenTelemetry.getMeterProvider()).thenReturn(mockMeterProvider); + mockMeter = mock(Meter.class); + when(mockOpenTelemetry.getMeter(anyString())).thenReturn(mockMeter); + MeterBuilder mockMeterBuilder = mock(MeterBuilder.class); + when(mockMeterBuilder.setInstrumentationVersion(anyString())).thenReturn(mockMeterBuilder); + when(mockMeterBuilder.build()).thenReturn(mockMeter); + when(mockOpenTelemetry.meterBuilder(anyString())).thenReturn(mockMeterBuilder); + + // Mock Histogram and Counter builders + DoubleHistogramBuilder mockHistogramBuilder = mock(DoubleHistogramBuilder.class); + when(mockMeter.histogramBuilder(anyString())).thenReturn(mockHistogramBuilder); + when(mockHistogramBuilder.setDescription(anyString())).thenReturn(mockHistogramBuilder); + when(mockHistogramBuilder.setUnit(anyString())).thenReturn(mockHistogramBuilder); + LongCounterBuilder mockCounterBuilder = mock(LongCounterBuilder.class); + when(mockMeter.counterBuilder(anyString())).thenReturn(mockCounterBuilder); + when(mockCounterBuilder.setDescription(anyString())).thenReturn(mockCounterBuilder); + when(mockCounterBuilder.setUnit(anyString())).thenReturn(mockCounterBuilder); + + // Mock Histogram and Counter objects + mockEndToEndLatency = mock(DoubleHistogram.class); + mockFirstResponseLatency = mock(DoubleHistogram.class); + mockTransactionLatency = mock(DoubleHistogram.class); + mockTransactionAttemptCount = mock(LongCounter.class); + + // Configure mockHistogramBuilder to return each histogram mock in sequence + // The SDK configures 4 SDK layer metrics first, the other RPC metrics created in GAX layer is + // not tested here. + when(mockHistogramBuilder.build()) + .thenReturn(mockEndToEndLatency) + .thenReturn(mockFirstResponseLatency) + .thenReturn(mockTransactionLatency); + + // Mock the counter builder to return the transaction attempt counter + when(mockCounterBuilder.build()).thenReturn(mockTransactionAttemptCount); + + // Set up BuiltinMetricsProvider instance with mocked OpenTelemetry + metricsProvider = new BuiltinMetricsProvider(mockOpenTelemetry); + } + + @Test + public void testConfigureSDKLayerMetrics() { + metricsProvider = new BuiltinMetricsProvider(mockOpenTelemetry); + + assertNotNull(metricsProvider.getHistogram(MetricType.END_TO_END_LATENCY)); + assertNotNull(metricsProvider.getHistogram(MetricType.FIRST_RESPONSE_LATENCY)); + assertNotNull(metricsProvider.getHistogram(MetricType.TRANSACTION_LATENCY)); + assertNotNull(metricsProvider.getCounter(MetricType.TRANSACTION_ATTEMPT_COUNT)); + } + + @Test + public void testLatencyRecorder() { + Map attributes = new HashMap<>(); + attributes.put("attribute1", "value1"); + attributes.put("attribute2", "value2"); + + // Record end-to-end latency + metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 100.0, attributes); + verify(mockEndToEndLatency).record(eq(100.0), any(Attributes.class)); + + // Record first response latency + metricsProvider.latencyRecorder(MetricType.FIRST_RESPONSE_LATENCY, 50.0, attributes); + verify(mockFirstResponseLatency).record(eq(50.0), any(Attributes.class)); + + // Record transaction latency + metricsProvider.latencyRecorder(MetricType.TRANSACTION_LATENCY, 200.0, attributes); + verify(mockTransactionLatency).record(eq(200.0), any(Attributes.class)); + } + + // @Test + // void testLatencyRecorder_withMultipleAttributes() { + // Map attributes = new HashMap<>(); + // attributes.put("attribute1", "value1"); + // attributes.put("attribute2", "value2"); + + // metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 120.0, attributes); + + // // Verify the histogram was called with the correct values + // verify(mockEndToEndLatency).record(eq(120.0), argThat(attrs -> + // attrs.getAttribute("attribute2").equals("value2"))); + // } + + @Test + public void testCounterRecorder() { + Map attributes = new HashMap<>(); + attributes.put("key", "value"); + + // Record transaction attempt count + metricsProvider.counterRecorder(MetricType.TRANSACTION_ATTEMPT_COUNT, 5, attributes); + verify(mockTransactionAttemptCount).add(eq(5L), any(Attributes.class)); + } + + @Test + public void testGetHistogram_forMetricType() { + DoubleHistogram mockHistogram = metricsProvider.getHistogram(MetricType.END_TO_END_LATENCY); + assertEquals(mockEndToEndLatency, mockHistogram); + + mockHistogram = metricsProvider.getHistogram(MetricType.FIRST_RESPONSE_LATENCY); + assertEquals(mockFirstResponseLatency, mockHistogram); + + mockHistogram = metricsProvider.getHistogram(MetricType.TRANSACTION_LATENCY); + assertEquals(mockTransactionLatency, mockHistogram); + } + + @Test + public void testGetHistogramWithInvalidMetricType() { + Exception exception = + assertThrows( + IllegalArgumentException.class, + () -> metricsProvider.getHistogram(MetricType.TRANSACTION_ATTEMPT_COUNT)); + assertEquals("Unknown latency MetricType: TRANSACTION_ATTEMPT_COUNT", exception.getMessage()); + } + + @Test + public void testGetCounter_forMetricType() { + LongCounter mockCounter = metricsProvider.getCounter(MetricType.TRANSACTION_ATTEMPT_COUNT); + assertEquals(mockTransactionAttemptCount, mockCounter); + } + + @Test + public void testGetCounterWithInvalidMetricType() { + Exception exception = + assertThrows( + IllegalArgumentException.class, + () -> metricsProvider.getCounter(MetricType.END_TO_END_LATENCY)); + assertEquals("Unknown counter MetricType: END_TO_END_LATENCY", exception.getMessage()); + } + + @Test + public void testLatencyRecorder_exceptionHandling() { + // Set up mock to throw an exception + doThrow(new RuntimeException("Test Exception")) + .when(mockEndToEndLatency) + .record(anyDouble(), any(Attributes.class)); + + // Capture log output + Logger logger = Logger.getLogger(BuiltinMetricsProvider.class.getName()); + + try { + Map attributes = new HashMap<>(); + attributes.put("key1", "value1"); + metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 100.0, attributes); + + // Check that the exception was logged + assertTrue(logger.getHandlers().length > 0); + } finally { + + } + } + + @Test + public void testNoMetricsCollectedWhenDisabledNOOP() { + + Map attributes = new HashMap<>(); + attributes.put("key", "disabledTest"); + + metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 50.0, attributes); + metricsProvider.counterRecorder(MetricType.TRANSACTION_ATTEMPT_COUNT, 1, attributes); + + // Verify that the methods were not called + verify(mockEndToEndLatency, times(0)).record(anyDouble(), any(Attributes.class)); + verify(mockTransactionAttemptCount, times(0)).add(anyLong(), any(Attributes.class)); + } + + // @Test + // void testToOtelAttributes() { + // Map attributes = new HashMap<>(); + // attributes.put("key1", "value1"); + // attributes.put("key2", "value2"); + + // Attributes otelAttributes = metricsProvider.toOtelAttributes(attributes); + // assertEquals("value1", otelAttributes.get("key1")); + // assertEquals("value2", otelAttributes.get("key2")); + // } + +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java new file mode 100644 index 000000000..b99d713b6 --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java @@ -0,0 +1,237 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.firestore.telemetry; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import com.google.api.gax.tracing.ApiTracer; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.threeten.bp.Duration; + +@RunWith(JUnit4.class) +public class CompositeApiTracerTest { + + private final ApiTracer child1 = mock(ApiTracer.class); + private final ApiTracer child2 = mock(ApiTracer.class); + private final CompositeApiTracer compositeApiTracer = + new CompositeApiTracer(Arrays.asList(child1, child2)); + + @Test + public void inScope_callsInScopeOnChildren() { + compositeApiTracer.inScope(); + + verify(child1, times(1)).inScope(); + verify(child2, times(1)).inScope(); + } + + @Test + public void inScope_closesChildScopes() { + ApiTracer.Scope childScope1 = mock(ApiTracer.Scope.class); + ApiTracer.Scope childScope2 = mock(ApiTracer.Scope.class); + when(child1.inScope()).thenReturn(childScope1); + when(child2.inScope()).thenReturn(childScope2); + + compositeApiTracer.inScope().close(); + + verify(child1).inScope(); + verify(child2).inScope(); + verify(childScope1).close(); + verify(childScope2).close(); + } + + @Test + public void operationSucceeded_callsOperationSucceededOnChildren() { + compositeApiTracer.operationSucceeded(); + + verify(child1, times(1)).operationSucceeded(); + verify(child2, times(1)).operationSucceeded(); + } + + @Test + public void operationCancelled_callsOperationCancelledOnChildren() { + compositeApiTracer.operationCancelled(); + + verify(child1, times(1)).operationCancelled(); + verify(child2, times(1)).operationCancelled(); + } + + @Test + public void operationFailed_callsOperationFailedOnChildren() { + Exception error = new Exception("Test error"); + compositeApiTracer.operationFailed(error); + + verify(child1, times(1)).operationFailed(error); + verify(child2, times(1)).operationFailed(error); + } + + @Test + public void connectionSelected_callsConnectionSelectedOnChildren() { + String connectionId = "connection-id"; + compositeApiTracer.connectionSelected(connectionId); + + verify(child1, times(1)).connectionSelected(connectionId); + verify(child2, times(1)).connectionSelected(connectionId); + } + + @Test + public void attemptStarted_callsAttemptStartedOnChildren() { + int attemptNumber = 1; + compositeApiTracer.attemptStarted(attemptNumber); + + verify(child1, times(1)).attemptStarted(null, attemptNumber); + verify(child2, times(1)).attemptStarted(null, attemptNumber); + } + + @Test + public void attemptStartedWithRequest_callsAttemptStartedOnChildren() { + Object request = new Object(); + int attemptNumber = 1; + compositeApiTracer.attemptStarted(request, attemptNumber); + + verify(child1, times(1)).attemptStarted(request, attemptNumber); + verify(child2, times(1)).attemptStarted(request, attemptNumber); + } + + @Test + public void attemptSucceeded_callsAttemptSucceededOnChildren() { + compositeApiTracer.attemptSucceeded(); + + verify(child1, times(1)).attemptSucceeded(); + verify(child2, times(1)).attemptSucceeded(); + } + + @Test + public void attemptCancelled_callsAttemptCancelledOnChildren() { + compositeApiTracer.attemptCancelled(); + + verify(child1, times(1)).attemptCancelled(); + verify(child2, times(1)).attemptCancelled(); + } + + @Test + public void attemptFailed_callsAttemptFailedOnChildren() { + Exception error = new Exception("Test error"); + Duration delay = Duration.ofSeconds(1); + compositeApiTracer.attemptFailed(error, delay); + + verify(child1, times(1)).attemptFailed(error, delay); + verify(child2, times(1)).attemptFailed(error, delay); + } + + @Test + public void attemptFailedDuration_callsAttemptFailedDurationOnChildren() { + Exception error = new Exception("Test error"); + java.time.Duration delay = java.time.Duration.ofSeconds(1); + compositeApiTracer.attemptFailedDuration(error, delay); + + verify(child1, times(1)).attemptFailedDuration(error, delay); + verify(child2, times(1)).attemptFailedDuration(error, delay); + } + + @Test + public void attemptFailedRetriesExhausted_callsAttemptFailedRetriesExhaustedOnChildren() { + Exception error = new Exception("Test error"); + compositeApiTracer.attemptFailedRetriesExhausted(error); + + verify(child1, times(1)).attemptFailedRetriesExhausted(error); + verify(child2, times(1)).attemptFailedRetriesExhausted(error); + } + + @Test + public void attemptPermanentFailure_callsAttemptPermanentFailureOnChildren() { + Exception error = new Exception("Test error"); + compositeApiTracer.attemptPermanentFailure(error); + + verify(child1, times(1)).attemptPermanentFailure(error); + verify(child2, times(1)).attemptPermanentFailure(error); + } + + @Test + public void lroStartFailed_callsLroStartFailedOnChildren() { + Exception error = new Exception("Test error"); + compositeApiTracer.lroStartFailed(error); + + verify(child1, times(1)).lroStartFailed(error); + verify(child2, times(1)).lroStartFailed(error); + } + + @Test + public void lroStartSucceeded_callsLroStartSucceededOnChildren() { + compositeApiTracer.lroStartSucceeded(); + + verify(child1, times(1)).lroStartSucceeded(); + verify(child2, times(1)).lroStartSucceeded(); + } + + @Test + public void responseReceived_callsResponseReceivedOnChildren() { + compositeApiTracer.responseReceived(); + + verify(child1, times(1)).responseReceived(); + verify(child2, times(1)).responseReceived(); + } + + @Test + public void requestSent_callsRequestSentOnChildren() { + compositeApiTracer.requestSent(); + + verify(child1, times(1)).requestSent(); + verify(child2, times(1)).requestSent(); + } + + @Test + public void batchRequestSent_callsBatchRequestSentOnChildren() { + long elementCount = 10; + long requestSize = 100; + compositeApiTracer.batchRequestSent(elementCount, requestSize); + + verify(child1, times(1)).batchRequestSent(elementCount, requestSize); + verify(child2, times(1)).batchRequestSent(elementCount, requestSize); + } + + @Test + public void noInteractions_whenNoChildren() { + CompositeApiTracer emptyTracer = new CompositeApiTracer(Arrays.asList()); + + emptyTracer.inScope(); + emptyTracer.operationSucceeded(); + emptyTracer.operationCancelled(); + emptyTracer.operationFailed(new Exception("Test error")); + emptyTracer.connectionSelected("connection-id"); + emptyTracer.attemptStarted(1); + emptyTracer.attemptStarted(new Object(), 1); + emptyTracer.attemptSucceeded(); + emptyTracer.attemptCancelled(); + emptyTracer.attemptFailed(new Exception("Test error"), Duration.ofSeconds(1)); + emptyTracer.attemptFailedDuration(new Exception("Test error"), java.time.Duration.ofSeconds(1)); + emptyTracer.attemptFailedRetriesExhausted(new Exception("Test error")); + emptyTracer.attemptPermanentFailure(new Exception("Test error")); + emptyTracer.lroStartFailed(new Exception("Test error")); + emptyTracer.lroStartSucceeded(); + emptyTracer.responseReceived(); + emptyTracer.requestSent(); + emptyTracer.batchRequestSent(10, 100); + + verifyNoMoreInteractions(child1, child2); + } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java index 08be02fcd..f49122f37 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -16,6 +16,9 @@ package com.google.cloud.firestore.telemetry; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; @@ -33,16 +36,40 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner.class) public class EnabledMetricsUtilTest { + + private BuiltinMetricsProvider defaultProvider; + private BuiltinMetricsProvider customProvider; + @Before public void setUp() { GlobalOpenTelemetry.resetForTest(); + defaultProvider = Mockito.mock(BuiltinMetricsProvider.class); + customProvider = Mockito.mock(BuiltinMetricsProvider.class); + } + + // Use reflection to inject mock metrics provider to the util class + private void injectMockProviders(EnabledMetricsUtil metricsUtil) throws Exception { + injectMockProvider(metricsUtil, "defaultMetricsProvider", defaultProvider); + injectMockProvider(metricsUtil, "customMetricsProvider", customProvider); + } + + private void injectMockProvider( + EnabledMetricsUtil metricsUtil, String fieldName, BuiltinMetricsProvider provider) + throws Exception { + Field field = EnabledMetricsUtil.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(metricsUtil, provider); } FirestoreOptions.Builder getBaseOptions() { @@ -55,8 +82,7 @@ EnabledMetricsUtil newEnabledMetricsUtil() { @Test public void createsDefaultBuiltinMetricsProviderWithBuiltinOpenTelemetryInstance() { - FirestoreOptions firestoreOptions = getBaseOptions().build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); assertThat(metricsProvider).isNotNull(); assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); @@ -114,8 +140,7 @@ public void usesGlobalOpenTelemetryIfCustomOpenTelemetryInstanceNotProvided() { @Test public void globalOpenTelemetryDefaultsToNoopIfNotRegistered() { - FirestoreOptions firestoreOptions = getBaseOptions().build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); assertThat(customMetricsProvider).isNotNull(); @@ -204,13 +229,11 @@ public void addsMetricsTracerFactoriesIndependentlyForMetricsProviders() { @Test public void extractErrorStatusShouldNotThrowOnNonFirestoreException() { EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - ApiException apiException = new ApiException( new IllegalStateException("Mock batchWrite failed in test"), GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), false); - String errorStatus = metricsUtil.extractErrorStatus(apiException); assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); } @@ -218,48 +241,39 @@ public void extractErrorStatusShouldNotThrowOnNonFirestoreException() { @Test public void testExtractErrorStatus_firestoreExceptionWithStatus() { EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - FirestoreException firestoreException = FirestoreException.forApiException( new ApiException( new IllegalStateException("Mock batchWrite failed in test"), GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), false)); - String errorStatus = metricsUtil.extractErrorStatus(firestoreException); // TODO(b/305998085):Change this to correct status code assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); } @Test - public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() { - EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); - BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); - BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); - enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); - enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); - - MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); + public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); + + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); ApiFuture future = ApiFutures.immediateFuture("success"); context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); - Mockito.verify(mockDefaultProvider) - .latencyRecorder( - Mockito.eq(MetricType.END_TO_END_LATENCY), Mockito.anyDouble(), Mockito.anyMap()); - Mockito.verify(mockCustomProvider) - .latencyRecorder( - Mockito.eq(MetricType.END_TO_END_LATENCY), Mockito.anyDouble(), Mockito.anyMap()); + + verify(defaultProvider) + .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); + verify(customProvider) + .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); } @Test - public void recordLatencyCalledWhenFutureIsCompletedWithError() { - EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); - BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); - BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); - enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); - enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); - - MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); + public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); + + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); FirestoreException firestoreException = FirestoreException.forApiException( new ApiException( @@ -268,8 +282,9 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() { false)); ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + // TODO(b/305998085):Change this to correct status code - Mockito.verify(mockDefaultProvider) + Mockito.verify(defaultProvider) .latencyRecorder( Mockito.eq(MetricType.END_TO_END_LATENCY), Mockito.anyDouble(), @@ -278,7 +293,7 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() { attributes .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) .equals(Status.Code.UNKNOWN.toString()))); - Mockito.verify(mockCustomProvider) + Mockito.verify(customProvider) .latencyRecorder( Mockito.eq(MetricType.END_TO_END_LATENCY), Mockito.anyDouble(), @@ -290,35 +305,28 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() { } @Test - public void recordCounterCalledWhenFutureIsCompletedWithSuccess() { - EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); - BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); - BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); - enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); - enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); - - MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); - ApiFuture future = ApiFutures.immediateFuture("success"); + public void recordCounterCalledWhenFutureIsCompletedWithSuccess() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); - context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT, future); + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); + ApiFuture future = ApiFutures.immediateFuture("success"); + context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, future); - Mockito.verify(mockDefaultProvider) + Mockito.verify(defaultProvider) .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT), Mockito.anyLong(), Mockito.anyMap()); - Mockito.verify(mockCustomProvider) + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.anyMap()); + Mockito.verify(customProvider) .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT), Mockito.anyLong(), Mockito.anyMap()); + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.anyMap()); } @Test - public void recordCounterCalledWhenFutureIsCompletedWithError() { - EnabledMetricsUtil enabledMetricsUtil = newEnabledMetricsUtil(); - BuiltinMetricsProvider mockDefaultProvider = Mockito.mock(BuiltinMetricsProvider.class); - BuiltinMetricsProvider mockCustomProvider = Mockito.mock(BuiltinMetricsProvider.class); - enabledMetricsUtil.setDefaultMetricsProvider(mockDefaultProvider); - enabledMetricsUtil.setCustomMetricsProvider(mockCustomProvider); - - MetricsContext context = enabledMetricsUtil.createMetricsContext("testMethod"); + public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); + + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); FirestoreException firestoreException = FirestoreException.forApiException( new ApiException( @@ -326,21 +334,21 @@ public void recordCounterCalledWhenFutureIsCompletedWithError() { GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), false)); ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); - context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT, future); + context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, future); // TODO(b/305998085):Change this to correct status code - Mockito.verify(mockDefaultProvider) + Mockito.verify(defaultProvider) .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT), + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.argThat( attributes -> attributes .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) .equals(Status.Code.UNKNOWN.toString()))); - Mockito.verify(mockCustomProvider) + Mockito.verify(customProvider) .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT), + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.argThat( attributes -> From 53cb8da8383897bef99798b114fa16c7105af5f0 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:45:05 -0500 Subject: [PATCH 063/103] finalize unit tests --- .../telemetry/BuiltinMetricsProvider.java | 3 +- .../telemetry/BuiltinMetricsProviderTest.java | 143 ++++++------------ .../telemetry/DisabledMetricsUtilTest.java | 2 +- .../telemetry/EnabledMetricsUtilTest.java | 77 ++++------ .../firestore/telemetry/MetricsUtilTest.java | 10 +- 5 files changed, 88 insertions(+), 147 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 73887338c..d981e7693 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -179,8 +179,7 @@ public LongCounter getCounter(MetricType metricType) { } } - @VisibleForTesting - Attributes toOtelAttributes(Map attributes) { + private Attributes toOtelAttributes(Map attributes) { AttributesBuilder attributesBuilder = Attributes.builder(); attributes.forEach(attributesBuilder::put); return attributesBuilder.build(); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java index 1f019cac4..20a6c0ae6 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java @@ -15,28 +15,25 @@ */ package com.google.cloud.firestore.telemetry; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyDouble; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.*; import java.util.HashMap; import java.util.Map; -import java.util.logging.Logger; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -75,15 +72,14 @@ public void setUp() { when(mockCounterBuilder.setDescription(anyString())).thenReturn(mockCounterBuilder); when(mockCounterBuilder.setUnit(anyString())).thenReturn(mockCounterBuilder); - // Mock Histogram and Counter objects mockEndToEndLatency = mock(DoubleHistogram.class); mockFirstResponseLatency = mock(DoubleHistogram.class); mockTransactionLatency = mock(DoubleHistogram.class); mockTransactionAttemptCount = mock(LongCounter.class); - // Configure mockHistogramBuilder to return each histogram mock in sequence - // The SDK configures 4 SDK layer metrics first, the other RPC metrics created in GAX layer is - // not tested here. + // Configure mockHistogramBuilder to return the first 3 histogram mocks created in sequence. + // The SDK configures 4 SDK layer metrics first, and then the RPC metrics gets created in GAX + // layer when(mockHistogramBuilder.build()) .thenReturn(mockEndToEndLatency) .thenReturn(mockFirstResponseLatency) @@ -97,9 +93,8 @@ public void setUp() { } @Test - public void testConfigureSDKLayerMetrics() { + public void SDKLayerMetricsConfiguredSuccessfully() { metricsProvider = new BuiltinMetricsProvider(mockOpenTelemetry); - assertNotNull(metricsProvider.getHistogram(MetricType.END_TO_END_LATENCY)); assertNotNull(metricsProvider.getHistogram(MetricType.FIRST_RESPONSE_LATENCY)); assertNotNull(metricsProvider.getHistogram(MetricType.TRANSACTION_LATENCY)); @@ -107,49 +102,7 @@ public void testConfigureSDKLayerMetrics() { } @Test - public void testLatencyRecorder() { - Map attributes = new HashMap<>(); - attributes.put("attribute1", "value1"); - attributes.put("attribute2", "value2"); - - // Record end-to-end latency - metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 100.0, attributes); - verify(mockEndToEndLatency).record(eq(100.0), any(Attributes.class)); - - // Record first response latency - metricsProvider.latencyRecorder(MetricType.FIRST_RESPONSE_LATENCY, 50.0, attributes); - verify(mockFirstResponseLatency).record(eq(50.0), any(Attributes.class)); - - // Record transaction latency - metricsProvider.latencyRecorder(MetricType.TRANSACTION_LATENCY, 200.0, attributes); - verify(mockTransactionLatency).record(eq(200.0), any(Attributes.class)); - } - - // @Test - // void testLatencyRecorder_withMultipleAttributes() { - // Map attributes = new HashMap<>(); - // attributes.put("attribute1", "value1"); - // attributes.put("attribute2", "value2"); - - // metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 120.0, attributes); - - // // Verify the histogram was called with the correct values - // verify(mockEndToEndLatency).record(eq(120.0), argThat(attrs -> - // attrs.getAttribute("attribute2").equals("value2"))); - // } - - @Test - public void testCounterRecorder() { - Map attributes = new HashMap<>(); - attributes.put("key", "value"); - - // Record transaction attempt count - metricsProvider.counterRecorder(MetricType.TRANSACTION_ATTEMPT_COUNT, 5, attributes); - verify(mockTransactionAttemptCount).add(eq(5L), any(Attributes.class)); - } - - @Test - public void testGetHistogram_forMetricType() { + public void getHistogramReturnsHistogramsInstrumentCorrectly() { DoubleHistogram mockHistogram = metricsProvider.getHistogram(MetricType.END_TO_END_LATENCY); assertEquals(mockEndToEndLatency, mockHistogram); @@ -161,7 +114,7 @@ public void testGetHistogram_forMetricType() { } @Test - public void testGetHistogramWithInvalidMetricType() { + public void getHistogramThrowsOnInvalidMetricType() { Exception exception = assertThrows( IllegalArgumentException.class, @@ -170,13 +123,13 @@ public void testGetHistogramWithInvalidMetricType() { } @Test - public void testGetCounter_forMetricType() { + public void getCounterReturnsCounterInstrumentCorrectly() { LongCounter mockCounter = metricsProvider.getCounter(MetricType.TRANSACTION_ATTEMPT_COUNT); assertEquals(mockTransactionAttemptCount, mockCounter); } @Test - public void testGetCounterWithInvalidMetricType() { + public void getCounterThrowsOnInvalidMetricType() { Exception exception = assertThrows( IllegalArgumentException.class, @@ -185,50 +138,54 @@ public void testGetCounterWithInvalidMetricType() { } @Test - public void testLatencyRecorder_exceptionHandling() { - // Set up mock to throw an exception - doThrow(new RuntimeException("Test Exception")) - .when(mockEndToEndLatency) - .record(anyDouble(), any(Attributes.class)); - - // Capture log output - Logger logger = Logger.getLogger(BuiltinMetricsProvider.class.getName()); + public void latencyRecorderTriggersCorrectInstrument() { + Map attributes = new HashMap<>(); + attributes.put("attribute", "value"); - try { - Map attributes = new HashMap<>(); - attributes.put("key1", "value1"); - metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 100.0, attributes); + metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 100.0, attributes); + verify(mockEndToEndLatency) + .record( + eq(100.0), + argThat( + arguments -> arguments.get(AttributeKey.stringKey("attribute")).equals("value"))); - // Check that the exception was logged - assertTrue(logger.getHandlers().length > 0); - } finally { + metricsProvider.latencyRecorder(MetricType.FIRST_RESPONSE_LATENCY, 50.0, attributes); + verify(mockFirstResponseLatency) + .record( + eq(50.0), + argThat( + arguments -> arguments.get(AttributeKey.stringKey("attribute")).equals("value"))); - } + metricsProvider.latencyRecorder(MetricType.TRANSACTION_LATENCY, 200.0, attributes); + verify(mockTransactionLatency) + .record( + eq(200.0), + argThat( + arguments -> arguments.get(AttributeKey.stringKey("attribute")).equals("value"))); } @Test - public void testNoMetricsCollectedWhenDisabledNOOP() { - + public void counterRecorderTriggersCorrectInstrument() { Map attributes = new HashMap<>(); - attributes.put("key", "disabledTest"); - - metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 50.0, attributes); - metricsProvider.counterRecorder(MetricType.TRANSACTION_ATTEMPT_COUNT, 1, attributes); + attributes.put("key", "value"); - // Verify that the methods were not called - verify(mockEndToEndLatency, times(0)).record(anyDouble(), any(Attributes.class)); - verify(mockTransactionAttemptCount, times(0)).add(anyLong(), any(Attributes.class)); + metricsProvider.counterRecorder(MetricType.TRANSACTION_ATTEMPT_COUNT, 5, attributes); + verify(mockTransactionAttemptCount).add(eq(5L), any(Attributes.class)); } - // @Test - // void testToOtelAttributes() { - // Map attributes = new HashMap<>(); - // attributes.put("key1", "value1"); - // attributes.put("key2", "value2"); - - // Attributes otelAttributes = metricsProvider.toOtelAttributes(attributes); - // assertEquals("value1", otelAttributes.get("key1")); - // assertEquals("value2", otelAttributes.get("key2")); - // } + @Test + public void handlesNoopMetricProviderGracefully() throws Exception { + BuiltinMetricsProvider provider = new BuiltinMetricsProvider(OpenTelemetry.noop()); + Map attributes = new HashMap<>(); + attributes.put("key", "disabledTest"); + try { + provider.latencyRecorder(MetricType.END_TO_END_LATENCY, 50.0, attributes); + provider.latencyRecorder(MetricType.FIRST_RESPONSE_LATENCY, 100.0, attributes); + provider.latencyRecorder(MetricType.TRANSACTION_LATENCY, 150.0, attributes); + provider.counterRecorder(MetricType.TRANSACTION_ATTEMPT_COUNT, 1, attributes); + } catch (Exception e) { + assertThat(e).isNull(); // Fail the test if any exception is thrown + } + } } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java index 4d3374c3f..8d2695610 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java @@ -52,7 +52,7 @@ public void shouldNotAddMetricsTracerFactory() { } @Test - public void metricsContextShouldNotThrow() { + public void shouldNotThrowOnMetricsCollection() { MetricsContext context = disabledMetricsUtil.createMetricsContext("testMethod"); // Ensure no exceptions are thrown by no-op methods diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java index f49122f37..ee1d2992f 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -58,20 +58,6 @@ public void setUp() { customProvider = Mockito.mock(BuiltinMetricsProvider.class); } - // Use reflection to inject mock metrics provider to the util class - private void injectMockProviders(EnabledMetricsUtil metricsUtil) throws Exception { - injectMockProvider(metricsUtil, "defaultMetricsProvider", defaultProvider); - injectMockProvider(metricsUtil, "customMetricsProvider", customProvider); - } - - private void injectMockProvider( - EnabledMetricsUtil metricsUtil, String fieldName, BuiltinMetricsProvider provider) - throws Exception { - Field field = EnabledMetricsUtil.class.getDeclaredField(fieldName); - field.setAccessible(true); - field.set(metricsUtil, provider); - } - FirestoreOptions.Builder getBaseOptions() { return FirestoreOptions.newBuilder().setProjectId("test-project").setDatabaseId("(default)"); } @@ -101,10 +87,10 @@ public void canDisableBuiltinMetricsProviderWithFirestoreOpenTelemetryOptions() .build()) .build(); EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); assertThat(metricsProvider).isNotNull(); assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); - // Metrics collection is "disabled" with OpenTelemetry No-op meter provider instance assertThat(metricsProvider.getOpenTelemetry().getMeterProvider()) .isEqualTo(MeterProvider.noop()); @@ -129,29 +115,16 @@ public void usesCustomOpenTelemetryFromOptions() { @Test public void usesGlobalOpenTelemetryIfCustomOpenTelemetryInstanceNotProvided() { OpenTelemetrySdk.builder().buildAndRegisterGlobal(); - FirestoreOptions firestoreOptions = getBaseOptions().build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); - - BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); - assertThat(customMetricsProvider).isNotNull(); - assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); - assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get()); - } - - @Test - public void globalOpenTelemetryDefaultsToNoopIfNotRegistered() { EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); assertThat(customMetricsProvider).isNotNull(); assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get()); - assertThat(customMetricsProvider.getOpenTelemetry().getMeterProvider()) - .isEqualTo(MeterProvider.noop()); } @Test - public void usesIndependentOpenTelemetryInstanceInDefaultAndCustomMetricsProvider() { + public void usesIndependentOpenTelemetryInstanceForDefaultAndCustomMetricsProvider() { OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); FirestoreOptions firestoreOptions = getBaseOptions() @@ -172,7 +145,6 @@ public void usesIndependentOpenTelemetryInstanceInDefaultAndCustomMetricsProvide @Test public void canCreateMetricsContext() { MetricsContext context = newEnabledMetricsUtil().createMetricsContext("testMethod"); - assertThat(context).isNotNull(); assertThat(context instanceof EnabledMetricsUtil.MetricsContext).isTrue(); assertThat(context.methodName).isEqualTo("testMethod"); @@ -181,9 +153,10 @@ public void canCreateMetricsContext() { @Test public void addsMetricsTracerFactoryForDefaultMetricsProvider() { List factories = new ArrayList<>(); + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); metricsUtil.addMetricsTracerFactory(factories); - // Add tracer factory for default metrics provider only as the custom metrics provider is not + // Adds tracer factory for default metrics provider only as the custom metrics provider is not // enabled. assertThat(factories.size()).isEqualTo(1); } @@ -201,7 +174,6 @@ public void addsMetricsTracerFactoriesForBothMetricsProvider() { EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); metricsUtil.addMetricsTracerFactory(factories); - // One from each provider assertThat(factories.size()).isEqualTo(2); } @@ -227,19 +199,7 @@ public void addsMetricsTracerFactoriesIndependentlyForMetricsProviders() { } @Test - public void extractErrorStatusShouldNotThrowOnNonFirestoreException() { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - ApiException apiException = - new ApiException( - new IllegalStateException("Mock batchWrite failed in test"), - GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), - false); - String errorStatus = metricsUtil.extractErrorStatus(apiException); - assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); - } - - @Test - public void testExtractErrorStatus_firestoreExceptionWithStatus() { + public void extractsErrorStatusFromFirestoreException() { EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); FirestoreException firestoreException = FirestoreException.forApiException( @@ -252,6 +212,32 @@ public void testExtractErrorStatus_firestoreExceptionWithStatus() { assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); } + @Test + public void errorStatusSetToUnknownOnNonFirestoreException() { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + ApiException apiException = + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false); + String errorStatus = metricsUtil.extractErrorStatus(apiException); + assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); + } + + // Use reflection to inject mock metrics provider + private void injectMockProviders(EnabledMetricsUtil metricsUtil) throws Exception { + injectMockProvider(metricsUtil, "defaultMetricsProvider", defaultProvider); + injectMockProvider(metricsUtil, "customMetricsProvider", customProvider); + } + + private void injectMockProvider( + EnabledMetricsUtil metricsUtil, String fieldName, BuiltinMetricsProvider provider) + throws Exception { + Field field = EnabledMetricsUtil.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(metricsUtil, provider); + } + @Test public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() throws Exception { EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); @@ -259,7 +245,6 @@ public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() throws Excepti MetricsContext context = metricsUtil.createMetricsContext("testMethod"); ApiFuture future = ApiFutures.immediateFuture("success"); - context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); verify(defaultProvider) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java index 6efddb332..8aa5d3645 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -24,7 +24,7 @@ public class MetricsUtilTest { @Test - public void defaultOptionsCreateEnabledMetricsUtil() { + public void defaultFirestoreOptionsCreatesEnabledMetricsUtil() { MetricsUtil util = MetricsUtil.getInstance( FirestoreOptions.newBuilder() @@ -36,7 +36,7 @@ public void defaultOptionsCreateEnabledMetricsUtil() { } @Test - public void createEnabledMetricsUtilWithOnEnv() throws Exception { + public void createEnabledMetricsUtilWithEnvOn() throws Exception { withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "ON") .execute( () -> { @@ -52,7 +52,7 @@ public void createEnabledMetricsUtilWithOnEnv() throws Exception { } @Test - public void createEnabledMetricsUtilWithTrueEnv() throws Exception { + public void createEnabledMetricsUtilWithEnvTrue() throws Exception { withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "True") .execute( () -> { @@ -68,7 +68,7 @@ public void createEnabledMetricsUtilWithTrueEnv() throws Exception { } @Test - public void createDisabledMetricsUtilWithOffEnv() throws Exception { + public void createDisabledMetricsUtilWithEnvOff() throws Exception { withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "OFF") .execute( () -> { @@ -84,7 +84,7 @@ public void createDisabledMetricsUtilWithOffEnv() throws Exception { } @Test - public void createDisabledMetricsUtilWithFalseEnv() throws Exception { + public void createDisabledMetricsUtilWithEnvFalse() throws Exception { withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "false") .execute( () -> { From d4a5a41dbce89d0ab6c34cbdd9f57672ab0913c2 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:17:34 -0500 Subject: [PATCH 064/103] try removing `FirestoreOptions.getDefaultInstance()` from tracing tests --- .../cloud/firestore/telemetry/EnabledTraceUtilTest.java | 2 +- .../google/cloud/firestore/telemetry/TraceUtilTest.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java index 22e835f19..169c5f1d5 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java @@ -33,7 +33,7 @@ public void setUp() { } EnabledTraceUtil defaultEnabledTraceUtil() { - return new EnabledTraceUtil(FirestoreOptions.getDefaultInstance()); + return new EnabledTraceUtil(getBaseOptions().build()); } FirestoreOptions.Builder getBaseOptions() { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java index e2c20b1df..5970643b8 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java @@ -24,7 +24,12 @@ public class TraceUtilTest { @Test public void defaultOptionsUseNoopTracer() { - TraceUtil traceUtil = TraceUtil.getInstance(FirestoreOptions.getDefaultInstance()); + TraceUtil traceUtil = + TraceUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); assertThat(traceUtil instanceof EnabledTraceUtil).isTrue(); EnabledTraceUtil enabledTraceUtil = (EnabledTraceUtil) traceUtil; assertThat( From ef0afb6a2e29e183a567618d19c4f6de448449b4 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:28:07 -0500 Subject: [PATCH 065/103] use logger to check enabled vs disable --- .../com/google/cloud/firestore/telemetry/MetricsUtil.java | 1 + .../com/google/cloud/firestore/telemetry/TraceUtil.java | 4 ++++ .../cloud/firestore/telemetry/EnabledTraceUtilTest.java | 2 +- .../google/cloud/firestore/telemetry/TraceUtilTest.java | 7 +------ 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index be7ac785c..1a255bfb0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -46,6 +46,7 @@ public interface MetricsUtil { */ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { if (shouldCreateEnabledInstance()) { + logger.info("client side metrics is enabled"); return new EnabledMetricsUtil(firestoreOptions); } else { return new DisabledMetricsUtil(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java index 847eff58a..9bb76d170 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java @@ -22,6 +22,7 @@ import com.google.cloud.firestore.FirestoreOptions; import io.grpc.ManagedChannelBuilder; import java.util.Map; +import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -44,6 +45,8 @@ public interface TraceUtil { String ENABLE_TRACING_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; String LIBRARY_NAME = "com.google.cloud.firestore"; + final Logger logger = Logger.getLogger(TraceUtil.class.getName()); + /** * Creates and returns an instance of the TraceUtil class. * @@ -69,6 +72,7 @@ static TraceUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { } if (createEnabledInstance) { + logger.info("client side tracing is enabled"); return new EnabledTraceUtil(firestoreOptions); } else { return new DisabledTraceUtil(); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java index 169c5f1d5..22e835f19 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java @@ -33,7 +33,7 @@ public void setUp() { } EnabledTraceUtil defaultEnabledTraceUtil() { - return new EnabledTraceUtil(getBaseOptions().build()); + return new EnabledTraceUtil(FirestoreOptions.getDefaultInstance()); } FirestoreOptions.Builder getBaseOptions() { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java index 5970643b8..e2c20b1df 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java @@ -24,12 +24,7 @@ public class TraceUtilTest { @Test public void defaultOptionsUseNoopTracer() { - TraceUtil traceUtil = - TraceUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); + TraceUtil traceUtil = TraceUtil.getInstance(FirestoreOptions.getDefaultInstance()); assertThat(traceUtil instanceof EnabledTraceUtil).isTrue(); EnabledTraceUtil enabledTraceUtil = (EnabledTraceUtil) traceUtil; assertThat( From 4285d0e2b7dcf50cbfc0bcbf651382be6b3cb4d1 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:33:14 -0500 Subject: [PATCH 066/103] is projectId null on github checks? --- .../com/google/cloud/firestore/telemetry/MetricsUtil.java | 4 +++- .../java/com/google/cloud/firestore/telemetry/TraceUtil.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 1a255bfb0..9b83d4230 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -46,7 +46,9 @@ public interface MetricsUtil { */ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { if (shouldCreateEnabledInstance()) { - logger.info("client side metrics is enabled"); + logger.info( + "client side metrics is enabled. Is projectId null: " + + (firestoreOptions.getProjectId() == null)); return new EnabledMetricsUtil(firestoreOptions); } else { return new DisabledMetricsUtil(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java index 9bb76d170..6bf3221e2 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java @@ -72,7 +72,9 @@ static TraceUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { } if (createEnabledInstance) { - logger.info("client side tracing is enabled"); + logger.info( + "client side tracing is enabled. Is projectId null: " + + (firestoreOptions.getProjectId() == null)); return new EnabledTraceUtil(firestoreOptions); } else { return new DisabledTraceUtil(); From 5a6884b857f76ab16856e8b4443d2f65309d6e61 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:41:57 -0500 Subject: [PATCH 067/103] did serviceOptions set the projectId? --- .../main/java/com/google/cloud/firestore/FirestoreOptions.java | 1 + 1 file changed, 1 insertion(+) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index b0887f602..397eae59c 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -338,6 +338,7 @@ public void refresh() {} @InternalApi("This class should only be extended within google-cloud-java") protected FirestoreOptions(Builder builder) { super(FirestoreFactory.class, FirestoreRpcFactory.class, builder, new FirestoreDefaults()); + System.out.println("The projectId is not set: " + this.getProjectId() == null); // FirestoreOptions must contain non-null open-telemetry options. // If the builder doesn't have any open-telemetry options, use a default (disabled) one. From 675a686ea2ea614344b76330726153c8e9ed08b4 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 13:47:48 -0500 Subject: [PATCH 068/103] the test did not go through FirestoreOptions(Builder builder) --- .../java/com/google/cloud/firestore/FirestoreOptions.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index 397eae59c..14f61600e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -46,6 +46,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -70,6 +71,8 @@ public final class FirestoreOptions extends ServiceOptions Date: Thu, 7 Nov 2024 14:43:16 -0500 Subject: [PATCH 069/103] did enabled metrics causes this? --- .../FirestoreOpenTelemetryOptions.java | 2 +- .../firestore/telemetry/MetricsUtil.java | 2 +- .../telemetry/EnabledMetricsUtilTest.java | 344 ------------------ .../firestore/telemetry/MetricsUtilTest.java | 132 ------- 4 files changed, 2 insertions(+), 478 deletions(-) delete mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java delete mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 86a2c2069..8cabe2368 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -60,7 +60,7 @@ public static class Builder { private Builder() { // TODO(metrics): default this to true when feature is ready - exportBuiltinMetricsToGoogleCloudMonitoring = true; + exportBuiltinMetricsToGoogleCloudMonitoring = false; openTelemetry = null; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 9b83d4230..b14ef70d7 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -59,7 +59,7 @@ static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables // TODO(metrics): The feature is disabled before it is ready for general release. - boolean shouldCreateEnabledInstance = true; + boolean shouldCreateEnabledInstance = false; String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java deleted file mode 100644 index ee1d2992f..000000000 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.cloud.firestore.telemetry; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyDouble; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; - -import com.google.api.core.ApiFuture; -import com.google.api.core.ApiFutures; -import com.google.api.gax.grpc.GrpcStatusCode; -import com.google.api.gax.rpc.ApiException; -import com.google.api.gax.rpc.StatusCode; -import com.google.api.gax.tracing.ApiTracerFactory; -import com.google.cloud.firestore.FirestoreException; -import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; -import com.google.cloud.firestore.FirestoreOptions; -import com.google.cloud.firestore.telemetry.EnabledMetricsUtil.MetricsContext; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; -import io.grpc.Status; -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.MeterProvider; -import io.opentelemetry.sdk.OpenTelemetrySdk; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class EnabledMetricsUtilTest { - - private BuiltinMetricsProvider defaultProvider; - private BuiltinMetricsProvider customProvider; - - @Before - public void setUp() { - GlobalOpenTelemetry.resetForTest(); - defaultProvider = Mockito.mock(BuiltinMetricsProvider.class); - customProvider = Mockito.mock(BuiltinMetricsProvider.class); - } - - FirestoreOptions.Builder getBaseOptions() { - return FirestoreOptions.newBuilder().setProjectId("test-project").setDatabaseId("(default)"); - } - - EnabledMetricsUtil newEnabledMetricsUtil() { - return new EnabledMetricsUtil(getBaseOptions().build()); - } - - @Test - public void createsDefaultBuiltinMetricsProviderWithBuiltinOpenTelemetryInstance() { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); - assertThat(metricsProvider).isNotNull(); - assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); - // The default OpenTelemetry MeterProvider has registered GoogleCloudMonitoringExporter. - assertThat(metricsProvider.getOpenTelemetry().getMeterProvider()) - .isNotEqualTo(MeterProvider.noop()); - } - - @Test - public void canDisableBuiltinMetricsProviderWithFirestoreOpenTelemetryOptions() { - FirestoreOptions firestoreOptions = - getBaseOptions() - .setOpenTelemetryOptions( - FirestoreOpenTelemetryOptions.newBuilder() - .exportBuiltinMetricsToGoogleCloudMonitoring(false) - .build()) - .build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); - - BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); - assertThat(metricsProvider).isNotNull(); - assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); - // Metrics collection is "disabled" with OpenTelemetry No-op meter provider instance - assertThat(metricsProvider.getOpenTelemetry().getMeterProvider()) - .isEqualTo(MeterProvider.noop()); - } - - @Test - public void usesCustomOpenTelemetryFromOptions() { - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); - FirestoreOptions firestoreOptions = - getBaseOptions() - .setOpenTelemetryOptions( - FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) - .build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); - - BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); - assertThat(customMetricsProvider).isNotNull(); - assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); - assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(openTelemetry); - } - - @Test - public void usesGlobalOpenTelemetryIfCustomOpenTelemetryInstanceNotProvided() { - OpenTelemetrySdk.builder().buildAndRegisterGlobal(); - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - - BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); - assertThat(customMetricsProvider).isNotNull(); - assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); - assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get()); - } - - @Test - public void usesIndependentOpenTelemetryInstanceForDefaultAndCustomMetricsProvider() { - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); - FirestoreOptions firestoreOptions = - getBaseOptions() - .setOpenTelemetryOptions( - FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) - .build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); - - BuiltinMetricsProvider defaultMetricsProvider = metricsUtil.getDefaultMetricsProvider(); - BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); - assertThat(defaultMetricsProvider).isNotNull(); - assertThat(customMetricsProvider).isNotNull(); - assertThat(defaultMetricsProvider).isNotEqualTo(customMetricsProvider); - assertThat(defaultMetricsProvider.getOpenTelemetry()) - .isNotEqualTo(customMetricsProvider.getOpenTelemetry()); - } - - @Test - public void canCreateMetricsContext() { - MetricsContext context = newEnabledMetricsUtil().createMetricsContext("testMethod"); - assertThat(context).isNotNull(); - assertThat(context instanceof EnabledMetricsUtil.MetricsContext).isTrue(); - assertThat(context.methodName).isEqualTo("testMethod"); - } - - @Test - public void addsMetricsTracerFactoryForDefaultMetricsProvider() { - List factories = new ArrayList<>(); - - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - metricsUtil.addMetricsTracerFactory(factories); - // Adds tracer factory for default metrics provider only as the custom metrics provider is not - // enabled. - assertThat(factories.size()).isEqualTo(1); - } - - @Test - public void addsMetricsTracerFactoriesForBothMetricsProvider() { - List factories = new ArrayList<>(); - - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); - FirestoreOptions firestoreOptions = - getBaseOptions() - .setOpenTelemetryOptions( - FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) - .build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); - - metricsUtil.addMetricsTracerFactory(factories); - assertThat(factories.size()).isEqualTo(2); - } - - @Test - public void addsMetricsTracerFactoriesIndependentlyForMetricsProviders() { - List factories = new ArrayList<>(); - - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); - FirestoreOptions firestoreOptions = - getBaseOptions() - .setOpenTelemetryOptions( - FirestoreOpenTelemetryOptions.newBuilder() - .setOpenTelemetry(openTelemetry) - .exportBuiltinMetricsToGoogleCloudMonitoring(false) - .build()) - .build(); - EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); - - metricsUtil.addMetricsTracerFactory(factories); - // Add tracer factory for custom metrics provider only as the default metrics provider is not - // enabled. - assertThat(factories.size()).isEqualTo(1); - } - - @Test - public void extractsErrorStatusFromFirestoreException() { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - FirestoreException firestoreException = - FirestoreException.forApiException( - new ApiException( - new IllegalStateException("Mock batchWrite failed in test"), - GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), - false)); - String errorStatus = metricsUtil.extractErrorStatus(firestoreException); - // TODO(b/305998085):Change this to correct status code - assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); - } - - @Test - public void errorStatusSetToUnknownOnNonFirestoreException() { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - ApiException apiException = - new ApiException( - new IllegalStateException("Mock batchWrite failed in test"), - GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), - false); - String errorStatus = metricsUtil.extractErrorStatus(apiException); - assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); - } - - // Use reflection to inject mock metrics provider - private void injectMockProviders(EnabledMetricsUtil metricsUtil) throws Exception { - injectMockProvider(metricsUtil, "defaultMetricsProvider", defaultProvider); - injectMockProvider(metricsUtil, "customMetricsProvider", customProvider); - } - - private void injectMockProvider( - EnabledMetricsUtil metricsUtil, String fieldName, BuiltinMetricsProvider provider) - throws Exception { - Field field = EnabledMetricsUtil.class.getDeclaredField(fieldName); - field.setAccessible(true); - field.set(metricsUtil, provider); - } - - @Test - public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() throws Exception { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - injectMockProviders(metricsUtil); - - MetricsContext context = metricsUtil.createMetricsContext("testMethod"); - ApiFuture future = ApiFutures.immediateFuture("success"); - context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); - - verify(defaultProvider) - .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); - verify(customProvider) - .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); - } - - @Test - public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - injectMockProviders(metricsUtil); - - MetricsContext context = metricsUtil.createMetricsContext("testMethod"); - FirestoreException firestoreException = - FirestoreException.forApiException( - new ApiException( - new IllegalStateException("Mock batchWrite failed in test"), - GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), - false)); - ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); - context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); - - // TODO(b/305998085):Change this to correct status code - Mockito.verify(defaultProvider) - .latencyRecorder( - Mockito.eq(MetricType.END_TO_END_LATENCY), - Mockito.anyDouble(), - Mockito.argThat( - attributes -> - attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) - .equals(Status.Code.UNKNOWN.toString()))); - Mockito.verify(customProvider) - .latencyRecorder( - Mockito.eq(MetricType.END_TO_END_LATENCY), - Mockito.anyDouble(), - Mockito.argThat( - attributes -> - attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) - .equals(Status.Code.UNKNOWN.toString()))); - } - - @Test - public void recordCounterCalledWhenFutureIsCompletedWithSuccess() throws Exception { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - injectMockProviders(metricsUtil); - - MetricsContext context = metricsUtil.createMetricsContext("testMethod"); - ApiFuture future = ApiFutures.immediateFuture("success"); - context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, future); - - Mockito.verify(defaultProvider) - .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.anyMap()); - Mockito.verify(customProvider) - .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.anyMap()); - } - - @Test - public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception { - EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); - injectMockProviders(metricsUtil); - - MetricsContext context = metricsUtil.createMetricsContext("testMethod"); - FirestoreException firestoreException = - FirestoreException.forApiException( - new ApiException( - new IllegalStateException("Mock batchWrite failed in test"), - GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), - false)); - ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); - context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, future); - - // TODO(b/305998085):Change this to correct status code - Mockito.verify(defaultProvider) - .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), - Mockito.anyLong(), - Mockito.argThat( - attributes -> - attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) - .equals(Status.Code.UNKNOWN.toString()))); - Mockito.verify(customProvider) - .counterRecorder( - Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), - Mockito.anyLong(), - Mockito.argThat( - attributes -> - attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) - .equals(Status.Code.UNKNOWN.toString()))); - } -} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java deleted file mode 100644 index 8aa5d3645..000000000 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2024 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.cloud.firestore.telemetry; - -import static com.github.stefanbirkner.systemlambda.SystemLambda.*; -import static com.google.common.truth.Truth.assertThat; - -import com.google.cloud.firestore.FirestoreOptions; -import org.junit.Test; - -public class MetricsUtilTest { - @Test - public void defaultFirestoreOptionsCreatesEnabledMetricsUtil() { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - - assertThat(util instanceof EnabledMetricsUtil).isTrue(); - } - - @Test - public void createEnabledMetricsUtilWithEnvOn() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "ON") - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - - assertThat(util instanceof EnabledMetricsUtil).isTrue(); - }); - } - - @Test - public void createEnabledMetricsUtilWithEnvTrue() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "True") - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - - assertThat(util instanceof EnabledMetricsUtil).isTrue(); - }); - } - - @Test - public void createDisabledMetricsUtilWithEnvOff() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "OFF") - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - - assertThat(util instanceof DisabledMetricsUtil).isTrue(); - }); - } - - @Test - public void createDisabledMetricsUtilWithEnvFalse() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "false") - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - - assertThat(util instanceof DisabledMetricsUtil).isTrue(); - }); - } - - @Test - public void invalidEnvironmentVariableDefaultsToEnabledMetricsUtil() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "Invalid") - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - - assertThat(util instanceof EnabledMetricsUtil).isTrue(); - }); - } - - @Test - public void nullEnvironmentVariableDefaultsToEnabledMetricsUtil() throws Exception { - withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", null) - .execute( - () -> { - MetricsUtil util = - MetricsUtil.getInstance( - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setDatabaseId("(default)") - .build()); - assertThat(util instanceof EnabledMetricsUtil).isTrue(); - }); - } -} From fac2d092240ef2b0ed773a17cdf4a337bc4cca6a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:45:38 -0500 Subject: [PATCH 070/103] Revert "did enabled metrics causes this?" This reverts commit b40b7d40574fc5d2138ddd9f98548270d80c3cb1. --- .../FirestoreOpenTelemetryOptions.java | 2 +- .../firestore/telemetry/MetricsUtil.java | 2 +- .../telemetry/EnabledMetricsUtilTest.java | 344 ++++++++++++++++++ .../firestore/telemetry/MetricsUtilTest.java | 132 +++++++ 4 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 8cabe2368..86a2c2069 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -60,7 +60,7 @@ public static class Builder { private Builder() { // TODO(metrics): default this to true when feature is ready - exportBuiltinMetricsToGoogleCloudMonitoring = false; + exportBuiltinMetricsToGoogleCloudMonitoring = true; openTelemetry = null; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index b14ef70d7..9b83d4230 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -59,7 +59,7 @@ static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables // TODO(metrics): The feature is disabled before it is ready for general release. - boolean shouldCreateEnabledInstance = false; + boolean shouldCreateEnabledInstance = true; String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); if (enableMetricsEnvVar != null) { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java new file mode 100644 index 000000000..ee1d2992f --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -0,0 +1,344 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.cloud.firestore.telemetry; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.rpc.ApiException; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.tracing.ApiTracerFactory; +import com.google.cloud.firestore.FirestoreException; +import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; +import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.telemetry.EnabledMetricsUtil.MetricsContext; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; +import io.grpc.Status; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class EnabledMetricsUtilTest { + + private BuiltinMetricsProvider defaultProvider; + private BuiltinMetricsProvider customProvider; + + @Before + public void setUp() { + GlobalOpenTelemetry.resetForTest(); + defaultProvider = Mockito.mock(BuiltinMetricsProvider.class); + customProvider = Mockito.mock(BuiltinMetricsProvider.class); + } + + FirestoreOptions.Builder getBaseOptions() { + return FirestoreOptions.newBuilder().setProjectId("test-project").setDatabaseId("(default)"); + } + + EnabledMetricsUtil newEnabledMetricsUtil() { + return new EnabledMetricsUtil(getBaseOptions().build()); + } + + @Test + public void createsDefaultBuiltinMetricsProviderWithBuiltinOpenTelemetryInstance() { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); + assertThat(metricsProvider).isNotNull(); + assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); + // The default OpenTelemetry MeterProvider has registered GoogleCloudMonitoringExporter. + assertThat(metricsProvider.getOpenTelemetry().getMeterProvider()) + .isNotEqualTo(MeterProvider.noop()); + } + + @Test + public void canDisableBuiltinMetricsProviderWithFirestoreOpenTelemetryOptions() { + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + BuiltinMetricsProvider metricsProvider = metricsUtil.getDefaultMetricsProvider(); + assertThat(metricsProvider).isNotNull(); + assertThat(metricsProvider.getOpenTelemetry()).isNotNull(); + // Metrics collection is "disabled" with OpenTelemetry No-op meter provider instance + assertThat(metricsProvider.getOpenTelemetry().getMeterProvider()) + .isEqualTo(MeterProvider.noop()); + } + + @Test + public void usesCustomOpenTelemetryFromOptions() { + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); + assertThat(customMetricsProvider).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(openTelemetry); + } + + @Test + public void usesGlobalOpenTelemetryIfCustomOpenTelemetryInstanceNotProvided() { + OpenTelemetrySdk.builder().buildAndRegisterGlobal(); + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + + BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); + assertThat(customMetricsProvider).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); + assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get()); + } + + @Test + public void usesIndependentOpenTelemetryInstanceForDefaultAndCustomMetricsProvider() { + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + BuiltinMetricsProvider defaultMetricsProvider = metricsUtil.getDefaultMetricsProvider(); + BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); + assertThat(defaultMetricsProvider).isNotNull(); + assertThat(customMetricsProvider).isNotNull(); + assertThat(defaultMetricsProvider).isNotEqualTo(customMetricsProvider); + assertThat(defaultMetricsProvider.getOpenTelemetry()) + .isNotEqualTo(customMetricsProvider.getOpenTelemetry()); + } + + @Test + public void canCreateMetricsContext() { + MetricsContext context = newEnabledMetricsUtil().createMetricsContext("testMethod"); + assertThat(context).isNotNull(); + assertThat(context instanceof EnabledMetricsUtil.MetricsContext).isTrue(); + assertThat(context.methodName).isEqualTo("testMethod"); + } + + @Test + public void addsMetricsTracerFactoryForDefaultMetricsProvider() { + List factories = new ArrayList<>(); + + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + metricsUtil.addMetricsTracerFactory(factories); + // Adds tracer factory for default metrics provider only as the custom metrics provider is not + // enabled. + assertThat(factories.size()).isEqualTo(1); + } + + @Test + public void addsMetricsTracerFactoriesForBothMetricsProvider() { + List factories = new ArrayList<>(); + + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetry).build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + metricsUtil.addMetricsTracerFactory(factories); + assertThat(factories.size()).isEqualTo(2); + } + + @Test + public void addsMetricsTracerFactoriesIndependentlyForMetricsProviders() { + List factories = new ArrayList<>(); + + OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + FirestoreOptions firestoreOptions = + getBaseOptions() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .setOpenTelemetry(openTelemetry) + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()) + .build(); + EnabledMetricsUtil metricsUtil = new EnabledMetricsUtil(firestoreOptions); + + metricsUtil.addMetricsTracerFactory(factories); + // Add tracer factory for custom metrics provider only as the default metrics provider is not + // enabled. + assertThat(factories.size()).isEqualTo(1); + } + + @Test + public void extractsErrorStatusFromFirestoreException() { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + FirestoreException firestoreException = + FirestoreException.forApiException( + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false)); + String errorStatus = metricsUtil.extractErrorStatus(firestoreException); + // TODO(b/305998085):Change this to correct status code + assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); + } + + @Test + public void errorStatusSetToUnknownOnNonFirestoreException() { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + ApiException apiException = + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false); + String errorStatus = metricsUtil.extractErrorStatus(apiException); + assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); + } + + // Use reflection to inject mock metrics provider + private void injectMockProviders(EnabledMetricsUtil metricsUtil) throws Exception { + injectMockProvider(metricsUtil, "defaultMetricsProvider", defaultProvider); + injectMockProvider(metricsUtil, "customMetricsProvider", customProvider); + } + + private void injectMockProvider( + EnabledMetricsUtil metricsUtil, String fieldName, BuiltinMetricsProvider provider) + throws Exception { + Field field = EnabledMetricsUtil.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(metricsUtil, provider); + } + + @Test + public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); + + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); + ApiFuture future = ApiFutures.immediateFuture("success"); + context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + + verify(defaultProvider) + .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); + verify(customProvider) + .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); + } + + @Test + public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); + + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); + FirestoreException firestoreException = + FirestoreException.forApiException( + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false)); + ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); + context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + + // TODO(b/305998085):Change this to correct status code + Mockito.verify(defaultProvider) + .latencyRecorder( + Mockito.eq(MetricType.END_TO_END_LATENCY), + Mockito.anyDouble(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + Mockito.verify(customProvider) + .latencyRecorder( + Mockito.eq(MetricType.END_TO_END_LATENCY), + Mockito.anyDouble(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + } + + @Test + public void recordCounterCalledWhenFutureIsCompletedWithSuccess() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); + + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); + ApiFuture future = ApiFutures.immediateFuture("success"); + context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, future); + + Mockito.verify(defaultProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.anyMap()); + Mockito.verify(customProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), Mockito.anyLong(), Mockito.anyMap()); + } + + @Test + public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + injectMockProviders(metricsUtil); + + MetricsContext context = metricsUtil.createMetricsContext("testMethod"); + FirestoreException firestoreException = + FirestoreException.forApiException( + new ApiException( + new IllegalStateException("Mock batchWrite failed in test"), + GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), + false)); + ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); + context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, future); + + // TODO(b/305998085):Change this to correct status code + Mockito.verify(defaultProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), + Mockito.anyLong(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + Mockito.verify(customProvider) + .counterRecorder( + Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), + Mockito.anyLong(), + Mockito.argThat( + attributes -> + attributes + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .equals(Status.Code.UNKNOWN.toString()))); + } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java new file mode 100644 index 000000000..8aa5d3645 --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/MetricsUtilTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.telemetry; + +import static com.github.stefanbirkner.systemlambda.SystemLambda.*; +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.firestore.FirestoreOptions; +import org.junit.Test; + +public class MetricsUtilTest { + @Test + public void defaultFirestoreOptionsCreatesEnabledMetricsUtil() { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + } + + @Test + public void createEnabledMetricsUtilWithEnvOn() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "ON") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } + + @Test + public void createEnabledMetricsUtilWithEnvTrue() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "True") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } + + @Test + public void createDisabledMetricsUtilWithEnvOff() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "OFF") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof DisabledMetricsUtil).isTrue(); + }); + } + + @Test + public void createDisabledMetricsUtilWithEnvFalse() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "false") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof DisabledMetricsUtil).isTrue(); + }); + } + + @Test + public void invalidEnvironmentVariableDefaultsToEnabledMetricsUtil() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", "Invalid") + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } + + @Test + public void nullEnvironmentVariableDefaultsToEnabledMetricsUtil() throws Exception { + withEnvironmentVariable("FIRESTORE_ENABLE_METRICS", null) + .execute( + () -> { + MetricsUtil util = + MetricsUtil.getInstance( + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setDatabaseId("(default)") + .build()); + assertThat(util instanceof EnabledMetricsUtil).isTrue(); + }); + } +} From 8be64d08ffb68c28792b0abb5e5ad146c086ffce Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:03:10 -0500 Subject: [PATCH 071/103] handle null projectId exception --- .../cloud/firestore/FirestoreOpenTelemetryOptions.java | 2 -- .../java/com/google/cloud/firestore/FirestoreOptions.java | 4 ---- .../main/java/com/google/cloud/firestore/Transaction.java | 7 ------- .../cloud/firestore/telemetry/EnabledMetricsUtil.java | 3 +++ .../com/google/cloud/firestore/telemetry/MetricsUtil.java | 4 ---- .../com/google/cloud/firestore/telemetry/TraceUtil.java | 6 ------ 6 files changed, 3 insertions(+), 23 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java index 86a2c2069..ab54fc0fd 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java @@ -59,7 +59,6 @@ public static class Builder { @Nullable private OpenTelemetry openTelemetry; private Builder() { - // TODO(metrics): default this to true when feature is ready exportBuiltinMetricsToGoogleCloudMonitoring = true; openTelemetry = null; } @@ -75,7 +74,6 @@ public FirestoreOpenTelemetryOptions build() { return new FirestoreOpenTelemetryOptions(this); } - // TODO(metrics): make this public when feature is ready. /** * Sets whether built-in metrics should be exported to Google Cloud Monitoring * diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index 14f61600e..b0887f602 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java @@ -46,7 +46,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -71,8 +70,6 @@ public final class FirestoreOptions extends ServiceOptions apiTracerFactories) { * metrics to Google Cloud Monitoring. */ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws IOException { + if (projectId == null) { + throw new IllegalArgumentException("Project ID is null."); + } SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); // Filter out attributes that are not defined diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 9b83d4230..b04a5157d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -46,9 +46,6 @@ public interface MetricsUtil { */ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { if (shouldCreateEnabledInstance()) { - logger.info( - "client side metrics is enabled. Is projectId null: " - + (firestoreOptions.getProjectId() == null)); return new EnabledMetricsUtil(firestoreOptions); } else { return new DisabledMetricsUtil(); @@ -58,7 +55,6 @@ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables - // TODO(metrics): The feature is disabled before it is ready for general release. boolean shouldCreateEnabledInstance = true; String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java index 6bf3221e2..847eff58a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java @@ -22,7 +22,6 @@ import com.google.cloud.firestore.FirestoreOptions; import io.grpc.ManagedChannelBuilder; import java.util.Map; -import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -45,8 +44,6 @@ public interface TraceUtil { String ENABLE_TRACING_ENV_VAR = "FIRESTORE_ENABLE_TRACING"; String LIBRARY_NAME = "com.google.cloud.firestore"; - final Logger logger = Logger.getLogger(TraceUtil.class.getName()); - /** * Creates and returns an instance of the TraceUtil class. * @@ -72,9 +69,6 @@ static TraceUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { } if (createEnabledInstance) { - logger.info( - "client side tracing is enabled. Is projectId null: " - + (firestoreOptions.getProjectId() == null)); return new EnabledTraceUtil(firestoreOptions); } else { return new DisabledTraceUtil(); From 23236f0303326af0a5f83088f6713af2f0f7b64d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:26:33 -0500 Subject: [PATCH 072/103] Update EnabledMetricsUtil.java --- .../telemetry/EnabledMetricsUtil.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 203ffc52a..942a14414 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -75,7 +75,7 @@ class EnabledMetricsUtil implements MetricsUtil { try { this.defaultMetricsProvider = configureDefaultMetricsProvider(firestoreOptions); this.customMetricsProvider = configureCustomMetricsProvider(firestoreOptions); - } catch (IOException e) { + } catch (Exception e) { logger.warning( "Unable to create MetricsUtil object for client side metrics, will skip exporting client side metrics" + e); @@ -92,15 +92,22 @@ BuiltinMetricsProvider getDefaultMetricsProvider() { return defaultMetricsProvider; } - private BuiltinMetricsProvider configureDefaultMetricsProvider(FirestoreOptions firestoreOptions) - throws IOException { - OpenTelemetry defaultOpenTelemetry; - boolean exportBuiltinMetricsToGoogleCloudMonitoring = - firestoreOptions.getOpenTelemetryOptions().exportBuiltinMetricsToGoogleCloudMonitoring(); - if (exportBuiltinMetricsToGoogleCloudMonitoring) { - defaultOpenTelemetry = getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId()); - } else { - defaultOpenTelemetry = OpenTelemetry.noop(); + private BuiltinMetricsProvider configureDefaultMetricsProvider( + FirestoreOptions firestoreOptions) { + OpenTelemetry defaultOpenTelemetry = OpenTelemetry.noop(); + if (firestoreOptions.getOpenTelemetryOptions().exportBuiltinMetricsToGoogleCloudMonitoring()) { + String projectId = firestoreOptions.getProjectId(); + if (projectId == null) { + logger.warning( + "Project ID is null, skipping client side metrics export to Cloud Monitoring."); + } + try { + defaultOpenTelemetry = getDefaultOpenTelemetryInstance(projectId); + } catch (IOException e) { + logger.warning( + "Unable to create default OpenTelemetry instance for client side metrics, will skip exporting client side metrics to Cloud Monitoring: " + + e); + } } return new BuiltinMetricsProvider(defaultOpenTelemetry); } From 7b4fc14e871498aeb34bbe9d9bb4dcfdd56ed693 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:30:56 -0500 Subject: [PATCH 073/103] Update EnabledMetricsUtil.java --- .../telemetry/EnabledMetricsUtil.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 942a14414..d83ebff62 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -82,16 +82,6 @@ class EnabledMetricsUtil implements MetricsUtil { } } - @VisibleForTesting - BuiltinMetricsProvider getCustomMetricsProvider() { - return customMetricsProvider; - } - - @VisibleForTesting - BuiltinMetricsProvider getDefaultMetricsProvider() { - return defaultMetricsProvider; - } - private BuiltinMetricsProvider configureDefaultMetricsProvider( FirestoreOptions firestoreOptions) { OpenTelemetry defaultOpenTelemetry = OpenTelemetry.noop(); @@ -133,14 +123,21 @@ public void addMetricsTracerFactory(List apiTracerFactories) { addTracerFactory(apiTracerFactories, customMetricsProvider); } + @VisibleForTesting + BuiltinMetricsProvider getCustomMetricsProvider() { + return customMetricsProvider; + } + + @VisibleForTesting + BuiltinMetricsProvider getDefaultMetricsProvider() { + return defaultMetricsProvider; + } + /** * Creates a default {@link OpenTelemetry} instance to collect and export built-in client side * metrics to Google Cloud Monitoring. */ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws IOException { - if (projectId == null) { - throw new IllegalArgumentException("Project ID is null."); - } SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); // Filter out attributes that are not defined From f7ec3c00f4170acba20078de604f315ca59cd6d7 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:44:39 -0500 Subject: [PATCH 074/103] add integration tests --- .../ServerSideTransactionRunner.java | 3 +- .../telemetry/BuiltinMetricsProvider.java | 4 +- .../firestore/telemetry/ClientIdentifier.java | 3 +- .../telemetry/EnabledMetricsUtil.java | 6 +- .../telemetry/TelemetryConstants.java | 1 + .../cloud/firestore/it/ITMetricsTest.java | 334 ++++++++++++++++++ .../telemetry/EnabledMetricsUtilTest.java | 22 +- 7 files changed, 362 insertions(+), 11 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index 29c92331a..f26c3e0c9 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -93,7 +93,7 @@ final class ServerSideTransactionRunner { firestore .getOptions() .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN); + .createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_TRANSACTION); } @Nonnull @@ -150,6 +150,7 @@ ApiFuture begin() { serverSideTransaction.setTransactionTraceContext(runTransactionContext); return serverSideTransaction; }); + metricsContext.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, result); span.endAtFuture(result); return result; } catch (Exception error) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index d981e7693..56b156bdf 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -61,7 +61,6 @@ class BuiltinMetricsProvider { private static final String MILLISECOND_UNIT = "ms"; private static final String INTEGER_UNIT = "1"; - private static final String FIRESTORE_LIBRARY_NAME = "com.google.cloud.firestore"; public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -81,7 +80,8 @@ OpenTelemetry getOpenTelemetry() { private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); - staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), FIRESTORE_LIBRARY_NAME); + staticAttributes.put( + METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), TelemetryConstants.FIRESTORE_LIBRARY_NAME); String pkgVersion = this.getClass().getPackage().getImplementationVersion(); if (pkgVersion != null) { staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), pkgVersion); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java index dc4a3eae0..71135547a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java @@ -22,7 +22,8 @@ import java.util.UUID; /** A utility class for retrieving a unique client identifier (CLIENT_UID) */ -final class ClientIdentifier { +@InternalApi +public final class ClientIdentifier { private ClientIdentifier() {} diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index d83ebff62..106b2db35 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -252,10 +252,8 @@ public void onSuccess(T result) { private void recordCounter(MetricType metric, String status) { Map attributes = createAttributes(status, methodName); - defaultMetricsProvider.counterRecorder( - MetricType.TRANSACTION_ATTEMPT_COUNT, (long) counter, attributes); - customMetricsProvider.counterRecorder( - MetricType.TRANSACTION_ATTEMPT_COUNT, (long) counter, attributes); + defaultMetricsProvider.counterRecorder(metric, (long) counter, attributes); + customMetricsProvider.counterRecorder(metric, (long) counter, attributes); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index d2ab8bf17..d3527dc27 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -66,6 +66,7 @@ public interface TelemetryConstants { String METRIC_PREFIX = "custom.googleapis.com/internal/client"; String FIRESTORE_METER_NAME = "java_firestore"; String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; + String FIRESTORE_LIBRARY_NAME = "com.google.cloud.firestore"; // Monitored resource keys for labels String RESOURCE_KEY_RESOURCE_CONTAINER = "resource_container"; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java new file mode 100644 index 000000000..3f7de78e8 --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -0,0 +1,334 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.it; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.firestore.DocumentReference; +import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; +import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.Query; +import com.google.cloud.firestore.telemetry.ClientIdentifier; +import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.common.base.Preconditions; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.data.*; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.util.*; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.junit.*; +import org.junit.rules.TestName; + +public class ITMetricsTest { + + private static final Logger logger = + Logger.getLogger(com.google.cloud.firestore.it.ITMetricsTest.class.getName()); + + private static OpenTelemetrySdk openTelemetrySdk; + + protected InMemoryMetricReader metricReader; + + protected Firestore firestore; + + private static Attributes expectedBaseAttributes; + + private final String ClientUid = ClientIdentifier.getClientUid(); + private final String libraryVersion = this.getClass().getPackage().getImplementationVersion(); + + @Rule public TestName testName = new TestName(); + + @Before + public void setup() { + metricReader = InMemoryMetricReader.create(); + SdkMeterProviderBuilder meterProvider = + SdkMeterProvider.builder().registerMetricReader(metricReader); + + openTelemetrySdk = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + + firestore = + FirestoreOptions.newBuilder() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .setOpenTelemetry(openTelemetrySdk) + .build()) + .build() + .getService(); + + AttributesBuilder expectedAttributes = Attributes.builder(); + expectedAttributes.put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), + TelemetryConstants.FIRESTORE_LIBRARY_NAME); + expectedAttributes.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientUid); + if (libraryVersion != null) { + expectedAttributes.put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), libraryVersion); + } + + expectedBaseAttributes = expectedAttributes.build(); + } + + @After + public void tearDown() throws Exception { + Preconditions.checkNotNull( + firestore, + "Error instantiating Firestore. Check that the service account credentials were properly set."); + try { + metricReader.close(); + } finally { + // This block will always execute, even if an exception occurs above + firestore.shutdown(); + } + } + + @Test + public void simpleOperation() throws Exception { + firestore.collection("col").document().create(Collections.singletonMap("foo", "bar")).get(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.Commit") + .build(); + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "DocumentReference.Create") + .build(); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void operationWithFirstResponseLatency() throws Exception { + firestore.collection("col").get(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunQuery") + .build(); + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunQuery.Get") + .build(); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void operationWithTransaction() throws Exception { + firestore + .runTransaction( + transaction -> { + Query q = firestore.collection("col").whereGreaterThan("bla", ""); + DocumentReference d = firestore.collection("col").document("foo"); + DocumentReference[] docList = {d, d}; + // Document Query. + transaction.get(q).get(); + + // Aggregation Query. + transaction.get(q.count()); + + // Commit 2 documents. + transaction.set( + firestore.collection("foo").document("bar"), + Collections.singletonMap("foo", "bar")); + transaction.set( + firestore.collection("foo").document("bar2"), + Collections.singletonMap("foo2", "bar2")); + return 0; + }) + .get(); + + List attributesList = new ArrayList<>(); + String[] methods = { + "Firestore.BeginTransaction", + "Firestore.RunQuery", + "Firestore.RunAggregationQuery", + "Firestore.Commit" + }; + + for (String method : methods) { + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + attributesList.add(attributes); + } + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributesList); + + // Validate SDK layer metric + List attributesList2 = new ArrayList<>(); + + String[] methods2 = { + "RunTransaction", "RunQuery.Transactional", "RunAggregationQuery.Transactional", + }; + + for (String method : methods2) { + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + attributesList2.add(attributes); + } + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributesList2); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + validateMetricData(dataFromReader, attributesList2); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunTransaction") + .build(); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + validateMetricData(dataFromReader, attributes); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + } + + private void validateMetricData(MetricData metricData, Attributes expectedAttributes) { + validateMetricData(metricData, Arrays.asList(expectedAttributes)); + } + + private void validateMetricData(MetricData metricData, List expectedAttributesList) { + boolean isHistogram = metricData.getType() == MetricDataType.HISTOGRAM; + Collection points = + isHistogram + ? metricData.getHistogramData().getPoints() + : metricData.getLongSumData().getPoints(); + + assertThat(points.size()).isEqualTo(expectedAttributesList.size()); + + List actualAttributesList = new ArrayList<>(); + for (PointData point : points) { + if (isHistogram) { + assertThat(((HistogramPointData) point).getCount()).isAtLeast(1); + assertThat(((HistogramPointData) point).getSum()).isGreaterThan(0.0); + } else { + assertThat(((LongPointData) point).getValue()).isEqualTo(1); + } + actualAttributesList.add(point.getAttributes()); + } + + for (Attributes expectedAttributes : expectedAttributesList) { + assertThat( + actualAttributesList.stream() + .anyMatch( + actualAttributes -> + actualAttributes + .asMap() + .entrySet() + .containsAll(expectedAttributes.asMap().entrySet()))) + .isTrue(); + } + } + + private MetricData getMetricData(InMemoryMetricReader reader, String metricName) { + String fullMetricName = TelemetryConstants.METRIC_PREFIX + "/" + metricName; + // Fetch the MetricData with retries + for (int attemptsLeft = 1000; attemptsLeft > 0; attemptsLeft--) { + List matchingMetadata = + reader.collectAllMetrics().stream() + .filter(md -> md.getName().equals(fullMetricName)) + .collect(Collectors.toList()); + assertWithMessage( + "Found unexpected MetricData with the same name: %s, in: %s", + fullMetricName, matchingMetadata) + .that(matchingMetadata.size()) + .isAtMost(1); + + if (!matchingMetadata.isEmpty()) { + return matchingMetadata.get(0); + } + + try { + Thread.sleep(1); + } catch (InterruptedException interruptedException) { + Thread.currentThread().interrupt(); + throw new RuntimeException(interruptedException); + } + } + + assertTrue(String.format("MetricData is missing for metric %s", fullMetricName), false); + return null; + } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java index ee1d2992f..876e76ea0 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -36,6 +36,8 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -98,7 +100,11 @@ public void canDisableBuiltinMetricsProviderWithFirestoreOpenTelemetryOptions() @Test public void usesCustomOpenTelemetryFromOptions() { - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); FirestoreOptions firestoreOptions = getBaseOptions() .setOpenTelemetryOptions( @@ -114,18 +120,28 @@ public void usesCustomOpenTelemetryFromOptions() { @Test public void usesGlobalOpenTelemetryIfCustomOpenTelemetryInstanceNotProvided() { - OpenTelemetrySdk.builder().buildAndRegisterGlobal(); + InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).buildAndRegisterGlobal(); EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); BuiltinMetricsProvider customMetricsProvider = metricsUtil.getCustomMetricsProvider(); assertThat(customMetricsProvider).isNotNull(); assertThat(customMetricsProvider.getOpenTelemetry()).isNotNull(); assertThat(customMetricsProvider.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get()); + assertThat(customMetricsProvider.getOpenTelemetry().getMeterProvider()) + .isEqualTo(openTelemetry.getMeterProvider()); } @Test public void usesIndependentOpenTelemetryInstanceForDefaultAndCustomMetricsProvider() { - OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().build(); + InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create(); + SdkMeterProvider sdkMeterProvider = + SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build(); + OpenTelemetry openTelemetry = + OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build(); FirestoreOptions firestoreOptions = getBaseOptions() .setOpenTelemetryOptions( From 57c4a3d376f1cba23ec8cdeed86a54859d1d43c5 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:45:32 -0500 Subject: [PATCH 075/103] format --- .../com/google/cloud/firestore/telemetry/ClientIdentifier.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java index 71135547a..08f2a42fd 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java @@ -16,6 +16,8 @@ package com.google.cloud.firestore.telemetry; +import com.google.api.core.InternalApi; + import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.net.InetAddress; From cfd78eeaabdc79b64a28d85be010bbcec8fa8779 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:37:35 -0500 Subject: [PATCH 076/103] add e2e latency to UpdateBuilder --- .../google/cloud/firestore/UpdateBuilder.java | 21 + .../firestore/telemetry/ClientIdentifier.java | 1 - .../cloud/firestore/it/ITMetricsTest.java | 489 +++++++++++++++++- 3 files changed, 486 insertions(+), 25 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index cfa852ce4..5bb146dfd 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -26,7 +26,9 @@ import com.google.api.core.InternalExtensionOnly; import com.google.cloud.firestore.UserDataConverter.EncodingOptions; import com.google.cloud.firestore.encoding.CustomClassMapper; +import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.common.base.Preconditions; @@ -621,6 +623,19 @@ ApiFuture> commit(@Nullable ByteString transactionId) { : TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT); span.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, writes.size()); span.setAttribute(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null); + + MetricsContext metricsContext = null; + + // End to end latency for transactions recorded under RunTransaction, including the + // Transaction.Commit operation. + if (transactionId == null) { + metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TelemetryConstants.METHOD_NAME_BATCH_COMMIT); + } + try (Scope ignored = span.makeCurrent()) { // Sequence is thread safe. // @@ -654,9 +669,15 @@ ApiFuture> commit(@Nullable ByteString transactionId) { }, MoreExecutors.directExecutor()); span.endAtFuture(returnValue); + if (metricsContext != null) { + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, returnValue); + } return returnValue; } catch (Exception error) { span.end(error); + if (metricsContext != null) { + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); + } throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java index 08f2a42fd..64a60508f 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java @@ -17,7 +17,6 @@ package com.google.cloud.firestore.telemetry; import com.google.api.core.InternalApi; - import java.lang.management.ManagementFactory; import java.lang.reflect.Method; import java.net.InetAddress; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index 3f7de78e8..8ab4832db 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -16,15 +16,23 @@ package com.google.cloud.firestore.it; +import static com.google.cloud.firestore.LocalFirestoreHelper.map; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertTrue; +import com.google.api.gax.retrying.RetrySettings; +import com.google.cloud.firestore.BulkWriter; +import com.google.cloud.firestore.BulkWriterOptions; +import com.google.cloud.firestore.CollectionGroup; +import com.google.cloud.firestore.CollectionReference; import com.google.cloud.firestore.DocumentReference; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.Query; +import com.google.cloud.firestore.SetOptions; +import com.google.cloud.firestore.WriteBatch; import com.google.cloud.firestore.telemetry.ClientIdentifier; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.common.base.Preconditions; @@ -33,13 +41,29 @@ import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; -import io.opentelemetry.sdk.metrics.data.*; +import io.opentelemetry.sdk.metrics.data.HistogramPointData; +import io.opentelemetry.sdk.metrics.data.LongPointData; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.data.MetricDataType; +import io.opentelemetry.sdk.metrics.data.PointData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.Collectors; -import org.junit.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; import org.junit.rules.TestName; +import org.mockito.Spy; +import org.threeten.bp.Duration; public class ITMetricsTest { @@ -52,6 +76,22 @@ public class ITMetricsTest { protected Firestore firestore; + @Spy + private final Firestore firestoreMock = + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setRetrySettings( + RetrySettings.newBuilder() + .setInitialRetryDelay(Duration.ZERO) + .setMaxRetryDelay(Duration.ZERO) + .setRetryDelayMultiplier(1) + .setJittered(false) + .build()) + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetrySdk).build()) + .build() + .getService(); + private static Attributes expectedBaseAttributes; private final String ClientUid = ClientIdentifier.getClientUid(); @@ -97,14 +137,92 @@ public void tearDown() throws Exception { try { metricReader.close(); } finally { - // This block will always execute, even if an exception occurs above firestore.shutdown(); } } @Test - public void simpleOperation() throws Exception { - firestore.collection("col").document().create(Collections.singletonMap("foo", "bar")).get(); + public void queryGet() throws Exception { + firestore.collection("col").get().get(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunQuery") + .build(); + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunQuery.Get") + .build(); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void aggregateQueryGet() throws Exception { + firestore.collection("col").count().get().get(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunAggregationQuery") + .build(); + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunAggregationQuery.Get") + .build(); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void writeBatch() throws Exception { + WriteBatch batch = firestore.batch(); + DocumentReference docRef = firestore.collection("foo").document(); + batch.create(docRef, Collections.singletonMap("foo", "bar")); + batch.update(docRef, Collections.singletonMap("foo", "bar")); + batch.delete(docRef); + batch.commit().get(); Attributes attributes = expectedBaseAttributes @@ -129,21 +247,29 @@ public void simpleOperation() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "DocumentReference.Create") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Batch.Commit") .build(); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); validateMetricData(dataFromReader, attributes); } @Test - public void operationWithFirstResponseLatency() throws Exception { - firestore.collection("col").get(); + public void bulkWriterCommit() throws Exception { + ScheduledExecutorService bulkWriterExecutor = Executors.newSingleThreadScheduledExecutor(); + BulkWriter bulkWriter = + firestore.bulkWriter(BulkWriterOptions.builder().setExecutor(bulkWriterExecutor).build()); + bulkWriter.set( + firestore.collection("col").document("foo"), + Collections.singletonMap("bulk-foo", "bulk-bar")); + bulkWriter.close(); + bulkWriterExecutor.awaitTermination(100, TimeUnit.MILLISECONDS); Attributes attributes = expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunQuery") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") .build(); // Validate GAX layer metrics @@ -162,8 +288,166 @@ public void operationWithFirstResponseLatency() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunQuery.Get") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "BulkWriter.Commit") .build(); + + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void partitionQuery() throws Exception { + CollectionGroup collectionGroup = firestore.collectionGroup("col"); + collectionGroup.getPartitions(3).get(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") + .build(); + + // Why it doesn't have GAX metrics??? + // Validate GAX layer metrics + // MetricData dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + // validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "PartitionQuery") + .build(); + + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void collectionListDocuments() throws Exception { + firestore.collection("col").listDocuments(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") + .build(); + + // Validate GAX layer metrics + // MetricData dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + // validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "CollectionReference.ListDocuments") + .build(); + + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void docRefSet() throws Exception { + firestore + .collection("col") + .document("foo") + .set(Collections.singletonMap("foo", "bar"), SetOptions.merge()) + .get(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.Commit") + .build(); + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + + List attributesList = new ArrayList<>(); + String[] methods = {"Batch.Commit", "DocumentReference.Set"}; + + for (String method : methods) { + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + attributesList.add(attributes); + } + + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributesList); + } + + @Test + public void getAll() throws Exception { + DocumentReference docRef0 = firestore.collection("col").document(); + DocumentReference docRef1 = firestore.collection("col").document(); + DocumentReference[] docs = {docRef0, docRef1}; + firestore.getAll(docs).get(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchGetDocuments") + .build(); + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "BatchGetDocuments.GetAll") + .build(); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); validateMetricData(dataFromReader, attributes); @@ -173,20 +457,20 @@ public void operationWithFirstResponseLatency() throws Exception { } @Test - public void operationWithTransaction() throws Exception { + public void transaction() throws Exception { firestore - .runTransaction( + .runTransaction( // Has end-to-end, first response latency and transaction metrics. transaction -> { Query q = firestore.collection("col").whereGreaterThan("bla", ""); - DocumentReference d = firestore.collection("col").document("foo"); - DocumentReference[] docList = {d, d}; - // Document Query. + // Document Query. Has end-to-end and first response latency which is marked + // transactional transaction.get(q).get(); - // Aggregation Query. + // Aggregation Query. Has end-to-end and first response latency which is marked + // transactional transaction.get(q.count()); - // Commit 2 documents. + // Commit 2 documents. Only has RPC layer metrics transaction.set( firestore.collection("foo").document("bar"), Collections.singletonMap("foo", "bar")); @@ -226,8 +510,8 @@ public void operationWithTransaction() throws Exception { dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); validateMetricData(dataFromReader, attributesList); - // Validate SDK layer metric - List attributesList2 = new ArrayList<>(); + // Validate SDK layer metric + attributesList = new ArrayList<>(); String[] methods2 = { "RunTransaction", "RunQuery.Transactional", "RunAggregationQuery.Transactional", @@ -240,14 +524,14 @@ public void operationWithTransaction() throws Exception { .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) .build(); - attributesList2.add(attributes); + attributesList.add(attributes); } dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributesList2); + validateMetricData(dataFromReader, attributesList); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributesList2); + validateMetricData(dataFromReader, attributesList); Attributes attributes = expectedBaseAttributes @@ -265,6 +549,163 @@ public void operationWithTransaction() throws Exception { validateMetricData(dataFromReader, attributes); } + @Test + public void transactionWithRollback() throws Exception { + String myErrorMessage = "My error message."; + try { + firestore + .runTransaction( + transaction -> { + if (true) { + throw (new Exception(myErrorMessage)); + } + return 0; + }) + .get(); + } catch (Exception e) { + // Catch and move on. + } + + List attributesList = new ArrayList<>(); + String[] methods = { + "Firestore.BeginTransaction", "Firestore.Rollback", + }; + + for (String method : methods) { + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + attributesList.add(attributes); + } + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributesList); + + // Validate SDK layer metric + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, + "OK") // Transaction began successfully. + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunTransaction") + .build(); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + validateMetricData(dataFromReader, attributes); + + attributes = + expectedBaseAttributes + .toBuilder() + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, + "UNKNOWN") // Transaction ended with exception + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunTransaction") + .build(); + + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + validateMetricData(dataFromReader, attributes); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + } + + @Test + public void multipleOperations() throws Exception { + CollectionReference col = firestore.collection("col"); + col.get().get(); + col.count().get().get(); + DocumentReference ref = col.document("doc1"); + ref.create(Collections.singletonMap("foo", "bar")).get(); + ref.update(map("foo", "newBar")).get(); + ref.delete().get(); + + List attributesList = new ArrayList<>(); + String[] methods = {"Firestore.RunQuery", "Firestore.RunAggregationQuery", "Firestore.Commit"}; + + for (String method : methods) { + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + attributesList.add(attributes); + } + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributesList); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributesList); + + // Validate SDK layer metric + attributesList = new ArrayList<>(); + + String[] methods2 = { + "RunQuery.Get", + "RunAggregationQuery.Get", + "DocumentReference.Create", + "DocumentReference.Update", + "DocumentReference.Delete", + "Batch.Commit", + }; + + for (String method : methods2) { + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + attributesList.add(attributes); + } + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributesList); + + attributesList = new ArrayList<>(); + + String[] methods3 = { + "RunQuery.Get", "RunAggregationQuery.Get", + }; + + for (String method : methods3) { + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + attributesList.add(attributes); + } + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + validateMetricData(dataFromReader, attributesList); + } + private void validateMetricData(MetricData metricData, Attributes expectedAttributes) { validateMetricData(metricData, Arrays.asList(expectedAttributes)); } @@ -284,7 +725,7 @@ private void validateMetricData(MetricData metricData, List expected assertThat(((HistogramPointData) point).getCount()).isAtLeast(1); assertThat(((HistogramPointData) point).getSum()).isGreaterThan(0.0); } else { - assertThat(((LongPointData) point).getValue()).isEqualTo(1); + assertThat(((LongPointData) point).getValue()).isAtLeast(1); } actualAttributesList.add(point.getAttributes()); } From 06c8c2dbb889ee406854683870ed1977d0fe4575 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:14:43 -0500 Subject: [PATCH 077/103] add tests that doesn't trigger rpc metrics --- .../cloud/firestore/it/ITMetricsTest.java | 149 ++++++++++++++---- 1 file changed, 118 insertions(+), 31 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index 8ab4832db..165e2999d 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -20,13 +20,18 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import com.google.api.gax.retrying.RetrySettings; +import com.google.api.core.ApiFuture; +import com.google.api.gax.rpc.ApiStreamObserver; import com.google.cloud.firestore.BulkWriter; import com.google.cloud.firestore.BulkWriterOptions; import com.google.cloud.firestore.CollectionGroup; import com.google.cloud.firestore.CollectionReference; import com.google.cloud.firestore.DocumentReference; +import com.google.cloud.firestore.DocumentSnapshot; +import com.google.cloud.firestore.ExplainMetrics; +import com.google.cloud.firestore.ExplainOptions; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; import com.google.cloud.firestore.FirestoreOptions; @@ -62,8 +67,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; -import org.mockito.Spy; -import org.threeten.bp.Duration; public class ITMetricsTest { @@ -76,22 +79,6 @@ public class ITMetricsTest { protected Firestore firestore; - @Spy - private final Firestore firestoreMock = - FirestoreOptions.newBuilder() - .setProjectId("test-project") - .setRetrySettings( - RetrySettings.newBuilder() - .setInitialRetryDelay(Duration.ZERO) - .setMaxRetryDelay(Duration.ZERO) - .setRetryDelayMultiplier(1) - .setJittered(false) - .build()) - .setOpenTelemetryOptions( - FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetrySdk).build()) - .build() - .getService(); - private static Attributes expectedBaseAttributes; private final String ClientUid = ClientIdentifier.getClientUid(); @@ -178,6 +165,64 @@ public void queryGet() throws Exception { validateMetricData(dataFromReader, attributes); } + @Test + public void queryExplain() throws Exception { + Query query = firestore.collection("col"); + ApiFuture metricsFuture = + query.explainStream( + ExplainOptions.builder().setAnalyze(false).build(), + new ApiStreamObserver() { + @Override + public void onNext(DocumentSnapshot documentSnapshot) { + fail("No DocumentSnapshot should be received because analyze option was disabled."); + } + + @Override + public void onError(Throwable throwable) { + fail(throwable.getMessage()); + } + + @Override + public void onCompleted() {} + }); + + ExplainMetrics metrics = metricsFuture.get(); + assertThat(metrics.getPlanSummary().getIndexesUsed().size()).isGreaterThan(0); + assertThat(metrics.getExecutionStats()).isNull(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunQuery") + .build(); + + // Validate GAX layer metrics + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + validateMetricData(dataFromReader, attributes); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunQuery.Explain") + .build(); + dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + + dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + validateMetricData(dataFromReader, attributes); + } + @Test public void aggregateQueryGet() throws Exception { firestore.collection("col").count().get().get(); @@ -333,6 +378,46 @@ public void partitionQuery() throws Exception { validateMetricData(dataFromReader, attributes); } + @Test + public void listCollection() throws Exception { + firestore.collection("col").document("doc0").listCollections(); + + Attributes attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") + .build(); + + // Why it doesn't have GAX metrics??? + // Validate GAX layer metrics + // MetricData dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + // validateMetricData(dataFromReader, attributes); + + // Validate SDK layer metric + attributes = + expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "DocumentReference.ListCollections") + .build(); + + MetricData dataFromReader = + getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); + validateMetricData(dataFromReader, attributes); + } + @Test public void collectionListDocuments() throws Exception { firestore.collection("col").listDocuments(); @@ -345,16 +430,18 @@ public void collectionListDocuments() throws Exception { .build(); // Validate GAX layer metrics - // MetricData dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - // validateMetricData(dataFromReader, attributes); + // MetricData dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_OPERATION_COUNT); + // validateMetricData(dataFromReader, attributes); + // dataFromReader = getMetricData(metricReader, + // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + // validateMetricData(dataFromReader, attributes); // Validate SDK layer metric attributes = @@ -633,7 +720,7 @@ public void multipleOperations() throws Exception { col.get().get(); col.count().get().get(); DocumentReference ref = col.document("doc1"); - ref.create(Collections.singletonMap("foo", "bar")).get(); + ref.set(Collections.singletonMap("foo", "bar")).get(); ref.update(map("foo", "newBar")).get(); ref.delete().get(); @@ -667,7 +754,7 @@ public void multipleOperations() throws Exception { String[] methods2 = { "RunQuery.Get", "RunAggregationQuery.Get", - "DocumentReference.Create", + "DocumentReference.Set", "DocumentReference.Update", "DocumentReference.Delete", "Batch.Commit", From a78cd66a3e07739ff24e62a9bf6ed2abe77e2530 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:22:21 -0500 Subject: [PATCH 078/103] replace RunQuery.get with Query.get --- .../cloud/firestore/AggregateQuery.java | 18 ++- .../google/cloud/firestore/FirestoreImpl.java | 4 +- .../com/google/cloud/firestore/Query.java | 5 +- .../cloud/firestore/StreamableQuery.java | 8 +- .../telemetry/TelemetryConstants.java | 11 +- .../cloud/firestore/it/ITMetricsTest.java | 138 +++++++----------- 6 files changed, 73 insertions(+), 111 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 89702e423..2d62bca8f 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -16,7 +16,6 @@ package com.google.cloud.firestore; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY; import static com.google.cloud.firestore.telemetry.TraceUtil.ATTRIBUTE_KEY_ATTEMPT; import com.google.api.core.ApiFuture; @@ -108,7 +107,7 @@ public ApiFuture> explain(ExplainOptions getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET); MetricsContext metricsContext = - createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN); + createMetricsContext(TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_EXPLAIN); try (Scope ignored = span.makeCurrent()) { AggregateQueryExplainResponseDeliverer responseDeliverer = @@ -141,8 +140,8 @@ ApiFuture get( MetricsContext metricsContext = createMetricsContext( transactionId == null - ? TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_GET - : TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL); + ? TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET + : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY); try (Scope ignored = span.makeCurrent()) { AggregateQueryResponseDeliverer responseDeliverer = @@ -341,7 +340,9 @@ private boolean isExplainQuery() { public void onStart(StreamController streamController) { getTraceUtil() .currentSpan() - .addEvent(METHOD_NAME_RUN_AGGREGATION_QUERY + " Stream started.", getAttemptAttributes()); + .addEvent( + TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY + " Stream started.", + getAttemptAttributes()); } @Override @@ -354,7 +355,8 @@ public void onResponse(RunAggregationQueryResponse response) { getTraceUtil() .currentSpan() .addEvent( - METHOD_NAME_RUN_AGGREGATION_QUERY + " Response Received.", getAttemptAttributes()); + TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY + " Response Received.", + getAttemptAttributes()); if (response.hasReadTime()) { readTime = Timestamp.fromProto(response.getReadTime()); } @@ -383,7 +385,7 @@ public void onError(Throwable throwable) { getTraceUtil() .currentSpan() .addEvent( - METHOD_NAME_RUN_AGGREGATION_QUERY + ": Retryable Error", + TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY + ": Retryable Error", Collections.singletonMap("error.message", throwable.toString())); runQuery(responseDeliverer, attempt + 1); @@ -391,7 +393,7 @@ public void onError(Throwable throwable) { getTraceUtil() .currentSpan() .addEvent( - METHOD_NAME_RUN_AGGREGATION_QUERY + ": Error", + TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY + ": Error", Collections.singletonMap("error.message", throwable.toString())); responseDeliverer.deliverError(throwable); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index 5c7217141..e7c219434 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -235,8 +235,8 @@ void getAll( .getMetricsUtil() .createMetricsContext( transactionId == null - ? TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_GET_ALL - : TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_TRANSACTIONAL); + ? TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + : TelemetryConstants.METHOD_NAME_TRANSACTION_BATCH_GET_DOCUMENTS); ResponseObserver responseObserver = new ResponseObserver() { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index f93ec2c29..87b41702e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -1489,8 +1489,7 @@ public void stream(@Nonnull final ApiStreamObserver responseOb "Query results for queries that include limitToLast() constraints cannot be streamed. " + "Use Query.get() instead."); - MetricsContext metricsContext = - createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_GET); + MetricsContext metricsContext = createMetricsContext(TelemetryConstants.METHOD_NAME_QUERY_GET); ApiStreamObserver observer = new ApiStreamObserver() { @@ -1542,7 +1541,7 @@ public ApiFuture explainStream( + "Use Query.explain() instead."); MetricsContext metricsContext = - createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN); + createMetricsContext(TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN); final SettableApiFuture metricsFuture = SettableApiFuture.create(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java index a959f9a15..532698ed8 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java @@ -115,9 +115,9 @@ ApiFuture get( MetricsContext metricsContext = createMetricsContext( - transactionId != null - ? TelemetryConstants.METHOD_NAME_RUN_QUERY_TRANSACTIONAL - : TelemetryConstants.METHOD_NAME_RUN_QUERY_GET); + transactionId == null + ? TelemetryConstants.METHOD_NAME_QUERY_GET + : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY); try (Scope ignored = span.makeCurrent()) { final SettableApiFuture result = SettableApiFuture.create(); @@ -193,7 +193,7 @@ public ApiFuture> explain(ExplainOptions options) { .startSpan(TelemetryConstants.METHOD_NAME_QUERY_GET); MetricsContext metricsContext = - createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN); + createMetricsContext(TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN); try (Scope ignored = span.makeCurrent()) { final SettableApiFuture> result = SettableApiFuture.create(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index d3527dc27..c88f56bb0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -35,24 +35,19 @@ public interface TelemetryConstants { String METHOD_NAME_COL_REF_ADD = "CollectionReference.Add"; String METHOD_NAME_COL_REF_LIST_DOCUMENTS = "CollectionReference.ListDocuments"; String METHOD_NAME_QUERY_GET = "Query.Get"; + String METHOD_NAME_QUERY_EXPLAIN = "Query.Explain"; String METHOD_NAME_AGGREGATION_QUERY_GET = "AggregationQuery.Get"; + String METHOD_NAME_AGGREGATION_QUERY_EXPLAIN = "AggregationQuery.Explain"; String METHOD_NAME_RUN_QUERY = "RunQuery"; - String METHOD_NAME_RUN_QUERY_EXPLAIN = "RunQuery.Explain"; - String METHOD_NAME_RUN_QUERY_GET = "RunQuery.Get"; - String METHOD_NAME_RUN_QUERY_TRANSACTIONAL = "RunQuery.Transactional"; String METHOD_NAME_RUN_AGGREGATION_QUERY = "RunAggregationQuery"; - String METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN = "RunAggregationQuery.Explain"; - String METHOD_NAME_RUN_AGGREGATION_QUERY_GET = "RunAggregationQuery.Get"; - String METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL = "RunAggregationQuery.Transactional"; String METHOD_NAME_BATCH_GET_DOCUMENTS = "BatchGetDocuments"; - String METHOD_NAME_BATCH_GET_DOCUMENTS_GET_ALL = "BatchGetDocuments.GetAll"; - String METHOD_NAME_BATCH_GET_DOCUMENTS_TRANSACTIONAL = "BatchGetDocuments.Transactional"; String METHOD_NAME_TRANSACTION_RUN = "Transaction.Run"; String METHOD_NAME_TRANSACTION_BEGIN = "Transaction.Begin"; String METHOD_NAME_TRANSACTION_GET_QUERY = "Transaction.Get.Query"; String METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY = "Transaction.Get.AggregationQuery"; String METHOD_NAME_TRANSACTION_GET_DOCUMENT = "Transaction.Get.Document"; String METHOD_NAME_TRANSACTION_GET_DOCUMENTS = "Transaction.Get.Documents"; + String METHOD_NAME_TRANSACTION_BATCH_GET_DOCUMENTS = "Transaction.BatchGetDocuments"; String METHOD_NAME_TRANSACTION_ROLLBACK = "Transaction.Rollback"; String METHOD_NAME_BATCH_COMMIT = "Batch.Commit"; String METHOD_NAME_TRANSACTION_COMMIT = "Transaction.Commit"; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index 165e2999d..dfc6b3421 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -155,7 +155,9 @@ public void queryGet() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunQuery.Get") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_QUERY_GET) .build(); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); validateMetricData(dataFromReader, attributes); @@ -213,7 +215,9 @@ public void onCompleted() {} expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunQuery.Explain") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN) .build(); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); validateMetricData(dataFromReader, attributes); @@ -250,7 +254,9 @@ public void aggregateQueryGet() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunAggregationQuery.Get") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET) .build(); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); validateMetricData(dataFromReader, attributes); @@ -292,7 +298,9 @@ public void writeBatch() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Batch.Commit") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_BATCH_COMMIT) .build(); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); @@ -333,7 +341,9 @@ public void bulkWriterCommit() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "BulkWriter.Commit") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT) .build(); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); @@ -344,33 +354,16 @@ public void bulkWriterCommit() throws Exception { public void partitionQuery() throws Exception { CollectionGroup collectionGroup = firestore.collectionGroup("col"); collectionGroup.getPartitions(3).get(); - - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") - .build(); - - // Why it doesn't have GAX metrics??? - // Validate GAX layer metrics - // MetricData dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - // validateMetricData(dataFromReader, attributes); + // TODO(Metrics): pagedCalled reqeusts are not traced at GAX layer // Validate SDK layer metric - attributes = + Attributes attributes = expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "PartitionQuery") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_PARTITION_QUERY) .build(); MetricData dataFromReader = @@ -381,36 +374,16 @@ public void partitionQuery() throws Exception { @Test public void listCollection() throws Exception { firestore.collection("col").document("doc0").listCollections(); - - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") - .build(); - - // Why it doesn't have GAX metrics??? - // Validate GAX layer metrics - // MetricData dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - // validateMetricData(dataFromReader, attributes); + // TODO(Metrics): pagedCalled reqeusts are not traced at GAX layer // Validate SDK layer metric - attributes = + Attributes attributes = expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "DocumentReference.ListCollections") + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS) .build(); MetricData dataFromReader = @@ -421,35 +394,16 @@ public void listCollection() throws Exception { @Test public void collectionListDocuments() throws Exception { firestore.collection("col").listDocuments(); - - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") - .build(); - - // Validate GAX layer metrics - // MetricData dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - // validateMetricData(dataFromReader, attributes); - // dataFromReader = getMetricData(metricReader, - // TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - // validateMetricData(dataFromReader, attributes); + // TODO(Metrics): pagedCalled reqeusts are not traced at GAX layer // Validate SDK layer metric - attributes = + Attributes attributes = expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "CollectionReference.ListDocuments") + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS) .build(); MetricData dataFromReader = @@ -486,7 +440,9 @@ public void docRefSet() throws Exception { // Validate SDK layer metric List attributesList = new ArrayList<>(); - String[] methods = {"Batch.Commit", "DocumentReference.Set"}; + String[] methods = { + TelemetryConstants.METHOD_NAME_BATCH_COMMIT, TelemetryConstants.METHOD_NAME_DOC_REF_SET + }; for (String method : methods) { attributes = @@ -532,7 +488,9 @@ public void getAll() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "BatchGetDocuments.GetAll") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS) .build(); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); @@ -601,7 +559,9 @@ public void transaction() throws Exception { attributesList = new ArrayList<>(); String[] methods2 = { - "RunTransaction", "RunQuery.Transactional", "RunAggregationQuery.Transactional", + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, }; for (String method : methods2) { @@ -624,7 +584,9 @@ public void transaction() throws Exception { expectedBaseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunTransaction") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION) .build(); dataFromReader = @@ -686,7 +648,9 @@ public void transactionWithRollback() throws Exception { .put( TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") // Transaction began successfully. - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunTransaction") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION) .build(); dataFromReader = @@ -699,7 +663,9 @@ public void transactionWithRollback() throws Exception { .put( TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "UNKNOWN") // Transaction ended with exception - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "RunTransaction") + .put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION) .build(); dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); @@ -752,12 +718,12 @@ public void multipleOperations() throws Exception { attributesList = new ArrayList<>(); String[] methods2 = { - "RunQuery.Get", - "RunAggregationQuery.Get", - "DocumentReference.Set", - "DocumentReference.Update", - "DocumentReference.Delete", - "Batch.Commit", + TelemetryConstants.METHOD_NAME_QUERY_GET, + TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, + TelemetryConstants.METHOD_NAME_DOC_REF_SET, + TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE, + TelemetryConstants.METHOD_NAME_DOC_REF_DELETE, + TelemetryConstants.METHOD_NAME_BATCH_COMMIT, }; for (String method : methods2) { From 12282147255ab0e801aae5c1ba1a1ab7363f699a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:15:43 -0500 Subject: [PATCH 079/103] Update clirr-ignored-differences.xml --- google-cloud-firestore/clirr-ignored-differences.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index a1064b15c..0c0a847ca 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -346,4 +346,11 @@ void internalStream(*) * + + + + 6011 + com/google/cloud/firestore/telemetry/TelemetryConstants + METHOD_NAME_* + From 693260cb5956854914b27afea9b840e227ea90a6 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:03:42 -0500 Subject: [PATCH 080/103] format --- .../java/com/google/cloud/firestore/it/ITMetricsTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index dfc6b3421..1670d0c2c 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -354,8 +354,8 @@ public void bulkWriterCommit() throws Exception { public void partitionQuery() throws Exception { CollectionGroup collectionGroup = firestore.collectionGroup("col"); collectionGroup.getPartitions(3).get(); - // TODO(Metrics): pagedCalled reqeusts are not traced at GAX layer + // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric Attributes attributes = expectedBaseAttributes @@ -374,8 +374,8 @@ public void partitionQuery() throws Exception { @Test public void listCollection() throws Exception { firestore.collection("col").document("doc0").listCollections(); - // TODO(Metrics): pagedCalled reqeusts are not traced at GAX layer + // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric Attributes attributes = expectedBaseAttributes @@ -394,8 +394,8 @@ public void listCollection() throws Exception { @Test public void collectionListDocuments() throws Exception { firestore.collection("col").listDocuments(); - // TODO(Metrics): pagedCalled reqeusts are not traced at GAX layer + // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric Attributes attributes = expectedBaseAttributes From 16cd25f7f6a27da875a11cea1911e2f4cfaf3993 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:48:40 -0500 Subject: [PATCH 081/103] refactor test code --- .../cloud/firestore/AggregateQuery.java | 5 +- .../ServerSideTransactionRunner.java | 7 +- .../google/cloud/firestore/UpdateBuilder.java | 27 +- .../cloud/firestore/it/ITMetricsTest.java | 968 +++++++++--------- 4 files changed, 500 insertions(+), 507 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 2d62bca8f..36eb01607 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -120,9 +120,11 @@ public ApiFuture> explain(ExplainOptions runQuery(responseDeliverer, /* attempt */ 0); ApiFuture> result = responseDeliverer.getFuture(); span.endAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -153,9 +155,11 @@ ApiFuture get( runQuery(responseDeliverer, /* attempt= */ 0); ApiFuture result = responseDeliverer.getFuture(); span.endAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -238,7 +242,6 @@ void deliverResult( try { T result = processResult(serverData, readTime, metrics); future.set(result); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); } catch (Exception error) { deliverError(error); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index f26c3e0c9..4dca138ea 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -150,7 +150,12 @@ ApiFuture begin() { serverSideTransaction.setTransactionTraceContext(runTransactionContext); return serverSideTransaction; }); - metricsContext.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, result); + + // Record the first time latency for the first BeginTransaction call only + Boolean isFirstAttempt = (transactionOptions.getNumberOfAttempts() - attemptsRemaining) == 1; + if (isFirstAttempt) { + metricsContext.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, result); + } span.endAtFuture(result); return result; } catch (Exception error) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index 5bb146dfd..1ab0dcb37 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -626,15 +626,14 @@ ApiFuture> commit(@Nullable ByteString transactionId) { MetricsContext metricsContext = null; - // End to end latency for transactions recorded under RunTransaction, including the - // Transaction.Commit operation. - if (transactionId == null) { - metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_BATCH_COMMIT); - } + metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext( + transactionId == null + ? TelemetryConstants.METHOD_NAME_BATCH_COMMIT + : TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT); try (Scope ignored = span.makeCurrent()) { // Sequence is thread safe. @@ -669,15 +668,13 @@ ApiFuture> commit(@Nullable ByteString transactionId) { }, MoreExecutors.directExecutor()); span.endAtFuture(returnValue); - if (metricsContext != null) { - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, returnValue); - } + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, returnValue); + return returnValue; } catch (Exception error) { span.end(error); - if (metricsContext != null) { - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); - } + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); + throw error; } } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index 1670d0c2c..a9adb26df 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -23,24 +23,14 @@ import static org.junit.Assert.fail; import com.google.api.core.ApiFuture; +import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.ApiStreamObserver; -import com.google.cloud.firestore.BulkWriter; -import com.google.cloud.firestore.BulkWriterOptions; -import com.google.cloud.firestore.CollectionGroup; -import com.google.cloud.firestore.CollectionReference; -import com.google.cloud.firestore.DocumentReference; -import com.google.cloud.firestore.DocumentSnapshot; -import com.google.cloud.firestore.ExplainMetrics; -import com.google.cloud.firestore.ExplainOptions; -import com.google.cloud.firestore.Firestore; -import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; -import com.google.cloud.firestore.FirestoreOptions; -import com.google.cloud.firestore.Query; -import com.google.cloud.firestore.SetOptions; -import com.google.cloud.firestore.WriteBatch; +import com.google.cloud.firestore.*; import com.google.cloud.firestore.telemetry.ClientIdentifier; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.common.base.Preconditions; +import io.grpc.Status; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.OpenTelemetrySdk; @@ -52,15 +42,15 @@ import io.opentelemetry.sdk.metrics.data.MetricDataType; import io.opentelemetry.sdk.metrics.data.PointData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; import java.util.stream.Collectors; import org.junit.After; import org.junit.Before; @@ -69,10 +59,6 @@ import org.junit.rules.TestName; public class ITMetricsTest { - - private static final Logger logger = - Logger.getLogger(com.google.cloud.firestore.it.ITMetricsTest.class.getName()); - private static OpenTelemetrySdk openTelemetrySdk; protected InMemoryMetricReader metricReader; @@ -89,82 +75,98 @@ public class ITMetricsTest { @Before public void setup() { metricReader = InMemoryMetricReader.create(); + openTelemetrySdk = setupOpenTelemetrySdk(); + firestore = setupFirestoreService(); + expectedBaseAttributes = buildExpectedAttributes(); + } + + private OpenTelemetrySdk setupOpenTelemetrySdk() { SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder().registerMetricReader(metricReader); + return OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + } - openTelemetrySdk = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - - firestore = - FirestoreOptions.newBuilder() - .setOpenTelemetryOptions( - FirestoreOpenTelemetryOptions.newBuilder() - .setOpenTelemetry(openTelemetrySdk) - .build()) - .build() - .getService(); + private Firestore setupFirestoreService() { + return FirestoreOptions.newBuilder() + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder().setOpenTelemetry(openTelemetrySdk).build()) + .build() + .getService(); + } - AttributesBuilder expectedAttributes = Attributes.builder(); - expectedAttributes.put( + private Attributes buildExpectedAttributes() { + AttributesBuilder attributesBuilder = Attributes.builder(); + attributesBuilder.put( TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), TelemetryConstants.FIRESTORE_LIBRARY_NAME); - expectedAttributes.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientUid); + attributesBuilder.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientUid); if (libraryVersion != null) { - expectedAttributes.put( + attributesBuilder.put( TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), libraryVersion); } - - expectedBaseAttributes = expectedAttributes.build(); + return attributesBuilder.build(); } @After - public void tearDown() throws Exception { + public void tearDown() { Preconditions.checkNotNull( firestore, "Error instantiating Firestore. Check that the service account credentials were properly set."); try { - metricReader.close(); + metricReader.shutdown(); } finally { firestore.shutdown(); } } + class MetricInfo { + // The expected number of measurements is called + public int count; + // Attributes expected to be recorded in the measurements + public Attributes attributes; + + public MetricInfo(int expectedCount, Attributes expectedAttributes) { + this.count = expectedCount; + this.attributes = expectedAttributes; + } + } + + class MetricsExpectationBuilder { + private final Map expectedMetrics = new HashMap<>(); + + public MetricsExpectationBuilder expectMetric( + String metricName, String expectedStatus, int expectedCount) { + Attributes attributes = buildAttributes(metricName, expectedStatus); + expectedMetrics.put(metricName, new MetricInfo(expectedCount, attributes)); + return this; + } + + public Map build() { + return expectedMetrics; + } + } + @Test public void queryGet() throws Exception { firestore.collection("col").get().get(); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunQuery") - .build(); - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_QUERY_GET) - .build(); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributes); + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -192,78 +194,50 @@ public void onCompleted() {} assertThat(metrics.getPlanSummary().getIndexesUsed().size()).isGreaterThan(0); assertThat(metrics.getExecutionStats()).isNull(); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunQuery") - .build(); - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN) - .build(); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributes); + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN, Status.OK.getCode().toString(), 1) + .build(); + + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test public void aggregateQueryGet() throws Exception { firestore.collection("col").count().get().get(); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.RunAggregationQuery") - .build(); - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET) - .build(); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributes); + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, + Status.OK.getCode().toString(), + 1) + .build(); + + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -275,36 +249,24 @@ public void writeBatch() throws Exception { batch.delete(docRef); batch.commit().get(); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.Commit") - .build(); - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_BATCH_COMMIT) + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 1) .build(); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -318,36 +280,26 @@ public void bulkWriterCommit() throws Exception { bulkWriter.close(); bulkWriterExecutor.awaitTermination(100, TimeUnit.MILLISECONDS); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchWrite") - .build(); - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.BatchWrite", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT) + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT, + Status.OK.getCode().toString(), + 1) .build(); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -357,18 +309,16 @@ public void partitionQuery() throws Exception { // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_PARTITION_QUERY) - .build(); - - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_PARTITION_QUERY, Status.OK.getCode().toString(), 1) + .build(); + + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -377,18 +327,18 @@ public void listCollection() throws Exception { // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS) - .build(); - - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS, + Status.OK.getCode().toString(), + 1) + .build(); + + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -397,18 +347,18 @@ public void collectionListDocuments() throws Exception { // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS) - .build(); - - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS, + Status.OK.getCode().toString(), + 1) + .build(); + + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -419,43 +369,26 @@ public void docRefSet() throws Exception { .set(Collections.singletonMap("foo", "bar"), SetOptions.merge()) .get(); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.Commit") - .build(); - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_DOC_REF_SET, Status.OK.getCode().toString(), 1) + .build(); - List attributesList = new ArrayList<>(); - String[] methods = { - TelemetryConstants.METHOD_NAME_BATCH_COMMIT, TelemetryConstants.METHOD_NAME_DOC_REF_SET - }; - - for (String method : methods) { - attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); - attributesList.add(attributes); - } - - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributesList); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -465,40 +398,26 @@ public void getAll() throws Exception { DocumentReference[] docs = {docRef0, docRef1}; firestore.getAll(docs).get(); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, "Firestore.BatchGetDocuments") - .build(); - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributes); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributes); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.BatchGetDocuments", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS) + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS, + Status.OK.getCode().toString(), + 1) .build(); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributes); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @Test @@ -515,7 +434,7 @@ public void transaction() throws Exception { // transactional transaction.get(q.count()); - // Commit 2 documents. Only has RPC layer metrics + // Commit 2 documents. Has end-to-end latency. transaction.set( firestore.collection("foo").document("bar"), Collections.singletonMap("foo", "bar")); @@ -526,76 +445,59 @@ public void transaction() throws Exception { }) .get(); - List attributesList = new ArrayList<>(); - String[] methods = { - "Firestore.BeginTransaction", - "Firestore.RunQuery", - "Firestore.RunAggregationQuery", - "Firestore.Commit" - }; - - for (String method : methods) { - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); - attributesList.add(attributes); - } - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributesList); - - // Validate SDK layer metric - attributesList = new ArrayList<>(); - - String[] methods2 = { - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, - TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, - TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, - }; - - for (String method : methods2) { - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); - attributesList.add(attributes); - } - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributesList); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) + .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 1) + .expectMetric("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) + .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .build(); - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributesList); + validateGaxMetrics(expectedMetrics); - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION) + // Validate SDK layer metric + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, + Status.OK.getCode().toString(), + 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, + Status.OK.getCode().toString(), + 1) .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, + Status.OK.getCode().toString(), + 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, + Status.OK.getCode().toString(), + 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, + Status.OK.getCode().toString(), + 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); - validateMetricData(dataFromReader, attributes); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, expectedMetrics); } @Test @@ -604,8 +506,10 @@ public void transactionWithRollback() throws Exception { try { firestore .runTransaction( + // BeginTransaction is successful, RunTransaction receives its first response. transaction -> { if (true) { + // Transaction encounters not retryable error, ends with failure throw (new Exception(myErrorMessage)); } return 0; @@ -615,193 +519,263 @@ public void transactionWithRollback() throws Exception { // Catch and move on. } - List attributesList = new ArrayList<>(); - String[] methods = { - "Firestore.BeginTransaction", "Firestore.Rollback", - }; - - for (String method : methods) { - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); - attributesList.add(attributes); + // Validate GAX layer metrics + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) + .expectMetric("Firestore.Rollback", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); + + // Validate SDK layer metric + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, + Status.UNKNOWN.getCode().toString(), + 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, expectedMetrics); + } + + @Test + public void transactionWithFailure() throws Exception { + Firestore invalidFirestore = + FirestoreOptions.newBuilder() + .setDatabaseId("foo.bar") // Invalid databaseId + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .setOpenTelemetry(openTelemetrySdk) + .build()) + .build() + .getService(); + + try { + invalidFirestore + .runTransaction( + // Transaction cannot get started due to the invalid database ID + transaction -> { + return 0; + }) + .get(); + } catch (Exception e) { + // Catch and move on. + } + + // Validate GAX layer metrics + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.BeginTransaction", Status.NOT_FOUND.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); + + // Validate SDK layer metric + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, + Status.UNKNOWN + .getCode() + .toString(), // TODO(b/305998085):Change this to correct status code + 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, expectedMetrics); + } + + @Test + public void transactionWithRetry() throws Exception { + // Throw a retryable error + ApiException RETRYABLE_API_EXCEPTION = + new ApiException( + new Exception("Test exception"), GrpcStatusCode.of(Status.Code.UNKNOWN), true); + + try { + firestore + .runTransaction( + // RunTransaction retries from BeginTransaction for 3 times + new Transaction.Function() { + int cnt = 1; + + @Override + public Integer updateCallback(Transaction transaction) throws Exception { + cnt++; + if (cnt <= 3) { + throw RETRYABLE_API_EXCEPTION; + } + return 0; + } + }) + .get(); + } catch (Exception e) { + // Catch and move on. } // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributesList); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.BeginTransaction", Status.OK.getCode().toString(), 3) + .expectMetric("Firestore.Rollback", Status.OK.getCode().toString(), 2) + .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, - "OK") // Transaction began successfully. - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION) - .build(); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributes); - - attributes = - expectedBaseAttributes - .toBuilder() - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, - "UNKNOWN") // Transaction ended with exception - .put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION) - .build(); - - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributes); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); - validateMetricData(dataFromReader, attributes); - - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributes); + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, + Status.OK.getCode().toString(), + 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 3) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, expectedMetrics); } @Test public void multipleOperations() throws Exception { - CollectionReference col = firestore.collection("col"); - col.get().get(); - col.count().get().get(); - DocumentReference ref = col.document("doc1"); + // 2 get query + firestore.collection("col1").get().get(); + firestore.collection("col2").get().get(); + // 1 aggregation get query + firestore.collection("col").count().get().get(); + // 3 batch commits on document reference: set, update,delete + DocumentReference ref = firestore.collection("col").document("doc1"); ref.set(Collections.singletonMap("foo", "bar")).get(); ref.update(map("foo", "newBar")).get(); ref.delete().get(); - List attributesList = new ArrayList<>(); - String[] methods = {"Firestore.RunQuery", "Firestore.RunAggregationQuery", "Firestore.Commit"}; - - for (String method : methods) { - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); - attributesList.add(attributes); - } - // Validate GAX layer metrics - MetricData dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_COUNT); - validateMetricData(dataFromReader, attributesList); - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); - validateMetricData(dataFromReader, attributesList); + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 2) + .expectMetric("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) + .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 3) + .build(); + validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - attributesList = new ArrayList<>(); - - String[] methods2 = { - TelemetryConstants.METHOD_NAME_QUERY_GET, - TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, - TelemetryConstants.METHOD_NAME_DOC_REF_SET, - TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE, - TelemetryConstants.METHOD_NAME_DOC_REF_DELETE, - TelemetryConstants.METHOD_NAME_BATCH_COMMIT, - }; - - for (String method : methods2) { - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); - attributesList.add(attributes); - } - dataFromReader = getMetricData(metricReader, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY); - validateMetricData(dataFromReader, attributesList); - - attributesList = new ArrayList<>(); - - String[] methods3 = { - "RunQuery.Get", "RunAggregationQuery.Get", - }; - - for (String method : methods3) { - Attributes attributes = - expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, "OK") - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); - attributesList.add(attributes); - } + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 2) + .expectMetric( + TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, + Status.OK.getCode().toString(), + 1) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetric( + TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 2) + .expectMetric( + TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, + Status.OK.getCode().toString(), + 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_DOC_REF_SET, Status.OK.getCode().toString(), 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE, Status.OK.getCode().toString(), 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_DOC_REF_DELETE, Status.OK.getCode().toString(), 1) + .expectMetric( + TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 3) + .build(); + validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); + + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); + assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); + } - dataFromReader = - getMetricData(metricReader, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - validateMetricData(dataFromReader, attributesList); + private Attributes buildAttributes(String method, String status) { + return expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, status) + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); } - private void validateMetricData(MetricData metricData, Attributes expectedAttributes) { - validateMetricData(metricData, Arrays.asList(expectedAttributes)); + private void validateSDKMetrics(String metric, Map expectedMetrics) { + MetricData dataFromReader = getMetricData(metric); + validateMetricData(dataFromReader, expectedMetrics); } - private void validateMetricData(MetricData metricData, List expectedAttributesList) { + private void validateGaxMetrics(Map expectedMetrics) { + List gaxMetricNames = + Arrays.asList( + TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, + TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, + TelemetryConstants.METRIC_NAME_OPERATION_COUNT, + TelemetryConstants.METRIC_NAME_OPERATION_LATENCY); + for (String metricName : gaxMetricNames) { + MetricData dataFromReader = getMetricData(metricName); + validateMetricData(dataFromReader, expectedMetrics); + } + } + + private void validateMetricData(MetricData metricData, Map expectedMetrics) { boolean isHistogram = metricData.getType() == MetricDataType.HISTOGRAM; Collection points = isHistogram ? metricData.getHistogramData().getPoints() : metricData.getLongSumData().getPoints(); + assertThat(points.size()).isEqualTo(expectedMetrics.size()); - assertThat(points.size()).isEqualTo(expectedAttributesList.size()); - - List actualAttributesList = new ArrayList<>(); for (PointData point : points) { + String method = point.getAttributes().get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD); + MetricInfo expectedMetricInfo = expectedMetrics.get(method); if (isHistogram) { - assertThat(((HistogramPointData) point).getCount()).isAtLeast(1); + assertThat(((HistogramPointData) point).getCount()).isEqualTo(expectedMetricInfo.count); assertThat(((HistogramPointData) point).getSum()).isGreaterThan(0.0); } else { - assertThat(((LongPointData) point).getValue()).isAtLeast(1); + assertThat(((LongPointData) point).getValue()).isEqualTo(expectedMetricInfo.count); } - actualAttributesList.add(point.getAttributes()); - } - - for (Attributes expectedAttributes : expectedAttributesList) { - assertThat( - actualAttributesList.stream() - .anyMatch( - actualAttributes -> - actualAttributes - .asMap() - .entrySet() - .containsAll(expectedAttributes.asMap().entrySet()))) - .isTrue(); + assertThat(point.getAttributes().asMap()) + .containsAtLeastEntriesIn(expectedMetricInfo.attributes.asMap()); } } - private MetricData getMetricData(InMemoryMetricReader reader, String metricName) { + private MetricData getMetricData(String metricName) { String fullMetricName = TelemetryConstants.METRIC_PREFIX + "/" + metricName; // Fetch the MetricData with retries for (int attemptsLeft = 1000; attemptsLeft > 0; attemptsLeft--) { List matchingMetadata = - reader.collectAllMetrics().stream() + metricReader.collectAllMetrics().stream() .filter(md -> md.getName().equals(fullMetricName)) .collect(Collectors.toList()); assertWithMessage( @@ -810,6 +784,7 @@ private MetricData getMetricData(InMemoryMetricReader reader, String metricName) .that(matchingMetadata.size()) .isAtMost(1); + // Tests could be flaky as the metric reader could have matching data, but it is partial. if (!matchingMetadata.isEmpty()) { return matchingMetadata.get(0); } @@ -825,4 +800,17 @@ private MetricData getMetricData(InMemoryMetricReader reader, String metricName) assertTrue(String.format("MetricData is missing for metric %s", fullMetricName), false); return null; } + + private void assertMetricAbsent(String metricName) { + String fullMetricName = TelemetryConstants.METRIC_PREFIX + "/" + metricName; + List matchingMetadata = + metricReader.collectAllMetrics().stream() + .filter(md -> md.getName().equals(fullMetricName)) + .collect(Collectors.toList()); + assertWithMessage( + "Found unexpected MetricData with the same name: %s, in: %s", + fullMetricName, matchingMetadata) + .that(matchingMetadata.size()) + .isEqualTo(0); + } } From 7f81d12687c936001a292f736a89745bcbc391cd Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:46:54 -0500 Subject: [PATCH 082/103] add E2E test --- .../cloud/firestore/it/ITE2EMetricsTest.java | 405 ++++++++++++++++++ .../cloud/firestore/it/ITMetricsTest.java | 119 ++--- 2 files changed, 465 insertions(+), 59 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java new file mode 100644 index 000000000..a2723e89e --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -0,0 +1,405 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.it; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.api.MetricDescriptor; +import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.telemetry.ClientIdentifier; +import com.google.cloud.firestore.telemetry.TelemetryConstants; +import com.google.cloud.monitoring.v3.MetricServiceClient; +import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableSet; +import com.google.monitoring.v3.*; +import com.google.protobuf.util.Timestamps; +import io.grpc.Status; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.sdk.metrics.data.*; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.threeten.bp.Duration; +import org.threeten.bp.Instant; + +// This End-to-End test verifies Client-side Tracing Functionality instrumented using the +// OpenTelemetry API. +// The test depends on the following external APIs/Services: +// 1. Java OpenTelemetry SDK +// 2. Cloud Trace Exporter +// 3. TraceServiceClient from Cloud Trace API v1. +// +// Permissions required to run this test (https://cloud.google.com/trace/docs/iam#trace-roles): +// 1. gcloud auth application-default login must be run with the test user. +// 2. To write traces, test user must have one of roles/cloudtrace.[admin|agent|user] roles. +// 3. To read traces, test user must have one of roles/cloudtrace.[admin|user] roles. +// +// Each test-case has the following workflow: +// 1. OpenTelemetry SDK is initialized with Cloud Trace Exporter and 100% Trace Sampling +// 2. On initialization, Firestore client is provided the OpenTelemetry SDK object from (1) +// 3. A custom TraceID is generated and injected using a custom SpanContext +// 4. Firestore operations are run inside a root TraceSpan created using the custom SpanContext from +// (3). +// 5. Traces are read-back using TraceServiceClient and verified against expected Call Stacks. +// TODO In the future it would be great to have a single test-driver for this test and +// ITTracingTest. + +@RunWith(JUnit4.class) +public class ITE2EMetricsTest extends ITBaseTest { + + private static MetricServiceClient metricClient; + + private static final Logger logger = Logger.getLogger(ITE2EMetricsTest.class.getName()); + + private static boolean isNightlyTesting; + private static String projectId; + + private static Firestore firestore; + + private static Attributes expectedBaseAttributes; + + private final String ClientUid = ClientIdentifier.getClientUid(); + private final String libraryVersion = this.getClass().getPackage().getImplementationVersion(); + + private FirestoreOptions.Builder optionsBuilder; + + private static final Set GAX_METRICS = + ImmutableSet.of( + TelemetryConstants.METRIC_NAME_OPERATION_LATENCY, + TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, + TelemetryConstants.METRIC_NAME_OPERATION_COUNT, + TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); + + @BeforeClass + public static void setup() throws IOException { + String jobType = System.getenv("GITHUB_ENV_VAR_KOKORO_JOB_TYPE"); + isNightlyTesting = jobType != null && jobType.equalsIgnoreCase("nightly"); + + projectId = FirestoreOptions.getDefaultProjectId(); + logger.info("projectId:" + projectId); + } + + @Before + public void before() throws Exception { + // We only perform end-to-end tracing tests on a nightly basis. + // assumeTrue(isNightlyTesting); + + metricClient = MetricServiceClient.create(); + + optionsBuilder = FirestoreOptions.newBuilder(); + + String namedDb = System.getProperty("FIRESTORE_NAMED_DATABASE"); + if (namedDb != null) { + logger.log(Level.INFO, "Integration test using named database " + namedDb); + optionsBuilder = optionsBuilder.setDatabaseId(namedDb); + } else { + logger.log(Level.INFO, "Integration test using default database."); + } + + firestore = optionsBuilder.build().getService(); + Preconditions.checkNotNull( + firestore, + "Error instantiating Firestore. Check that the service account credentials " + + "were properly set."); + + expectedBaseAttributes = buildExpectedAttributes(); + } + + @After + public void after() throws Exception { + firestore.shutdown(); + metricClient.close(); + // metricClient.shutdown() + // metricClient.awaitTermination(2,TimeUnit.SECONDS); + } + + private Attributes buildExpectedAttributes() { + AttributesBuilder attributesBuilder = Attributes.builder(); + attributesBuilder.put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), + TelemetryConstants.FIRESTORE_LIBRARY_NAME); + attributesBuilder.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientUid); + if (libraryVersion != null) { + attributesBuilder.put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), libraryVersion); + } + return attributesBuilder.build(); + } + + @Test + public void testBuiltinMetricsWithDefaultOTEL() throws Exception { + // This stopwatch is used for to limit fetching of metric data in verifyMetrics + Stopwatch metricsPollingStopwatch = Stopwatch.createStarted(); + TimeInterval interval = createTimeInterval(); + + firestore.collection("col").get().get(); + firestore.collection("col").whereGreaterThan("bla", "").get().get(); + + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 2) + .build(); + + for (String METRIC : GAX_METRICS) { + verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); + } + + Set SDK_METRICS = + ImmutableSet.of( + TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, + TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetricData( + TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 2) + .build(); + + for (String METRIC : SDK_METRICS) { + verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); + } + } + + @Test + public void testBuiltinMetricsWithTransaction() throws Exception { + // This stopwatch is used for to limit fetching of metric data in verifyMetrics + Stopwatch metricsPollingStopwatch = Stopwatch.createStarted(); + TimeInterval interval = createTimeInterval(); + + firestore + .runTransaction( // Has end-to-end, first response latency and transaction metrics. + transaction -> { + // Document Query. Has end-to-end and first response latency which is marked + // transactional + transaction.get(firestore.collection("col")).get(); + transaction.get(firestore.collection("col").whereGreaterThan("bla", "")).get(); + + // Commit 2 documents. Has end-to-end latency. + transaction.set( + firestore.collection("foo").document("bar"), + Collections.singletonMap("foo", "bar")); + transaction.set( + firestore.collection("foo").document("bar2"), + Collections.singletonMap("foo2", "bar2")); + return 0; + }) + .get(); + + Map expectedMetrics = + new MetricsExpectationBuilder() + .expectMetricData("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 2) + .expectMetricData("Firestore.Commit", Status.OK.getCode().toString(), 1) + .build(); + + for (String METRIC : GAX_METRICS) { + verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); + } + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetricData( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .expectMetricData( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, + Status.OK.getCode().toString(), + 2) + .build(); + + verifyPublishedMetrics( + TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, + interval, + metricsPollingStopwatch, + expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetricData( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .expectMetricData( + TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, + Status.OK.getCode().toString(), + 2) + .expectMetricData( + TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, + Status.OK.getCode().toString(), + 1) + .build(); + + verifyPublishedMetrics( + TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, + interval, + metricsPollingStopwatch, + expectedMetrics); + + expectedMetrics = + new MetricsExpectationBuilder() + .expectMetricData( + TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) + .build(); + + Set SDK_METRICS = + ImmutableSet.of( + TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, + TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); + + for (String METRIC : SDK_METRICS) { + verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); + } + } + + private TimeInterval createTimeInterval() { + Instant startTime = Instant.now(); + Instant endTime = Instant.now().plus(Duration.ofMinutes(1)); + System.out.println( + "Test Start Time: " + startTime.toString() + " - Nanos: " + startTime.getNano()); + System.out.println("Test End Time: " + endTime.toString() + " - Nanos: " + endTime.getNano()); + + return TimeInterval.newBuilder() + .setStartTime(Timestamps.fromMillis(startTime.toEpochMilli())) + .setEndTime(Timestamps.fromMillis(endTime.toEpochMilli())) + .build(); + } + + private ListTimeSeriesResponse verifyPublishedMetrics( + String metric, + TimeInterval interval, + Stopwatch metricsPollingStopwatch, + Map expectedMetrics) + throws Exception { + + String metricFilter = + String.format("metric.type=\"%s/%s\"", TelemetryConstants.METRIC_PREFIX, metric); + + // 2. Calculate alignment period dynamically based on your interval + // long alignmentSeconds = + // Timestamps.toMillis(interval.getEndTime()) + // - Timestamps.toMillis(interval.getStartTime()); + // Duration alignmentPeriod = Duration.ofMillis(alignmentSeconds); + // + // // Convert Duration to com.google.protobuf.Duration + // com.google.protobuf.Duration protoAlignmentPeriod = + // com.google.protobuf.Duration.newBuilder() + // .setSeconds(alignmentPeriod.getSeconds()) + // .setNanos(alignmentPeriod.getNano()) + // .build(); + + ListTimeSeriesRequest request = + ListTimeSeriesRequest.newBuilder() + .setName("projects/" + projectId) + .setFilter(metricFilter) + .setInterval(interval) + // .setView(ListTimeSeriesRequest.TimeSeriesView.FULL) + // .setAggregation( + // Aggregation.newBuilder() + // .setAlignmentPeriod(protoAlignmentPeriod) // Dynamic + // alignment period + // .setPerSeriesAligner(Aggregation.Aligner.ALIGN_DELTA) + // // Use ALIGN_DELTA + // ) + .build(); + + System.out.println("ListTimeSeriesRequest: " + request); // Log the request + + ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request); + while (response.getTimeSeriesCount() == 0 + && metricsPollingStopwatch.elapsed(TimeUnit.MINUTES) < 3) { + // Call listTimeSeries every minute + Thread.sleep(Duration.ofMinutes(1).toMillis()); + response = metricClient.listTimeSeriesCallable().call(request); + } + + System.out.println("ListTimeSeriesResponse: " + response); // Log the response + + assertWithMessage("Metric " + metric + " didn't return any data.") + .that(response.getTimeSeriesCount()) + .isGreaterThan(0); + + assertThat(response.getTimeSeriesCount()).isEqualTo(expectedMetrics.size()); + + for (TimeSeries timeSeries : response.getTimeSeriesList()) { + boolean isDistribution = timeSeries.getValueType() == MetricDescriptor.ValueType.DISTRIBUTION; + + Map metricLabels = timeSeries.getMetric().getLabelsMap(); + MetricInfo expectedMetricInfo = expectedMetrics.get(metricLabels.get("method")); + + if (isDistribution) { + assertThat(expectedMetricInfo.count) + .isEqualTo(timeSeries.getPoints(0).getValue().getDistributionValue().getCount()); + } else { + assertThat(expectedMetricInfo.count) + .isEqualTo((int) timeSeries.getPoints(0).getValue().getInt64Value()); + } + + assertThat(metricLabels) + .containsAtLeastEntriesIn( + expectedMetricInfo.attributes.asMap().entrySet().stream() + .collect( + Collectors.toMap(entry -> entry.getKey().getKey(), Map.Entry::getValue))); + } + + return response; + } + + class MetricInfo { + // The expected number of measurements is called + public int count; + // Attributes expected to be recorded in the measurements + public Attributes attributes; + + public MetricInfo(int expectedCount, Attributes expectedAttributes) { + this.count = expectedCount; + this.attributes = expectedAttributes; + } + } + + class MetricsExpectationBuilder { + private final Map expectedMetrics = new HashMap<>(); + + public MetricsExpectationBuilder expectMetricData( + String method, String expectedStatus, int expectedCount) { + Attributes attributes = buildAttributes(method, expectedStatus); + expectedMetrics.put(method, new MetricInfo(expectedCount, attributes)); + return this; + } + + public Map build() { + return expectedMetrics; + } + } + + private Attributes buildAttributes(String method, String status) { + return expectedBaseAttributes + .toBuilder() + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, status) + .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) + .build(); + } +} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index a9adb26df..ad24c3168 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -134,10 +134,10 @@ public MetricInfo(int expectedCount, Attributes expectedAttributes) { class MetricsExpectationBuilder { private final Map expectedMetrics = new HashMap<>(); - public MetricsExpectationBuilder expectMetric( - String metricName, String expectedStatus, int expectedCount) { - Attributes attributes = buildAttributes(metricName, expectedStatus); - expectedMetrics.put(metricName, new MetricInfo(expectedCount, attributes)); + public MetricsExpectationBuilder expectMetricData( + String method, String expectedStatus, int expectedCount) { + Attributes attributes = buildAttributes(method, expectedStatus); + expectedMetrics.put(method, new MetricInfo(expectedCount, attributes)); return this; } @@ -153,14 +153,14 @@ public void queryGet() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); @@ -197,14 +197,14 @@ public void onCompleted() {} // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN, Status.OK.getCode().toString(), 1) .build(); @@ -221,14 +221,14 @@ public void aggregateQueryGet() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, Status.OK.getCode().toString(), 1) @@ -252,14 +252,14 @@ public void writeBatch() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.Commit", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 1) .build(); @@ -283,14 +283,14 @@ public void bulkWriterCommit() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.BatchWrite", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.BatchWrite", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT, Status.OK.getCode().toString(), 1) @@ -311,7 +311,7 @@ public void partitionQuery() throws Exception { // Validate SDK layer metric Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_PARTITION_QUERY, Status.OK.getCode().toString(), 1) .build(); @@ -329,7 +329,7 @@ public void listCollection() throws Exception { // Validate SDK layer metric Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS, Status.OK.getCode().toString(), 1) @@ -349,7 +349,7 @@ public void collectionListDocuments() throws Exception { // Validate SDK layer metric Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS, Status.OK.getCode().toString(), 1) @@ -372,16 +372,16 @@ public void docRefSet() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.Commit", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_DOC_REF_SET, Status.OK.getCode().toString(), 1) .build(); @@ -401,14 +401,14 @@ public void getAll() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.BatchGetDocuments", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.BatchGetDocuments", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS, Status.OK.getCode().toString(), 1) @@ -448,10 +448,10 @@ public void transaction() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) - .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 1) - .expectMetric("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) - .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.Commit", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); @@ -459,13 +459,13 @@ public void transaction() throws Exception { // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, Status.OK.getCode().toString(), 1) @@ -474,17 +474,17 @@ public void transaction() throws Exception { expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, Status.OK.getCode().toString(), 1) @@ -493,7 +493,7 @@ public void transaction() throws Exception { expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); @@ -522,22 +522,22 @@ public void transactionWithRollback() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) - .expectMetric("Firestore.Rollback", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.Rollback", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.UNKNOWN.getCode().toString(), 1) @@ -574,14 +574,15 @@ public void transactionWithFailure() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.BeginTransaction", Status.NOT_FOUND.getCode().toString(), 1) + .expectMetricData( + "Firestore.BeginTransaction", Status.NOT_FOUND.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.UNKNOWN .getCode() @@ -625,25 +626,25 @@ public Integer updateCallback(Transaction transaction) throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.BeginTransaction", Status.OK.getCode().toString(), 3) - .expectMetric("Firestore.Rollback", Status.OK.getCode().toString(), 2) - .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.BeginTransaction", Status.OK.getCode().toString(), 3) + .expectMetricData("Firestore.Rollback", Status.OK.getCode().toString(), 2) + .expectMetricData("Firestore.Commit", Status.OK.getCode().toString(), 1) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, Status.OK.getCode().toString(), 1) @@ -652,14 +653,14 @@ public Integer updateCallback(Transaction transaction) throws Exception { expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 3) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, expectedMetrics); @@ -681,18 +682,18 @@ public void multipleOperations() throws Exception { // Validate GAX layer metrics Map expectedMetrics = new MetricsExpectationBuilder() - .expectMetric("Firestore.RunQuery", Status.OK.getCode().toString(), 2) - .expectMetric("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) - .expectMetric("Firestore.Commit", Status.OK.getCode().toString(), 3) + .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 2) + .expectMetricData("Firestore.RunAggregationQuery", Status.OK.getCode().toString(), 1) + .expectMetricData("Firestore.Commit", Status.OK.getCode().toString(), 3) .build(); validateGaxMetrics(expectedMetrics); // Validate SDK layer metric expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 2) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, Status.OK.getCode().toString(), 1) @@ -701,19 +702,19 @@ public void multipleOperations() throws Exception { expectedMetrics = new MetricsExpectationBuilder() - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 2) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_DOC_REF_SET, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_DOC_REF_DELETE, Status.OK.getCode().toString(), 1) - .expectMetric( + .expectMetricData( TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 3) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); From 25f9b59961a05ccce473fd90f75869a104d39a8f Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:48:30 -0500 Subject: [PATCH 083/103] Update ITE2EMetricsTest.java --- .../cloud/firestore/it/ITE2EMetricsTest.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index a2723e89e..a302d0876 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -49,28 +49,6 @@ import org.threeten.bp.Duration; import org.threeten.bp.Instant; -// This End-to-End test verifies Client-side Tracing Functionality instrumented using the -// OpenTelemetry API. -// The test depends on the following external APIs/Services: -// 1. Java OpenTelemetry SDK -// 2. Cloud Trace Exporter -// 3. TraceServiceClient from Cloud Trace API v1. -// -// Permissions required to run this test (https://cloud.google.com/trace/docs/iam#trace-roles): -// 1. gcloud auth application-default login must be run with the test user. -// 2. To write traces, test user must have one of roles/cloudtrace.[admin|agent|user] roles. -// 3. To read traces, test user must have one of roles/cloudtrace.[admin|user] roles. -// -// Each test-case has the following workflow: -// 1. OpenTelemetry SDK is initialized with Cloud Trace Exporter and 100% Trace Sampling -// 2. On initialization, Firestore client is provided the OpenTelemetry SDK object from (1) -// 3. A custom TraceID is generated and injected using a custom SpanContext -// 4. Firestore operations are run inside a root TraceSpan created using the custom SpanContext from -// (3). -// 5. Traces are read-back using TraceServiceClient and verified against expected Call Stacks. -// TODO In the future it would be great to have a single test-driver for this test and -// ITTracingTest. - @RunWith(JUnit4.class) public class ITE2EMetricsTest extends ITBaseTest { From ffe72db36a465b18c52def5a1108eb980e49f63c Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:25:18 -0500 Subject: [PATCH 084/103] E2E tests verifys metrics are published only --- .../google/cloud/firestore/FirestoreImpl.java | 1 + .../telemetry/DisabledMetricsUtil.java | 3 + .../telemetry/EnabledMetricsUtil.java | 9 +- .../firestore/telemetry/MetricsUtil.java | 2 + .../cloud/firestore/it/ITE2EMetricsTest.java | 351 +++++++----------- .../cloud/firestore/it/ITMetricsTest.java | 11 +- 6 files changed, 161 insertions(+), 216 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index e7c219434..636f144ec 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -556,6 +556,7 @@ public void close() throws Exception { @Override public void shutdown() { firestoreClient.shutdown(); + firestoreOptions.getMetricsUtil().shutdown(); closed = true; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index 0642033c0..cfa344c6a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -45,6 +45,9 @@ public void recordCounterAtFuture(MetricType metric, ApiFuture futureValu public void incrementCounter() {} } + @Override + public void shutdown() {} + @Override public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 106b2db35..5b5861f3a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -69,6 +69,8 @@ class EnabledMetricsUtil implements MetricsUtil { private BuiltinMetricsProvider defaultMetricsProvider; private BuiltinMetricsProvider customMetricsProvider; + private MetricExporter metricExporter; + private static final Logger logger = Logger.getLogger(EnabledMetricsUtil.class.getName()); EnabledMetricsUtil(FirestoreOptions firestoreOptions) { @@ -145,7 +147,7 @@ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws I sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); } - MetricExporter metricExporter = + metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder() .setProjectId(projectId) @@ -186,6 +188,11 @@ private void addTracerFactory( } } + @Override + public void shutdown() { + metricExporter.close(); + } + class MetricsContext implements MetricsUtil.MetricsContext { private final Stopwatch stopwatch; private int counter; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index b04a5157d..67acbcf08 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -52,6 +52,8 @@ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { } } + abstract void shutdown(); + static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index a302d0876..d829269f2 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -16,27 +16,24 @@ package com.google.cloud.firestore.it; -import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import com.google.api.MetricDescriptor; import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; import com.google.cloud.firestore.FirestoreOptions; -import com.google.cloud.firestore.telemetry.ClientIdentifier; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.common.base.Preconditions; -import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableSet; import com.google.monitoring.v3.*; import com.google.protobuf.util.Timestamps; -import io.grpc.Status; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.common.AttributesBuilder; -import io.opentelemetry.sdk.metrics.data.*; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.util.*; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -61,20 +58,8 @@ public class ITE2EMetricsTest extends ITBaseTest { private static Firestore firestore; - private static Attributes expectedBaseAttributes; - - private final String ClientUid = ClientIdentifier.getClientUid(); - private final String libraryVersion = this.getClass().getPackage().getImplementationVersion(); - private FirestoreOptions.Builder optionsBuilder; - private static final Set GAX_METRICS = - ImmutableSet.of( - TelemetryConstants.METRIC_NAME_OPERATION_LATENCY, - TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, - TelemetryConstants.METRIC_NAME_OPERATION_COUNT, - TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT); - @BeforeClass public static void setup() throws IOException { String jobType = System.getenv("GITHUB_ENV_VAR_KOKORO_JOB_TYPE"); @@ -106,278 +91,224 @@ public void before() throws Exception { firestore, "Error instantiating Firestore. Check that the service account credentials " + "were properly set."); - - expectedBaseAttributes = buildExpectedAttributes(); } @After public void after() throws Exception { firestore.shutdown(); - metricClient.close(); - // metricClient.shutdown() - // metricClient.awaitTermination(2,TimeUnit.SECONDS); + metricClient.shutdown(); + Thread.sleep(Duration.ofSeconds(30).toMillis()); } - private Attributes buildExpectedAttributes() { - AttributesBuilder attributesBuilder = Attributes.builder(); - attributesBuilder.put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), - TelemetryConstants.FIRESTORE_LIBRARY_NAME); - attributesBuilder.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientUid); - if (libraryVersion != null) { - attributesBuilder.put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), libraryVersion); + @Test + public void builtinMetricsWithDefaultOTEL() throws Exception { + TimeInterval interval = createTimeInterval(); + + firestore.collection("col").get().get(); + + Set METRICS = + ImmutableSet.of( + TelemetryConstants.METRIC_NAME_OPERATION_LATENCY, + TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, + TelemetryConstants.METRIC_NAME_OPERATION_COUNT, + TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, + TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, + TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + + for (String METRIC : METRICS) { + assertMetricsArePublished(METRIC, interval); } - return attributesBuilder.build(); } @Test - public void testBuiltinMetricsWithDefaultOTEL() throws Exception { - // This stopwatch is used for to limit fetching of metric data in verifyMetrics - Stopwatch metricsPollingStopwatch = Stopwatch.createStarted(); + public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { + firestore.shutdown(); + + InMemoryMetricReader metricReader = InMemoryMetricReader.create(); + SdkMeterProviderBuilder meterProvider = + SdkMeterProvider.builder().registerMetricReader(metricReader); + OpenTelemetrySdk customOpenTelemetrySdk = + OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); + + firestore = + optionsBuilder + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .setOpenTelemetry(customOpenTelemetrySdk) + .build()) + .build() + .getService(); + TimeInterval interval = createTimeInterval(); - firestore.collection("col").get().get(); - firestore.collection("col").whereGreaterThan("bla", "").get().get(); + firestore.collection("col").count().get().get(); - Map expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 2) - .build(); + // Verify metric data are published to Cloud Monitoring + Set METRICS = + ImmutableSet.of( + TelemetryConstants.METRIC_NAME_OPERATION_LATENCY, + TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, + TelemetryConstants.METRIC_NAME_OPERATION_COUNT, + TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, + TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, + TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); - for (String METRIC : GAX_METRICS) { - verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); + for (String METRIC : METRICS) { + assertMetricsArePublished(METRIC, interval); } - Set SDK_METRICS = + // Verify metric data are collected to 3rd party backend (InMemoryMetricReader) + METRICS = ImmutableSet.of( + TelemetryConstants.METRIC_NAME_OPERATION_LATENCY, + TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, + TelemetryConstants.METRIC_NAME_OPERATION_COUNT, + TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + for (String METRIC : METRICS) { + assertMetricsAreCollected(metricReader, METRIC); + } - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 2) - .build(); + // Verify no transaction related metric data are collected + METRICS = + ImmutableSet.of( + TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, + TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); - for (String METRIC : SDK_METRICS) { - verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); + for (String METRIC : METRICS) { + assertMetricsAreAbsent(metricReader, METRIC); } + metricReader.forceFlush(); + metricReader.shutdown(); } @Test - public void testBuiltinMetricsWithTransaction() throws Exception { - // This stopwatch is used for to limit fetching of metric data in verifyMetrics - Stopwatch metricsPollingStopwatch = Stopwatch.createStarted(); + public void builtinMetricsCreatedByTransaction() throws Exception { TimeInterval interval = createTimeInterval(); firestore - .runTransaction( // Has end-to-end, first response latency and transaction metrics. + .runTransaction( transaction -> { - // Document Query. Has end-to-end and first response latency which is marked - // transactional transaction.get(firestore.collection("col")).get(); - transaction.get(firestore.collection("col").whereGreaterThan("bla", "")).get(); - - // Commit 2 documents. Has end-to-end latency. transaction.set( firestore.collection("foo").document("bar"), Collections.singletonMap("foo", "bar")); - transaction.set( - firestore.collection("foo").document("bar2"), - Collections.singletonMap("foo2", "bar2")); return 0; }) .get(); - Map expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData("Firestore.BeginTransaction", Status.OK.getCode().toString(), 1) - .expectMetricData("Firestore.RunQuery", Status.OK.getCode().toString(), 2) - .expectMetricData("Firestore.Commit", Status.OK.getCode().toString(), 1) - .build(); - - for (String METRIC : GAX_METRICS) { - verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); - } - - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, - Status.OK.getCode().toString(), - 2) - .build(); - - verifyPublishedMetrics( - TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, - interval, - metricsPollingStopwatch, - expectedMetrics); - - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, - Status.OK.getCode().toString(), - 2) - .expectMetricData( - TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, - Status.OK.getCode().toString(), - 1) - .build(); - - verifyPublishedMetrics( - TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, - interval, - metricsPollingStopwatch, - expectedMetrics); - - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .build(); - - Set SDK_METRICS = + Set METRICS = ImmutableSet.of( + TelemetryConstants.METRIC_NAME_OPERATION_LATENCY, + TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, + TelemetryConstants.METRIC_NAME_OPERATION_COUNT, + TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, + TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, + TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); - for (String METRIC : SDK_METRICS) { - verifyPublishedMetrics(METRIC, interval, metricsPollingStopwatch, expectedMetrics); + for (String METRIC : METRICS) { + assertMetricsArePublished(METRIC, interval); } } private TimeInterval createTimeInterval() { Instant startTime = Instant.now(); Instant endTime = Instant.now().plus(Duration.ofMinutes(1)); - System.out.println( - "Test Start Time: " + startTime.toString() + " - Nanos: " + startTime.getNano()); - System.out.println("Test End Time: " + endTime.toString() + " - Nanos: " + endTime.getNano()); - return TimeInterval.newBuilder() .setStartTime(Timestamps.fromMillis(startTime.toEpochMilli())) .setEndTime(Timestamps.fromMillis(endTime.toEpochMilli())) .build(); } - private ListTimeSeriesResponse verifyPublishedMetrics( - String metric, - TimeInterval interval, - Stopwatch metricsPollingStopwatch, - Map expectedMetrics) + private ListTimeSeriesResponse assertMetricsArePublished(String metric, TimeInterval interval) throws Exception { String metricFilter = String.format("metric.type=\"%s/%s\"", TelemetryConstants.METRIC_PREFIX, metric); - // 2. Calculate alignment period dynamically based on your interval - // long alignmentSeconds = - // Timestamps.toMillis(interval.getEndTime()) - // - Timestamps.toMillis(interval.getStartTime()); - // Duration alignmentPeriod = Duration.ofMillis(alignmentSeconds); - // - // // Convert Duration to com.google.protobuf.Duration - // com.google.protobuf.Duration protoAlignmentPeriod = - // com.google.protobuf.Duration.newBuilder() - // .setSeconds(alignmentPeriod.getSeconds()) - // .setNanos(alignmentPeriod.getNano()) - // .build(); - ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder() .setName("projects/" + projectId) .setFilter(metricFilter) .setInterval(interval) - // .setView(ListTimeSeriesRequest.TimeSeriesView.FULL) - // .setAggregation( - // Aggregation.newBuilder() - // .setAlignmentPeriod(protoAlignmentPeriod) // Dynamic - // alignment period - // .setPerSeriesAligner(Aggregation.Aligner.ALIGN_DELTA) - // // Use ALIGN_DELTA - // ) .build(); - System.out.println("ListTimeSeriesRequest: " + request); // Log the request - ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request); - while (response.getTimeSeriesCount() == 0 - && metricsPollingStopwatch.elapsed(TimeUnit.MINUTES) < 3) { + int attemptsMade = 0; + while (response.getTimeSeriesCount() == 0 && attemptsMade < 3) { // Call listTimeSeries every minute Thread.sleep(Duration.ofMinutes(1).toMillis()); response = metricClient.listTimeSeriesCallable().call(request); + attemptsMade++; } - System.out.println("ListTimeSeriesResponse: " + response); // Log the response + // When querying from Cloud Monitoring database, we cannot reset the state for each individual + // test cases, and the response fetched could include time series data from previous test cases. + // So filter the response based on the time interval used by each single test case to make sure + // each test case has published expected metric data. + List filteredData = + response.getTimeSeriesList().stream() + .filter( + ts -> + ts.getPoints(0).getInterval().getStartTime().getSeconds() + >= interval.getStartTime().getSeconds()) + .collect(Collectors.toList()); assertWithMessage("Metric " + metric + " didn't return any data.") - .that(response.getTimeSeriesCount()) + .that(filteredData.size()) .isGreaterThan(0); - assertThat(response.getTimeSeriesCount()).isEqualTo(expectedMetrics.size()); - - for (TimeSeries timeSeries : response.getTimeSeriesList()) { - boolean isDistribution = timeSeries.getValueType() == MetricDescriptor.ValueType.DISTRIBUTION; - - Map metricLabels = timeSeries.getMetric().getLabelsMap(); - MetricInfo expectedMetricInfo = expectedMetrics.get(metricLabels.get("method")); - - if (isDistribution) { - assertThat(expectedMetricInfo.count) - .isEqualTo(timeSeries.getPoints(0).getValue().getDistributionValue().getCount()); - } else { - assertThat(expectedMetricInfo.count) - .isEqualTo((int) timeSeries.getPoints(0).getValue().getInt64Value()); - } - - assertThat(metricLabels) - .containsAtLeastEntriesIn( - expectedMetricInfo.attributes.asMap().entrySet().stream() - .collect( - Collectors.toMap(entry -> entry.getKey().getKey(), Map.Entry::getValue))); - } - return response; } - class MetricInfo { - // The expected number of measurements is called - public int count; - // Attributes expected to be recorded in the measurements - public Attributes attributes; - - public MetricInfo(int expectedCount, Attributes expectedAttributes) { - this.count = expectedCount; - this.attributes = expectedAttributes; + private void assertMetricsAreCollected(InMemoryMetricReader metricReader, String metricName) { + String fullMetricName = TelemetryConstants.METRIC_PREFIX + "/" + metricName; + + List matchingMetadata = + metricReader.collectAllMetrics().stream() + .filter(md -> md.getName().equals(fullMetricName)) + .collect(Collectors.toList()); + // Fetch the MetricData with retries + int attemptsMade = 0; + while (matchingMetadata.size() == 0 && attemptsMade < 10) { + // Fetch metric data every seconds + try { + Thread.sleep(Duration.ofSeconds(1).toMillis()); + } catch (InterruptedException interruptedException) { + Thread.currentThread().interrupt(); + throw new RuntimeException(interruptedException); + } + matchingMetadata = + metricReader.collectAllMetrics().stream() + .filter(md -> md.getName().equals(fullMetricName)) + .collect(Collectors.toList()); + attemptsMade++; } - } - class MetricsExpectationBuilder { - private final Map expectedMetrics = new HashMap<>(); + assertWithMessage( + "Found unexpected MetricData with the same name: %s, in: %s", + fullMetricName, matchingMetadata) + .that(matchingMetadata.size()) + .isAtMost(1); - public MetricsExpectationBuilder expectMetricData( - String method, String expectedStatus, int expectedCount) { - Attributes attributes = buildAttributes(method, expectedStatus); - expectedMetrics.put(method, new MetricInfo(expectedCount, attributes)); - return this; - } - - public Map build() { - return expectedMetrics; - } + assertWithMessage("MetricData is missing for metric %s", fullMetricName) + .that(matchingMetadata.size()) + .isEqualTo(1); } - private Attributes buildAttributes(String method, String status) { - return expectedBaseAttributes - .toBuilder() - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, status) - .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) - .build(); + private void assertMetricsAreAbsent(InMemoryMetricReader metricReader, String metricName) { + String fullMetricName = TelemetryConstants.METRIC_PREFIX + "/" + metricName; + List matchingMetadata = + metricReader.collectAllMetrics().stream() + .filter(md -> md.getName().equals(fullMetricName)) + .collect(Collectors.toList()); + assertWithMessage( + "Found unexpected MetricData with the same name: %s, in: %s", + fullMetricName, matchingMetadata) + .that(matchingMetadata.size()) + .isEqualTo(0); } } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index ad24c3168..b13b715e0 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -57,6 +57,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; +import org.threeten.bp.Duration; public class ITMetricsTest { private static OpenTelemetrySdk openTelemetrySdk; @@ -77,6 +78,9 @@ public void setup() { metricReader = InMemoryMetricReader.create(); openTelemetrySdk = setupOpenTelemetrySdk(); firestore = setupFirestoreService(); + Preconditions.checkNotNull( + firestore, + "Error instantiating Firestore. Check that the service account credentials were properly set."); expectedBaseAttributes = buildExpectedAttributes(); } @@ -109,9 +113,6 @@ private Attributes buildExpectedAttributes() { @After public void tearDown() { - Preconditions.checkNotNull( - firestore, - "Error instantiating Firestore. Check that the service account credentials were properly set."); try { metricReader.shutdown(); } finally { @@ -774,7 +775,7 @@ private void validateMetricData(MetricData metricData, Map e private MetricData getMetricData(String metricName) { String fullMetricName = TelemetryConstants.METRIC_PREFIX + "/" + metricName; // Fetch the MetricData with retries - for (int attemptsLeft = 1000; attemptsLeft > 0; attemptsLeft--) { + for (int attemptsLeft = 10; attemptsLeft > 0; attemptsLeft--) { List matchingMetadata = metricReader.collectAllMetrics().stream() .filter(md -> md.getName().equals(fullMetricName)) @@ -791,7 +792,7 @@ private MetricData getMetricData(String metricName) { } try { - Thread.sleep(1); + Thread.sleep(Duration.ofSeconds(1).toMillis()); } catch (InterruptedException interruptedException) { Thread.currentThread().interrupt(); throw new RuntimeException(interruptedException); From 5d26ef7849a51666df4df2770333a470dae32e45 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:07:16 -0500 Subject: [PATCH 085/103] remove metricUtil shutdown --- .../main/java/com/google/cloud/firestore/FirestoreImpl.java | 1 - .../cloud/firestore/telemetry/DisabledMetricsUtil.java | 3 --- .../google/cloud/firestore/telemetry/EnabledMetricsUtil.java | 5 ----- .../com/google/cloud/firestore/telemetry/MetricsUtil.java | 2 -- 4 files changed, 11 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index 636f144ec..e7c219434 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -556,7 +556,6 @@ public void close() throws Exception { @Override public void shutdown() { firestoreClient.shutdown(); - firestoreOptions.getMetricsUtil().shutdown(); closed = true; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index cfa344c6a..0642033c0 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -45,9 +45,6 @@ public void recordCounterAtFuture(MetricType metric, ApiFuture futureValu public void incrementCounter() {} } - @Override - public void shutdown() {} - @Override public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 5b5861f3a..120a693fd 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -188,11 +188,6 @@ private void addTracerFactory( } } - @Override - public void shutdown() { - metricExporter.close(); - } - class MetricsContext implements MetricsUtil.MetricsContext { private final Stopwatch stopwatch; private int counter; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index 67acbcf08..b04a5157d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -52,8 +52,6 @@ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { } } - abstract void shutdown(); - static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables From c55f2f63fc4664cac9aa258074c717beb10c718f Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:10:04 -0500 Subject: [PATCH 086/103] shutdown metricReader gracefully --- .../clirr-ignored-differences.xml | 9 ++++++++- google-cloud-firestore/pom.xml | 11 +++++------ .../google/cloud/firestore/FirestoreImpl.java | 3 +++ .../telemetry/DisabledMetricsUtil.java | 3 +++ .../telemetry/EnabledMetricsUtil.java | 18 +++++++++++++----- .../cloud/firestore/telemetry/MetricsUtil.java | 2 ++ .../cloud/firestore/it/ITE2EMetricsTest.java | 9 +++++++-- .../cloud/firestore/it/ITMetricsTest.java | 2 +- 8 files changed, 42 insertions(+), 15 deletions(-) diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index 0c0a847ca..302a10201 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -331,7 +331,7 @@ 7009 void internalStream(*) - + 6011 @@ -353,4 +353,11 @@ com/google/cloud/firestore/telemetry/TelemetryConstants METHOD_NAME_* + + + 7012 + com/google/cloud/firestore/telemetry/MetricsUtil + void shutdown() + + diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 0343638aa..f6517a96f 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -131,6 +131,11 @@ com.google.cloud.opentelemetry exporter-metrics + + io.opentelemetry + opentelemetry-sdk-common + ${opentelemetry.version} + @@ -217,12 +222,6 @@ ${opentelemetry.version} test - - io.opentelemetry - opentelemetry-sdk-common - ${opentelemetry.version} - test - com.google.cloud.opentelemetry exporter-trace diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index e7c219434..361a50072 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -550,18 +550,21 @@ public FirestoreOptions getOptions() { @Override public void close() throws Exception { firestoreClient.close(); + firestoreOptions.getMetricsUtil().shutdown(); closed = true; } @Override public void shutdown() { firestoreClient.shutdown(); + firestoreOptions.getMetricsUtil().shutdown(); closed = true; } @Override public void shutdownNow() { firestoreClient.shutdownNow(); + firestoreOptions.getMetricsUtil().shutdown(); closed = true; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java index 0642033c0..cfa344c6a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -45,6 +45,9 @@ public void recordCounterAtFuture(MetricType metric, ApiFuture futureValu public void incrementCounter() {} } + @Override + public void shutdown() {} + @Override public MetricsContext createMetricsContext(String methodName) { return new MetricsContext(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 120a693fd..d6a749221 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -50,6 +50,7 @@ import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; import io.opentelemetry.sdk.metrics.View; import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import java.io.IOException; import java.util.HashMap; @@ -68,8 +69,7 @@ class EnabledMetricsUtil implements MetricsUtil { private BuiltinMetricsProvider defaultMetricsProvider; private BuiltinMetricsProvider customMetricsProvider; - - private MetricExporter metricExporter; + private MetricReader metricReader; private static final Logger logger = Logger.getLogger(EnabledMetricsUtil.class.getName()); @@ -147,16 +147,18 @@ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws I sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); } - metricExporter = + MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder() .setProjectId(projectId) // Ignore library info as it is collected by the metric attributes as well .setInstrumentationLibraryLabelsEnabled(false) .build()); - sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter)); + metricReader = PeriodicMetricReader.create(metricExporter); - return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); + return OpenTelemetrySdk.builder() + .setMeterProvider(sdkMeterProviderBuilder.registerMetricReader(metricReader).build()) + .build(); } private static Map getAllViews() { @@ -188,6 +190,12 @@ private void addTracerFactory( } } + @Override + public void shutdown() { + // Gracefully shutdown the metric reader registered to the default OTEL instance inside the sdk. + metricReader.shutdown(); + } + class MetricsContext implements MetricsUtil.MetricsContext { private final Stopwatch stopwatch; private int counter; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index b04a5157d..67acbcf08 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -52,6 +52,8 @@ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) { } } + abstract void shutdown(); + static boolean shouldCreateEnabledInstance() { // Client side metrics feature is default on unless it is manually turned off by // environment variables diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index d829269f2..dd81589f5 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -72,7 +72,7 @@ public static void setup() throws IOException { @Before public void before() throws Exception { // We only perform end-to-end tracing tests on a nightly basis. - // assumeTrue(isNightlyTesting); + // assumeTrue(isNightlyTesting); metricClient = MetricServiceClient.create(); @@ -95,9 +95,11 @@ public void before() throws Exception { @After public void after() throws Exception { + // Wait for 30 seconds to avoid duplicate time series error triggered by metric exporter's + // shutdown. + Thread.sleep(Duration.ofSeconds(30).toMillis()); firestore.shutdown(); metricClient.shutdown(); - Thread.sleep(Duration.ofSeconds(30).toMillis()); } @Test @@ -239,6 +241,7 @@ private ListTimeSeriesResponse assertMetricsArePublished(String metric, TimeInte ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request); int attemptsMade = 0; while (response.getTimeSeriesCount() == 0 && attemptsMade < 3) { + System.out.println("*** fetch response"); // Call listTimeSeries every minute Thread.sleep(Duration.ofMinutes(1).toMillis()); response = metricClient.listTimeSeriesCallable().call(request); @@ -257,6 +260,8 @@ private ListTimeSeriesResponse assertMetricsArePublished(String metric, TimeInte >= interval.getStartTime().getSeconds()) .collect(Collectors.toList()); + System.out.println(filteredData.size()); + assertWithMessage("Metric " + metric + " didn't return any data.") .that(filteredData.size()) .isGreaterThan(0); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index b13b715e0..48b325c88 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -114,7 +114,7 @@ private Attributes buildExpectedAttributes() { @After public void tearDown() { try { - metricReader.shutdown(); + openTelemetrySdk.getSdkMeterProvider().shutdown(); } finally { firestore.shutdown(); } From 457dde52f070ea1a746404a6b7d1dae6805a416d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:12:19 -0500 Subject: [PATCH 087/103] format --- .../java/com/google/cloud/firestore/it/ITE2EMetricsTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index dd81589f5..3fc57e17d 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -241,7 +241,6 @@ private ListTimeSeriesResponse assertMetricsArePublished(String metric, TimeInte ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request); int attemptsMade = 0; while (response.getTimeSeriesCount() == 0 && attemptsMade < 3) { - System.out.println("*** fetch response"); // Call listTimeSeries every minute Thread.sleep(Duration.ofMinutes(1).toMillis()); response = metricClient.listTimeSeriesCallable().call(request); @@ -260,8 +259,6 @@ private ListTimeSeriesResponse assertMetricsArePublished(String metric, TimeInte >= interval.getStartTime().getSeconds()) .collect(Collectors.toList()); - System.out.println(filteredData.size()); - assertWithMessage("Metric " + metric + " didn't return any data.") .that(filteredData.size()) .isGreaterThan(0); From 95e088e5341678930fb122f4c1998c29159ae415 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 28 Nov 2024 13:55:27 -0500 Subject: [PATCH 088/103] configure unit tests to not enable metrics collection --- .../telemetry/EnabledMetricsUtil.java | 24 +++++++++++------ .../cloud/firestore/ConformanceTest.java | 9 ++++++- .../google/cloud/firestore/it/ITBaseTest.java | 7 +++++ .../cloud/firestore/it/ITE2EMetricsTest.java | 27 +++++++++---------- .../cloud/firestore/it/ITE2ETracingTest.java | 1 + .../cloud/firestore/it/ITTracingTest.java | 3 ++- .../spi/v1/GrpcFirestoreRpcTest.java | 13 ++++++++- 7 files changed, 59 insertions(+), 25 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index d6a749221..e3109569e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -92,13 +92,14 @@ private BuiltinMetricsProvider configureDefaultMetricsProvider( if (projectId == null) { logger.warning( "Project ID is null, skipping client side metrics export to Cloud Monitoring."); - } - try { - defaultOpenTelemetry = getDefaultOpenTelemetryInstance(projectId); - } catch (IOException e) { - logger.warning( - "Unable to create default OpenTelemetry instance for client side metrics, will skip exporting client side metrics to Cloud Monitoring: " - + e); + } else { + try { + defaultOpenTelemetry = getDefaultOpenTelemetryInstance(projectId); + } catch (Exception e) { + logger.warning( + "Unable to create default OpenTelemetry instance for client side metrics, will skip exporting client side metrics to Cloud Monitoring: " + + e); + } } } return new BuiltinMetricsProvider(defaultOpenTelemetry); @@ -193,7 +194,14 @@ private void addTracerFactory( @Override public void shutdown() { // Gracefully shutdown the metric reader registered to the default OTEL instance inside the sdk. - metricReader.shutdown(); + if (metricReader != null) { + try { + metricReader.shutdown(); + } catch (Exception e) { + // Handle the exception or retry with exponential backoff + logger.warning("Error shutting down MetricReader: " + e.getMessage()); + } + } } class MetricsContext implements MetricsUtil.MetricsContext { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java index 8b759e10d..517f77552 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java @@ -196,7 +196,14 @@ protected BaseConformanceTestRunner(final String description, final T testParame this.firestore = Mockito.spy( new FirestoreImpl( - FirestoreOptions.newBuilder().setProjectId("projectID").build(), firestoreRpc)); + FirestoreOptions.newBuilder() + .setProjectId("projectID") + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()) + .build(), + firestoreRpc)); } @Override diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITBaseTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITBaseTest.java index 54292a00f..dc24389be 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITBaseTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITBaseTest.java @@ -22,6 +22,7 @@ import com.google.api.gax.rpc.TransportChannelProvider; import com.google.cloud.firestore.DocumentReference; import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.FirestoreSpy; import com.google.cloud.firestore.ListenerRegistration; @@ -55,6 +56,12 @@ public abstract class ITBaseTest { public void before() throws Exception { FirestoreOptions.Builder optionsBuilder = FirestoreOptions.newBuilder(); + // disable the OpenTelemetry monitoring data for tests + optionsBuilder.setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()); + String dbPropertyName = "FIRESTORE_NAMED_DATABASE"; String namedDb = System.getProperty(dbPropertyName); if (namedDb == null) { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index 3fc57e17d..897dd03aa 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -25,7 +25,10 @@ import com.google.cloud.monitoring.v3.MetricServiceClient; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; -import com.google.monitoring.v3.*; +import com.google.monitoring.v3.ListTimeSeriesRequest; +import com.google.monitoring.v3.ListTimeSeriesResponse; +import com.google.monitoring.v3.TimeInterval; +import com.google.monitoring.v3.TimeSeries; import com.google.protobuf.util.Timestamps; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -33,7 +36,9 @@ import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -85,25 +90,22 @@ public void before() throws Exception { } else { logger.log(Level.INFO, "Integration test using default database."); } + } - firestore = optionsBuilder.build().getService(); + @After + public void after() throws Exception { Preconditions.checkNotNull( firestore, "Error instantiating Firestore. Check that the service account credentials " + "were properly set."); - } - - @After - public void after() throws Exception { - // Wait for 30 seconds to avoid duplicate time series error triggered by metric exporter's - // shutdown. - Thread.sleep(Duration.ofSeconds(30).toMillis()); firestore.shutdown(); metricClient.shutdown(); } @Test public void builtinMetricsWithDefaultOTEL() throws Exception { + firestore = optionsBuilder.build().getService(); + TimeInterval interval = createTimeInterval(); firestore.collection("col").get().get(); @@ -124,8 +126,6 @@ public void builtinMetricsWithDefaultOTEL() throws Exception { @Test public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { - firestore.shutdown(); - InMemoryMetricReader metricReader = InMemoryMetricReader.create(); SdkMeterProviderBuilder meterProvider = SdkMeterProvider.builder().registerMetricReader(metricReader); @@ -142,7 +142,6 @@ public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { .getService(); TimeInterval interval = createTimeInterval(); - firestore.collection("col").count().get().get(); // Verify metric data are published to Cloud Monitoring @@ -181,12 +180,12 @@ public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { for (String METRIC : METRICS) { assertMetricsAreAbsent(metricReader, METRIC); } - metricReader.forceFlush(); metricReader.shutdown(); } @Test public void builtinMetricsCreatedByTransaction() throws Exception { + firestore = optionsBuilder.build().getService(); TimeInterval interval = createTimeInterval(); firestore diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java index 82dbe9914..0494e0071 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java @@ -325,6 +325,7 @@ public void before() throws Exception { optionsBuilder.setOpenTelemetryOptions( FirestoreOpenTelemetryOptions.newBuilder() .setOpenTelemetry(openTelemetrySdk) + .exportBuiltinMetricsToGoogleCloudMonitoring(false) .build()); } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java index 225ff0162..6e9471e9b 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java @@ -113,7 +113,8 @@ public void before() { SpanProcessor inMemorySpanProcessor = SimpleSpanProcessor.create(inMemorySpanExporter); FirestoreOptions.Builder optionsBuilder = FirestoreOptions.newBuilder(); FirestoreOpenTelemetryOptions.Builder otelOptionsBuilder = - FirestoreOpenTelemetryOptions.newBuilder(); + FirestoreOpenTelemetryOptions.newBuilder() + .exportBuiltinMetricsToGoogleCloudMonitoring(false); OpenTelemetrySdkBuilder openTelemetrySdkBuilder = OpenTelemetrySdk.builder() .setTracerProvider( diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpcTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpcTest.java index e4e888ff0..35ebe67c5 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpcTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpcTest.java @@ -26,6 +26,7 @@ import com.google.api.gax.rpc.ServerStreamingCallSettings; import com.google.api.gax.rpc.StatusCode.Code; import com.google.api.gax.rpc.UnaryCallSettings; +import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.v1.FirestoreClient.ListDocumentsPagedResponse; import com.google.cloud.firestore.v1.FirestoreClient.PartitionQueryPagedResponse; @@ -59,7 +60,13 @@ public class GrpcFirestoreRpcTest { private static FirestoreStubSettings defaultStubSettings; private final FirestoreOptions firestoreOptionsWithoutOverride = - FirestoreOptions.newBuilder().setProjectId("test-project").build(); + FirestoreOptions.newBuilder() + .setProjectId("test-project") + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()) + .build(); @BeforeClass public static void beforeClass() throws IOException { @@ -85,6 +92,10 @@ public void retrySettingsOverride() throws Exception { FirestoreOptions.newBuilder() .setProjectId("test-project") .setRetrySettings(retrySettings) + .setOpenTelemetryOptions( + FirestoreOpenTelemetryOptions.newBuilder() + .exportBuiltinMetricsToGoogleCloudMonitoring(false) + .build()) .build(); GrpcFirestoreRpc grpcFirestoreRpc = new GrpcFirestoreRpc(firestoreOptions); From ab9bb92a6e5374b5f5c1f48124a56aa10ccdd515 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:15:03 -0500 Subject: [PATCH 089/103] skip E2E metric test unless running against nightly --- google-cloud-firestore/pom.xml | 1 - .../cloud/firestore/it/ITE2EMetricsTest.java | 16 +++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index f6517a96f..f49ce53ed 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -134,7 +134,6 @@ io.opentelemetry opentelemetry-sdk-common - ${opentelemetry.version} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index 897dd03aa..3f38972b0 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -17,13 +17,13 @@ package com.google.cloud.firestore.it; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assume.assumeTrue; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; import com.google.cloud.firestore.FirestoreOptions; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.monitoring.v3.MetricServiceClient; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.monitoring.v3.ListTimeSeriesRequest; import com.google.monitoring.v3.ListTimeSeriesResponse; @@ -76,9 +76,6 @@ public static void setup() throws IOException { @Before public void before() throws Exception { - // We only perform end-to-end tracing tests on a nightly basis. - // assumeTrue(isNightlyTesting); - metricClient = MetricServiceClient.create(); optionsBuilder = FirestoreOptions.newBuilder(); @@ -90,15 +87,16 @@ public void before() throws Exception { } else { logger.log(Level.INFO, "Integration test using default database."); } + + // We only perform end-to-end metrics tests on a nightly basis. + assumeTrue(isNightlyTesting); } @After public void after() throws Exception { - Preconditions.checkNotNull( - firestore, - "Error instantiating Firestore. Check that the service account credentials " - + "were properly set."); - firestore.shutdown(); + if (firestore != null) { + firestore.shutdown(); + } metricClient.shutdown(); } From 02909a9eaf84dc8fa6e6f83f99a8fe630790fbfd Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:24:00 -0500 Subject: [PATCH 090/103] remove override function for obsolete api in api tracer --- .../cloud/firestore/telemetry/CompositeApiTracer.java | 7 ++----- .../firestore/telemetry/CompositeApiTracerTest.java | 10 ---------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java index 31dbddbbf..7b793d2cb 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import org.threeten.bp.Duration; /** Combines multiple {@link ApiTracer}s into a single {@link ApiTracer}. */ class CompositeApiTracer extends BaseApiTracer { @@ -82,10 +81,8 @@ public void attemptCancelled() { children.forEach(ApiTracer::attemptCancelled); } - @Override - public void attemptFailed(Throwable error, Duration delay) { - children.forEach(child -> child.attemptFailed(error, delay)); - } + // Note: attemptFailed is obsolete and replaced by attemptFailedDuration. Override function not + // required @Override public void attemptFailedDuration(Throwable error, java.time.Duration delay) { diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java index b99d713b6..2b996d54e 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/CompositeApiTracerTest.java @@ -128,16 +128,6 @@ public void attemptCancelled_callsAttemptCancelledOnChildren() { verify(child2, times(1)).attemptCancelled(); } - @Test - public void attemptFailed_callsAttemptFailedOnChildren() { - Exception error = new Exception("Test error"); - Duration delay = Duration.ofSeconds(1); - compositeApiTracer.attemptFailed(error, delay); - - verify(child1, times(1)).attemptFailed(error, delay); - verify(child2, times(1)).attemptFailed(error, delay); - } - @Test public void attemptFailedDuration_callsAttemptFailedDurationOnChildren() { Exception error = new Exception("Test error"); From f28794dbe82fbc8475fc38a5fc839d831887e6c5 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Thu, 28 Nov 2024 19:29:44 +0000 Subject: [PATCH 091/103] chore: generate libraries at Thu Nov 28 19:28:06 UTC 2024 --- README.md | 2 +- .../google/cloud/firestore/telemetry/CompositeApiTracer.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a7b06ad9d..69fb83c20 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies: com.google.cloud google-cloud-firestore - 3.29.1 + 3.30.0 ``` diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java index 3473aed77..955697f01 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java @@ -16,7 +16,6 @@ package com.google.cloud.firestore.telemetry; -import static com.google.api.gax.util.TimeConversionUtils.toJavaTimeDuration; import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.BaseApiTracer; From 3c2c01790e741e5b2b55c6fa1be20d671e505318 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Thu, 28 Nov 2024 19:31:57 +0000 Subject: [PATCH 092/103] chore: generate libraries at Thu Nov 28 19:30:10 UTC 2024 --- .../com/google/cloud/firestore/telemetry/CompositeApiTracer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java index 955697f01..7b793d2cb 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java @@ -16,7 +16,6 @@ package com.google.cloud.firestore.telemetry; - import com.google.api.gax.tracing.ApiTracer; import com.google.api.gax.tracing.BaseApiTracer; import com.google.common.collect.ImmutableList; From ec7556d59775fce166f071d398c8ec62849f5327 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:37:48 -0500 Subject: [PATCH 093/103] fix dependency error, test run E2E metric tests on CI --- google-cloud-firestore/pom.xml | 12 ++++++++++++ .../google/cloud/firestore/it/ITE2EMetricsTest.java | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index f49ce53ed..fe87ecdc3 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -241,6 +241,18 @@ 2.53.0 test + + com.google.api.grpc + proto-google-cloud-monitoring-v3 + 3.55.0 + test + + + com.google.cloud + google-cloud-monitoring + 3.55.0 + test + com.google.http-client google-http-client diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index 3f38972b0..755eb6594 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -17,7 +17,6 @@ package com.google.cloud.firestore.it; import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assume.assumeTrue; import com.google.cloud.firestore.Firestore; import com.google.cloud.firestore.FirestoreOpenTelemetryOptions; @@ -89,7 +88,7 @@ public void before() throws Exception { } // We only perform end-to-end metrics tests on a nightly basis. - assumeTrue(isNightlyTesting); + // assumeTrue(isNightlyTesting); } @After From d258d9503d3f35db3b101bb9d862062c47d79f04 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Thu, 17 Apr 2025 16:50:54 +0000 Subject: [PATCH 094/103] chore: generate libraries at Thu Apr 17 16:49:05 UTC 2025 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 570b04409..d5188f622 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies: com.google.cloud google-cloud-firestore - 3.30.12 + 3.30.13 ``` From d97a85bb17df98132014785aad8945b6459cc5ac Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 23 Apr 2025 15:48:38 -0400 Subject: [PATCH 095/103] fix the flakiness of the E2E tests --- google-cloud-firestore/pom.xml | 12 +++ .../cloud/firestore/it/ITE2EMetricsTest.java | 78 +++++++++++-------- 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index cfed7bf36..2e0d3e763 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -18,6 +18,18 @@ google-cloud-firestore 1.49.0 + + + + + io.opentelemetry + opentelemetry-bom + 1.49.0 pom + import + + + + ${project.groupId} diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index 755eb6594..1308f9183 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -87,13 +87,18 @@ public void before() throws Exception { logger.log(Level.INFO, "Integration test using default database."); } - // We only perform end-to-end metrics tests on a nightly basis. + // These end-to-end metrics tests are resource-intensive and are only intended to run in a + // nightly testing environment. // assumeTrue(isNightlyTesting); } @After public void after() throws Exception { if (firestore != null) { + // Shutting down Firestore can trigger a final export of metrics, potentially leading to + // "frequent write" errors in Cloud Monitoring. This sleep attempts to mitigate this by + // allowing a brief pause. + Thread.sleep(Duration.ofSeconds(30).toMillis()); firestore.shutdown(); } metricClient.shutdown(); @@ -102,7 +107,6 @@ public void after() throws Exception { @Test public void builtinMetricsWithDefaultOTEL() throws Exception { firestore = optionsBuilder.build().getService(); - TimeInterval interval = createTimeInterval(); firestore.collection("col").get().get(); @@ -116,6 +120,7 @@ public void builtinMetricsWithDefaultOTEL() throws Exception { TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); + // Verify metric data are published to Cloud Monitoring for (String METRIC : METRICS) { assertMetricsArePublished(METRIC, interval); } @@ -128,7 +133,6 @@ public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { SdkMeterProvider.builder().registerMetricReader(metricReader); OpenTelemetrySdk customOpenTelemetrySdk = OpenTelemetrySdk.builder().setMeterProvider(meterProvider.build()).build(); - firestore = optionsBuilder .setOpenTelemetryOptions( @@ -137,8 +141,8 @@ public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { .build()) .build() .getService(); - TimeInterval interval = createTimeInterval(); + firestore.collection("col").count().get().get(); // Verify metric data are published to Cloud Monitoring @@ -207,26 +211,26 @@ public void builtinMetricsCreatedByTransaction() throws Exception { TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); + // Verify metric data are published to Cloud Monitoring for (String METRIC : METRICS) { assertMetricsArePublished(METRIC, interval); } } private TimeInterval createTimeInterval() { - Instant startTime = Instant.now(); - Instant endTime = Instant.now().plus(Duration.ofMinutes(1)); + Instant startTime = Instant.now().minus(Duration.ofSeconds(10)); + // Set a wider time interval to make sure SDK has finished the batching (60s) and exporting of + // the metrics + Instant endTime = Instant.now().plus(Duration.ofSeconds(90)); return TimeInterval.newBuilder() .setStartTime(Timestamps.fromMillis(startTime.toEpochMilli())) .setEndTime(Timestamps.fromMillis(endTime.toEpochMilli())) .build(); } - private ListTimeSeriesResponse assertMetricsArePublished(String metric, TimeInterval interval) - throws Exception { - + private void assertMetricsArePublished(String metric, TimeInterval interval) throws Exception { String metricFilter = String.format("metric.type=\"%s/%s\"", TelemetryConstants.METRIC_PREFIX, metric); - ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder() .setName("projects/" + projectId) @@ -234,41 +238,51 @@ private ListTimeSeriesResponse assertMetricsArePublished(String metric, TimeInte .setInterval(interval) .build(); - ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request); - int attemptsMade = 0; - while (response.getTimeSeriesCount() == 0 && attemptsMade < 3) { - // Call listTimeSeries every minute - Thread.sleep(Duration.ofMinutes(1).toMillis()); - response = metricClient.listTimeSeriesCallable().call(request); - attemptsMade++; + List filteredData = Collections.emptyList(); + // Metrics are batched and exported periodically in the SDK (e.g., every 60s). And there is a + // potential delay between when the metric is published by the client library and when it + // becomes available. So we retry the fetching for multiple times with specified interval, to + // allow the monitoring system to catch up and for the data to become available. + int maxAttempts = 5; + Duration retryDelay = Duration.ofSeconds(30); + + for (int attempt = 0; attempt < maxAttempts; attempt++) { + // Query Cloud Monitoring for the specific metric within the test's time window. + ListTimeSeriesResponse response = metricClient.listTimeSeriesCallable().call(request); + + // In the testing environment where multiple tests might be running, the initial + // response could contain data from previous test runs. Filter the response to + // ensure we're validating the metrics generated by this specific test run and + // avoid interference from older data. + filteredData = + response.getTimeSeriesList().stream() + .filter( + ts -> + ts.getPoints(0).getInterval().getStartTime().getSeconds() + >= interval.getStartTime().getSeconds()) + .collect(Collectors.toList()); + if (!filteredData.isEmpty()) { + break; + } + logger.info( + "Retry fetching metrics data from Cloud Monitoring after " + + retryDelay.getSeconds() + + " seconds."); + Thread.sleep(retryDelay.toMillis()); } - // When querying from Cloud Monitoring database, we cannot reset the state for each individual - // test cases, and the response fetched could include time series data from previous test cases. - // So filter the response based on the time interval used by each single test case to make sure - // each test case has published expected metric data. - List filteredData = - response.getTimeSeriesList().stream() - .filter( - ts -> - ts.getPoints(0).getInterval().getStartTime().getSeconds() - >= interval.getStartTime().getSeconds()) - .collect(Collectors.toList()); - assertWithMessage("Metric " + metric + " didn't return any data.") .that(filteredData.size()) .isGreaterThan(0); - - return response; } private void assertMetricsAreCollected(InMemoryMetricReader metricReader, String metricName) { String fullMetricName = TelemetryConstants.METRIC_PREFIX + "/" + metricName; - List matchingMetadata = metricReader.collectAllMetrics().stream() .filter(md -> md.getName().equals(fullMetricName)) .collect(Collectors.toList()); + // Fetch the MetricData with retries int attemptsMade = 0; while (matchingMetadata.size() == 0 && attemptsMade < 10) { From c6baaa6c36542686fa7928dcc53f520864dad828 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 23 Apr 2025 17:15:08 -0400 Subject: [PATCH 096/103] remove end to end latency metric --- .../cloud/firestore/AggregateQuery.java | 5 --- .../google/cloud/firestore/BulkWriter.java | 3 -- .../cloud/firestore/CollectionGroup.java | 4 --- .../cloud/firestore/CollectionReference.java | 6 ---- .../cloud/firestore/DocumentReference.java | 35 ------------------- .../google/cloud/firestore/FirestoreImpl.java | 4 --- .../cloud/firestore/StreamableQuery.java | 4 --- .../google/cloud/firestore/UpdateBuilder.java | 3 -- .../telemetry/BuiltinMetricsProvider.java | 11 ------ .../telemetry/TelemetryConstants.java | 3 -- .../cloud/firestore/it/ITE2EMetricsTest.java | 4 --- .../cloud/firestore/it/ITMetricsTest.java | 15 -------- .../telemetry/BuiltinMetricsProviderTest.java | 25 +++---------- .../telemetry/DisabledMetricsUtilTest.java | 5 ++- .../telemetry/EnabledMetricsUtilTest.java | 12 +++---- 15 files changed, 13 insertions(+), 126 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 36eb01607..1f526ea00 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -120,11 +120,9 @@ public ApiFuture> explain(ExplainOptions runQuery(responseDeliverer, /* attempt */ 0); ApiFuture> result = responseDeliverer.getFuture(); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -155,11 +153,9 @@ ApiFuture get( runQuery(responseDeliverer, /* attempt= */ 0); ApiFuture result = responseDeliverer.getFuture(); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -232,7 +228,6 @@ void deliverFirstResponse() { void deliverError(Throwable throwable) { future.setException(throwable); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); } void deliverResult( diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java index 1fc35bd35..c54e86ab5 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java @@ -28,7 +28,6 @@ import com.google.api.gax.rpc.StatusCode.Code; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Context; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -939,10 +938,8 @@ private void sendBatchLocked(final BulkCommitBatch batch) { bulkWriterExecutor); } span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } else { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java index 159f7077d..751b711f7 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java @@ -23,7 +23,6 @@ import com.google.api.gax.rpc.ApiStreamObserver; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.PartitionQueryPagedResponse; @@ -138,15 +137,12 @@ public ApiFuture> getPartitions(long desiredPartitionCount) }, MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); throw throwable; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java index 75954d82d..ff121026a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java @@ -26,7 +26,6 @@ import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListDocumentsPagedResponse; @@ -185,15 +184,12 @@ public void remove() { } }; span.end(); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); throw throwable; } } @@ -230,11 +226,9 @@ public ApiFuture add(@Nonnull final Map field ApiFutures.transform( createFuture, writeResult -> documentReference, MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index 2b0cc1ddc..54ced0a53 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -24,7 +24,6 @@ import com.google.cloud.firestore.telemetry.MetricsUtil; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse; @@ -166,11 +165,9 @@ public ApiFuture create(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -192,11 +189,9 @@ public ApiFuture create(@Nonnull Object pojo) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, pojo).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -218,11 +213,9 @@ public ApiFuture set(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -247,11 +240,9 @@ public ApiFuture set( WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields, options).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -273,11 +264,9 @@ public ApiFuture set(@Nonnull Object pojo) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -301,11 +290,9 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo, options).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -327,11 +314,9 @@ public ApiFuture update(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.update(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -355,11 +340,9 @@ public ApiFuture update(@Nonnull Map fields, Precon ApiFuture result = extractFirst(writeBatch.update(this, fields, options).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -385,11 +368,9 @@ public ApiFuture update( ApiFuture result = extractFirst(writeBatch.update(this, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -415,11 +396,9 @@ public ApiFuture update( ApiFuture result = extractFirst(writeBatch.update(this, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -450,11 +429,9 @@ public ApiFuture update( extractFirst( writeBatch.update(this, options, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -485,11 +462,9 @@ public ApiFuture update( extractFirst( writeBatch.update(this, options, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -510,11 +485,9 @@ public ApiFuture delete(@Nonnull Precondition options) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this, options).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -534,11 +507,9 @@ public ApiFuture delete() { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this).commit()); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -559,11 +530,9 @@ public ApiFuture get() { try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -586,11 +555,9 @@ public ApiFuture get(FieldMask fieldMask) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(new DocumentReference[] {this}, fieldMask)); span.endAtFuture(result); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -642,11 +609,9 @@ public void remove() { } }; span.end(); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index 871abcbc1..ee5e68218 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -313,7 +313,6 @@ public void onResponse(BatchGetDocumentsResponse response) { @Override public void onError(Throwable throwable) { getTraceUtil().currentSpan().end(throwable); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); apiStreamObserver.onError(throwable); } @@ -329,7 +328,6 @@ public void onComplete() { + numResponses + " responses.", Collections.singletonMap(ATTRIBUTE_KEY_NUM_RESPONSES, numResponses)); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); apiStreamObserver.onCompleted(); } }; @@ -462,9 +460,7 @@ public ApiFuture runAsyncTransaction( // that cannot be tracked client side. result = new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run(); } - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); } catch (Exception error) { - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } return result; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java index dab781921..17344d633 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java @@ -171,7 +171,6 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -265,7 +264,6 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -293,13 +291,11 @@ public void onNext(RunQueryResponse value) { @Override public void onError(Throwable t) { - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, t); observer.onError(t); } @Override public void onCompleted() { - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); observer.onCompleted(); } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index 1ab0dcb37..8139e0b3d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -28,7 +28,6 @@ import com.google.cloud.firestore.encoding.CustomClassMapper; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.common.base.Preconditions; @@ -668,12 +667,10 @@ ApiFuture> commit(@Nullable ByteString transactionId) { }, MoreExecutors.directExecutor()); span.endAtFuture(returnValue); - metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, returnValue); return returnValue; } catch (Exception error) { span.end(error); - metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); throw error; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index 56b156bdf..a572f43b3 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -20,7 +20,6 @@ import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT; import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY; @@ -51,7 +50,6 @@ class BuiltinMetricsProvider { private static final Logger logger = Logger.getLogger(BuiltinMetricsProvider.class.getName()); private OpenTelemetry openTelemetry; - private DoubleHistogram endToEndLatency; private DoubleHistogram firstResponseLatency; private DoubleHistogram transactionLatency; private LongCounter transactionAttemptCount; @@ -100,13 +98,6 @@ private void configureRPCLayerMetrics() { private void configureSDKLayerMetrics() { Meter meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); - this.endToEndLatency = - meter - .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_END_TO_END_LATENCY) - .setDescription("Firestore operations' end-to-end latency") - .setUnit(MILLISECOND_UNIT) - .build(); - this.firstResponseLatency = meter .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_FIRST_RESPONSE_LATENCY) @@ -160,8 +151,6 @@ public void counterRecorder(MetricType metricType, long count, Map FIRESTORE_METRICS = ImmutableSet.of( METRIC_NAME_FIRST_RESPONSE_LATENCY, - METRIC_NAME_END_TO_END_LATENCY, METRIC_NAME_TRANSACTION_LATENCY, METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); public enum MetricType { - END_TO_END_LATENCY, FIRST_RESPONSE_LATENCY, TRANSACTION_LATENCY, TRANSACTION_ATTEMPT_COUNT diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java index 1308f9183..fe615b0a8 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2EMetricsTest.java @@ -117,7 +117,6 @@ public void builtinMetricsWithDefaultOTEL() throws Exception { TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, TelemetryConstants.METRIC_NAME_OPERATION_COUNT, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, - TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); // Verify metric data are published to Cloud Monitoring @@ -152,7 +151,6 @@ public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, TelemetryConstants.METRIC_NAME_OPERATION_COUNT, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, - TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); for (String METRIC : METRICS) { @@ -166,7 +164,6 @@ public void builtinMetricsWithDefaultAndCustomOTEL() throws Exception { TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, TelemetryConstants.METRIC_NAME_OPERATION_COUNT, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, - TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); for (String METRIC : METRICS) { assertMetricsAreCollected(metricReader, METRIC); @@ -206,7 +203,6 @@ public void builtinMetricsCreatedByTransaction() throws Exception { TelemetryConstants.METRIC_NAME_ATTEMPT_LATENCY, TelemetryConstants.METRIC_NAME_OPERATION_COUNT, TelemetryConstants.METRIC_NAME_ATTEMPT_COUNT, - TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index 48b325c88..e4f330d4d 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -165,7 +165,6 @@ public void queryGet() throws Exception { TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @@ -210,7 +209,6 @@ public void onCompleted() {} .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @@ -236,7 +234,6 @@ public void aggregateQueryGet() throws Exception { .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @@ -264,7 +261,6 @@ public void writeBatch() throws Exception { TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -297,7 +293,6 @@ public void bulkWriterCommit() throws Exception { 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -316,7 +311,6 @@ public void partitionQuery() throws Exception { TelemetryConstants.METHOD_NAME_PARTITION_QUERY, Status.OK.getCode().toString(), 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -336,7 +330,6 @@ public void listCollection() throws Exception { 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -356,7 +349,6 @@ public void collectionListDocuments() throws Exception { 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -386,7 +378,6 @@ public void docRefSet() throws Exception { TelemetryConstants.METHOD_NAME_DOC_REF_SET, Status.OK.getCode().toString(), 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -416,7 +407,6 @@ public void getAll() throws Exception { .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } @@ -490,7 +480,6 @@ public void transaction() throws Exception { Status.OK.getCode().toString(), 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); expectedMetrics = new MetricsExpectationBuilder() @@ -543,7 +532,6 @@ public void transactionWithRollback() throws Exception { Status.UNKNOWN.getCode().toString(), 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, expectedMetrics); } @@ -591,7 +579,6 @@ public void transactionWithFailure() throws Exception { 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY, expectedMetrics); validateSDKMetrics(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT, expectedMetrics); } @@ -650,7 +637,6 @@ public Integer updateCallback(Transaction transaction) throws Exception { Status.OK.getCode().toString(), 1) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); expectedMetrics = new MetricsExpectationBuilder() @@ -718,7 +704,6 @@ public void multipleOperations() throws Exception { .expectMetricData( TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 3) .build(); - validateSDKMetrics(TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY, expectedMetrics); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java index 20a6c0ae6..92a95e068 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProviderTest.java @@ -43,7 +43,6 @@ public final class BuiltinMetricsProviderTest { private OpenTelemetry mockOpenTelemetry; private Meter mockMeter; - private DoubleHistogram mockEndToEndLatency; private DoubleHistogram mockFirstResponseLatency; private DoubleHistogram mockTransactionLatency; private LongCounter mockTransactionAttemptCount; @@ -72,16 +71,14 @@ public void setUp() { when(mockCounterBuilder.setDescription(anyString())).thenReturn(mockCounterBuilder); when(mockCounterBuilder.setUnit(anyString())).thenReturn(mockCounterBuilder); - mockEndToEndLatency = mock(DoubleHistogram.class); mockFirstResponseLatency = mock(DoubleHistogram.class); mockTransactionLatency = mock(DoubleHistogram.class); mockTransactionAttemptCount = mock(LongCounter.class); - // Configure mockHistogramBuilder to return the first 3 histogram mocks created in sequence. - // The SDK configures 4 SDK layer metrics first, and then the RPC metrics gets created in GAX + // Configure mockHistogramBuilder to return the first 2 histogram mocks created in sequence. + // The SDK configures 3 SDK layer metrics first, and then the RPC metrics gets created in GAX // layer when(mockHistogramBuilder.build()) - .thenReturn(mockEndToEndLatency) .thenReturn(mockFirstResponseLatency) .thenReturn(mockTransactionLatency); @@ -95,7 +92,6 @@ public void setUp() { @Test public void SDKLayerMetricsConfiguredSuccessfully() { metricsProvider = new BuiltinMetricsProvider(mockOpenTelemetry); - assertNotNull(metricsProvider.getHistogram(MetricType.END_TO_END_LATENCY)); assertNotNull(metricsProvider.getHistogram(MetricType.FIRST_RESPONSE_LATENCY)); assertNotNull(metricsProvider.getHistogram(MetricType.TRANSACTION_LATENCY)); assertNotNull(metricsProvider.getCounter(MetricType.TRANSACTION_ATTEMPT_COUNT)); @@ -103,10 +99,7 @@ public void SDKLayerMetricsConfiguredSuccessfully() { @Test public void getHistogramReturnsHistogramsInstrumentCorrectly() { - DoubleHistogram mockHistogram = metricsProvider.getHistogram(MetricType.END_TO_END_LATENCY); - assertEquals(mockEndToEndLatency, mockHistogram); - - mockHistogram = metricsProvider.getHistogram(MetricType.FIRST_RESPONSE_LATENCY); + DoubleHistogram mockHistogram = metricsProvider.getHistogram(MetricType.FIRST_RESPONSE_LATENCY); assertEquals(mockFirstResponseLatency, mockHistogram); mockHistogram = metricsProvider.getHistogram(MetricType.TRANSACTION_LATENCY); @@ -133,8 +126,8 @@ public void getCounterThrowsOnInvalidMetricType() { Exception exception = assertThrows( IllegalArgumentException.class, - () -> metricsProvider.getCounter(MetricType.END_TO_END_LATENCY)); - assertEquals("Unknown counter MetricType: END_TO_END_LATENCY", exception.getMessage()); + () -> metricsProvider.getCounter(MetricType.FIRST_RESPONSE_LATENCY)); + assertEquals("Unknown counter MetricType: FIRST_RESPONSE_LATENCY", exception.getMessage()); } @Test @@ -142,13 +135,6 @@ public void latencyRecorderTriggersCorrectInstrument() { Map attributes = new HashMap<>(); attributes.put("attribute", "value"); - metricsProvider.latencyRecorder(MetricType.END_TO_END_LATENCY, 100.0, attributes); - verify(mockEndToEndLatency) - .record( - eq(100.0), - argThat( - arguments -> arguments.get(AttributeKey.stringKey("attribute")).equals("value"))); - metricsProvider.latencyRecorder(MetricType.FIRST_RESPONSE_LATENCY, 50.0, attributes); verify(mockFirstResponseLatency) .record( @@ -180,7 +166,6 @@ public void handlesNoopMetricProviderGracefully() throws Exception { attributes.put("key", "disabledTest"); try { - provider.latencyRecorder(MetricType.END_TO_END_LATENCY, 50.0, attributes); provider.latencyRecorder(MetricType.FIRST_RESPONSE_LATENCY, 100.0, attributes); provider.latencyRecorder(MetricType.TRANSACTION_LATENCY, 150.0, attributes); provider.counterRecorder(MetricType.TRANSACTION_ATTEMPT_COUNT, 1, attributes); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java index 8d2695610..877b6ef96 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtilTest.java @@ -57,13 +57,12 @@ public void shouldNotThrowOnMetricsCollection() { // Ensure no exceptions are thrown by no-op methods try { - context.recordLatency(MetricType.END_TO_END_LATENCY); - context.recordLatency(MetricType.END_TO_END_LATENCY, new Exception("test")); + context.recordLatency(MetricType.FIRST_RESPONSE_LATENCY, new Exception("test")); context.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); context.incrementCounter(); ApiFuture future = ApiFutures.immediateFuture("test"); - context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + context.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, future); } catch (Exception e) { assertThat(e).isNull(); // Fail the test if any exception is thrown } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java index 876e76ea0..7a9b46cca 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -261,12 +261,12 @@ public void recordLatencyCalledWhenFutureIsCompletedWithSuccess() throws Excepti MetricsContext context = metricsUtil.createMetricsContext("testMethod"); ApiFuture future = ApiFutures.immediateFuture("success"); - context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + context.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, future); verify(defaultProvider) - .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); + .latencyRecorder(eq(MetricType.FIRST_RESPONSE_LATENCY), anyDouble(), Mockito.anyMap()); verify(customProvider) - .latencyRecorder(eq(MetricType.END_TO_END_LATENCY), anyDouble(), Mockito.anyMap()); + .latencyRecorder(eq(MetricType.FIRST_RESPONSE_LATENCY), anyDouble(), Mockito.anyMap()); } @Test @@ -282,12 +282,12 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), false)); ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); - context.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, future); + context.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, future); // TODO(b/305998085):Change this to correct status code Mockito.verify(defaultProvider) .latencyRecorder( - Mockito.eq(MetricType.END_TO_END_LATENCY), + Mockito.eq(MetricType.FIRST_RESPONSE_LATENCY), Mockito.anyDouble(), Mockito.argThat( attributes -> @@ -296,7 +296,7 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception .equals(Status.Code.UNKNOWN.toString()))); Mockito.verify(customProvider) .latencyRecorder( - Mockito.eq(MetricType.END_TO_END_LATENCY), + Mockito.eq(MetricType.FIRST_RESPONSE_LATENCY), Mockito.anyDouble(), Mockito.argThat( attributes -> From d2e1e5c868d3cfc66cc524ff76bdbcca08d9878a Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 25 Apr 2025 10:20:13 -0400 Subject: [PATCH 097/103] format --- .../clirr-ignored-differences.xml | 12 ++++++ .../google/cloud/firestore/BulkWriter.java | 7 ---- .../cloud/firestore/CollectionGroup.java | 8 ---- .../cloud/firestore/CollectionReference.java | 15 -------- .../cloud/firestore/DocumentReference.java | 37 ------------------- .../google/cloud/firestore/FirestoreImpl.java | 4 -- .../google/cloud/firestore/UpdateBuilder.java | 12 ------ 7 files changed, 12 insertions(+), 83 deletions(-) diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index 302a10201..4a37c6238 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -354,6 +354,18 @@ METHOD_NAME_* + + 6011 + com/google/cloud/firestore/telemetry/TelemetryConstants + METRIC_NAME_END_TO_END_LATENCY + + + + 6001 + com/google/cloud/firestore/telemetry/TelemetryConstants$MetricType + END_TO_END_LATENCY + + 7012 com/google/cloud/firestore/telemetry/MetricsUtil diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java index c54e86ab5..8437f16b4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java @@ -26,7 +26,6 @@ import com.google.api.core.SettableApiFuture; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode.Code; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Context; @@ -918,12 +917,6 @@ private void sendBatchLocked(final BulkCommitBatch batch) { .startSpan(TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT, traceContext) .setAttribute(ATTRIBUTE_KEY_DOC_COUNT, batch.getMutationsSize()); - MetricsContext metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT); - try (Scope ignored = span.makeCurrent()) { ApiFuture result = batch.bulkCommit(); if (!lastFlushOperation.isDone()) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java index 751b711f7..78ec902e1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java @@ -21,7 +21,6 @@ import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.ApiExceptions; import com.google.api.gax.rpc.ApiStreamObserver; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -113,13 +112,6 @@ public ApiFuture> getPartitions(long desiredPartitionCount) .getTraceUtil() .startSpan(TelemetryConstants.METHOD_NAME_PARTITION_QUERY); - MetricsContext metricsContext = - rpcContext - .getFirestore() - .getOptions() - .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_PARTITION_QUERY); - try (Scope ignored = span.makeCurrent()) { ApiFuture> result = ApiFutures.transform( diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java index ff121026a..b4419d9e1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java @@ -24,7 +24,6 @@ import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.firestore.encoding.CustomClassMapper; import com.google.cloud.firestore.spi.v1.FirestoreRpc; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -138,13 +137,6 @@ public Iterable listDocuments() { .getTraceUtil() .startSpan(TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS); - MetricsContext metricsContext = - rpcContext - .getFirestore() - .getOptions() - .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS); - try (Scope ignored = span.makeCurrent()) { ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder(); request.setParent(options.getParentPath().toString()); @@ -212,13 +204,6 @@ public ApiFuture add(@Nonnull final Map field .getTraceUtil() .startSpan(TelemetryConstants.METHOD_NAME_COL_REF_ADD); - MetricsContext metricsContext = - rpcContext - .getFirestore() - .getOptions() - .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_COL_REF_ADD); - try (Scope ignored = span.makeCurrent()) { final DocumentReference documentReference = document(); ApiFuture createFuture = documentReference.create(fields); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java index 54ced0a53..b394278dd 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java @@ -22,7 +22,6 @@ import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.ApiExceptions; import com.google.cloud.firestore.telemetry.MetricsUtil; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -158,9 +157,6 @@ private MetricsUtil getMetricsUtil() { public ApiFuture create(@Nonnull Map fields) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); - try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, fields).commit()); @@ -182,8 +178,6 @@ public ApiFuture create(@Nonnull Map fields) { @Nonnull public ApiFuture create(@Nonnull Object pojo) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -206,8 +200,6 @@ public ApiFuture create(@Nonnull Object pojo) { @Nonnull public ApiFuture set(@Nonnull Map fields) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -233,8 +225,6 @@ public ApiFuture set(@Nonnull Map fields) { public ApiFuture set( @Nonnull Map fields, @Nonnull SetOptions options) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -257,8 +247,6 @@ public ApiFuture set( @Nonnull public ApiFuture set(@Nonnull Object pojo) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -283,8 +271,6 @@ public ApiFuture set(@Nonnull Object pojo) { @Nonnull public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions options) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_SET); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -307,8 +293,6 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti @Nonnull public ApiFuture update(@Nonnull Map fields) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -332,8 +316,6 @@ public ApiFuture update(@Nonnull Map fields) { @Nonnull public ApiFuture update(@Nonnull Map fields, Precondition options) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -360,8 +342,6 @@ public ApiFuture update(@Nonnull Map fields, Precon public ApiFuture update( @Nonnull String field, @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -388,8 +368,6 @@ public ApiFuture update( public ApiFuture update( @Nonnull FieldPath fieldPath, @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -420,8 +398,6 @@ public ApiFuture update( @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -453,8 +429,6 @@ public ApiFuture update( @Nullable Object value, Object... moreFieldsAndValues) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -478,8 +452,6 @@ public ApiFuture update( @Nonnull public ApiFuture delete(@Nonnull Precondition options) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -500,8 +472,6 @@ public ApiFuture delete(@Nonnull Precondition options) { @Nonnull public ApiFuture delete() { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE); try (Scope ignored = span.makeCurrent()) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); @@ -524,8 +494,6 @@ public ApiFuture delete() { @Nonnull public ApiFuture get() { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_GET); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_GET); try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); @@ -548,8 +516,6 @@ public ApiFuture get() { @Nonnull public ApiFuture get(FieldMask fieldMask) { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_GET); - MetricsContext metricsContext = - getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_GET); try (Scope ignored = span.makeCurrent()) { ApiFuture result = @@ -572,9 +538,6 @@ public ApiFuture get(FieldMask fieldMask) { public Iterable listCollections() { TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS); - MetricsContext metricsContext = - getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS); try (Scope ignored = span.makeCurrent()) { ListCollectionIdsRequest.Builder request = ListCollectionIdsRequest.newBuilder(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index ee5e68218..f25ea349a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -442,10 +442,6 @@ public ApiFuture runAsyncTransaction( @Nonnull final Transaction.AsyncFunction updateFunction, @Nonnull TransactionOptions transactionOptions) { - MetricsContext metricsContext = - getOptions() - .getMetricsUtil() - .createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_TRANSACTION); ApiFuture result; try { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java index 8139e0b3d..60cb49422 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java @@ -26,7 +26,6 @@ import com.google.api.core.InternalExtensionOnly; import com.google.cloud.firestore.UserDataConverter.EncodingOptions; import com.google.cloud.firestore.encoding.CustomClassMapper; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; @@ -623,17 +622,6 @@ ApiFuture> commit(@Nullable ByteString transactionId) { span.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, writes.size()); span.setAttribute(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null); - MetricsContext metricsContext = null; - - metricsContext = - firestore - .getOptions() - .getMetricsUtil() - .createMetricsContext( - transactionId == null - ? TelemetryConstants.METHOD_NAME_BATCH_COMMIT - : TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT); - try (Scope ignored = span.makeCurrent()) { // Sequence is thread safe. // From a899489d34f5d72b00124fe1dad4dad1c6be63ec Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Thu, 1 May 2025 16:10:55 -0400 Subject: [PATCH 098/103] format TelemetryConstants, remove MonitoredStreamResponseObserver class --- .../clirr-ignored-differences.xml | 16 ++- google-cloud-firestore/pom.xml | 4 + .../cloud/firestore/AggregateQuery.java | 3 +- .../google/cloud/firestore/FirestoreImpl.java | 3 +- .../com/google/cloud/firestore/Query.java | 11 +- .../ServerSideTransactionRunner.java | 9 +- .../cloud/firestore/StreamableQuery.java | 64 +++------- .../telemetry/BuiltinMetricsProvider.java | 22 +--- .../telemetry/EnabledMetricsUtil.java | 84 ++++++++----- .../telemetry/TelemetryConstants.java | 40 +++--- .../cloud/firestore/it/ITMetricsTest.java | 118 +++--------------- .../telemetry/EnabledMetricsUtilTest.java | 8 +- 12 files changed, 146 insertions(+), 236 deletions(-) diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index 4a37c6238..71bbecd39 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -347,17 +347,25 @@ * - + 6011 com/google/cloud/firestore/telemetry/TelemetryConstants - METHOD_NAME_* + * + + + + 6001 + com/google/cloud/firestore/telemetry/TelemetryConstants + * - 6011 + 6004 com/google/cloud/firestore/telemetry/TelemetryConstants - METRIC_NAME_END_TO_END_LATENCY + METRIC_ATTRIBUTE_KEY_* + io.opentelemetry.api.common.AttributeKey + java.lang.String diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 2e0d3e763..78ab7e17e 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -153,6 +153,10 @@ io.opentelemetry opentelemetry-sdk-common + + com.google.cloud.opentelemetry + detector-resources-support + diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java index 1f526ea00..21a8c9d3d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java @@ -28,7 +28,6 @@ import com.google.cloud.Timestamp; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -223,7 +222,7 @@ ApiFuture getFuture() { } void deliverFirstResponse() { - metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); + metricsContext.recordLatency(TelemetryConstants.MetricType.FIRST_RESPONSE_LATENCY); } void deliverError(Throwable throwable) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index f25ea349a..7e7408aa4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -36,7 +36,6 @@ import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -268,7 +267,7 @@ public void onResponse(BatchGetDocumentsResponse response) { .addEvent( TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + ": First response received"); - metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); + metricsContext.recordLatency(TelemetryConstants.MetricType.FIRST_RESPONSE_LATENCY); } else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) { getTraceUtil() .currentSpan() diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index 87b41702e..6159327a4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -35,8 +35,6 @@ import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.QueryOptions.Builder; import com.google.cloud.firestore.encoding.CustomClassMapper; -import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; -import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.firestore.bundle.BundledQuery; @@ -1489,8 +1487,6 @@ public void stream(@Nonnull final ApiStreamObserver responseOb "Query results for queries that include limitToLast() constraints cannot be streamed. " + "Use Query.get() instead."); - MetricsContext metricsContext = createMetricsContext(TelemetryConstants.METHOD_NAME_QUERY_GET); - ApiStreamObserver observer = new ApiStreamObserver() { @Override @@ -1516,7 +1512,7 @@ public void onCompleted() { }; internalStream( - new MonitoredStreamResponseObserver(observer, metricsContext), + observer, /* startTimeNanos= */ rpcContext.getClock().nanoTime(), /* transactionId= */ null, /* readTime= */ null, @@ -1540,9 +1536,6 @@ public ApiFuture explainStream( "Query results for queries that include limitToLast() constraints cannot be streamed. " + "Use Query.explain() instead."); - MetricsContext metricsContext = - createMetricsContext(TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN); - final SettableApiFuture metricsFuture = SettableApiFuture.create(); ApiStreamObserver observer = @@ -1580,7 +1573,7 @@ public void onCompleted() { }; internalStream( - new MonitoredStreamResponseObserver(observer, metricsContext), + observer, /* startTimeNanos= */ rpcContext.getClock().nanoTime(), /* transactionId= */ null, /* readTime= */ null, diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java index 4dca138ea..7877b9fd9 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java @@ -28,7 +28,6 @@ import com.google.api.gax.rpc.ApiException; import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext; import com.google.cloud.firestore.telemetry.TelemetryConstants; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.telemetry.TraceUtil.Span; @@ -103,8 +102,9 @@ private TraceUtil getTraceUtil() { ApiFuture run() { ApiFuture result = runInternally(); - metricsContext.recordLatencyAtFuture(MetricType.TRANSACTION_LATENCY, result); - metricsContext.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, result); + metricsContext.recordLatencyAtFuture(TelemetryConstants.MetricType.TRANSACTION_LATENCY, result); + metricsContext.recordCounterAtFuture( + TelemetryConstants.MetricType.TRANSACTION_ATTEMPT_COUNT, result); return result; } @@ -154,7 +154,8 @@ ApiFuture begin() { // Record the first time latency for the first BeginTransaction call only Boolean isFirstAttempt = (transactionOptions.getNumberOfAttempts() - attemptsRemaining) == 1; if (isFirstAttempt) { - metricsContext.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, result); + metricsContext.recordLatencyAtFuture( + TelemetryConstants.MetricType.FIRST_RESPONSE_LATENCY, result); } span.endAtFuture(result); return result; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java index 17344d633..c6743a3d1 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java @@ -113,12 +113,6 @@ ApiFuture get( ? TelemetryConstants.METHOD_NAME_QUERY_GET : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY); - MetricsContext metricsContext = - createMetricsContext( - transactionId == null - ? TelemetryConstants.METHOD_NAME_QUERY_GET - : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY); - try (Scope ignored = span.makeCurrent()) { final SettableApiFuture result = SettableApiFuture.create(); @@ -160,7 +154,7 @@ public void onCompleted() { }; internalStream( - new MonitoredStreamResponseObserver(observer, metricsContext), + observer, /* startTimeNanos= */ rpcContext.getClock().nanoTime(), transactionId, /* readTime= */ requestReadTime, @@ -191,9 +185,6 @@ public ApiFuture> explain(ExplainOptions options) { .getTraceUtil() .startSpan(TelemetryConstants.METHOD_NAME_QUERY_GET); - MetricsContext metricsContext = - createMetricsContext(TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN); - try (Scope ignored = span.makeCurrent()) { final SettableApiFuture> result = SettableApiFuture.create(); @@ -253,7 +244,7 @@ public void onCompleted() { }; internalStream( - new MonitoredStreamResponseObserver(observer, metricsContext), + observer, /* startTimeNanos= */ rpcContext.getClock().nanoTime(), /* transactionId= */ null, /* readTime= */ null, @@ -268,40 +259,8 @@ public void onCompleted() { } } - class MonitoredStreamResponseObserver implements ApiStreamObserver { - private final ApiStreamObserver observer; - private final MetricsContext metricsContext; - private boolean receivedFirstResponse = false; - - // Constructor to initialize with the delegate and MetricsContext - public MonitoredStreamResponseObserver( - ApiStreamObserver observer, MetricsContext metricsContext) { - this.observer = observer; - this.metricsContext = metricsContext; - } - - @Override - public void onNext(RunQueryResponse value) { - if (!receivedFirstResponse) { - receivedFirstResponse = true; - metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); - } - observer.onNext(value); - } - - @Override - public void onError(Throwable t) { - observer.onError(t); - } - - @Override - public void onCompleted() { - observer.onCompleted(); - } - } - void internalStream( - final MonitoredStreamResponseObserver streamResponseObserver, + final ApiStreamObserver runQueryResponseObserver, final long startTimeNanos, @Nullable final ByteString transactionId, @Nullable final Timestamp readTime, @@ -321,6 +280,14 @@ void internalStream( .put(ATTRIBUTE_KEY_IS_RETRY_WITH_CURSOR, isRetryRequestWithCursor) .build()); + String methodName = + (transactionId != null) + ? TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY + : (explainOptions != null + ? TelemetryConstants.METHOD_NAME_QUERY_EXPLAIN + : TelemetryConstants.METHOD_NAME_QUERY_GET); + MetricsContext metricsContext = createMetricsContext(methodName); + final AtomicReference lastReceivedDocument = new AtomicReference<>(); ResponseObserver observer = @@ -341,9 +308,10 @@ public void onResponse(RunQueryResponse response) { if (!firstResponse) { firstResponse = true; currentSpan.addEvent(TelemetryConstants.METHOD_NAME_RUN_QUERY + ": First Response"); + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } - streamResponseObserver.onNext(response); + runQueryResponseObserver.onNext(response); if (response.hasDocument()) { numDocuments++; @@ -378,7 +346,7 @@ public void onError(Throwable throwable) { startAfter(cursor) .internalStream( - streamResponseObserver, + runQueryResponseObserver, startTimeNanos, /* transactionId= */ null, options.getRequireConsistency() ? cursor.getReadTime() : null, @@ -388,7 +356,7 @@ public void onError(Throwable throwable) { currentSpan.addEvent( TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Error", Collections.singletonMap("error.message", throwable.toString())); - streamResponseObserver.onError(throwable); + runQueryResponseObserver.onError(throwable); } } @@ -399,7 +367,7 @@ public void onComplete() { currentSpan.addEvent( TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Completed", Collections.singletonMap(ATTRIBUTE_KEY_DOC_COUNT, numDocuments)); - streamResponseObserver.onCompleted(); + runQueryResponseObserver.onCompleted(); } boolean shouldRetry(DocumentSnapshot lastDocument, Throwable t) { diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index a572f43b3..fe278013a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -16,19 +16,11 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METER_NAME; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.*; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.api.gax.tracing.MetricsTracerFactory; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; @@ -77,13 +69,11 @@ OpenTelemetry getOpenTelemetry() { private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); - staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid()); - staticAttributes.put( - METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), TelemetryConstants.FIRESTORE_LIBRARY_NAME); - String pkgVersion = this.getClass().getPackage().getImplementationVersion(); - if (pkgVersion != null) { - staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), pkgVersion); - } + staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID, ClientIdentifier.getClientUid()); + + staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_NAME, FIRESTORE_LIBRARY_NAME); + staticAttributes.put(METRIC_ATTRIBUTE_KEY_SERVICE, FIRESTORE_SERVICE); + return staticAttributes; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index e3109569e..432537f46 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -16,14 +16,8 @@ package com.google.cloud.firestore.telemetry; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.COMMON_ATTRIBUTES; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METER_NAME; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METRICS; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METER_NAME; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METRICS; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS; -import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX; +import static com.google.cloud.firestore.telemetry.TelemetryConstants.*; +import static com.google.cloud.opentelemetry.detection.GCPPlatformDetector.SupportedPlatform.GOOGLE_KUBERNETES_ENGINE; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; @@ -32,9 +26,12 @@ import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreException; import com.google.cloud.firestore.FirestoreOptions; -import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; +import com.google.cloud.opentelemetry.detection.AttributeKeys; +import com.google.cloud.opentelemetry.detection.DetectedPlatform; +import com.google.cloud.opentelemetry.detection.GCPPlatformDetector; import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter; import com.google.cloud.opentelemetry.metric.MetricConfiguration; +import com.google.cloud.opentelemetry.metric.MonitoredResourceDescription; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMap; @@ -43,7 +40,8 @@ import io.grpc.Status; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -52,6 +50,7 @@ import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.MetricReader; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -67,6 +66,8 @@ * `FirestoreOpenTelemetryOptions` in `FirestoreOptions` can be used to configure its behavior. */ class EnabledMetricsUtil implements MetricsUtil { + + private FirestoreOptions firestoreOptions; private BuiltinMetricsProvider defaultMetricsProvider; private BuiltinMetricsProvider customMetricsProvider; private MetricReader metricReader; @@ -74,9 +75,10 @@ class EnabledMetricsUtil implements MetricsUtil { private static final Logger logger = Logger.getLogger(EnabledMetricsUtil.class.getName()); EnabledMetricsUtil(FirestoreOptions firestoreOptions) { + this.firestoreOptions = firestoreOptions; try { - this.defaultMetricsProvider = configureDefaultMetricsProvider(firestoreOptions); - this.customMetricsProvider = configureCustomMetricsProvider(firestoreOptions); + this.defaultMetricsProvider = configureDefaultMetricsProvider(); + this.customMetricsProvider = configureCustomMetricsProvider(); } catch (Exception e) { logger.warning( "Unable to create MetricsUtil object for client side metrics, will skip exporting client side metrics" @@ -84,17 +86,15 @@ class EnabledMetricsUtil implements MetricsUtil { } } - private BuiltinMetricsProvider configureDefaultMetricsProvider( - FirestoreOptions firestoreOptions) { + private BuiltinMetricsProvider configureDefaultMetricsProvider() { OpenTelemetry defaultOpenTelemetry = OpenTelemetry.noop(); if (firestoreOptions.getOpenTelemetryOptions().exportBuiltinMetricsToGoogleCloudMonitoring()) { - String projectId = firestoreOptions.getProjectId(); - if (projectId == null) { + if (firestoreOptions.getProjectId() == null) { logger.warning( "Project ID is null, skipping client side metrics export to Cloud Monitoring."); } else { try { - defaultOpenTelemetry = getDefaultOpenTelemetryInstance(projectId); + defaultOpenTelemetry = getDefaultOpenTelemetryInstance(); } catch (Exception e) { logger.warning( "Unable to create default OpenTelemetry instance for client side metrics, will skip exporting client side metrics to Cloud Monitoring: " @@ -105,8 +105,7 @@ private BuiltinMetricsProvider configureDefaultMetricsProvider( return new BuiltinMetricsProvider(defaultOpenTelemetry); } - private BuiltinMetricsProvider configureCustomMetricsProvider(FirestoreOptions firestoreOptions) - throws IOException { + private BuiltinMetricsProvider configureCustomMetricsProvider() throws IOException { OpenTelemetry customOpenTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry(); if (customOpenTelemetry == null) { @@ -140,7 +139,7 @@ BuiltinMetricsProvider getDefaultMetricsProvider() { * Creates a default {@link OpenTelemetry} instance to collect and export built-in client side * metrics to Google Cloud Monitoring. */ - private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws IOException { + private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder(); // Filter out attributes that are not defined @@ -148,18 +147,48 @@ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws I sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); } + sdkMeterProviderBuilder.setResource(Resource.create(createResourceAttributes())); + + MonitoredResourceDescription monitoredResourceMapping = + new MonitoredResourceDescription(FIRESTORE_RESOURCE_TYPE, FIRESTORE_RESOURCE_LABELS); + MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder() - .setProjectId(projectId) + .setProjectId(firestoreOptions.getProjectId()) // Ignore library info as it is collected by the metric attributes as well .setInstrumentationLibraryLabelsEnabled(false) + .setMonitoredResourceDescription(monitoredResourceMapping) + // .setUseServiceTimeSeries(true) .build()); metricReader = PeriodicMetricReader.create(metricExporter); + sdkMeterProviderBuilder.registerMetricReader(metricReader); + + return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); + } + + Attributes createResourceAttributes() { + AttributesBuilder attributesBuilder = + Attributes.builder() + .put(RESOURCE_KEY_LOCATION, detectClientLocation()) + .put(RESOURCE_KEY_PROJECT, firestoreOptions.getProjectId()) + .put(RESOURCE_KEY_DATABASE, firestoreOptions.getDatabaseId()); + String pkgVersion = this.getClass().getPackage().getImplementationVersion(); + attributesBuilder.put( + RESOURCE_KEY_INSTANCE, "java_" + (pkgVersion != null ? pkgVersion : "unknown")); + + return attributesBuilder.build(); + } - return OpenTelemetrySdk.builder() - .setMeterProvider(sdkMeterProviderBuilder.registerMetricReader(metricReader).build()) - .build(); + private String detectClientLocation() { + GCPPlatformDetector detector = GCPPlatformDetector.DEFAULT_INSTANCE; + DetectedPlatform detectedPlatform = detector.detectPlatform(); + // All platform except GKE uses "cloud_region" for region attribute. + String region = detectedPlatform.getAttributes().get("cloud_region"); + if (detectedPlatform.getSupportedPlatform() == GOOGLE_KUBERNETES_ENGINE) { + region = detectedPlatform.getAttributes().get(AttributeKeys.GKE_LOCATION_TYPE_REGION); + } + return region == null ? "global" : region; } private static Map getAllViews() { @@ -175,8 +204,7 @@ private static void defineView( InstrumentSelector.builder().setMeterName(meter).setName(METRIC_PREFIX + "/" + id).build(); Set attributesFilter = ImmutableSet.builder() - .addAll( - COMMON_ATTRIBUTES.stream().map(AttributeKey::getKey).collect(Collectors.toSet())) + .addAll(COMMON_ATTRIBUTES.stream().collect(Collectors.toSet())) .build(); View view = View.builder().setAttributeFilter(attributesFilter).build(); @@ -277,8 +305,8 @@ private void recordCounter(MetricType metric, String status) { private Map createAttributes(String status, String methodName) { Map attributes = new HashMap<>(); - attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName); - attributes.put(METRIC_ATTRIBUTE_KEY_STATUS.getKey(), status); + attributes.put(METRIC_ATTRIBUTE_KEY_METHOD, methodName); + attributes.put(METRIC_ATTRIBUTE_KEY_STATUS, status); return attributes; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index c941a7ac9..7acb92656 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -19,7 +19,6 @@ import com.google.api.core.InternalApi; import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder; import com.google.common.collect.ImmutableSet; -import io.opentelemetry.api.common.AttributeKey; import java.util.Set; /** Constants used for telemetry in the Firestore SDK. */ @@ -55,36 +54,45 @@ public interface TelemetryConstants { String METHOD_NAME_BULK_WRITER_COMMIT = "BulkWriter.Commit"; String METHOD_NAME_RUN_TRANSACTION = "RunTransaction"; - // OpenTelemetry built-in metrics constants - String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw"; // TODO(metrics): change to firestore.googleapis.com String METRIC_PREFIX = "custom.googleapis.com/internal/client"; String FIRESTORE_METER_NAME = "java_firestore"; String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; String FIRESTORE_LIBRARY_NAME = "com.google.cloud.firestore"; - // Monitored resource keys for labels - String RESOURCE_KEY_RESOURCE_CONTAINER = "resource_container"; + String FIRESTORE_SERVICE = "firestore V1"; + + // Monitored resource + String FIRESTORE_RESOURCE_TYPE = + "firestore.googleapis.com/Database"; // or maybe "firestore.googleapis.com/Database" String RESOURCE_KEY_LOCATION = "location"; - String RESOURCE_KEY_DATABASE_ID = "database_id"; + String RESOURCE_KEY_INSTANCE = "instance"; + String RESOURCE_KEY_DATABASE = "database"; + String RESOURCE_KEY_PROJECT = "project"; Set FIRESTORE_RESOURCE_LABELS = ImmutableSet.of( - RESOURCE_KEY_RESOURCE_CONTAINER, RESOURCE_KEY_LOCATION, RESOURCE_KEY_DATABASE_ID); + RESOURCE_KEY_LOCATION, + RESOURCE_KEY_INSTANCE, + RESOURCE_KEY_DATABASE, + RESOURCE_KEY_PROJECT); // Metric attribute keys for labels - AttributeKey METRIC_ATTRIBUTE_KEY_METHOD = AttributeKey.stringKey("method"); - AttributeKey METRIC_ATTRIBUTE_KEY_STATUS = AttributeKey.stringKey("status"); - AttributeKey METRIC_ATTRIBUTE_KEY_LIBRARY_NAME = AttributeKey.stringKey("library_name"); - AttributeKey METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION = - AttributeKey.stringKey("library_version"); - AttributeKey METRIC_ATTRIBUTE_KEY_CLIENT_UID = AttributeKey.stringKey("client_uid"); - Set COMMON_ATTRIBUTES = + String METRIC_ATTRIBUTE_KEY_METHOD = "method"; + String METRIC_ATTRIBUTE_KEY_STATUS = "status"; + String METRIC_ATTRIBUTE_KEY_LIBRARY_NAME = "library_name"; + String METRIC_ATTRIBUTE_KEY_CLIENT_UID = "client_uid"; + String METRIC_ATTRIBUTE_KEY_SERVICE = "service"; + Set COMMON_ATTRIBUTES = ImmutableSet.of( METRIC_ATTRIBUTE_KEY_CLIENT_UID, METRIC_ATTRIBUTE_KEY_LIBRARY_NAME, - METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION, METRIC_ATTRIBUTE_KEY_STATUS, - METRIC_ATTRIBUTE_KEY_METHOD); + METRIC_ATTRIBUTE_KEY_METHOD, + METRIC_ATTRIBUTE_KEY_SERVICE, + RESOURCE_KEY_LOCATION, + RESOURCE_KEY_INSTANCE, + RESOURCE_KEY_DATABASE, + RESOURCE_KEY_PROJECT); // Metric names String METRIC_NAME_OPERATION_LATENCY = "operation_latency"; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index e4f330d4d..77069f4ab 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -31,6 +31,7 @@ import com.google.cloud.firestore.telemetry.TelemetryConstants; import com.google.common.base.Preconditions; import io.grpc.Status; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.sdk.OpenTelemetrySdk; @@ -66,10 +67,9 @@ public class ITMetricsTest { protected Firestore firestore; - private static Attributes expectedBaseAttributes; + private static Attributes baseAttributes; private final String ClientUid = ClientIdentifier.getClientUid(); - private final String libraryVersion = this.getClass().getPackage().getImplementationVersion(); @Rule public TestName testName = new TestName(); @@ -81,7 +81,7 @@ public void setup() { Preconditions.checkNotNull( firestore, "Error instantiating Firestore. Check that the service account credentials were properly set."); - expectedBaseAttributes = buildExpectedAttributes(); + baseAttributes = buildBaseAttributes(); } private OpenTelemetrySdk setupOpenTelemetrySdk() { @@ -98,16 +98,15 @@ private Firestore setupFirestoreService() { .getService(); } - private Attributes buildExpectedAttributes() { + private Attributes buildBaseAttributes() { AttributesBuilder attributesBuilder = Attributes.builder(); attributesBuilder.put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), + TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME, TelemetryConstants.FIRESTORE_LIBRARY_NAME); - attributesBuilder.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientUid); - if (libraryVersion != null) { - attributesBuilder.put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), libraryVersion); - } + attributesBuilder.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID, ClientUid); + attributesBuilder.put( + TelemetryConstants.METRIC_ATTRIBUTE_KEY_SERVICE, TelemetryConstants.FIRESTORE_SERVICE); + return attributesBuilder.build(); } @@ -121,7 +120,7 @@ public void tearDown() { } class MetricInfo { - // The expected number of measurements is called + // The expected number of measurements collected public int count; // Attributes expected to be recorded in the measurements public Attributes attributes; @@ -255,12 +254,6 @@ public void writeBatch() throws Exception { validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 1) - .build(); - assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -285,14 +278,6 @@ public void bulkWriterCommit() throws Exception { validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT, - Status.OK.getCode().toString(), - 1) - .build(); - assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -305,12 +290,6 @@ public void partitionQuery() throws Exception { // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric - Map expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_PARTITION_QUERY, Status.OK.getCode().toString(), 1) - .build(); - assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -322,14 +301,6 @@ public void listCollection() throws Exception { // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric - Map expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS, - Status.OK.getCode().toString(), - 1) - .build(); - assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -341,14 +312,6 @@ public void collectionListDocuments() throws Exception { // Note: pagedCallable requests are not traced at GAX layer // Validate SDK layer metric - Map expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS, - Status.OK.getCode().toString(), - 1) - .build(); - assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -370,14 +333,6 @@ public void docRefSet() throws Exception { validateGaxMetrics(expectedMetrics); // Validate SDK layer metric - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_DOC_REF_SET, Status.OK.getCode().toString(), 1) - .build(); - assertMetricAbsent(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); @@ -463,24 +418,6 @@ public void transaction() throws Exception { .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY, - Status.OK.getCode().toString(), - 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY, - Status.OK.getCode().toString(), - 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, - Status.OK.getCode().toString(), - 1) - .build(); - expectedMetrics = new MetricsExpectationBuilder() .expectMetricData( @@ -628,16 +565,6 @@ public Integer updateCallback(Transaction transaction) throws Exception { .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT, - Status.OK.getCode().toString(), - 1) - .build(); - expectedMetrics = new MetricsExpectationBuilder() .expectMetricData( @@ -687,30 +614,12 @@ public void multipleOperations() throws Exception { .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); - expectedMetrics = - new MetricsExpectationBuilder() - .expectMetricData( - TelemetryConstants.METHOD_NAME_QUERY_GET, Status.OK.getCode().toString(), 2) - .expectMetricData( - TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET, - Status.OK.getCode().toString(), - 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_DOC_REF_SET, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_DOC_REF_DELETE, Status.OK.getCode().toString(), 1) - .expectMetricData( - TelemetryConstants.METHOD_NAME_BATCH_COMMIT, Status.OK.getCode().toString(), 3) - .build(); - assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY); assertMetricAbsent(TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT); } private Attributes buildAttributes(String method, String status) { - return expectedBaseAttributes + return baseAttributes .toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, status) .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) @@ -744,7 +653,10 @@ private void validateMetricData(MetricData metricData, Map e assertThat(points.size()).isEqualTo(expectedMetrics.size()); for (PointData point : points) { - String method = point.getAttributes().get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD); + String method = + point + .getAttributes() + .get(AttributeKey.stringKey(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD)); MetricInfo expectedMetricInfo = expectedMetrics.get(method); if (isHistogram) { assertThat(((HistogramPointData) point).getCount()).isEqualTo(expectedMetricInfo.count); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java index 7a9b46cca..a9cf57b1f 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -292,7 +292,7 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception Mockito.argThat( attributes -> attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) .equals(Status.Code.UNKNOWN.toString()))); Mockito.verify(customProvider) .latencyRecorder( @@ -301,7 +301,7 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception Mockito.argThat( attributes -> attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) .equals(Status.Code.UNKNOWN.toString()))); } @@ -345,7 +345,7 @@ public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception Mockito.argThat( attributes -> attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) .equals(Status.Code.UNKNOWN.toString()))); Mockito.verify(customProvider) .counterRecorder( @@ -354,7 +354,7 @@ public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception Mockito.argThat( attributes -> attributes - .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS.getKey()) + .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) .equals(Status.Code.UNKNOWN.toString()))); } } From ace18e5f384a9e347d1da26c11fa69f63bf556a0 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Fri, 2 May 2025 14:27:21 +0000 Subject: [PATCH 099/103] chore: generate libraries at Fri May 2 14:25:11 UTC 2025 --- .../cloud/firestore/telemetry/EnabledMetricsUtil.java | 3 ++- .../java/com/google/cloud/firestore/it/ITMetricsTest.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 579e910e9..b36cb0969 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -98,7 +98,8 @@ private BuiltinMetricsProvider configureDefaultMetricsProvider() { defaultOpenTelemetry = getDefaultOpenTelemetryInstance(); } catch (Exception e) { logger.warning( - "Unable to create default OpenTelemetry instance for client side metrics, will skip exporting client side metrics to Cloud Monitoring: " + "Unable to create default OpenTelemetry instance for client side metrics, will skip" + + " exporting client side metrics to Cloud Monitoring: " + e); } } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index 77069f4ab..2309ee8a8 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -80,7 +80,8 @@ public void setup() { firestore = setupFirestoreService(); Preconditions.checkNotNull( firestore, - "Error instantiating Firestore. Check that the service account credentials were properly set."); + "Error instantiating Firestore. Check that the service account credentials were properly" + + " set."); baseAttributes = buildBaseAttributes(); } @@ -619,8 +620,7 @@ public void multipleOperations() throws Exception { } private Attributes buildAttributes(String method, String status) { - return baseAttributes - .toBuilder() + return baseAttributes.toBuilder() .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS, status) .put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD, method) .build(); From 77de8344b18503644607847d6ea4404bf6e4f03e Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 14 May 2025 12:59:58 -0400 Subject: [PATCH 100/103] resolve TODO related to Status --- .../telemetry/EnabledMetricsUtil.java | 12 ++++++--- .../cloud/firestore/it/ITMetricsTest.java | 4 +-- .../telemetry/EnabledMetricsUtilTest.java | 25 ++++++++++++------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index b36cb0969..46a160217 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -22,10 +22,12 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; +import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.tracing.ApiTracerFactory; import com.google.cloud.firestore.FirestoreException; import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; import com.google.cloud.opentelemetry.detection.AttributeKeys; import com.google.cloud.opentelemetry.detection.DetectedPlatform; import com.google.cloud.opentelemetry.detection.GCPPlatformDetector; @@ -175,6 +177,7 @@ Attributes createResourceAttributes() { .put(RESOURCE_KEY_LOCATION, detectClientLocation()) .put(RESOURCE_KEY_PROJECT, firestoreOptions.getProjectId()) .put(RESOURCE_KEY_DATABASE, firestoreOptions.getDatabaseId()); + String pkgVersion = this.getClass().getPackage().getImplementationVersion(); attributesBuilder.put( RESOURCE_KEY_INSTANCE, "java_" + (pkgVersion != null ? pkgVersion : "unknown")); @@ -314,11 +317,14 @@ private Map createAttributes(String status, String methodName) { @VisibleForTesting String extractErrorStatus(@Nullable Throwable throwable) { - if (!(throwable instanceof FirestoreException)) { - return Status.Code.UNKNOWN.toString(); + Status status = null; + + if (throwable instanceof FirestoreException) { + status = ((FirestoreException) throwable).getStatus(); + } else if (throwable instanceof ApiException) { + status = FirestoreException.forApiException((ApiException) throwable).getStatus(); } - Status status = ((FirestoreException) throwable).getStatus(); if (status == null) { return Status.Code.UNKNOWN.toString(); } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index 2309ee8a8..faf4d2613 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -511,9 +511,7 @@ public void transactionWithFailure() throws Exception { new MetricsExpectationBuilder() .expectMetricData( TelemetryConstants.METHOD_NAME_RUN_TRANSACTION, - Status.UNKNOWN - .getCode() - .toString(), // TODO(b/305998085):Change this to correct status code + Status.NOT_FOUND.getCode().toString(), 1) .build(); validateSDKMetrics(TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY, expectedMetrics); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java index a9cf57b1f..bdc7ab39b 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtilTest.java @@ -223,13 +223,13 @@ public void extractsErrorStatusFromFirestoreException() { new IllegalStateException("Mock batchWrite failed in test"), GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), false)); + String errorStatus = metricsUtil.extractErrorStatus(firestoreException); - // TODO(b/305998085):Change this to correct status code - assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); + assertThat(errorStatus).isEqualTo(StatusCode.Code.INVALID_ARGUMENT.toString()); } @Test - public void errorStatusSetToUnknownOnNonFirestoreException() { + public void extractsErrorStatusFromApiException() { EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); ApiException apiException = new ApiException( @@ -237,6 +237,14 @@ public void errorStatusSetToUnknownOnNonFirestoreException() { GrpcStatusCode.of(Status.Code.INVALID_ARGUMENT), false); String errorStatus = metricsUtil.extractErrorStatus(apiException); + assertThat(errorStatus).isEqualTo(StatusCode.Code.INVALID_ARGUMENT.toString()); + } + + @Test + public void errorStatusSetToUnknownOnNotRecognizedException() { + EnabledMetricsUtil metricsUtil = newEnabledMetricsUtil(); + Throwable t = new Throwable(new IllegalStateException("Mock batchWrite failed in test")); + String errorStatus = metricsUtil.extractErrorStatus(t); assertThat(errorStatus).isEqualTo(StatusCode.Code.UNKNOWN.toString()); } @@ -284,7 +292,7 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); context.recordLatencyAtFuture(MetricType.FIRST_RESPONSE_LATENCY, future); - // TODO(b/305998085):Change this to correct status code + // TODO(b/376473320):Change this to correct status code Mockito.verify(defaultProvider) .latencyRecorder( Mockito.eq(MetricType.FIRST_RESPONSE_LATENCY), @@ -293,7 +301,7 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception attributes -> attributes .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) - .equals(Status.Code.UNKNOWN.toString()))); + .equals(Status.Code.INVALID_ARGUMENT.toString()))); Mockito.verify(customProvider) .latencyRecorder( Mockito.eq(MetricType.FIRST_RESPONSE_LATENCY), @@ -302,7 +310,7 @@ public void recordLatencyCalledWhenFutureIsCompletedWithError() throws Exception attributes -> attributes .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) - .equals(Status.Code.UNKNOWN.toString()))); + .equals(Status.Code.INVALID_ARGUMENT.toString()))); } @Test @@ -337,7 +345,6 @@ public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception ApiFuture future = ApiFutures.immediateFailedFuture(firestoreException); context.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, future); - // TODO(b/305998085):Change this to correct status code Mockito.verify(defaultProvider) .counterRecorder( Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), @@ -346,7 +353,7 @@ public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception attributes -> attributes .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) - .equals(Status.Code.UNKNOWN.toString()))); + .equals(Status.Code.INVALID_ARGUMENT.toString()))); Mockito.verify(customProvider) .counterRecorder( Mockito.eq(MetricType.TRANSACTION_ATTEMPT_COUNT), @@ -355,6 +362,6 @@ public void recordCounterCalledWhenFutureIsCompletedWithError() throws Exception attributes -> attributes .get(TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS) - .equals(Status.Code.UNKNOWN.toString()))); + .equals(Status.Code.INVALID_ARGUMENT.toString()))); } } From ae8e16d09a903931d05b26193653f7245568d1a7 Mon Sep 17 00:00:00 2001 From: cloud-java-bot Date: Tue, 17 Jun 2025 17:44:16 +0000 Subject: [PATCH 101/103] chore: generate libraries at Tue Jun 17 17:42:08 UTC 2025 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 707edc829..4a3a690e6 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file: com.google.cloud libraries-bom - 26.61.0 + 26.62.0 pom import @@ -41,7 +41,7 @@ If you are using Maven without the BOM, add this to your dependencies: com.google.cloud google-cloud-firestore - 3.31.5 + 3.31.6 ``` From 94e930a500909bc61cab199cd9e1b3b4d931e74c Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 17 Jun 2025 16:58:29 -0400 Subject: [PATCH 102/103] add TODO comments --- .../telemetry/BuiltinMetricsProvider.java | 2 -- .../firestore/telemetry/EnabledMetricsUtil.java | 9 ++++++--- .../firestore/telemetry/TelemetryConstants.java | 17 +++++++++++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java index fe278013a..abbcd4763 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -70,8 +70,6 @@ OpenTelemetry getOpenTelemetry() { private Map createStaticAttributes() { Map staticAttributes = new HashMap<>(); staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID, ClientIdentifier.getClientUid()); - - staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_NAME, FIRESTORE_LIBRARY_NAME); staticAttributes.put(METRIC_ATTRIBUTE_KEY_SERVICE, FIRESTORE_SERVICE); return staticAttributes; diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java index 46a160217..48513aadc 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -156,14 +156,16 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException { MonitoredResourceDescription monitoredResourceMapping = new MonitoredResourceDescription(FIRESTORE_RESOURCE_TYPE, FIRESTORE_RESOURCE_LABELS); + // TODO: uncomment the configuration below MetricExporter metricExporter = GoogleCloudMetricExporter.createWithConfiguration( MetricConfiguration.builder() .setProjectId(firestoreOptions.getProjectId()) + // .setPrefix("firestore.googleapis.com") // Ignore library info as it is collected by the metric attributes as well .setInstrumentationLibraryLabelsEnabled(false) - .setMonitoredResourceDescription(monitoredResourceMapping) - // .setUseServiceTimeSeries(true) + // .setMonitoredResourceDescription(monitoredResourceMapping) + // .setUseServiceTimeSeries(true) .build()); metricReader = PeriodicMetricReader.create(metricExporter); sdkMeterProviderBuilder.registerMetricReader(metricReader); @@ -176,7 +178,8 @@ Attributes createResourceAttributes() { Attributes.builder() .put(RESOURCE_KEY_LOCATION, detectClientLocation()) .put(RESOURCE_KEY_PROJECT, firestoreOptions.getProjectId()) - .put(RESOURCE_KEY_DATABASE, firestoreOptions.getDatabaseId()); + .put(RESOURCE_KEY_DATABASE, firestoreOptions.getDatabaseId()) + .put(RESOURCE_KEY_UID, ClientIdentifier.getClientUid()); String pkgVersion = this.getClass().getPackage().getImplementationVersion(); attributesBuilder.put( diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java index 7acb92656..eae7db6a5 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -60,39 +60,44 @@ public interface TelemetryConstants { String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME; String FIRESTORE_LIBRARY_NAME = "com.google.cloud.firestore"; - String FIRESTORE_SERVICE = "firestore V1"; + String FIRESTORE_SERVICE = "firestore v1"; // Monitored resource + // TODO: check the monitored resource type with jimit String FIRESTORE_RESOURCE_TYPE = - "firestore.googleapis.com/Database"; // or maybe "firestore.googleapis.com/Database" + // "datastore_request"; + // "datastore_client_raw"; + "firestore.googleapis.com/Database"; String RESOURCE_KEY_LOCATION = "location"; String RESOURCE_KEY_INSTANCE = "instance"; String RESOURCE_KEY_DATABASE = "database"; String RESOURCE_KEY_PROJECT = "project"; + String RESOURCE_KEY_UID = "uid"; + Set FIRESTORE_RESOURCE_LABELS = ImmutableSet.of( RESOURCE_KEY_LOCATION, RESOURCE_KEY_INSTANCE, RESOURCE_KEY_DATABASE, - RESOURCE_KEY_PROJECT); + RESOURCE_KEY_PROJECT, + RESOURCE_KEY_UID); // Metric attribute keys for labels String METRIC_ATTRIBUTE_KEY_METHOD = "method"; String METRIC_ATTRIBUTE_KEY_STATUS = "status"; - String METRIC_ATTRIBUTE_KEY_LIBRARY_NAME = "library_name"; String METRIC_ATTRIBUTE_KEY_CLIENT_UID = "client_uid"; String METRIC_ATTRIBUTE_KEY_SERVICE = "service"; Set COMMON_ATTRIBUTES = ImmutableSet.of( METRIC_ATTRIBUTE_KEY_CLIENT_UID, - METRIC_ATTRIBUTE_KEY_LIBRARY_NAME, METRIC_ATTRIBUTE_KEY_STATUS, METRIC_ATTRIBUTE_KEY_METHOD, METRIC_ATTRIBUTE_KEY_SERVICE, RESOURCE_KEY_LOCATION, RESOURCE_KEY_INSTANCE, RESOURCE_KEY_DATABASE, - RESOURCE_KEY_PROJECT); + RESOURCE_KEY_PROJECT, + RESOURCE_KEY_UID); // Metric names String METRIC_NAME_OPERATION_LATENCY = "operation_latency"; From 37b876fdea342bab00e8676feb2d93b9433f5e9d Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:11:30 -0400 Subject: [PATCH 103/103] Update ITMetricsTest.java --- .../test/java/com/google/cloud/firestore/it/ITMetricsTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java index faf4d2613..29819cc5f 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITMetricsTest.java @@ -101,9 +101,6 @@ private Firestore setupFirestoreService() { private Attributes buildBaseAttributes() { AttributesBuilder attributesBuilder = Attributes.builder(); - attributesBuilder.put( - TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME, - TelemetryConstants.FIRESTORE_LIBRARY_NAME); attributesBuilder.put(TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID, ClientUid); attributesBuilder.put( TelemetryConstants.METRIC_ATTRIBUTE_KEY_SERVICE, TelemetryConstants.FIRESTORE_SERVICE);