diff --git a/.github/workflows/hermetic_library_generation.yaml b/.github/workflows/hermetic_library_generation.yaml index 9f1a24bd6..46b80edc1 100644 --- a/.github/workflows/hermetic_library_generation.yaml +++ b/.github/workflows/hermetic_library_generation.yaml @@ -37,7 +37,7 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }} - - uses: googleapis/sdk-platform-java/.github/scripts@v2.46.1 + - uses: googleapis/sdk-platform-java/.github/scripts@v2.49.0 if: env.SHOULD_RUN == 'true' with: base_ref: ${{ github.base_ref }} diff --git a/.github/workflows/unmanaged-dependency-check.yaml b/.github/workflows/unmanaged-dependency-check.yaml index 54b4586ba..b9df243af 100644 --- a/.github/workflows/unmanaged-dependency-check.yaml +++ b/.github/workflows/unmanaged-dependency-check.yaml @@ -14,6 +14,6 @@ jobs: shell: bash run: .kokoro/build.sh - name: Unmanaged dependency check - uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.36.1 + uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.39.0 with: bom-path: google-cloud-firestore-bom/pom.xml diff --git a/.kokoro/presubmit/graalvm-native-17.cfg b/.kokoro/presubmit/graalvm-native-17.cfg index 0b57b9330..f4570c657 100644 --- a/.kokoro/presubmit/graalvm-native-17.cfg +++ b/.kokoro/presubmit/graalvm-native-17.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.36.1" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.39.0" } env_vars: { diff --git a/.kokoro/presubmit/graalvm-native.cfg b/.kokoro/presubmit/graalvm-native.cfg index e00ff1103..e8a2b9a3a 100644 --- a/.kokoro/presubmit/graalvm-native.cfg +++ b/.kokoro/presubmit/graalvm-native.cfg @@ -3,7 +3,7 @@ # Configure the docker image for kokoro-trampoline. env_vars: { key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.36.1" + value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.39.0" } env_vars: { diff --git a/CHANGELOG.md b/CHANGELOG.md index d59955cbf..a3eac07be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,64 @@ # Changelog +## [3.28.0](https://github.com/googleapis/java-firestore/compare/v3.27.4...v3.28.0) (2024-10-29) + + +### Features + +* Improve Logging ([#1892](https://github.com/googleapis/java-firestore/issues/1892)) ([e74457a](https://github.com/googleapis/java-firestore/commit/e74457a1448189978e791fb3efe76d5fe8370ff7)) + + +### Dependencies + +* Update dependency com.google.api.grpc:proto-google-cloud-trace-v1 to v2.53.0 ([#1913](https://github.com/googleapis/java-firestore/issues/1913)) ([cbaa7a7](https://github.com/googleapis/java-firestore/commit/cbaa7a7c44c82a173194e3c36289bc6b867b93b7)) +* Update dependency com.google.cloud:google-cloud-trace to v2.53.0 ([#1914](https://github.com/googleapis/java-firestore/issues/1914)) ([d3834e1](https://github.com/googleapis/java-firestore/commit/d3834e10b509b3d5c0e1a55318001f9eb80e7f45)) + +## [3.27.4](https://github.com/googleapis/java-firestore/compare/v3.27.3...v3.27.4) (2024-10-28) + + +### Bug Fixes + +* Add the deprecation notice for tracing enable/disable option. ([#1866](https://github.com/googleapis/java-firestore/issues/1866)) ([d213245](https://github.com/googleapis/java-firestore/commit/d213245a77b42eca110ef579a0c9fa7108357717)) +* **deps:** Update the Java code generator (gapic-generator-java) to 2.49.0 ([0bd75f1](https://github.com/googleapis/java-firestore/commit/0bd75f11587a55eb380b094f2900f2f847a7a103)) + + +### Dependencies + +* Update beam.version to v2.60.0 ([#1894](https://github.com/googleapis/java-firestore/issues/1894)) ([434e6e0](https://github.com/googleapis/java-firestore/commit/434e6e0b9a738f9ac08788dbf5dd94a0678733aa)) +* Update googleapis/sdk-platform-java action to v2.48.0 ([#1899](https://github.com/googleapis/java-firestore/issues/1899)) ([eaf3c0c](https://github.com/googleapis/java-firestore/commit/eaf3c0cfe3dd4dce8afeb9fbb8e2455492c31443)) +* Update sdk-platform-java dependencies ([#1901](https://github.com/googleapis/java-firestore/issues/1901)) ([a698223](https://github.com/googleapis/java-firestore/commit/a698223668a5886c65f00760051b8e022d18559c)) +* Update sdk-platform-java dependencies ([#1906](https://github.com/googleapis/java-firestore/issues/1906)) ([d70f77a](https://github.com/googleapis/java-firestore/commit/d70f77a85c2e17930626674ff292713a276d71ce)) + +## [3.27.3](https://github.com/googleapis/java-firestore/compare/v3.27.2...v3.27.3) (2024-10-16) + + +### Dependencies + +* Update opentelemetry.version to v1.43.0 ([#1884](https://github.com/googleapis/java-firestore/issues/1884)) ([f07ac99](https://github.com/googleapis/java-firestore/commit/f07ac990fece6d59d898419d1cca0b2a91a64248)) + +## [3.27.2](https://github.com/googleapis/java-firestore/compare/v3.27.1...v3.27.2) (2024-10-10) + + +### Dependencies + +* Update dependency com.google.api.grpc:proto-google-cloud-trace-v1 to v2.52.0 ([#1879](https://github.com/googleapis/java-firestore/issues/1879)) ([33cdd41](https://github.com/googleapis/java-firestore/commit/33cdd41949739d37c66830d9b85757d19dbbe31e)) +* Update dependency com.google.cloud:google-cloud-trace to v2.52.0 ([#1880](https://github.com/googleapis/java-firestore/issues/1880)) ([2827f77](https://github.com/googleapis/java-firestore/commit/2827f777bf08bdda3599f1a81193e6957533aa19)) +* Update dependency com.google.cloud.opentelemetry:exporter-trace to v0.33.0 ([#1876](https://github.com/googleapis/java-firestore/issues/1876)) ([b3fba1f](https://github.com/googleapis/java-firestore/commit/b3fba1f58317e00573f37060bd082283ce9ec2ed)) + +## [3.27.1](https://github.com/googleapis/java-firestore/compare/v3.27.0...v3.27.1) (2024-10-07) + + +### Bug Fixes + +* **deps:** Update the Java code generator (gapic-generator-java) to 2.47.0 ([c606cea](https://github.com/googleapis/java-firestore/commit/c606ceaccd61cede8799d12b074682a15a03ccff)) +* Update to Java 11, since runtime doesn't support 8. ([#1867](https://github.com/googleapis/java-firestore/issues/1867)) ([723c7cc](https://github.com/googleapis/java-firestore/commit/723c7ccc783b2f56ca72867cd741df197a9f68d7)) + + +### Dependencies + +* Update dependency com.google.cloud:sdk-platform-java-config to v3.37.0 ([#1871](https://github.com/googleapis/java-firestore/issues/1871)) ([8503a3e](https://github.com/googleapis/java-firestore/commit/8503a3e6badc31eaae4d318925097d010d9d73e4)) +* Update googleapis/sdk-platform-java action to v2.47.0 ([#1870](https://github.com/googleapis/java-firestore/issues/1870)) ([971b164](https://github.com/googleapis/java-firestore/commit/971b164104fe072255a26dfaa4e93958fb43b706)) + ## [3.27.0](https://github.com/googleapis/java-firestore/compare/v3.26.5...v3.27.0) (2024-10-02) diff --git a/README.md b/README.md index a2308f34e..dce2c6bfd 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.48.0 + 26.49.0 pom import @@ -36,13 +36,12 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file: If you are using Maven without the BOM, add this to your dependencies: - ```xml com.google.cloud google-cloud-firestore - 3.27.0 + 3.27.3 ``` @@ -50,22 +49,21 @@ If you are using Maven without the BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.47.0') +implementation platform('com.google.cloud:libraries-bom:26.49.0') 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.27.0' +implementation 'com.google.cloud:google-cloud-firestore:3.27.3' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.27.0" +libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "3.27.3" ``` - ## Authentication @@ -221,7 +219,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.27.0 +[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-firestore/3.27.3 [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/generation_config.yaml b/generation_config.yaml index f6faade4b..070aa2045 100644 --- a/generation_config.yaml +++ b/generation_config.yaml @@ -1,6 +1,6 @@ -gapic_generator_version: 2.46.1 -googleapis_commitish: ce31830ddebce4442c987c7e5daff16d4905f7a3 -libraries_bom_version: 26.47.0 +gapic_generator_version: 2.49.0 +googleapis_commitish: 1f2e5aab4f95b9bd38dd1ac8c7486657f93c1975 +libraries_bom_version: 26.50.0 libraries: - api_shortname: firestore name_pretty: Cloud Firestore diff --git a/google-cloud-firestore-admin/pom.xml b/google-cloud-firestore-admin/pom.xml index 9634062d4..9ae4e0f4c 100644 --- a/google-cloud-firestore-admin/pom.xml +++ b/google-cloud-firestore-admin/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 google-cloud-firestore-admin - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT jar Google Cloud Firestore Admin Client https://github.com/googleapis/java-firestore @@ -14,7 +14,7 @@ com.google.cloud google-cloud-firestore-parent - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/google-cloud-firestore-admin/src/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json b/google-cloud-firestore-admin/src/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json index eee0e21ed..de6d04910 100644 --- a/google-cloud-firestore-admin/src/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json +++ b/google-cloud-firestore-admin/src/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json @@ -395,6 +395,24 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.api.SelectiveGapicGeneration", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.api.SelectiveGapicGeneration$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.cloud.location.GetLocationRequest", "queryAllDeclaredConstructors": true, diff --git a/google-cloud-firestore-bom/pom.xml b/google-cloud-firestore-bom/pom.xml index b56a470c2..e94832ba9 100644 --- a/google-cloud-firestore-bom/pom.xml +++ b/google-cloud-firestore-bom/pom.xml @@ -3,12 +3,12 @@ 4.0.0 com.google.cloud google-cloud-firestore-bom - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT pom com.google.cloud sdk-platform-java-config - 3.36.1 + 3.39.0 Google Cloud Firestore BOM @@ -52,37 +52,37 @@ com.google.cloud google-cloud-firestore - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.cloud google-cloud-firestore-admin - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-admin-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.api.grpc proto-google-cloud-firestore-admin-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.api.grpc proto-google-cloud-firestore-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.cloud proto-google-cloud-firestore-bundle-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index 23fa84422..0455ac95e 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -326,4 +326,25 @@ com.google.cloud.firestore.FirestoreOpenTelemetryOptions$Builder setTracingEnabled(boolean) + + com/google/cloud/firestore/StreamableQuery + 7009 + void internalStream(*) + + + + + 6011 + com/google/cloud/firestore/telemetry/TraceUtil + SPAN_NAME_* + + + + + 7005 + com/google/cloud/firestore/StreamableQuery + void internalStream(*) + * + +>>>>>>> origin/main diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 94a5f599f..fc1ed3b02 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/pom.xml @@ -2,7 +2,7 @@ 4.0.0 google-cloud-firestore - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT jar Google Cloud Firestore https://github.com/googleapis/java-firestore @@ -12,11 +12,11 @@ com.google.cloud google-cloud-firestore-parent - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT google-cloud-firestore - 1.42.1 + 1.43.0 @@ -119,6 +119,18 @@ io.opentelemetry.instrumentation opentelemetry-grpc-1.6 + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-sdk-metrics + + + com.google.cloud.opentelemetry + exporter-metrics + @@ -181,12 +193,6 @@ test - - io.opentelemetry - opentelemetry-sdk - ${opentelemetry.version} - test - io.opentelemetry opentelemetry-sdk-testing @@ -214,21 +220,7 @@ com.google.cloud.opentelemetry exporter-trace - 0.32.0 - test - - - - - com.google.api.grpc - proto-google-cloud-trace-v1 - 2.51.0 - test - - - com.google.cloud.opentelemetry - exporter-trace - 0.32.0 + 0.33.0 test @@ -236,13 +228,13 @@ com.google.api.grpc proto-google-cloud-trace-v1 - 2.51.0 + 2.53.0 test com.google.cloud google-cloud-trace - 2.51.0 + 2.53.0 test 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 e1f5aacd9..89702e423 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; @@ -27,6 +27,9 @@ 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.TelemetryConstants.MetricType; import com.google.cloud.firestore.telemetry.TraceUtil; import com.google.cloud.firestore.telemetry.TraceUtil.Scope; import com.google.cloud.firestore.v1.FirestoreSettings; @@ -70,6 +73,11 @@ private TraceUtil getTraceUtil() { return query.getFirestore().getOptions().getTraceUtil(); } + @Nonnull + private MetricsContext createMetricsContext(String method) { + return query.getFirestore().getOptions().getMetricsUtil().createMetricsContext(method); + } + /** Returns the query whose aggregations will be calculated by this object. */ @Nonnull public Query getQuery() { @@ -96,14 +104,20 @@ 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 = + 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); @@ -121,14 +135,22 @@ 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 = + 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); @@ -165,14 +187,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 @@ -198,15 +223,29 @@ ApiFuture getFuture() { return future; } - protected void setFuture(T value) { - future.set(value); + void deliverFirstResponse() { + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } void deliverError(Throwable throwable) { future.setException(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, 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.recordLatency(MetricType.END_TO_END_LATENCY); + } catch (Exception error) { + deliverError(error); + } + } + + abstract T processResult( @Nullable Map serverData, Timestamp readTime, @Nullable ExplainMetrics metrics); @@ -216,24 +255,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)); } } @@ -245,8 +283,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; } @@ -257,14 +296,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 @@ -273,7 +311,7 @@ void deliverResult( AggregateQuery.this, readTime, convertServerAggregateFieldsMapToClientAggregateFieldsMap(serverData)); - setFuture(new ExplainResults<>(metrics, snapshot)); + return new ExplainResults<>(metrics, snapshot); } } @@ -284,6 +322,7 @@ private final class AggregateQueryResponseObserver @Nullable private Map aggregateFieldsMap = null; @Nullable private ExplainMetrics metrics = null; private int attempt; + private boolean firstResponse = false; AggregateQueryResponseObserver(ResponseDeliverer responseDeliverer, int attempt) { this.responseDeliverer = responseDeliverer; @@ -302,15 +341,20 @@ 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 public void onResponse(RunAggregationQueryResponse response) { + if (!firstResponse) { + firstResponse = true; + responseDeliverer.deliverFirstResponse(); + } + 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()); } @@ -339,7 +383,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.toString())); runQuery(responseDeliverer, attempt + 1); @@ -347,7 +391,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.toString())); 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 bc881979c..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 @@ -26,6 +26,9 @@ 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.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; @@ -912,8 +915,15 @@ 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(TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT); + try (Scope ignored = span.makeCurrent()) { ApiFuture result = batch.bulkCommit(); result.addListener( @@ -926,8 +936,10 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) { }, 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 3026e5183..159f7077d 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,9 @@ 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.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; @@ -109,7 +112,15 @@ 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(TelemetryConstants.METHOD_NAME_PARTITION_QUERY); + try (Scope ignored = span.makeCurrent()) { ApiFuture> result = ApiFutures.transform( @@ -127,12 +138,15 @@ 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; } } @@ -174,4 +188,10 @@ private void consumePartitions( } consumer.apply(new QueryPartition(partitionQuery, lastCursor, null)); } + + @SuppressWarnings("MethodDoesntCallSuperMethod") + @Override + public String toString() { + return String.format("CollectionGroup{partitionQuery=%s, options=%s}", partitionQuery, options); + } } 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 e1c2841d6..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 @@ -24,6 +24,9 @@ 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.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; @@ -134,7 +137,15 @@ 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(TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS); + try (Scope ignored = span.makeCurrent()) { ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder(); request.setParent(options.getParentPath().toString()); @@ -174,12 +185,15 @@ 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; } } @@ -200,7 +214,15 @@ 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(TelemetryConstants.METHOD_NAME_COL_REF_ADD); + try (Scope ignored = span.makeCurrent()) { final DocumentReference documentReference = document(); ApiFuture createFuture = documentReference.create(fields); @@ -208,9 +230,11 @@ 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/DocumentChange.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentChange.java index cb2e8e0d5..5bfe91e74 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentChange.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentChange.java @@ -114,4 +114,11 @@ public boolean equals(Object obj) { public int hashCode() { return Objects.hash(type, document, oldIndex, newIndex); } + + @Override + public String toString() { + return String.format( + "DocumentChange{type=%s, document=%s, oldIndex=%d, newIndex=%d}", + type, document, oldIndex, newIndex); + } } 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..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 @@ -21,6 +21,10 @@ 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.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; @@ -139,6 +143,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. @@ -148,14 +157,20 @@ private TraceUtil getTraceUtil() { */ @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(TelemetryConstants.METHOD_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.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; } } @@ -169,14 +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); + 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, 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; } } @@ -190,14 +210,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); + 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(); 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; } } @@ -214,14 +239,19 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -235,14 +265,19 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -258,14 +293,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); + 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(); 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; } } @@ -279,14 +319,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); + 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(); 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; } } @@ -301,15 +346,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); + 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(); 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; } } @@ -326,15 +376,20 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -351,15 +406,20 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -380,16 +440,21 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -410,16 +475,21 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -432,14 +502,19 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -451,14 +526,19 @@ 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(TelemetryConstants.METHOD_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.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; } } @@ -472,13 +552,18 @@ 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(TelemetryConstants.METHOD_NAME_DOC_REF_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; } } @@ -493,14 +578,19 @@ 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(TelemetryConstants.METHOD_NAME_DOC_REF_GET); + try (Scope ignored = span.makeCurrent()) { 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; } } @@ -513,7 +603,12 @@ 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(TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS); + try (Scope ignored = span.makeCurrent()) { ListCollectionIdsRequest.Builder request = ListCollectionIdsRequest.newBuilder(); request.setParent(path.toString()); @@ -547,9 +642,11 @@ 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 48c691466..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 @@ -32,6 +32,9 @@ 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.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; @@ -227,6 +230,14 @@ void getAll( // that we receive from the server. final int NUM_RESPONSES_PER_TRACE_EVENT = 100; + MetricsContext metricsContext = + getOptions() + .getMetricsUtil() + .createMetricsContext( + transactionId == null + ? TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_GET_ALL + : TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_TRANSACTIONAL); + ResponseObserver responseObserver = new ResponseObserver() { int numResponses = 0; @@ -237,7 +248,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) @@ -253,12 +264,15 @@ public void onResponse(BatchGetDocumentsResponse response) { 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.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } 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"); @@ -298,6 +312,7 @@ 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); } @@ -308,11 +323,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.recordLatency(MetricType.END_TO_END_LATENCY); apiStreamObserver.onCompleted(); } }; @@ -427,16 +443,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(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 + // need a runner. + result = + 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. + 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; } @Nonnull 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 175a20c1a..6fe0c4375 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 @@ -27,12 +27,19 @@ */ @BetaApi public class FirestoreOpenTelemetryOptions { + private final boolean exportBuiltinMetricsToGoogleCloudMonitoring; private final @Nullable OpenTelemetry openTelemetry; FirestoreOpenTelemetryOptions(Builder builder) { + this.exportBuiltinMetricsToGoogleCloudMonitoring = + builder.exportBuiltinMetricsToGoogleCloudMonitoring; this.openTelemetry = builder.openTelemetry; } + public boolean exportBuiltinMetricsToGoogleCloudMonitoring() { + return exportBuiltinMetricsToGoogleCloudMonitoring; + } + public OpenTelemetry getOpenTelemetry() { return openTelemetry; } @@ -48,13 +55,18 @@ public static FirestoreOpenTelemetryOptions.Builder newBuilder() { } public static class Builder { + private boolean exportBuiltinMetricsToGoogleCloudMonitoring; @Nullable private OpenTelemetry openTelemetry; private Builder() { + // TODO(metrics): default this to true when feature is ready + exportBuiltinMetricsToGoogleCloudMonitoring = false; openTelemetry = null; } private Builder(FirestoreOpenTelemetryOptions options) { + this.exportBuiltinMetricsToGoogleCloudMonitoring = + options.exportBuiltinMetricsToGoogleCloudMonitoring; this.openTelemetry = options.openTelemetry; } @@ -63,6 +75,20 @@ 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 + * + * @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/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java index b80b9dacd..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 @@ -24,12 +24,15 @@ 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; 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; import com.google.common.collect.ImmutableMap; @@ -37,6 +40,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; @@ -64,6 +68,7 @@ public final class FirestoreOptions extends ServiceOptions 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. + metricsUtil.addMetricsTracerFactory(apiTracerFactories); + + if (apiTracerFactories.isEmpty()) { + return null; + } + return new CompositeApiTracerFactory(apiTracerFactories); + } + public String getDatabaseId() { return databaseId; } @@ -127,6 +149,11 @@ com.google.cloud.firestore.telemetry.TraceUtil getTraceUtil() { return traceUtil; } + @Nonnull + com.google.cloud.firestore.telemetry.MetricsUtil getMetricsUtil() { + return metricsUtil; + } + @BetaApi @Nonnull public FirestoreOpenTelemetryOptions getOpenTelemetryOptions() { @@ -325,6 +352,9 @@ protected FirestoreOptions(Builder builder) { ? builder.databaseId : FirestoreDefaults.INSTANCE.getDatabaseId(); + // Set up the `MetricsUtil` instance after the database ID has been set. + this.metricsUtil = MetricsUtil.getInstance(this); + if (builder.channelProvider == null) { ApiFunction channelConfigurator = this.traceUtil.getChannelConfigurator(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/GenericQuerySnapshot.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/GenericQuerySnapshot.java index a7b02e22a..cd7348de4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/GenericQuerySnapshot.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/GenericQuerySnapshot.java @@ -162,4 +162,11 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(query, this.getDocumentChanges(), this.getDocuments()); } + + @Override + public String toString() { + return String.format( + "%s{query=%s, readTime=%s, documentChanges=%s, documents=%s}", + getClass().getSimpleName(), query, readTime, documentChanges, documents); + } } 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/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..29c92331a 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,9 @@ 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.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; @@ -59,6 +62,7 @@ final class ServerSideTransactionRunner { private int attemptsRemaining; private Span runTransactionSpan; private TraceUtil.Context runTransactionContext; + private MetricsContext metricsContext; /** * @param firestore The active Firestore instance @@ -85,6 +89,11 @@ final class ServerSideTransactionRunner { new ExponentialRetryAlgorithm( firestore.getOptions().getRetrySettings(), CurrentMillisClock.getDefaultClock()); this.nextBackoffAttempt = backoffAlgorithm.createFirstAttempt(); + this.metricsContext = + firestore + .getOptions() + .getMetricsUtil() + .createMetricsContext(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN); } @Nonnull @@ -93,7 +102,14 @@ private TraceUtil getTraceUtil() { } ApiFuture run() { - runTransactionSpan = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_RUN); + ApiFuture result = runInternally(); + metricsContext.recordLatencyAtFuture(MetricType.TRANSACTION_LATENCY, result); + metricsContext.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, result); + return result; + } + + ApiFuture runInternally() { + runTransactionSpan = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN); runTransactionSpan.setAttribute( ATTRIBUTE_KEY_TRANSACTION_TYPE, transactionOptions.getType().name()); runTransactionSpan.setAttribute( @@ -102,6 +118,7 @@ ApiFuture run() { try (Scope ignored = runTransactionSpan.makeCurrent()) { runTransactionContext = getTraceUtil().currentContext(); --attemptsRemaining; + metricsContext.incrementCounter(); ApiFuture result = ApiFutures.catchingAsync( ApiFutures.transformAsync( @@ -119,7 +136,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; @@ -235,7 +253,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/SilenceableBidiStream.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/SilenceableBidiStream.java index fa358ecaa..634e08ca8 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/SilenceableBidiStream.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/SilenceableBidiStream.java @@ -45,11 +45,11 @@ */ final class SilenceableBidiStream implements BidiStreamObserver { + private static final Logger LOGGER = Logger.getLogger(SilenceableBidiStream.class.getName()); private final ClientStream stream; private final BidiStreamObserver delegate; private boolean silence = false; - private static final Logger LOGGER = Logger.getLogger(Watch.class.getName()); SilenceableBidiStream( BidiStreamObserver responseObserverT, @@ -63,17 +63,17 @@ public boolean isSilenced() { } public void send(RequestT request) { - LOGGER.info(stream.toString()); + LOGGER.fine(stream.toString()); stream.send(request); } public void closeSend() { - LOGGER.info(stream::toString); + LOGGER.fine(stream::toString); stream.closeSend(); } public void closeSendAndSilence() { - LOGGER.info(stream::toString); + LOGGER.fine(stream::toString); silence = true; stream.closeSend(); } 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 fcba40f47..a959f9a15 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 @@ -20,13 +20,18 @@ import static com.google.common.collect.Lists.reverse; import com.google.api.core.ApiFuture; +import com.google.api.core.InternalApi; import com.google.api.core.SettableApiFuture; import com.google.api.gax.rpc.ApiStreamObserver; import com.google.api.gax.rpc.ResponseObserver; 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.TelemetryConstants.MetricType; 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; @@ -49,6 +54,7 @@ * `isRetryableWithCursor`. Retrying with a cursor means that the StreamableQuery can be resumed * where it failed by first calling `startAfter(lastDocumentReceived)`. */ +@InternalApi public abstract class StreamableQuery { final Query.QueryOptions options; final FirestoreRpcContext rpcContext; @@ -79,6 +85,10 @@ abstract SnapshotType createSnaphot( public Firestore getFirestore() { return rpcContext.getFirestore(); } + + MetricsContext createMetricsContext(String methodName) { + return getFirestore().getOptions().getMetricsUtil().createMetricsContext(methodName); + } /** * Executes the query and returns the results as QuerySnapshot. * @@ -100,11 +110,19 @@ 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); + + 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; @@ -139,7 +157,10 @@ public void onCompleted() { SnapshotType querySnapshot = createSnaphot(responseReadTime, resultView); result.set(querySnapshot); } - }, + }; + + internalStream( + new MonitoredStreamResponseObserver(observer, metricsContext), /* startTimeNanos= */ rpcContext.getClock().nanoTime(), transactionId, /* readTime= */ requestReadTime, @@ -150,6 +171,7 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -165,11 +187,18 @@ 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); + + 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; @@ -222,7 +251,10 @@ public void onCompleted() { } result.set(new ExplainResults<>(metrics, snapshot)); } - }, + }; + + internalStream( + new MonitoredStreamResponseObserver(observer, metricsContext), /* startTimeNanos= */ rpcContext.getClock().nanoTime(), /* transactionId= */ null, /* readTime= */ null, @@ -233,25 +265,61 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } - protected void internalStream( - final ApiStreamObserver runQueryResponseObserver, + 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) { + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, t); + observer.onError(t); + } + + @Override + public void onCompleted() { + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); + observer.onCompleted(); + } + } + + void internalStream( + final MonitoredStreamResponseObserver streamResponseObserver, final long startTimeNanos, @Nullable final ByteString transactionId, @Nullable final Timestamp readTime, @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) @@ -276,16 +344,19 @@ 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"); } - runQueryResponseObserver.onNext(response); + streamResponseObserver.onNext(response); if (response.hasDocument()) { 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 +367,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,12 +377,12 @@ 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.toString())); startAfter(cursor) .internalStream( - runQueryResponseObserver, + streamResponseObserver, startTimeNanos, /* transactionId= */ null, options.getRequireConsistency() ? cursor.getReadTime() : null, @@ -319,9 +390,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.toString())); - runQueryResponseObserver.onError(throwable); + streamResponseObserver.onError(throwable); } } @@ -330,9 +401,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)); - runQueryResponseObserver.onCompleted(); + streamResponseObserver.onCompleted(); } boolean shouldRetry(DocumentSnapshot lastDocument, Throwable t) { @@ -397,4 +468,9 @@ private boolean isRetryableError(Throwable throwable, Set retry } return false; } + + @Override + public String toString() { + return String.format("%s{options=%s}", getClass().getSimpleName(), options); + } } 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..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 @@ -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,12 @@ 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/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/VectorQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorQuery.java index 2f8c2ac42..7cdc6ebb5 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorQuery.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorQuery.java @@ -171,6 +171,14 @@ VectorQuerySnapshot createSnaphot( return VectorQuerySnapshot.withDocuments(this, readTime, documents); } + @SuppressWarnings("MethodDoesntCallSuperMethod") + @Override + public String toString() { + return String.format( + "VectorQuery{query=%s, vectorField=%s, queryVector=%s, limit=%d, distanceMeasure=%s, options=%s, options=%s}", + query, vectorField, queryVector, limit, distanceMeasure, options, options); + } + /** * The distance measure to use when comparing vectors in a {@link VectorQuery}. * diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorQueryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorQueryOptions.java index 38ca68d23..f06cc9469 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorQueryOptions.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorQueryOptions.java @@ -148,6 +148,13 @@ public boolean equals(Object obj) { && Objects.equals(distanceThreshold, otherOptions.distanceThreshold); } + @Override + public String toString() { + return String.format( + "VectorQueryOptions{distanceResultField=%s, distanceThreshold=%s}", + distanceResultField, distanceThreshold); + } + /** Default VectorQueryOptions instance. */ private static VectorQueryOptions DEFAULT = newBuilder().build(); diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorValue.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorValue.java index 347e721f1..aac7dd11e 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorValue.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/VectorValue.java @@ -76,4 +76,9 @@ MapValue toProto() { int size() { return this.values.length; } + + @Override + public String toString() { + return String.format("VectorValue{values=%s}", Arrays.toString(values)); + } } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java index 52d18cecc..ef57821d4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java @@ -60,6 +60,8 @@ * synchronization. */ final class Watch implements BidiStreamObserver { + private static final Logger LOGGER = Logger.getLogger(Watch.class.getName()); + /** * Target ID used by watch. Watch uses a fixed target id since we only support one target per * stream. The actual target ID we use is arbitrary. @@ -115,8 +117,6 @@ static class ChangeSet { List updates = new ArrayList<>(); } - private static final Logger LOGGER = Logger.getLogger(Watch.class.getName()); - /** * @param firestore The Firestore Database client. * @param query The query that is used to order the document snapshots returned by this watch. @@ -474,7 +474,7 @@ private void pushSnapshot(final Timestamp readTime, ByteString nextResumeToken) if (!hasPushed || !changes.isEmpty()) { final QuerySnapshot querySnapshot = QuerySnapshot.withChanges(query, readTime, documentSet, changes); - LOGGER.info(querySnapshot.toString()); + LOGGER.fine(querySnapshot.toString()); userCallbackExecutor.execute(() -> listener.onEvent(querySnapshot, null)); hasPushed = true; } 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 c391e9b80..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,6 +158,11 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException { firestoreBuilder.batchGetDocumentsSettings().setRetrySettings(retrySettings); } + ApiTracerFactory apiTracerFactory = options.getApiTracerFactory(); + if (apiTracerFactory != null) { + firestoreBuilder.setTracerFactory(apiTracerFactory); + } + firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build()); } catch (Exception e) { throw new IOException(e); 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..621efb914 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java @@ -0,0 +1,181 @@ +/* + * 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.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_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; +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; +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; +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 DoubleHistogram endToEndLatency; + 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 = "com.google.cloud.firestore"; + + public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + this.staticAttributes = createStaticAttributes(); + + if (openTelemetry.getMeterProvider() != MeterProvider.noop()) { + configureRPCLayerMetrics(); + configureSDKLayerMetrics(); + } + } + + 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 = + new OpenTelemetryMetricsRecorder(openTelemetry, METRIC_PREFIX); + this.apiTracerFactory = new MetricsTracerFactory(recorder, staticAttributes); + } + + /** Registers metrics to be collected at the Firestore SDK layer */ + 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) + .setDescription("Firestore streaming operations' first response latency") + .setUnit(MILLISECOND_UNIT) + .build(); + + 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 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); + } + } + } + + public void counterRecorder(MetricType metricType, long count, Map attributes) { + LongCounter counter = getCounter(metricType); + if (counter != null) { + attributes.putAll(staticAttributes); + try { + counter.add(count, toOtelAttributes(attributes)); + } catch (Exception e) { + logger.log(Level.WARNING, "Failed to record counter metric:" + e.getMessage(), e); + } + } + } + + public DoubleHistogram getHistogram(MetricType metricType) { + switch (metricType) { + case END_TO_END_LATENCY: + return endToEndLatency; + case FIRST_RESPONSE_LATENCY: + return firstResponseLatency; + case TRANSACTION_LATENCY: + return transactionLatency; + default: + throw new IllegalArgumentException("Unknown latency MetricType: " + metricType); + } + } + + public LongCounter getCounter(MetricType metricType) { + if (metricType == MetricType.TRANSACTION_ATTEMPT_COUNT) { + return transactionAttemptCount; + } else { + throw new IllegalArgumentException("Unknown counter MetricType: " + metricType); + } + } + + 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/ClientIdentifier.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java new file mode 100644 index 000000000..dc4a3eae0 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java @@ -0,0 +1,77 @@ +/* + * 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 java.lang.management.ManagementFactory; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.util.UUID; + +/** A utility class for retrieving a unique client identifier (CLIENT_UID) */ +final class ClientIdentifier { + + private ClientIdentifier() {} + + private static String CLIENT_UID; + + /** Gets the unique identifier for the client. */ + public static String getClientUid() { + if (CLIENT_UID == null) { + CLIENT_UID = generateClientUid(); + } + 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(); + String hostname = getHostName(); + return String.format("%s@%s@%s", identifier, pid, hostname); + } + + private static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (Exception 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/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/telemetry/CompositeApiTracerFactory.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.java new file mode 100644 index 000000000..ddd0f226d --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.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.InternalApi; +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; + +/** Combines multiple {@link ApiTracerFactory} into a single {@link ApiTracerFactory}. */ +@InternalApi +public 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 CompositeApiTracer(children); + } +} 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..0642033c0 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java @@ -0,0 +1,55 @@ +/* + * 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 com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType; +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 { + + @Override + public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) {} + + @Override + public void recordLatency(MetricType metric) {} + + @Override + public void recordLatency(MetricType metric, Throwable t) {} + + @Override + public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) {} + + @Override + public void incrementCounter() {} + } + + @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/DisabledTraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java index de03b1276..16428b8cf 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 @@ -25,7 +25,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. */ @InternalApi 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..5a9bd4d7b --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java @@ -0,0 +1,262 @@ +/* + * 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.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 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.firestore.telemetry.TelemetryConstants.MetricType; +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; +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.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; + + private static final Logger logger = Logger.getLogger(EnabledMetricsUtil.class.getName()); + + EnabledMetricsUtil(FirestoreOptions firestoreOptions) { + try { + configureDefaultMetricsProvider(firestoreOptions); + configureCustomMetricsProvider(firestoreOptions); + } catch (IOException e) { + logger.warning( + "Unable to create MetricsUtil object for client side metrics, will skip exporting client side metrics" + + e); + } + } + + 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); + } + + @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 : getAllViews().entrySet()) { + sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue()); + } + + 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)); + + return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build(); + } + + 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(); + } + + 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( + List apiTracerFactories, BuiltinMetricsProvider metricsProvider) { + ApiTracerFactory tracerFactory = metricsProvider.getApiTracerFactory(); + if (tracerFactory != null) { + apiTracerFactories.add(tracerFactory); + } + } + + class MetricsContext implements MetricsUtil.MetricsContext { + private final Stopwatch stopwatch; + 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) { + ApiFutures.addCallback( + futureValue, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + recordLatency(metric, t); + } + + @Override + public void onSuccess(T result) { + recordLatency(metric); + } + }, + MoreExecutors.directExecutor()); + } + + public void recordLatency(MetricType metric) { + recordLatency(metric, StatusCode.Code.OK.toString()); + } + + public void recordLatency(MetricType metric, Throwable t) { + recordLatency(metric, extractErrorStatus(t)); + } + + private void recordLatency(MetricType metric, String status) { + double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + Map attributes = createAttributes(status, methodName); + defaultMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); + customMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); + } + + public void incrementCounter() { + counter++; + } + + public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) { + ApiFutures.addCallback( + futureValue, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + recordCounter(metric, extractErrorStatus(t)); + } + + @Override + public void onSuccess(T result) { + recordCounter(metric, StatusCode.Code.OK.toString()); + } + }, + MoreExecutors.directExecutor()); + } + + 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); + } + } + + 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(); + } + + 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 new file mode 100644 index 000000000..02ac3a26b --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -0,0 +1,119 @@ +/* + * 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.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; + +/** + * 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 { + 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()) { + return new EnabledMetricsUtil(firestoreOptions); + } else { + return new DisabledMetricsUtil(); + } + } + + 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; + + String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR); + if (enableMetricsEnvVar != null) { + switch (enableMetricsEnvVar.toLowerCase()) { + case "true": + case "on": + // The feature is default on. + break; + case "false": + case "off": + shouldCreateEnabledInstance = false; + break; + default: + logger.warning("Invalid value for FIRESTORE_ENABLE_METRICS: " + enableMetricsEnvVar); + } + } + + 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 in the Firestore SDK. */ + 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 recordLatencyAtFuture(MetricType metric, ApiFuture futureValue); + + /** Records specific type of latency for the current operation. */ + void recordLatency(MetricType metric); + + /** Records specific type of latency for the current operation, which ended with a throwable. */ + void recordLatency(MetricType metric, Throwable t); + + /** + * 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); + + /** 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 new file mode 100644 index 000000000..d2ab8bf17 --- /dev/null +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java @@ -0,0 +1,124 @@ +/* + * 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.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"; + 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_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_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"; + + // 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; + + // 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_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_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"; + 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"; + + // 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 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/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java index 96a47ab57..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 @@ -32,30 +32,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/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json b/google-cloud-firestore/src/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json index ce4db1383..9de8b9bb7 100644 --- a/google-cloud-firestore/src/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json +++ b/google-cloud-firestore/src/main/resources/META-INF/native-image/com.google.cloud.firestore.v1/reflect-config.json @@ -395,6 +395,24 @@ "allDeclaredClasses": true, "allPublicClasses": true }, + { + "name": "com.google.api.SelectiveGapicGeneration", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, + { + "name": "com.google.api.SelectiveGapicGeneration$Builder", + "queryAllDeclaredConstructors": true, + "queryAllPublicConstructors": true, + "queryAllDeclaredMethods": true, + "allPublicMethods": true, + "allDeclaredClasses": true, + "allPublicClasses": true + }, { "name": "com.google.cloud.location.GetLocationRequest", "queryAllDeclaredConstructors": true, diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java index 19295b901..d875c6411 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java @@ -24,11 +24,11 @@ import static com.google.cloud.firestore.LocalFirestoreHelper.UPDATED_FIELD_PROTO; import static com.google.cloud.firestore.LocalFirestoreHelper.map; import static com.google.cloud.firestore.LocalFirestoreHelper.string; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; import com.google.api.gax.grpc.GrpcStatusCode; @@ -169,10 +169,11 @@ public void before() { @After public void after() { - assertTrue(exceptions.isEmpty()); - assertTrue(requests.isEmpty()); - assertTrue(documentSnapshots.isEmpty()); - assertTrue(querySnapshots.isEmpty()); + Object[] emptyArray = new Object[0]; + assertArrayEquals(exceptions.toArray(), emptyArray); + assertArrayEquals(requests.toArray(), emptyArray); + assertArrayEquals(documentSnapshots.toArray(), emptyArray); + assertArrayEquals(querySnapshots.toArray(), emptyArray); listenerRegistration.remove(); } 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 75a34ef43..82dbe9914 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; @@ -552,18 +552,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 @@ -614,7 +614,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)); } @@ -636,8 +636,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 @@ -657,7 +657,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)); } @@ -678,8 +678,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)); } @@ -700,8 +700,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)); } @@ -722,8 +722,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)); } @@ -748,8 +748,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)); } @@ -770,8 +770,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)); } @@ -792,8 +792,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)); } @@ -818,8 +818,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)); } @@ -844,8 +844,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)); } @@ -866,8 +866,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)); } @@ -892,8 +892,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)); } @@ -918,8 +918,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)); } @@ -944,8 +944,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)); } @@ -966,8 +966,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)); } @@ -988,8 +988,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)); } @@ -1010,7 +1010,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)); } @@ -1031,7 +1031,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)); } @@ -1052,7 +1052,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)); } @@ -1092,7 +1092,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 @@ -1137,28 +1137,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)))); } @@ -1192,12 +1192,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)))); } @@ -1221,6 +1221,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 974fb8fdf..225ff0162 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; @@ -365,8 +366,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 +395,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 +405,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 +414,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 +424,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 +434,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 +444,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 +458,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 +468,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 +478,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 +492,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 +506,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 +516,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 +530,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 +544,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 +558,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 +568,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 +578,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 +587,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 +596,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 +606,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 +631,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 +688,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 +756,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 +801,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()); diff --git a/grpc-google-cloud-firestore-admin-v1/pom.xml b/grpc-google-cloud-firestore-admin-v1/pom.xml index b4c345912..03121861d 100644 --- a/grpc-google-cloud-firestore-admin-v1/pom.xml +++ b/grpc-google-cloud-firestore-admin-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-firestore-admin-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT grpc-google-cloud-firestore-admin-v1 GRPC library for grpc-google-cloud-firestore-admin-v1 com.google.cloud google-cloud-firestore-parent - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/grpc-google-cloud-firestore-v1/pom.xml b/grpc-google-cloud-firestore-v1/pom.xml index cf50c94b2..7130abd61 100644 --- a/grpc-google-cloud-firestore-v1/pom.xml +++ b/grpc-google-cloud-firestore-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc grpc-google-cloud-firestore-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT grpc-google-cloud-firestore-v1 GRPC library for grpc-google-cloud-firestore-v1 com.google.cloud google-cloud-firestore-parent - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/pom.xml b/pom.xml index c705f732d..d678af254 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-firestore-parent pom - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT Google Cloud Firestore Parent https://github.com/googleapis/java-firestore @@ -14,7 +14,7 @@ com.google.cloud sdk-platform-java-config - 3.36.1 + 3.39.0 @@ -150,32 +150,32 @@ com.google.api.grpc proto-google-cloud-firestore-admin-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.cloud google-cloud-firestore - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.cloud proto-google-cloud-firestore-bundle-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.api.grpc proto-google-cloud-firestore-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-admin-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/proto-google-cloud-firestore-admin-v1/pom.xml b/proto-google-cloud-firestore-admin-v1/pom.xml index 8fb124120..9d7cb3534 100644 --- a/proto-google-cloud-firestore-admin-v1/pom.xml +++ b/proto-google-cloud-firestore-admin-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-firestore-admin-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT proto-google-cloud-firestore-admin-v1 PROTO library for proto-google-cloud-firestore-admin-v1 com.google.cloud google-cloud-firestore-parent - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/proto-google-cloud-firestore-bundle-v1/pom.xml b/proto-google-cloud-firestore-bundle-v1/pom.xml index 1b2cfaa32..68ce7e089 100644 --- a/proto-google-cloud-firestore-bundle-v1/pom.xml +++ b/proto-google-cloud-firestore-bundle-v1/pom.xml @@ -5,14 +5,14 @@ 4.0.0 proto-google-cloud-firestore-bundle-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT proto-google-cloud-firestore-bundle-v1 PROTO library for proto-google-cloud-firestore-bundle-v1 com.google.cloud google-cloud-firestore-parent - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/proto-google-cloud-firestore-v1/pom.xml b/proto-google-cloud-firestore-v1/pom.xml index b4ee322e1..56de21c49 100644 --- a/proto-google-cloud-firestore-v1/pom.xml +++ b/proto-google-cloud-firestore-v1/pom.xml @@ -4,13 +4,13 @@ 4.0.0 com.google.api.grpc proto-google-cloud-firestore-v1 - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT proto-google-cloud-firestore-v1 PROTO library for proto-google-cloud-firestore-v1 com.google.cloud google-cloud-firestore-parent - 3.27.1-SNAPSHOT + 3.28.1-SNAPSHOT diff --git a/renovate.json b/renovate.json index a4117f5d7..3801bb657 100644 --- a/renovate.json +++ b/renovate.json @@ -29,6 +29,16 @@ "matchStrings": ["uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v(?.+?)\\n"], "depNameTemplate": "com.google.cloud:sdk-platform-java-config", "datasourceTemplate": "maven" + }, + { + "fileMatch": [ + ".github/workflows/hermetic_library_generation.yaml" + ], + "matchStrings": [ + "uses: googleapis/sdk-platform-java/.github/scripts@v(?.+?)\\n" + ], + "depNameTemplate": "com.google.api:gapic-generator-java", + "datasourceTemplate": "maven" } ], "packageRules": [ @@ -88,6 +98,13 @@ "^com.fasterxml.jackson.core" ], "groupName": "jackson dependencies" + }, + { + "packagePatterns": [ + "^com.google.api:gapic-generator-java", + "^com.google.cloud:sdk-platform-java-config" + ], + "groupName": "sdk-platform-java dependencies" } ], "semanticCommits": true, diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml index f726478bf..d5af9e33a 100644 --- a/samples/install-without-bom/pom.xml +++ b/samples/install-without-bom/pom.xml @@ -20,7 +20,7 @@ 1.8 1.8 - 2.59.0 + 2.60.0 UTF-8 @@ -30,7 +30,7 @@ com.google.cloud google-cloud-firestore - 3.27.0 + 3.28.0 diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml index 47887f925..a565c68e3 100644 --- a/samples/snapshot/pom.xml +++ b/samples/snapshot/pom.xml @@ -21,7 +21,7 @@ 1.8 1.8 UTF-8 - 2.59.0 + 2.60.0 @@ -29,7 +29,7 @@ com.google.cloud google-cloud-firestore - 3.27.0 + 3.28.0 diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml index 6d63fb760..53fa89e02 100644 --- a/samples/snippets/pom.xml +++ b/samples/snippets/pom.xml @@ -21,7 +21,7 @@ 1.8 1.8 UTF-8 - 2.59.0 + 2.60.0 @@ -34,7 +34,7 @@ com.google.cloud libraries-bom - 26.48.0 + 26.50.0 pom import @@ -79,7 +79,7 @@ org.codehaus.mojo exec-maven-plugin - 3.4.1 + 3.5.0