From 7f1fee6be0207c89e9d6351eaf9181ae9899fa21 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 22 Jun 2021 15:44:05 +0000 Subject: [PATCH 1/5] chore: release 2.5.2-SNAPSHOT (#672) :robot: I have created a release \*beep\* \*boop\* --- ### Updating meta-information for bleeding-edge SNAPSHOT release. --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- google-cloud-firestore-admin/pom.xml | 4 ++-- google-cloud-firestore-bom/pom.xml | 16 ++++++++-------- google-cloud-firestore/pom.xml | 4 ++-- grpc-google-cloud-firestore-admin-v1/pom.xml | 4 ++-- grpc-google-cloud-firestore-v1/pom.xml | 4 ++-- pom.xml | 12 ++++++------ proto-google-cloud-firestore-admin-v1/pom.xml | 4 ++-- proto-google-cloud-firestore-bundle-v1/pom.xml | 4 ++-- proto-google-cloud-firestore-v1/pom.xml | 4 ++-- versions.txt | 16 ++++++++-------- 10 files changed, 36 insertions(+), 36 deletions(-) diff --git a/google-cloud-firestore-admin/pom.xml b/google-cloud-firestore-admin/pom.xml index 48f940aec..36bf5fd18 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 - 2.5.1 + 2.5.2-SNAPSHOT jar Google Cloud Firestore Admin Client https://github.com/googleapis/java-firestore @@ -14,7 +14,7 @@ com.google.cloud google-cloud-firestore-parent - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/google-cloud-firestore-bom/pom.xml b/google-cloud-firestore-bom/pom.xml index 595127e76..f038ad1b1 100644 --- a/google-cloud-firestore-bom/pom.xml +++ b/google-cloud-firestore-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-firestore-bom - 2.5.1 + 2.5.2-SNAPSHOT pom com.google.cloud @@ -63,37 +63,37 @@ com.google.cloud google-cloud-firestore - 2.5.1 + 2.5.2-SNAPSHOT com.google.api.grpc proto-google-cloud-firestore-admin-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.cloud proto-google-cloud-firestore-bundle-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.api.grpc proto-google-cloud-firestore-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-admin-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.cloud google-cloud-firestore-admin - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index bfb17b1fd..36b90ab04 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/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 - 2.5.1 + 2.5.2-SNAPSHOT jar Google Cloud Firestore https://github.com/googleapis/java-firestore @@ -14,7 +14,7 @@ com.google.cloud google-cloud-firestore-parent - 2.5.1 + 2.5.2-SNAPSHOT google-cloud-firestore diff --git a/grpc-google-cloud-firestore-admin-v1/pom.xml b/grpc-google-cloud-firestore-admin-v1/pom.xml index 846e7b4c8..5129dbb61 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 - 2.5.1 + 2.5.2-SNAPSHOT grpc-google-cloud-firestore-admin-v1 GRPC library for grpc-google-cloud-firestore-admin-v1 com.google.cloud google-cloud-firestore-parent - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/grpc-google-cloud-firestore-v1/pom.xml b/grpc-google-cloud-firestore-v1/pom.xml index 0a269fc6a..67ae35b96 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 - 2.5.1 + 2.5.2-SNAPSHOT grpc-google-cloud-firestore-v1 GRPC library for grpc-google-cloud-firestore-v1 com.google.cloud google-cloud-firestore-parent - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/pom.xml b/pom.xml index 2dc41352c..c9c0ac390 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-firestore-parent pom - 2.5.1 + 2.5.2-SNAPSHOT Google Cloud Firestore Parent https://github.com/googleapis/java-firestore @@ -159,27 +159,27 @@ com.google.api.grpc proto-google-cloud-firestore-admin-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.cloud proto-google-cloud-firestore-bundle-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.api.grpc proto-google-cloud-firestore-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-admin-v1 - 2.5.1 + 2.5.2-SNAPSHOT com.google.api.grpc grpc-google-cloud-firestore-v1 - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/proto-google-cloud-firestore-admin-v1/pom.xml b/proto-google-cloud-firestore-admin-v1/pom.xml index 7f2a3dfcf..28cba0685 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 - 2.5.1 + 2.5.2-SNAPSHOT proto-google-cloud-firestore-admin-v1 PROTO library for proto-google-cloud-firestore-admin-v1 com.google.cloud google-cloud-firestore-parent - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/proto-google-cloud-firestore-bundle-v1/pom.xml b/proto-google-cloud-firestore-bundle-v1/pom.xml index f289d108f..a09b9381d 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 - 2.5.1 + 2.5.2-SNAPSHOT proto-google-cloud-firestore-bundle-v1 PROTO library for proto-google-cloud-firestore-bundle-v1 com.google.cloud google-cloud-firestore-parent - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/proto-google-cloud-firestore-v1/pom.xml b/proto-google-cloud-firestore-v1/pom.xml index c8b9c7d04..ad71bdd05 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 - 2.5.1 + 2.5.2-SNAPSHOT proto-google-cloud-firestore-v1 PROTO library for proto-google-cloud-firestore-v1 com.google.cloud google-cloud-firestore-parent - 2.5.1 + 2.5.2-SNAPSHOT diff --git a/versions.txt b/versions.txt index e7e076bb2..6eb70d23f 100644 --- a/versions.txt +++ b/versions.txt @@ -1,11 +1,11 @@ # Format: # module:released-version:current-version -google-cloud-firestore:2.5.1:2.5.1 -google-cloud-firestore-admin:2.5.1:2.5.1 -google-cloud-firestore-bom:2.5.1:2.5.1 -google-cloud-firestore-parent:2.5.1:2.5.1 -grpc-google-cloud-firestore-admin-v1:2.5.1:2.5.1 -grpc-google-cloud-firestore-v1:2.5.1:2.5.1 -proto-google-cloud-firestore-admin-v1:2.5.1:2.5.1 -proto-google-cloud-firestore-v1:2.5.1:2.5.1 +google-cloud-firestore:2.5.1:2.5.2-SNAPSHOT +google-cloud-firestore-admin:2.5.1:2.5.2-SNAPSHOT +google-cloud-firestore-bom:2.5.1:2.5.2-SNAPSHOT +google-cloud-firestore-parent:2.5.1:2.5.2-SNAPSHOT +grpc-google-cloud-firestore-admin-v1:2.5.1:2.5.2-SNAPSHOT +grpc-google-cloud-firestore-v1:2.5.1:2.5.2-SNAPSHOT +proto-google-cloud-firestore-admin-v1:2.5.1:2.5.2-SNAPSHOT +proto-google-cloud-firestore-v1:2.5.1:2.5.2-SNAPSHOT From 969f7fd72b07aa9c916609b73528ff0f17dfaead Mon Sep 17 00:00:00 2001 From: Brian Chen Date: Fri, 25 Jun 2021 16:38:59 -0700 Subject: [PATCH 2/5] fix: handle thrown exceptions in runAsyncTransaction callback (#671) --- .../cloud/firestore/TransactionRunner.java | 19 ++++++++----- .../cloud/firestore/TransactionTest.java | 27 ++++++++++++++++++- .../cloud/firestore/it/ITSystemTest.java | 18 +++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/TransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/TransactionRunner.java index 57bc4c3b0..3c479f0fc 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/TransactionRunner.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/TransactionRunner.java @@ -145,28 +145,35 @@ public void run() { * Invokes the user callback on the user callback executor and returns the user-provided result. */ private SettableApiFuture invokeUserCallback() { - final SettableApiFuture callbackResult = SettableApiFuture.create(); + final SettableApiFuture returnedResult = SettableApiFuture.create(); + userCallbackExecutor.execute( new Runnable() { @Override public void run() { + ApiFuture userCallbackResult; + try { + userCallbackResult = userCallback.updateCallback(transaction); + } catch (Exception e) { + userCallbackResult = ApiFutures.immediateFailedFuture(e); + } ApiFutures.addCallback( - userCallback.updateCallback(transaction), + userCallbackResult, new ApiFutureCallback() { @Override public void onFailure(Throwable t) { - callbackResult.setException(t); + returnedResult.setException(t); } @Override public void onSuccess(T result) { - callbackResult.set(result); + returnedResult.set(result); } }, - MoreExecutors.directExecutor()); + firestoreExecutor); } }); - return callbackResult; + return returnedResult; } /** A callback that invokes the BeginTransaction callback. */ diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java index a8405972a..5638bcda0 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java @@ -253,7 +253,7 @@ public String updateCallback(Transaction transaction) throws Exception { } @Test - public void rollbackOnCallbackErrorAsync() { + public void rollbackOnCallbackApiFutureErrorAsync() { doReturn(beginResponse()) .doReturn(rollbackResponse()) .when(firestoreMock) @@ -339,6 +339,31 @@ public ApiFuture updateCallback(Transaction transaction) { assertEquals(1, requests.size()); } + @Test + public void noRollbackOnThrownExceptionAsync() { + doReturn(beginResponse()) + .doReturn(rollbackResponse()) + .when(firestoreMock) + .sendRequest(requestCapture.capture(), Matchers.>any()); + + ApiFuture transaction = + firestoreMock.runAsyncTransaction( + new Transaction.AsyncFunction() { + @Override + public ApiFuture updateCallback(Transaction transaction) { + throw new RuntimeException("User exception"); + } + }, + options); + + try { + transaction.get(); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().endsWith("User exception")); + } + } + @Test public void limitsRetriesWithFailure() { ResponseStubber responseStubber = diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java index a5572efbe..fd17dab44 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITSystemTest.java @@ -688,6 +688,24 @@ public String updateCallback(Transaction transaction) { } } + @Test + public void asyncTxFailsWithUserError() throws Exception { + try { + firestore + .runAsyncTransaction( + new Transaction.AsyncFunction() { + @Override + public ApiFuture updateCallback(Transaction transaction) { + throw new RuntimeException("User exception"); + } + }) + .get(); + fail(); + } catch (Exception e) { + assertTrue(e.getMessage().endsWith("User exception")); + } + } + @Test public void doesNotRetryTransactionsWithFailedPreconditions() { final DocumentReference documentReference = randomColl.document(); From 9915e409ee1b69c770c492ebf69eeac00f6670df Mon Sep 17 00:00:00 2001 From: Yoshi Automation Bot Date: Fri, 25 Jun 2021 16:48:25 -0700 Subject: [PATCH 3/5] chore: regenerate README (#676) This PR was generated using Autosynth. :rainbow:
Log from Synthtool ``` 2021-06-25 23:41:12,796 synthtool [DEBUG] > Executing /root/.cache/synthtool/java-firestore/.github/readme/synth.py. On branch autosynth-readme nothing to commit, working tree clean 2021-06-25 23:41:14,066 synthtool [DEBUG] > Wrote metadata to .github/readme/synth.metadata/synth.metadata. ```
Full log will be available here: https://source.cloud.google.com/results/invocations/28f0e2fc-4f38-4e5c-8bc2-4b19dbb52f6c/targets - [ ] To automatically regenerate this PR, check this box. (May take up to 24 hours.) --- .github/readme/synth.metadata/synth.metadata | 4 ++-- README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/readme/synth.metadata/synth.metadata b/.github/readme/synth.metadata/synth.metadata index 0a64bc391..9724c9cc9 100644 --- a/.github/readme/synth.metadata/synth.metadata +++ b/.github/readme/synth.metadata/synth.metadata @@ -4,14 +4,14 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/java-firestore.git", - "sha": "abf03fd06d4f77582c2ad24b8721b3c3556bdef1" + "sha": "969f7fd72b07aa9c916609b73528ff0f17dfaead" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "2430f8d90ed8a508e8422a3a7191e656d5a6bf53" + "sha": "157678ee74b6ee0d954023ea7450a9c1d5a5743f" } } ] diff --git a/README.md b/README.md index 6e1246b0d..4f52cafa0 100644 --- a/README.md +++ b/README.md @@ -45,18 +45,18 @@ If you are using Maven without 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:20.6.0') +implementation platform('com.google.cloud:libraries-bom:20.7.0') compile 'com.google.cloud:google-cloud-firestore' ``` If you are using Gradle without BOM, add this to your dependencies ```Groovy -compile 'com.google.cloud:google-cloud-firestore:2.5.0' +compile 'com.google.cloud:google-cloud-firestore:2.5.1' ``` If you are using SBT, add this to your dependencies ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "2.5.0" +libraryDependencies += "com.google.cloud" % "google-cloud-firestore" % "2.5.1" ``` ## Authentication From 4f2085887981121316fc046d13e945e6800aaad1 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 29 Jun 2021 10:37:36 -0600 Subject: [PATCH 4/5] feat: add shutdown() and shutdownNow() (#673) --- .../clirr-ignored-differences.xml | 21 +++ .../com/google/cloud/firestore/Firestore.java | 9 ++ .../google/cloud/firestore/FirestoreImpl.java | 12 ++ .../com/google/cloud/firestore/Watch.java | 78 ++++++----- .../cloud/firestore/spi/v1/FirestoreRpc.java | 4 + .../firestore/spi/v1/GrpcFirestoreRpc.java | 36 +++++- .../cloud/firestore/it/ITQueryWatchTest.java | 91 +++++++++++-- .../cloud/firestore/it/ITShutdownTest.java | 122 ++++++++++++++++++ 8 files changed, 320 insertions(+), 53 deletions(-) create mode 100644 google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITShutdownTest.java diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml index d32596ef0..bf989e438 100644 --- a/google-cloud-firestore/clirr-ignored-differences.xml +++ b/google-cloud-firestore/clirr-ignored-differences.xml @@ -17,6 +17,27 @@ + + + 7012 + com/google/cloud/firestore/Firestore + void shutdown() + + + 7012 + com/google/cloud/firestore/Firestore + void shutdownNow() + + + 7012 + com/google/cloud/firestore/spi/v1/FirestoreRpc + void shutdown() + + + 7012 + com/google/cloud/firestore/spi/v1/FirestoreRpc + void shutdownNow() + diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Firestore.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Firestore.java index 2cd897b49..5175fd1b4 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Firestore.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Firestore.java @@ -296,4 +296,13 @@ void getAll( */ @Override void close() throws Exception; + + /** + * Initiates an orderly shutdown in which previously submitted work is finished, but no new work + * will be accepted. + */ + void shutdown(); + + /** Attempts to stop all actively executing work and halts the processing of waiting work. */ + void shutdownNow(); } 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 7a93e711d..a4ea4f0f2 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 @@ -451,6 +451,18 @@ public void close() throws Exception { closed = true; } + @Override + public void shutdown() { + firestoreClient.shutdown(); + closed = true; + } + + @Override + public void shutdownNow() { + firestoreClient.shutdownNow(); + closed = true; + } + private static class TransactionAsyncAdapter implements Transaction.AsyncFunction { private final Transaction.Function syncFunction; 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 f355836f0..7eda432cb 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 @@ -337,15 +337,18 @@ private void closeStream(final Throwable throwable) { new Runnable() { @Override public void run() { - listener.onEvent( - null, - throwable instanceof FirestoreException - ? (FirestoreException) throwable - : FirestoreException.forApiException( - new ApiException( - throwable, - GrpcStatusCode.of(getStatus(throwable).getCode()), - false))); + if (throwable instanceof FirestoreException) { + listener.onEvent(null, (FirestoreException) throwable); + } else { + Status status = getStatus(throwable); + FirestoreException firestoreException = + FirestoreException.forApiException( + new ApiException( + throwable, + GrpcStatusCode.of(status != null ? status.getCode() : Code.UNKNOWN), + false)); + listener.onEvent(null, firestoreException); + } } }); } @@ -383,31 +386,36 @@ private void initStream() { new Runnable() { @Override public void run() { - if (!isActive.get()) { - return; - } - - synchronized (Watch.this) { + try { if (!isActive.get()) { return; } - Preconditions.checkState(stream == null); + synchronized (Watch.this) { + if (!isActive.get()) { + return; + } - current = false; - nextAttempt = backoff.createNextAttempt(nextAttempt); + Preconditions.checkState(stream == null); - Tracing.getTracer().getCurrentSpan().addAnnotation(TraceUtil.SPAN_NAME_LISTEN); - stream = firestore.streamRequest(Watch.this, firestore.getClient().listenCallable()); + current = false; + nextAttempt = backoff.createNextAttempt(nextAttempt); - ListenRequest.Builder request = ListenRequest.newBuilder(); - request.setDatabase(firestore.getDatabaseName()); - request.setAddTarget(target); - if (resumeToken != null) { - request.getAddTargetBuilder().setResumeToken(resumeToken); - } + Tracing.getTracer().getCurrentSpan().addAnnotation(TraceUtil.SPAN_NAME_LISTEN); + stream = + firestore.streamRequest(Watch.this, firestore.getClient().listenCallable()); + + ListenRequest.Builder request = ListenRequest.newBuilder(); + request.setDatabase(firestore.getDatabaseName()); + request.setAddTarget(target); + if (resumeToken != null) { + request.getAddTargetBuilder().setResumeToken(resumeToken); + } - stream.onNext(request.build()); + stream.onNext(request.build()); + } + } catch (Throwable throwable) { + onError(throwable); } } }, @@ -549,6 +557,10 @@ private List computeSnapshot(Timestamp readTime) { private static boolean isPermanentError(Throwable throwable) { Status status = getStatus(throwable); + if (status == null) { + return true; + } + switch (status.getCode()) { case CANCELLED: case UNKNOWN: @@ -563,20 +575,20 @@ private static boolean isPermanentError(Throwable throwable) { } } - /** Extracts the GRPC status code if available. Returns UNKNOWN for non-GRPC exceptions. */ + /** Extracts the GRPC status code if available. Returns `null` for non-GRPC exceptions. */ + @Nullable private static Status getStatus(Throwable throwable) { - Status status = Status.UNKNOWN; - if (throwable instanceof StatusRuntimeException) { - status = ((StatusRuntimeException) throwable).getStatus(); + return ((StatusRuntimeException) throwable).getStatus(); } else if (throwable instanceof StatusException) { - status = ((StatusException) throwable).getStatus(); + return ((StatusException) throwable).getStatus(); } - return status; + return null; } /** Determines whether we need to initiate a longer backoff due to system overload. */ private static boolean isResourceExhaustedError(Throwable throwable) { - return getStatus(throwable).getCode().equals(Code.RESOURCE_EXHAUSTED); + Status status = getStatus(throwable); + return status != null && status.getCode().equals(Code.RESOURCE_EXHAUSTED); }; } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/FirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/FirestoreRpc.java index d31d6557c..c7fbcd3d9 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/FirestoreRpc.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/FirestoreRpc.java @@ -78,4 +78,8 @@ public interface FirestoreRpc extends AutoCloseable, ServiceRpc { /** Returns a bi-directional watch stream. */ BidiStreamingCallable listenCallable(); + + void shutdownNow(); + + void shutdown(); } 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 692cb77b4..edd547130 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,7 +28,6 @@ import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.TransportChannel; import com.google.api.gax.rpc.UnaryCallSettings; -import com.google.api.gax.rpc.UnaryCallSettings.Builder; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.NoCredentials; import com.google.cloud.ServiceOptions; @@ -127,7 +126,7 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException { clientContext = ClientContext.create(settingsBuilder.build()); } ApiFunction, Void> retrySettingsSetter = - new ApiFunction, Void>() { + new ApiFunction, Void>() { @Override public Void apply(UnaryCallSettings.Builder builder) { builder.setRetrySettings(options.getRetrySettings()); @@ -145,18 +144,43 @@ public Void apply(UnaryCallSettings.Builder builder) { @Override public void close() throws Exception { + if (!closed) { + firestoreStub.close(); + for (BackgroundResource resource : clientContext.getBackgroundResources()) { + resource.close(); + } + executorFactory.release(executor); + closed = true; + } + for (BackgroundResource resource : clientContext.getBackgroundResources()) { + resource.awaitTermination(1, TimeUnit.SECONDS); + } + } + + @Override + public void shutdown() { if (closed) { return; } - closed = true; - firestoreStub.close(); + firestoreStub.shutdown(); for (BackgroundResource resource : clientContext.getBackgroundResources()) { - resource.close(); + resource.shutdown(); } + executorFactory.release(executor); + closed = true; + } + + @Override + public void shutdownNow() { + if (closed) { + return; + } + firestoreStub.shutdownNow(); for (BackgroundResource resource : clientContext.getBackgroundResources()) { - resource.awaitTermination(1, TimeUnit.SECONDS); + resource.shutdownNow(); } executorFactory.release(executor); + closed = true; } @Override diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITQueryWatchTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITQueryWatchTest.java index cb9d69ec1..c053af510 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITQueryWatchTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITQueryWatchTest.java @@ -54,9 +54,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.annotation.Nullable; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -69,21 +68,17 @@ public final class ITQueryWatchTest { private CollectionReference randomColl; - @BeforeClass - public static void beforeClass() { - FirestoreOptions firestoreOptions = FirestoreOptions.newBuilder().build(); - firestore = firestoreOptions.getService(); - } - @Before public void before() { + FirestoreOptions firestoreOptions = FirestoreOptions.newBuilder().build(); + firestore = firestoreOptions.getService(); String autoId = LocalFirestoreHelper.autoId(); String collPath = String.format("java-%s-%s", testName.getMethodName(), autoId); randomColl = firestore.collection(collPath); } - @AfterClass - public static void afterClass() throws Exception { + @After + public void after() throws Exception { Preconditions.checkNotNull( firestore, "Error instantiating Firestore. Check that the service account credentials were properly set."); @@ -384,6 +379,36 @@ public void limitToLast() throws Exception { listenerAssertions.addedIdsIsAnyOf(emptyList(), asList("doc2", "doc3")); } + @Test + public void shutdownNowTerminatesActiveListener() throws Exception { + Query query = randomColl.whereEqualTo("foo", "bar"); + QuerySnapshotEventListener listener = + QuerySnapshotEventListener.builder().setExpectError().build(); + + query.addSnapshotListener(listener); + firestore.shutdownNow(); + + listener.eventsCountDownLatch.awaitError(); + + ListenerAssertions listenerAssertions = listener.assertions(); + listenerAssertions.hasError(); + } + + @Test + public void shutdownNowPreventsAddingNewListener() throws Exception { + Query query = randomColl.whereEqualTo("foo", "bar"); + QuerySnapshotEventListener listener = + QuerySnapshotEventListener.builder().setExpectError().build(); + + firestore.shutdownNow(); + query.addSnapshotListener(listener); + + listener.eventsCountDownLatch.awaitError(); + + ListenerAssertions listenerAssertions = listener.assertions(); + listenerAssertions.hasError(); + } + /** * A tuple class used by {@code #queryWatch}. This class represents an event delivered to the * registered query listener. @@ -402,6 +427,7 @@ private static final class ListenerEvent { private static final class EventsCountDownLatch { private final CountDownLatch initialEventsCountDownLatch; private final int initialEventCount; + private final CountDownLatch errorCountDownLatch; private final EnumMap eventsCounts; private final EnumMap eventsCountDownLatches; @@ -409,9 +435,11 @@ private static final class EventsCountDownLatch { int initialEventCount, int addedInitialCount, int modifiedInitialCount, - int removedInitialCount) { + int removedInitialCount, + int errorCount) { initialEventsCountDownLatch = new CountDownLatch(initialEventCount); this.initialEventCount = initialEventCount; + this.errorCountDownLatch = new CountDownLatch(errorCount); eventsCounts = new EnumMap<>(DocumentChange.Type.class); eventsCounts.put(DocumentChange.Type.ADDED, addedInitialCount); eventsCounts.put(DocumentChange.Type.MODIFIED, modifiedInitialCount); @@ -432,10 +460,18 @@ void countDown(DocumentChange.Type type) { eventsCountDownLatches.get(type).countDown(); } + void countError() { + errorCountDownLatch.countDown(); + } + void awaitInitialEvents() throws InterruptedException { initialEventsCountDownLatch.await(5 * initialEventCount, TimeUnit.SECONDS); } + void awaitError() throws InterruptedException { + errorCountDownLatch.await(5, TimeUnit.SECONDS); + } + void await(DocumentChange.Type type) throws InterruptedException { int count = eventsCounts.get(type); eventsCountDownLatches.get(type).await(5 * count, TimeUnit.SECONDS); @@ -447,11 +483,15 @@ static class QuerySnapshotEventListener implements EventListener final EventsCountDownLatch eventsCountDownLatch; private QuerySnapshotEventListener( - int initialCount, int addedEventCount, int modifiedEventCount, int removedEventCount) { + int initialCount, + int addedEventCount, + int modifiedEventCount, + int removedEventCount, + int errorCount) { this.receivedEvents = Collections.synchronizedList(new ArrayList()); this.eventsCountDownLatch = new EventsCountDownLatch( - initialCount, addedEventCount, modifiedEventCount, removedEventCount); + initialCount, addedEventCount, modifiedEventCount, removedEventCount, errorCount); } @Override @@ -463,6 +503,9 @@ public void onEvent(@Nullable QuerySnapshot value, @Nullable FirestoreException eventsCountDownLatch.countDown(docChange.getType()); } } + if (error != null) { + eventsCountDownLatch.countError(); + } eventsCountDownLatch.countDown(); } @@ -480,6 +523,7 @@ static final class Builder { private int addedEventCount = 0; private int modifiedEventCount = 0; private int removedEventCount = 0; + private int errorCount = 0; private Builder() {} @@ -503,9 +547,14 @@ Builder setRemovedEventCount(int removedEventCount) { return this; } + Builder setExpectError() { + this.errorCount = 1; + return this; + } + public QuerySnapshotEventListener build() { return new QuerySnapshotEventListener( - initialEventCount, addedEventCount, modifiedEventCount, removedEventCount); + initialEventCount, addedEventCount, modifiedEventCount, removedEventCount, errorCount); } } @@ -536,6 +585,20 @@ public boolean apply(ListenerEvent input) { assertWithMessage("snapshotListener received an error").that(anyError).isAbsent(); } + private void hasError() { + final Optional anyError = + events.firstMatch( + new Predicate() { + @Override + public boolean apply(ListenerEvent input) { + return input.error != null; + } + }); + assertWithMessage("snapshotListener did not receive an expected error") + .that(anyError) + .isPresent(); + } + private static List getQuerySnapshots(FluentIterable events) { return events .filter( diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITShutdownTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITShutdownTest.java new file mode 100644 index 000000000..b6e9e87d5 --- /dev/null +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITShutdownTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.firestore.it; + +import static org.junit.Assert.fail; + +import com.google.api.core.SettableApiFuture; +import com.google.cloud.firestore.EventListener; +import com.google.cloud.firestore.Firestore; +import com.google.cloud.firestore.FirestoreException; +import com.google.cloud.firestore.FirestoreOptions; +import com.google.cloud.firestore.ListenerRegistration; +import com.google.cloud.firestore.LocalFirestoreHelper; +import com.google.cloud.firestore.QuerySnapshot; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.rules.Timeout; + +public class ITShutdownTest { + @Rule public final Timeout timeout = new Timeout(5, TimeUnit.SECONDS); + @Rule public TestName testName = new TestName(); + + @Test + public void closeSuccess_withListenerRemove() throws Exception { + Firestore fs = FirestoreOptions.getDefaultInstance().getService(); + ListenerRegistration listener = attachListener(fs); + listener.remove(); + fs.close(); + } + + @Test + public void closeFailure_withoutListenerRemove() throws Exception { + final Firestore fs = FirestoreOptions.getDefaultInstance().getService(); + attachListener(fs); + + ExecutorService testExecutorService = Executors.newSingleThreadExecutor(); + final SettableApiFuture result = SettableApiFuture.create(); + testExecutorService.submit( + new Runnable() { + @Override + public void run() { + try { + fs.close(); + result.set(null); + } catch (Throwable throwable) { + result.setException(throwable); + } + } + }); + + try { + result.get(1, TimeUnit.SECONDS); + fail(); + } catch (TimeoutException e) { + // Expected + } finally { + testExecutorService.shutdown(); + } + } + + @Test + public void shutdownNowSuccess_withoutListenerRemove() throws Exception { + Firestore fs = FirestoreOptions.getDefaultInstance().getService(); + attachListener(fs); + fs.shutdownNow(); + } + + @Test + public void shutdownSuccess_withoutListenerRemove() throws Exception { + Firestore fs = FirestoreOptions.getDefaultInstance().getService(); + attachListener(fs); + fs.shutdown(); + } + + @Test + public void closeAndShutdown() throws Exception { + Firestore fs = FirestoreOptions.getDefaultInstance().getService(); + attachListener(fs); + fs.shutdown(); + fs.shutdownNow(); + fs.close(); + } + + private ListenerRegistration attachListener(Firestore fs) throws InterruptedException { + final CountDownLatch cdl = new CountDownLatch(1); + ListenerRegistration listenerRegistration = + fs.collection( + String.format( + "java-%s-%s", testName.getMethodName(), LocalFirestoreHelper.autoId())) + .addSnapshotListener( + new EventListener() { + @Override + public void onEvent( + @Nullable QuerySnapshot value, @Nullable FirestoreException error) { + cdl.countDown(); + } + }); + cdl.await(); + return listenerRegistration; + } +} From c97fb83083a375c9f962613e6c09955f0cfa3a1a Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 29 Jun 2021 12:19:19 -0600 Subject: [PATCH 5/5] chore: release 2.6.0 (#675) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 12 ++++++++++++ google-cloud-firestore-admin/pom.xml | 4 ++-- google-cloud-firestore-bom/pom.xml | 16 ++++++++-------- google-cloud-firestore/pom.xml | 4 ++-- grpc-google-cloud-firestore-admin-v1/pom.xml | 4 ++-- grpc-google-cloud-firestore-v1/pom.xml | 4 ++-- pom.xml | 12 ++++++------ proto-google-cloud-firestore-admin-v1/pom.xml | 4 ++-- proto-google-cloud-firestore-bundle-v1/pom.xml | 4 ++-- proto-google-cloud-firestore-v1/pom.xml | 4 ++-- versions.txt | 16 ++++++++-------- 11 files changed, 48 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b6955d57..5dd33d65f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [2.6.0](https://www.github.com/googleapis/java-firestore/compare/v2.5.1...v2.6.0) (2021-06-29) + + +### Features + +* add shutdown() and shutdownNow() ([#673](https://www.github.com/googleapis/java-firestore/issues/673)) ([4f20858](https://www.github.com/googleapis/java-firestore/commit/4f2085887981121316fc046d13e945e6800aaad1)) + + +### Bug Fixes + +* handle thrown exceptions in runAsyncTransaction callback ([#671](https://www.github.com/googleapis/java-firestore/issues/671)) ([969f7fd](https://www.github.com/googleapis/java-firestore/commit/969f7fd72b07aa9c916609b73528ff0f17dfaead)) + ### [2.5.1](https://www.github.com/googleapis/java-firestore/compare/v2.5.0...v2.5.1) (2021-06-22) diff --git a/google-cloud-firestore-admin/pom.xml b/google-cloud-firestore-admin/pom.xml index 36bf5fd18..c9aff9175 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 - 2.5.2-SNAPSHOT + 2.6.0 jar Google Cloud Firestore Admin Client https://github.com/googleapis/java-firestore @@ -14,7 +14,7 @@ com.google.cloud google-cloud-firestore-parent - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/google-cloud-firestore-bom/pom.xml b/google-cloud-firestore-bom/pom.xml index f038ad1b1..be61ad10e 100644 --- a/google-cloud-firestore-bom/pom.xml +++ b/google-cloud-firestore-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.cloud google-cloud-firestore-bom - 2.5.2-SNAPSHOT + 2.6.0 pom com.google.cloud @@ -63,37 +63,37 @@ com.google.cloud google-cloud-firestore - 2.5.2-SNAPSHOT + 2.6.0 com.google.api.grpc proto-google-cloud-firestore-admin-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.cloud proto-google-cloud-firestore-bundle-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.api.grpc proto-google-cloud-firestore-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.api.grpc grpc-google-cloud-firestore-admin-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.api.grpc grpc-google-cloud-firestore-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.cloud google-cloud-firestore-admin - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml index 36b90ab04..d617b7b3a 100644 --- a/google-cloud-firestore/pom.xml +++ b/google-cloud-firestore/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 - 2.5.2-SNAPSHOT + 2.6.0 jar Google Cloud Firestore https://github.com/googleapis/java-firestore @@ -14,7 +14,7 @@ com.google.cloud google-cloud-firestore-parent - 2.5.2-SNAPSHOT + 2.6.0 google-cloud-firestore diff --git a/grpc-google-cloud-firestore-admin-v1/pom.xml b/grpc-google-cloud-firestore-admin-v1/pom.xml index 5129dbb61..b4590def4 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 - 2.5.2-SNAPSHOT + 2.6.0 grpc-google-cloud-firestore-admin-v1 GRPC library for grpc-google-cloud-firestore-admin-v1 com.google.cloud google-cloud-firestore-parent - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/grpc-google-cloud-firestore-v1/pom.xml b/grpc-google-cloud-firestore-v1/pom.xml index 67ae35b96..9b35c8399 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 - 2.5.2-SNAPSHOT + 2.6.0 grpc-google-cloud-firestore-v1 GRPC library for grpc-google-cloud-firestore-v1 com.google.cloud google-cloud-firestore-parent - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/pom.xml b/pom.xml index c9c0ac390..8b34340ed 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.cloud google-cloud-firestore-parent pom - 2.5.2-SNAPSHOT + 2.6.0 Google Cloud Firestore Parent https://github.com/googleapis/java-firestore @@ -159,27 +159,27 @@ com.google.api.grpc proto-google-cloud-firestore-admin-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.cloud proto-google-cloud-firestore-bundle-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.api.grpc proto-google-cloud-firestore-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.api.grpc grpc-google-cloud-firestore-admin-v1 - 2.5.2-SNAPSHOT + 2.6.0 com.google.api.grpc grpc-google-cloud-firestore-v1 - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/proto-google-cloud-firestore-admin-v1/pom.xml b/proto-google-cloud-firestore-admin-v1/pom.xml index 28cba0685..fc32e3c66 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 - 2.5.2-SNAPSHOT + 2.6.0 proto-google-cloud-firestore-admin-v1 PROTO library for proto-google-cloud-firestore-admin-v1 com.google.cloud google-cloud-firestore-parent - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/proto-google-cloud-firestore-bundle-v1/pom.xml b/proto-google-cloud-firestore-bundle-v1/pom.xml index a09b9381d..3065f88f6 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 - 2.5.2-SNAPSHOT + 2.6.0 proto-google-cloud-firestore-bundle-v1 PROTO library for proto-google-cloud-firestore-bundle-v1 com.google.cloud google-cloud-firestore-parent - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/proto-google-cloud-firestore-v1/pom.xml b/proto-google-cloud-firestore-v1/pom.xml index ad71bdd05..0481dbdfb 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 - 2.5.2-SNAPSHOT + 2.6.0 proto-google-cloud-firestore-v1 PROTO library for proto-google-cloud-firestore-v1 com.google.cloud google-cloud-firestore-parent - 2.5.2-SNAPSHOT + 2.6.0 diff --git a/versions.txt b/versions.txt index 6eb70d23f..0c22ddc7c 100644 --- a/versions.txt +++ b/versions.txt @@ -1,11 +1,11 @@ # Format: # module:released-version:current-version -google-cloud-firestore:2.5.1:2.5.2-SNAPSHOT -google-cloud-firestore-admin:2.5.1:2.5.2-SNAPSHOT -google-cloud-firestore-bom:2.5.1:2.5.2-SNAPSHOT -google-cloud-firestore-parent:2.5.1:2.5.2-SNAPSHOT -grpc-google-cloud-firestore-admin-v1:2.5.1:2.5.2-SNAPSHOT -grpc-google-cloud-firestore-v1:2.5.1:2.5.2-SNAPSHOT -proto-google-cloud-firestore-admin-v1:2.5.1:2.5.2-SNAPSHOT -proto-google-cloud-firestore-v1:2.5.1:2.5.2-SNAPSHOT +google-cloud-firestore:2.6.0:2.6.0 +google-cloud-firestore-admin:2.6.0:2.6.0 +google-cloud-firestore-bom:2.6.0:2.6.0 +google-cloud-firestore-parent:2.6.0:2.6.0 +grpc-google-cloud-firestore-admin-v1:2.6.0:2.6.0 +grpc-google-cloud-firestore-v1:2.6.0:2.6.0 +proto-google-cloud-firestore-admin-v1:2.6.0:2.6.0 +proto-google-cloud-firestore-v1:2.6.0:2.6.0