From 5e1caf463d427e39bdef9ef109a5462655a2719b Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Sun, 18 Oct 2020 17:39:04 +0200 Subject: [PATCH 1/7] feat: add CommitStats --- .github/workflows/approve-readme.yaml | 54 + .../clirr-ignored-differences.xml | 23 + .../com/google/cloud/spanner/AsyncRunner.java | 14 + .../google/cloud/spanner/AsyncRunnerImpl.java | 29 +- .../spanner/AsyncTransactionManager.java | 14 + .../spanner/AsyncTransactionManagerImpl.java | 26 +- .../com/google/cloud/spanner/CommitStats.java | 52 + .../google/cloud/spanner/DatabaseClient.java | 24 + .../cloud/spanner/DatabaseClientImpl.java | 41 + .../com/google/cloud/spanner/SessionImpl.java | 54 +- .../com/google/cloud/spanner/SessionPool.java | 112 +- .../SessionPoolAsyncTransactionManager.java | 32 + .../cloud/spanner/TransactionManager.java | 10 + .../cloud/spanner/TransactionManagerImpl.java | 20 +- .../cloud/spanner/TransactionRunner.java | 14 + .../cloud/spanner/TransactionRunnerImpl.java | 39 +- .../AsyncTransactionManagerImplTest.java | 56 + .../spanner/AsyncTransactionManagerTest.java | 23 + .../cloud/spanner/DatabaseClientImplTest.java | 87 ++ .../cloud/spanner/MockSpannerServiceImpl.java | 22 +- .../spanner/TransactionContextImplTest.java | 33 + .../spanner/TransactionManagerImplTest.java | 19 +- .../spanner/TransactionRunnerImplTest.java | 23 +- .../connection/ConnectionImplTest.java | 21 + .../connection/ReadWriteTransactionTest.java | 11 + .../connection/SingleUseTransactionTest.java | 21 + .../cloud/spanner/it/ITAsyncAPITest.java | 64 + .../spanner/it/ITTransactionManagerTest.java | 26 + .../cloud/spanner/it/ITTransactionTest.java | 18 + .../google/cloud/spanner/it/ITWriteTest.java | 35 + .../com/google/spanner/v1/CommitRequest.java | 98 ++ .../spanner/v1/CommitRequestOrBuilder.java | 15 + .../com/google/spanner/v1/CommitResponse.java | 1163 +++++++++++++++++ .../spanner/v1/CommitResponseOrBuilder.java | 41 + .../com/google/spanner/v1/SpannerProto.java | 410 +++--- .../proto/google/spanner/v1/spanner.proto | 19 + synth.metadata | 289 ---- 37 files changed, 2543 insertions(+), 509 deletions(-) create mode 100644 .github/workflows/approve-readme.yaml create mode 100644 google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java create mode 100644 google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java diff --git a/.github/workflows/approve-readme.yaml b/.github/workflows/approve-readme.yaml new file mode 100644 index 0000000000..e2d841d6c5 --- /dev/null +++ b/.github/workflows/approve-readme.yaml @@ -0,0 +1,54 @@ +on: + pull_request: +name: auto-merge-readme +jobs: + approve: + runs-on: ubuntu-latest + if: github.repository_owner == 'googleapis' && github.head_ref == 'autosynth-readme' + steps: + - uses: actions/github-script@v3.0.0 + with: + github-token: ${{secrets.YOSHI_APPROVER_TOKEN}} + script: | + // only approve PRs from yoshi-automation + if (context.payload.pull_request.user.login !== "yoshi-automation") { + return; + } + + // only approve PRs like "chore: release " + if (!context.payload.pull_request.title === "chore: regenerate README") { + return; + } + + // only approve PRs with README.md and synth.metadata changes + const files = new Set( + ( + await github.paginate( + github.pulls.listFiles.endpoint({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }) + ) + ).map(file => file.filename) + ); + if (files.size != 2 || !files.has("README.md") || !files.has(".github/readme/synth.metadata/synth.metadata")) { + return; + } + + // approve README regeneration PR + await github.pulls.createReview({ + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Rubber stamped PR!', + pull_number: context.payload.pull_request.number, + event: 'APPROVE' + }); + + // attach automerge label + await github.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + labels: ['automerge'] + }); diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 1f7beb76e9..a594ffe51b 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -377,4 +377,27 @@ com/google/cloud/spanner/AsyncTransactionManager com.google.api.core.ApiFuture closeAsync() + + + + 7012 + com/google/cloud/spanner/TransactionManager + com.google.cloud.spanner.TransactionManager withCommitStats() + + + 7012 + com/google/cloud/spanner/TransactionManager + com.google.cloud.spanner.CommitStats getCommitStats() + + + 7012 + com/google/cloud/spanner/TransactionRunner + com.google.cloud.spanner.TransactionRunner withCommitStats() + + + 7012 + com/google/cloud/spanner/TransactionRunner + com.google.cloud.spanner.CommitStats getCommitStats() + + diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java index 3cae49e65b..32be0411ed 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java @@ -56,4 +56,18 @@ interface AsyncWork { * {@link ExecutionException} if the transaction did not commit. */ ApiFuture getCommitTimestamp(); + + /** + * Indicates that the {@link AsyncRunner} should request the backend to return {@link + * CommitStats}. The {@link CommitStats} can be retrieved by calling {@link #getCommitStats()} + * after the transaction has successfully committed. + */ + AsyncRunner withCommitStats(); + + /** + * Returns the {@link CommitStats} of this transaction. This method may only be called after the + * transaction has successfully committed, and only if {@link #withCommitStats()} was called + * before executing the transaction. + */ + ApiFuture getCommitStats(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java index 5b83402919..06383dc0dc 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java @@ -20,12 +20,15 @@ import com.google.api.core.SettableApiFuture; import com.google.cloud.Timestamp; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; +import com.google.common.base.Preconditions; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; class AsyncRunnerImpl implements AsyncRunner { private final TransactionRunnerImpl delegate; private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); + private final SettableApiFuture commitStats = SettableApiFuture.create(); + private boolean returnCommitStats; AsyncRunnerImpl(TransactionRunnerImpl delegate) { this.delegate = delegate; @@ -43,7 +46,7 @@ public void run() { } catch (Throwable t) { res.setException(t); } finally { - setCommitTimestamp(); + setCommitTimestampAndStats(); } } }); @@ -66,16 +69,38 @@ public R run(TransactionContext transaction) throws Exception { }); } - private void setCommitTimestamp() { + private void setCommitTimestampAndStats() { try { commitTimestamp.set(delegate.getCommitTimestamp()); } catch (Throwable t) { commitTimestamp.setException(t); } + if (returnCommitStats) { + try { + commitStats.set(delegate.getCommitStats()); + } catch (Throwable t) { + commitStats.setException(t); + } + } } @Override public ApiFuture getCommitTimestamp() { return commitTimestamp; } + + @Override + public AsyncRunner withCommitStats() { + delegate.withCommitStats(); + returnCommitStats = true; + return this; + } + + @Override + public ApiFuture getCommitStats() { + Preconditions.checkState( + returnCommitStats, + "getCommitStats may only be invoked if withCommitStats has been invoked before executing the transaction"); + return commitStats; + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java index 02d4a9dbd2..8ceb486b9e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java @@ -191,6 +191,20 @@ public interface AsyncTransactionFunction { /** Returns the state of the transaction. */ TransactionState getState(); + /** + * Indicates that the {@link AsyncTransactionManager} should request the backend to return {@link + * CommitStats}. The {@link CommitStats} can be retrieved by calling {@link #getCommitStats()} + * after the transaction has successfully committed. + */ + AsyncTransactionManager withCommitStats(); + + /** + * Returns the {@link CommitStats} of this transaction. This method may only be called after the + * transaction has successfully committed, and only if {@link #withCommitStats()} was called + * before committing the transaction. + */ + ApiFuture getCommitStats(); + /** * Closes the manager. If there is an active transaction, it will be rolled back. Underlying * session will be released back to the session pool. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java index 350349af16..192990a888 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java @@ -38,10 +38,12 @@ final class AsyncTransactionManagerImpl private final SessionImpl session; private Span span; + private boolean returnCommitStats; private TransactionRunnerImpl.TransactionContextImpl txn; private TransactionState txnState; private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); + private SettableApiFuture commitStats; AsyncTransactionManagerImpl(SessionImpl session, Span span) { this.session = session; @@ -53,6 +55,23 @@ public void setSpan(Span span) { this.span = span; } + @Override + public AsyncTransactionManager withCommitStats() { + this.returnCommitStats = true; + return this; + } + + @Override + public ApiFuture getCommitStats() { + Preconditions.checkState( + txnState == TransactionState.COMMITTED, + "getCommitStats can only be invoked if the transaction committed successfully"); + Preconditions.checkState( + returnCommitStats, + "getCommitStats can only be invoked if withCommitStats() was invoked before committing the transaction"); + return commitStats; + } + @Override public void close() { closeAsync(); @@ -119,7 +138,10 @@ public ApiFuture commitAsync() { SpannerExceptionFactory.newSpannerException( ErrorCode.ABORTED, "Transaction already aborted")); } - ApiFuture res = txn.commitAsync(); + ApiFuture res = txn.commitAsync(returnCommitStats); + if (returnCommitStats) { + commitStats = SettableApiFuture.create(); + } txnState = TransactionState.COMMITTED; ApiFutures.addCallback( res, @@ -131,12 +153,14 @@ public void onFailure(Throwable t) { } else { txnState = TransactionState.COMMIT_FAILED; commitTimestamp.setException(t); + commitStats.setException(t); } } @Override public void onSuccess(Timestamp result) { commitTimestamp.set(result); + commitStats.set(txn.commitStats()); } }, MoreExecutors.directExecutor()); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java new file mode 100644 index 0000000000..f07108f88b --- /dev/null +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 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.spanner; + +import com.google.common.base.Preconditions; +import org.threeten.bp.Duration; +import org.threeten.bp.temporal.ChronoUnit; + +/** + * Commit statistics are returned by a read/write transaction if specifically requested. + * + *

Usage: {@link TransactionRunner#withCommitStats()} + */ +public class CommitStats { + private final long mutationCount; + private final Duration overloadDelay; + + private CommitStats(long mutationCount, Duration overloadDelay) { + this.mutationCount = mutationCount; + this.overloadDelay = overloadDelay; + } + + static CommitStats fromProto(com.google.spanner.v1.CommitResponse.CommitStats proto) { + Preconditions.checkNotNull(proto); + return new CommitStats( + proto.getMutationCount(), + Duration.of(proto.getOverloadDelay().getSeconds(), ChronoUnit.SECONDS) + .plusNanos(proto.getOverloadDelay().getNanos())); + } + + public long getMutationCount() { + return mutationCount; + } + + public Duration getOverloadDelay() { + return overloadDelay; + } +} diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java index d52d1d892e..290265da83 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java @@ -83,6 +83,30 @@ public interface DatabaseClient { */ Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException; + /** + * Response for write methods that return both a commit {@link Timestamp} and {@link CommitStats}. + */ + interface WriteResponse { + /** The commit timestamp of the transaction. */ + Timestamp getCommitTimestamp(); + + /** The commit statistics of the transaction. */ + CommitStats getCommitStats(); + } + + /** + * Same as {@link #write(Iterable)}, but requests the backend to return both a commit timestamp + * and {@link CommitStats}. + */ + WriteResponse writeWithCommitStats(Iterable mutations) throws SpannerException; + + /** + * Same as {@link #writeAtLeastOnce(Iterable)}, but requests the backend to return both a commit + * timestamp and {@link CommitStats}. + */ + WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) + throws SpannerException; + /** * Returns a context in which a single read can be performed using {@link TimestampBound#strong()} * concurrency. This method will return a {@link ReadContext} that will not return the read diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java index 4dd10001c7..458368ec24 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java @@ -81,6 +81,26 @@ public Timestamp apply(Session session) { } } + public WriteResponse writeWithCommitStats(final Iterable mutations) + throws SpannerException { + Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); + try (Scope s = tracer.withSpan(span)) { + return runWithSessionRetry( + SessionMode.READ_WRITE, + new Function() { + @Override + public WriteResponse apply(Session session) { + return session.writeWithCommitStats(mutations); + } + }); + } catch (RuntimeException e) { + TraceUtil.setWithFailure(span, e); + throw e; + } finally { + span.end(TraceUtil.END_SPAN_OPTIONS); + } + } + @Override public Timestamp writeAtLeastOnce(final Iterable mutations) throws SpannerException { Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); @@ -101,6 +121,27 @@ public Timestamp apply(Session session) { } } + @Override + public WriteResponse writeAtLeastOnceWithCommitStats(final Iterable mutations) + throws SpannerException { + Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); + try (Scope s = tracer.withSpan(span)) { + return runWithSessionRetry( + SessionMode.READ_WRITE, + new Function() { + @Override + public WriteResponse apply(Session session) { + return session.writeAtLeastOnceWithCommitStats(mutations); + } + }); + } catch (RuntimeException e) { + TraceUtil.setWithFailure(span, e); + throw e; + } finally { + span.end(TraceUtil.END_SPAN_OPTIONS); + } + } + @Override public ReadContext singleUse() { Span span = tracer.spanBuilder(READ_ONLY_TRANSACTION).startSpan(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java index 6a91d85fef..1d99a544a2 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java @@ -84,6 +84,31 @@ static interface SessionTransaction { void setSpan(Span span); } + private static class WriteResponseImpl implements WriteResponse { + private final Timestamp commitTimestamp; + private final CommitStats commitStats; + + private WriteResponseImpl(TransactionRunner runner) { + this.commitTimestamp = runner.getCommitTimestamp(); + this.commitStats = runner.getCommitStats(); + } + + private WriteResponseImpl(CommitResponse response) { + this.commitTimestamp = Timestamp.fromProto(response.getCommitTimestamp()); + this.commitStats = CommitStats.fromProto(response.getCommitStats()); + } + + @Override + public Timestamp getCommitTimestamp() { + return this.commitTimestamp; + } + + @Override + public CommitStats getCommitStats() { + return this.commitStats; + } + } + private final SpannerImpl spanner; private final String name; private final DatabaseId databaseId; @@ -123,7 +148,19 @@ public long executePartitionedUpdate(Statement stmt) { @Override public Timestamp write(Iterable mutations) throws SpannerException { + return write(mutations, false).getCommitTimestamp(); + } + + @Override + public WriteResponse writeWithCommitStats(Iterable mutations) throws SpannerException { + return new WriteResponseImpl(write(mutations, true)); + } + + private TransactionRunner write(Iterable mutations, boolean withCommitStats) { TransactionRunner runner = readWriteTransaction(); + if (withCommitStats) { + runner = runner.withCommitStats(); + } final Collection finalMutations = mutations instanceof java.util.Collection ? (Collection) mutations @@ -136,17 +173,28 @@ public Void run(TransactionContext ctx) { return null; } }); - return runner.getCommitTimestamp(); + return runner; } @Override public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException { + return Timestamp.fromProto(writeAtLeastOnce(mutations, false).getCommitTimestamp()); + } + + @Override + public WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) + throws SpannerException { + return new WriteResponseImpl(writeAtLeastOnce(mutations, true)); + } + + private CommitResponse writeAtLeastOnce(Iterable mutations, boolean withCommitStats) { setActive(null); List mutationsProto = new ArrayList<>(); Mutation.toProto(mutations, mutationsProto); final CommitRequest request = CommitRequest.newBuilder() .setSession(name) + .setReturnCommitStats(withCommitStats) .addAllMutations(mutationsProto) .setSingleUseTransaction( TransactionOptions.newBuilder() @@ -154,9 +202,7 @@ public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerEx .build(); Span span = tracer.spanBuilder(SpannerImpl.COMMIT).startSpan(); try (Scope s = tracer.withSpan(span)) { - CommitResponse response = spanner.getRpc().commit(request, options); - Timestamp t = Timestamp.fromProto(response.getCommitTimestamp()); - return t; + return spanner.getRpc().commit(request, options); } catch (IllegalArgumentException e) { TraceUtil.setWithFailure(span, e); throw newSpannerException(ErrorCode.INTERNAL, "Could not parse commit timestamp", e); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java index 2512024117..0d8f23a3c6 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java @@ -720,6 +720,7 @@ public void close() { private TransactionManager delegate; private final SessionPool sessionPool; private PooledSessionFuture session; + private boolean returnCommitStats; private boolean closed; private boolean restartedAfterSessionNotFound; @@ -731,16 +732,27 @@ public void close() { @Override public TransactionContext begin() { this.delegate = session.get().transactionManager(); + if (returnCommitStats) { + this.delegate.withCommitStats(); + } while (true) { try { return internalBegin(); } catch (SessionNotFoundException e) { session = sessionPool.replaceReadWriteSession(e, session); - delegate = session.get().delegate.transactionManager(); + refreshDelegateTransactionManager(); } } } + @SuppressWarnings("resource") + private void refreshDelegateTransactionManager() { + delegate = session.get().delegate.transactionManager(); + if (returnCommitStats) { + delegate.withCommitStats(); + } + } + private TransactionContext internalBegin() { TransactionContext res = new SessionPoolTransactionContext(delegate.begin()); session.get().markUsed(); @@ -749,7 +761,7 @@ private TransactionContext internalBegin() { private SpannerException handleSessionNotFound(SessionNotFoundException notFound) { session = sessionPool.replaceReadWriteSession(notFound, session); - delegate = session.get().delegate.transactionManager(); + refreshDelegateTransactionManager(); restartedAfterSessionNotFound = true; return SpannerExceptionFactory.newSpannerException( ErrorCode.ABORTED, notFound.getMessage(), notFound); @@ -790,7 +802,7 @@ public TransactionContext resetForRetry() { } } catch (SessionNotFoundException e) { session = sessionPool.replaceReadWriteSession(e, session); - delegate = session.get().delegate.transactionManager(); + refreshDelegateTransactionManager(); restartedAfterSessionNotFound = true; } } @@ -801,6 +813,17 @@ public Timestamp getCommitTimestamp() { return delegate.getCommitTimestamp(); } + @Override + public TransactionManager withCommitStats() { + this.returnCommitStats = true; + return this; + } + + @Override + public CommitStats getCommitStats() { + return delegate.getCommitStats(); + } + @Override public void close() { if (closed) { @@ -880,12 +903,25 @@ public TransactionRunner allowNestedTransaction() { getRunner().allowNestedTransaction(); return this; } + + @Override + public TransactionRunner withCommitStats() { + getRunner().withCommitStats(); + return this; + } + + @Override + public CommitStats getCommitStats() { + return getRunner().getCommitStats(); + } } private static class SessionPoolAsyncRunner implements AsyncRunner { private final SessionPool sessionPool; private volatile PooledSessionFuture session; private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); + private final SettableApiFuture commitStats = SettableApiFuture.create(); + private boolean returnCommitStats; private SessionPoolAsyncRunner(SessionPool sessionPool, PooledSessionFuture session) { this.sessionPool = sessionPool; @@ -905,6 +941,9 @@ public void run() { while (true) { try { runner = session.get().runAsync(); + if (returnCommitStats) { + runner.withCommitStats(); + } r = runner.runAsync(work, MoreExecutors.directExecutor()).get(); break; } catch (ExecutionException e) { @@ -924,7 +963,7 @@ public void run() { } session.get().markUsed(); session.close(); - setCommitTimestamp(runner); + setCommitTimestampAndStats(runner); if (se != null) { res.setException(se); } else { @@ -935,18 +974,39 @@ public void run() { return res; } - private void setCommitTimestamp(AsyncRunner delegate) { + private void setCommitTimestampAndStats(AsyncRunner delegate) { try { commitTimestamp.set(delegate.getCommitTimestamp().get()); } catch (Throwable t) { commitTimestamp.setException(t); } + if (returnCommitStats) { + try { + commitStats.set(delegate.getCommitStats().get()); + } catch (Throwable t) { + commitStats.setException(t); + } + } } @Override public ApiFuture getCommitTimestamp() { return commitTimestamp; } + + @Override + public AsyncRunner withCommitStats() { + this.returnCommitStats = true; + return this; + } + + @Override + public ApiFuture getCommitStats() { + Preconditions.checkState( + returnCommitStats, + "getCommitStats may only be invoked if withCommitStats has been invoked before executing the transaction"); + return commitStats; + } } // Exception class used just to track the stack trace at the point when a session was handed out @@ -1103,6 +1163,16 @@ public Timestamp write(Iterable mutations) throws SpannerException { } } + @Override + public WriteResponse writeWithCommitStats(Iterable mutations) + throws SpannerException { + try { + return get().writeWithCommitStats(mutations); + } finally { + close(); + } + } + @Override public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException { try { @@ -1112,6 +1182,16 @@ public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerEx } } + @Override + public WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) + throws SpannerException { + try { + return get().writeAtLeastOnceWithCommitStats(mutations); + } finally { + close(); + } + } + @Override public ReadContext singleUse() { try { @@ -1347,6 +1427,17 @@ public Timestamp write(Iterable mutations) throws SpannerException { } } + @Override + public WriteResponse writeWithCommitStats(Iterable mutations) + throws SpannerException { + try { + markUsed(); + return delegate.writeWithCommitStats(mutations); + } catch (SpannerException e) { + throw lastException = e; + } + } + @Override public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException { try { @@ -1357,6 +1448,17 @@ public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerEx } } + @Override + public WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) + throws SpannerException { + try { + markUsed(); + return delegate.writeAtLeastOnceWithCommitStats(mutations); + } catch (SpannerException e) { + throw lastException = e; + } + } + @Override public long executePartitionedUpdate(Statement stmt) throws SpannerException { try { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java index 54b621b93b..0019c1df7a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java @@ -239,4 +239,36 @@ public TransactionState getState() { return txnState; } } + + @Override + public AsyncTransactionManager withCommitStats() { + ApiFutures.addCallback( + delegate, + new ApiFutureCallback() { + @Override + public void onFailure(Throwable t) { + // Ignore. + } + + @Override + public void onSuccess(AsyncTransactionManagerImpl result) { + result.withCommitStats(); + } + }, + MoreExecutors.directExecutor()); + return this; + } + + @Override + public ApiFuture getCommitStats() { + return ApiFutures.transformAsync( + delegate, + new ApiAsyncFunction() { + @Override + public ApiFuture apply(AsyncTransactionManagerImpl input) throws Exception { + return input.getCommitStats(); + } + }, + MoreExecutors.directExecutor()); + } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java index 34e2165715..e2e6ba7471 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java @@ -90,6 +90,16 @@ public enum TransactionState { /** Returns the state of the transaction. */ TransactionState getState(); + /** Instructs the {@link TransactionManager} to request {@link CommitStats} from the backend. */ + TransactionManager withCommitStats(); + + /** + * Returns the {@link CommitStats} if {@link #withCommitStats()} was called before the transaction + * was committed and the transaction committed successfully. Otherwise it will throw {@code + * IllegalStateException}. + */ + CommitStats getCommitStats(); + /** * Closes the manager. If there is an active transaction, it will be rolled back. Underlying * session will be released back to the session pool. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java index 8dbab88314..c7875c8314 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java @@ -30,6 +30,7 @@ final class TransactionManagerImpl implements TransactionManager, SessionTransac private final SessionImpl session; private Span span; + private boolean returnCommitStats; private TransactionRunnerImpl.TransactionContextImpl txn; private TransactionState txnState; @@ -43,6 +44,12 @@ Span getSpan() { return span; } + @Override + public TransactionManager withCommitStats() { + this.returnCommitStats = true; + return this; + } + @Override public void setSpan(Span span) { this.span = span; @@ -71,7 +78,7 @@ public void commit() { ErrorCode.ABORTED, "Transaction already aborted"); } try { - txn.commit(); + txn.commit(returnCommitStats); txnState = TransactionState.COMMITTED; } catch (AbortedException e1) { txnState = TransactionState.ABORTED; @@ -116,6 +123,17 @@ public Timestamp getCommitTimestamp() { return txn.commitTimestamp(); } + @Override + public CommitStats getCommitStats() { + Preconditions.checkState( + txnState == TransactionState.COMMITTED, + "getCommitStats can only be invoked if the transaction committed successfully"); + Preconditions.checkState( + returnCommitStats, + "getCommitStats can only be invoked if withCommitStats() was invoked before committing the transaction"); + return txn.commitStats(); + } + @Override public void close() { try { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java index 2ad27fcd7b..928079cdba 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java @@ -91,4 +91,18 @@ interface TransactionCallable { * @return this object */ TransactionRunner allowNestedTransaction(); + + /** + * Indicates that the {@link TransactionRunner} should request the backend to return {@link + * CommitStats}. The {@link CommitStats} can be retrieved by calling {@link #getCommitStats()} + * after the transaction has successfully committed. + */ + TransactionRunner withCommitStats(); + + /** + * Returns the {@link CommitStats} of this transaction. This method may only be called after the + * transaction has successfully committed, and only if {@link #withCommitStats()} was called + * before executing the transaction. + */ + CommitStats getCommitStats(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java index ab4a80b340..2d0a731d2f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java @@ -152,6 +152,7 @@ public void removeListener(Runnable listener) { private volatile ByteString transactionId; private Timestamp commitTimestamp; + private CommitStats commitStats; private TransactionContextImpl(Builder builder) { super(builder); @@ -234,9 +235,9 @@ public void run() { return res; } - void commit() { + void commit(boolean returnCommitStats) { try { - commitTimestamp = commitAsync().get(); + commitTimestamp = commitAsync(returnCommitStats).get(); } catch (InterruptedException e) { if (commitFuture != null) { commitFuture.cancel(true); @@ -249,7 +250,7 @@ void commit() { volatile ApiFuture commitFuture; - ApiFuture commitAsync() { + ApiFuture commitAsync(final boolean returnCommitStats) { final SettableApiFuture res = SettableApiFuture.create(); final SettableApiFuture latch; synchronized (lock) { @@ -263,6 +264,7 @@ public void run() { latch.get(); CommitRequest.Builder builder = CommitRequest.newBuilder() + .setReturnCommitStats(returnCommitStats) .setSession(session.getName()) .setTransactionId(transactionId); synchronized (lock) { @@ -292,6 +294,15 @@ public void run() { ErrorCode.INTERNAL, "Missing commitTimestamp:\n" + session.getName()); } + if (returnCommitStats && !commitResponse.hasCommitStats()) { + throw newSpannerException( + ErrorCode.INTERNAL, + "Missing commitStats:\n" + session.getName()); + } + if (commitResponse.hasCommitStats()) { + commitStats = + CommitStats.fromProto(commitResponse.getCommitStats()); + } Timestamp ts = Timestamp.fromProto(commitResponse.getCommitTimestamp()); span.addAnnotation("Commit Done"); @@ -336,6 +347,13 @@ Timestamp commitTimestamp() { return commitTimestamp; } + CommitStats commitStats() { + checkState( + commitStats != null, + "run() has not yet returned normally or no commit statistics were requested"); + return commitStats; + } + boolean isAborted() { synchronized (lock) { return aborted; @@ -617,6 +635,7 @@ public ListenableAsyncResultSet executeQueryAsync( } private boolean blockNestedTxn = true; + private boolean returnCommitStats = false; private final SessionImpl session; private Span span; private TransactionContextImpl txn; @@ -628,6 +647,12 @@ public TransactionRunner allowNestedTransaction() { return this; } + @Override + public TransactionRunner withCommitStats() { + returnCommitStats = true; + return this; + } + TransactionRunnerImpl(SessionImpl session, SpannerRpc rpc, int defaultPrefetchChunks) { this.session = session; this.txn = session.newTransaction(); @@ -716,7 +741,7 @@ public T call() { } try { - txn.commit(); + txn.commit(returnCommitStats); span.addAnnotation( "Transaction Attempt Succeeded", ImmutableMap.of( @@ -749,6 +774,12 @@ public Timestamp getCommitTimestamp() { return txn.commitTimestamp(); } + @Override + public CommitStats getCommitStats() { + checkState(txn != null, "run() has not yet returned normally"); + return txn.commitStats(); + } + @Override public void invalidate() { isValid = false; diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java new file mode 100644 index 0000000000..7d3f051053 --- /dev/null +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2020 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.spanner; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.api.core.ApiFutures; +import com.google.cloud.Timestamp; +import io.opencensus.trace.Span; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; + +@RunWith(JUnit4.class) +public class AsyncTransactionManagerImplTest { + + @Mock private SessionImpl session; + @Mock TransactionRunnerImpl.TransactionContextImpl txn; + private AsyncTransactionManagerImpl manager; + + @Before + public void setUp() { + initMocks(this); + manager = new AsyncTransactionManagerImpl(session, mock(Span.class)); + } + + @Test + public void commitReturnsCommitStats() { + when(session.newTransaction()).thenReturn(txn); + when(txn.ensureTxnAsync()).thenReturn(ApiFutures.immediateFuture(null)); + Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); + when(txn.commitAsync(true)).thenReturn(ApiFutures.immediateFuture(commitTimestamp)); + manager.withCommitStats().beginAsync(); + manager.commitAsync(); + verify(txn).commitAsync(true); + } +} diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java index 1d1b16d014..eba0c55c8f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java @@ -210,6 +210,29 @@ public boolean apply(AbstractMessage input) { 0L); } + @Test + public void asyncTransactionManager_returnsCommitStats() throws Exception { + try (AsyncTransactionManager manager = client().transactionManagerAsync().withCommitStats()) { + TransactionContextFuture txn = manager.beginAsync(); + while (true) { + try { + CommitTimestampFuture commitTimestamp = + txn.then( + AsyncTransactionManagerHelper.buffer( + Mutation.delete("FOO", Key.of("foo"))), + executor) + .commitAsync(); + assertThat(commitTimestamp.get()).isNotNull(); + assertThat(manager.getCommitStats().get()).isNotNull(); + assertThat(manager.getCommitStats().get().getMutationCount()).isEqualTo(1); + break; + } catch (AbortedException e) { + txn = manager.resetForRetryAsync(); + } + } + } + } + @Test public void asyncTransactionManagerUpdate() throws Exception { final SettableApiFuture updateCount = SettableApiFuture.create(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index 8775fb1b18..975058a524 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -29,6 +29,7 @@ import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; import com.google.cloud.spanner.AsyncResultSet.ReadyCallback; import com.google.cloud.spanner.AsyncRunner.AsyncWork; +import com.google.cloud.spanner.DatabaseClient.WriteResponse; import com.google.cloud.spanner.MockSpannerServiceImpl.SimulatedExecutionTime; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode; @@ -157,6 +158,19 @@ public void write() { Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); } + @Test + public void writeWithCommitStats() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + WriteResponse response = + client.writeWithCommitStats( + Arrays.asList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + assertThat(response).isNotNull(); + assertThat(response.getCommitTimestamp()).isNotNull(); + assertThat(response.getCommitStats()).isNotNull(); + } + @Test public void writeAtLeastOnce() { DatabaseClient client = @@ -166,6 +180,19 @@ public void writeAtLeastOnce() { Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); } + @Test + public void writeAtLeastOnceWithCommitStats() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + WriteResponse response = + client.writeAtLeastOnceWithCommitStats( + Arrays.asList( + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + assertThat(response).isNotNull(); + assertThat(response.getCommitTimestamp()).isNotNull(); + assertThat(response.getCommitStats()).isNotNull(); + } + @Test public void singleUse() { DatabaseClient client = @@ -434,6 +461,23 @@ public Void run(TransactionContext transaction) throws Exception { }); } + @Test + public void readWriteTransaction_returnsCommitStats() { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + TransactionRunner runner = client.readWriteTransaction().withCommitStats(); + runner.run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.buffer(Mutation.delete("FOO", Key.of("foo"))); + return null; + } + }); + assertThat(runner.getCommitStats()).isNotNull(); + assertThat(runner.getCommitStats().getMutationCount()).isEqualTo(1); + } + @Test public void readWriteTransactionIsNonBlocking() { mockSpanner.freeze(); @@ -473,6 +517,28 @@ public ApiFuture doWorkAsync(TransactionContext txn) { executor.shutdown(); } + @Test + public void runAsync_returnsCommitStats() throws Exception { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + ExecutorService executor = Executors.newSingleThreadExecutor(); + AsyncRunner runner = client.runAsync().withCommitStats(); + ApiFuture fut = + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + txn.buffer(Mutation.delete("FOO", Key.of("foo"))); + return ApiFutures.immediateFuture(null); + } + }, + executor); + assertThat(fut.get()).isNull(); + assertThat(runner.getCommitStats().get()).isNotNull(); + assertThat(runner.getCommitStats().get().getMutationCount()).isEqualTo(1); + executor.shutdown(); + } + @Test public void runAsyncIsNonBlocking() throws Exception { mockSpanner.freeze(); @@ -540,6 +606,27 @@ public void transactionManager() throws Exception { } } + @Test + public void transactionManager_returnsCommitStats() throws Exception { + DatabaseClient client = + spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); + try (TransactionManager txManager = client.transactionManager().withCommitStats()) { + while (true) { + TransactionContext tx = txManager.begin(); + try { + tx.buffer(Mutation.delete("FOO", Key.of("foo"))); + txManager.commit(); + assertThat(txManager.getCommitStats()).isNotNull(); + assertThat(txManager.getCommitStats().getMutationCount()).isEqualTo(1); + break; + } catch (AbortedException e) { + Thread.sleep(e.getRetryDelayInMillis() / 1000); + tx = txManager.resetForRetry(); + } + } + } + } + @Test public void transactionManagerIsNonBlocking() throws Exception { mockSpanner.freeze(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java index 5ecf9607a4..2ee5eef1da 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/MockSpannerServiceImpl.java @@ -42,6 +42,7 @@ import com.google.spanner.v1.BeginTransactionRequest; import com.google.spanner.v1.CommitRequest; import com.google.spanner.v1.CommitResponse; +import com.google.spanner.v1.CommitResponse.CommitStats; import com.google.spanner.v1.CreateSessionRequest; import com.google.spanner.v1.DeleteSessionRequest; import com.google.spanner.v1.ExecuteBatchDmlRequest; @@ -186,6 +187,8 @@ * } */ public class MockSpannerServiceImpl extends SpannerImplBase implements MockGrpcService { + private static final Random RANDOM = new Random(); + private static class PartialResultSetsIterator implements Iterator { private static final int MAX_ROWS_IN_CHUNK = 1; @@ -406,7 +409,6 @@ private StatusRuntimeException getException() { /** Class for simulating execution time of server calls. */ public static class SimulatedExecutionTime { - private static final Random RANDOM = new Random(); private final int minimumExecutionTime; private final int randomExecutionTime; private final Queue exceptions; @@ -498,7 +500,9 @@ void simulateExecutionTime( } if (minimumExecutionTime > 0 || randomExecutionTime > 0) { Uninterruptibles.sleepUninterruptibly( - (randomExecutionTime == 0 ? 0 : RANDOM.nextInt(randomExecutionTime)) + (randomExecutionTime == 0 + ? 0 + : MockSpannerServiceImpl.RANDOM.nextInt(randomExecutionTime)) + minimumExecutionTime, TimeUnit.MILLISECONDS); } @@ -1766,8 +1770,18 @@ public void commit(CommitRequest request, StreamObserver respons } simulateAbort(session, request.getTransactionId()); commitTransaction(transaction.getId()); - responseObserver.onNext( - CommitResponse.newBuilder().setCommitTimestamp(getCurrentGoogleTimestamp()).build()); + CommitResponse.Builder responseBuilder = + CommitResponse.newBuilder().setCommitTimestamp(getCurrentGoogleTimestamp()); + if (request.getReturnCommitStats()) { + responseBuilder.setCommitStats( + CommitStats.newBuilder() + // This is not really always equal, but at least it returns a value. + .setMutationCount(request.getMutationsCount()) + .setOverloadDelay( + Duration.newBuilder().setSeconds(0L).setNanos(RANDOM.nextInt(1_000_000_000))) + .build()); + } + responseObserver.onNext(responseBuilder.build()); responseObserver.onCompleted(); } catch (StatusRuntimeException t) { responseObserver.onError(t); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java index 077b660576..d01e20419c 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionContextImplTest.java @@ -16,14 +16,20 @@ package com.google.cloud.spanner; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyMap; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.api.core.ApiFutures; import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl; import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.protobuf.ByteString; import com.google.rpc.Code; import com.google.rpc.Status; +import com.google.spanner.v1.CommitRequest; +import com.google.spanner.v1.CommitResponse; import com.google.spanner.v1.ExecuteBatchDmlRequest; import com.google.spanner.v1.ExecuteBatchDmlResponse; import java.util.Arrays; @@ -45,6 +51,33 @@ public void batchDmlException() { batchDml(Code.FAILED_PRECONDITION_VALUE); } + @SuppressWarnings("unchecked") + @Test + public void testReturnCommitStats() { + SessionImpl session = mock(SessionImpl.class); + when(session.getName()).thenReturn("test"); + ByteString txId = ByteString.copyFromUtf8("test"); + SpannerRpc rpc = mock(SpannerRpc.class); + when(rpc.commitAsync(any(CommitRequest.class), anyMap())) + .thenReturn(ApiFutures.immediateFuture(CommitResponse.getDefaultInstance())); + + try (TransactionContextImpl impl = + TransactionContextImpl.newBuilder() + .setSession(session) + .setRpc(rpc) + .setTransactionId(txId) + .build()) { + impl.commitAsync(true); + CommitRequest request = + CommitRequest.newBuilder() + .setReturnCommitStats(true) + .setSession(session.getName()) + .setTransactionId(txId) + .build(); + verify(rpc).commitAsync(Mockito.eq(request), anyMap()); + } + } + @SuppressWarnings("unchecked") private void batchDml(int status) { SessionImpl session = mock(SessionImpl.class); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java index 38aa66516e..1bfea218f6 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java @@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -143,6 +144,16 @@ public void commitSucceeds() { assertThat(manager.getCommitTimestamp()).isEqualTo(commitTimestamp); } + @Test + public void commitReturnsCommitStats() { + when(session.newTransaction()).thenReturn(txn); + Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); + when(txn.commitTimestamp()).thenReturn(commitTimestamp); + manager.withCommitStats().begin(); + manager.commit(); + verify(txn).commit(true); + } + @Test public void resetAfterSuccessfulCommitFails() { when(session.newTransaction()).thenReturn(txn); @@ -160,7 +171,9 @@ public void resetAfterSuccessfulCommitFails() { public void resetAfterAbortSucceeds() { when(session.newTransaction()).thenReturn(txn); manager.begin(); - doThrow(SpannerExceptionFactory.newSpannerException(ErrorCode.ABORTED, "")).when(txn).commit(); + doThrow(SpannerExceptionFactory.newSpannerException(ErrorCode.ABORTED, "")) + .when(txn) + .commit(any(Boolean.class)); try { manager.commit(); fail("Expected AbortedException"); @@ -177,7 +190,9 @@ public void resetAfterAbortSucceeds() { public void resetAfterErrorFails() { when(session.newTransaction()).thenReturn(txn); manager.begin(); - doThrow(SpannerExceptionFactory.newSpannerException(ErrorCode.UNKNOWN, "")).when(txn).commit(); + doThrow(SpannerExceptionFactory.newSpannerException(ErrorCode.UNKNOWN, "")) + .when(txn) + .commit(any(Boolean.class)); try { manager.commit(); fail("Expected AbortedException"); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java index d61c89300f..60b2063c95 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -189,7 +190,21 @@ public Void run(TransactionContext transaction) { }); assertThat(numCalls.get()).isEqualTo(1); verify(txn).ensureTxn(); - verify(txn).commit(); + verify(txn).commit(false); + } + + @Test + public void testReturnCommitStats() { + transactionRunner + .withCommitStats() + .run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + return null; + } + }); + verify(txn).commit(true); } @Test @@ -203,7 +218,7 @@ public void runAbort() { public void commitAbort() { final SpannerException error = SpannerExceptionFactory.newSpannerException(abortedWithRetryInfo()); - doThrow(error).doNothing().when(txn).commit(); + doThrow(error).doNothing().when(txn).commit(any(Boolean.class)); final AtomicInteger numCalls = new AtomicInteger(0); transactionRunner.run( new TransactionCallable() { @@ -222,7 +237,7 @@ public void commitFailsWithNonAbort() { final SpannerException error = SpannerExceptionFactory.newSpannerException( SpannerExceptionFactory.newSpannerException(ErrorCode.UNKNOWN, "")); - doThrow(error).when(txn).commit(); + doThrow(error).when(txn).commit(any(Boolean.class)); final AtomicInteger numCalls = new AtomicInteger(0); try { transactionRunner.run( @@ -239,7 +254,7 @@ public Void run(TransactionContext transaction) { } assertThat(numCalls.get()).isEqualTo(1); verify(txn, times(1)).ensureTxn(); - verify(txn, times(1)).commit(); + verify(txn, times(1)).commit(false); } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java index 88f942122a..7da737ca3f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java @@ -37,6 +37,7 @@ import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.NoCredentials; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.CommitStats; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.ForwardingResultSet; @@ -129,6 +130,16 @@ public void close() { state = TransactionState.ROLLED_BACK; } } + + @Override + public TransactionManager withCommitStats() { + return this; + } + + @Override + public CommitStats getCommitStats() { + throw new UnsupportedOperationException(); + } } private static class SimpleResultSet extends ForwardingResultSet { @@ -340,6 +351,16 @@ public Timestamp getCommitTimestamp() { public TransactionRunner allowNestedTransaction() { return this; } + + @Override + public TransactionRunner withCommitStats() { + return this; + } + + @Override + public CommitStats getCommitStats() { + throw new UnsupportedOperationException(); + } }; return runner; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java index 1e094eaeb6..44245fc9e1 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java @@ -32,6 +32,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.spanner.AbortedException; +import com.google.cloud.spanner.CommitStats; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.ReadContext.QueryAnalyzeMode; @@ -129,6 +130,16 @@ public void close() { state = TransactionState.ROLLED_BACK; } } + + @Override + public TransactionManager withCommitStats() { + return this; + } + + @Override + public CommitStats getCommitStats() { + throw new UnsupportedOperationException(); + } } private ReadWriteTransaction createSubject() { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java index 76ef62a21a..3024db5924 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java @@ -29,6 +29,7 @@ import com.google.api.gax.longrunning.OperationFuture; import com.google.cloud.Timestamp; import com.google.cloud.spanner.AsyncResultSet; +import com.google.cloud.spanner.CommitStats; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.Key; @@ -157,6 +158,16 @@ public void close() { state = TransactionState.ROLLED_BACK; } } + + @Override + public TransactionManager withCommitStats() { + return this; + } + + @Override + public CommitStats getCommitStats() { + throw new UnsupportedOperationException(); + } } private static final class SimpleReadOnlyTransaction @@ -421,6 +432,16 @@ public Timestamp getCommitTimestamp() { public TransactionRunner allowNestedTransaction() { return this; } + + @Override + public TransactionRunner withCommitStats() { + return this; + } + + @Override + public CommitStats getCommitStats() { + throw new UnsupportedOperationException(); + } }; return runner; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java index 721536cb6b..f5436efb32 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java @@ -16,15 +16,21 @@ package com.google.cloud.spanner.it; +import static com.google.cloud.spanner.SpannerApiFutures.get; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.api.core.ApiFuture; +import com.google.api.core.ApiFutures; +import com.google.cloud.spanner.AbortedException; import com.google.cloud.spanner.AsyncResultSet; import com.google.cloud.spanner.AsyncResultSet.CallbackResponse; import com.google.cloud.spanner.AsyncResultSet.ReadyCallback; import com.google.cloud.spanner.AsyncRunner; import com.google.cloud.spanner.AsyncRunner.AsyncWork; +import com.google.cloud.spanner.AsyncTransactionManager; +import com.google.cloud.spanner.AsyncTransactionManager.AsyncTransactionFunction; +import com.google.cloud.spanner.AsyncTransactionManager.TransactionContextFuture; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; import com.google.cloud.spanner.DatabaseId; @@ -302,4 +308,62 @@ public ApiFuture doWorkAsync(TransactionContext txn) { assertThat(client.singleUse().readRow("TestTable", Key.of("k999"), ALL_COLUMNS)).isNull(); } } + + @Test + public void asyncRunnerReturnsCommitStats() { + AsyncRunner runner = client.runAsync().withCommitStats(); + runner.runAsync( + new AsyncWork() { + @Override + public ApiFuture doWorkAsync(TransactionContext txn) { + txn.buffer( + Mutation.newInsertOrUpdateBuilder(TABLE_NAME) + .set("Key") + .to("k_commit_stats") + .set("StringValue") + .to("Should return commit stats") + .build()); + return ApiFutures.immediateFuture(null); + } + }, + executor); + assertThat(get(runner.getCommitStats())).isNotNull(); + // MutationCount = 2 columns + 2 secondary indexes. + assertThat(get(runner.getCommitStats()).getMutationCount()).isEqualTo(4L); + } + + @Test + public void asyncTransactionManagerReturnsCommitStats() throws InterruptedException { + try (AsyncTransactionManager mgr = client.transactionManagerAsync().withCommitStats()) { + TransactionContextFuture ctx = mgr.beginAsync(); + while (true) { + try { + get( + ctx.then( + new AsyncTransactionFunction() { + @Override + public ApiFuture apply(TransactionContext txn, Void input) + throws Exception { + txn.buffer( + Mutation.newInsertOrUpdateBuilder(TABLE_NAME) + .set("Key") + .to("k_commit_stats") + .set("StringValue") + .to("Should return commit stats") + .build()); + return ApiFutures.immediateFuture(null); + } + }, + executor) + .commitAsync()); + assertThat(get(mgr.getCommitStats())).isNotNull(); + assertThat(get(mgr.getCommitStats()).getMutationCount()).isEqualTo(4L); + break; + } catch (AbortedException e) { + Thread.sleep(e.getRetryDelayInMillis() / 1000); + ctx = mgr.resetForRetryAsync(); + } + } + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java index 06087b9afe..d80589d4b8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java @@ -203,4 +203,30 @@ public void abortAndRetry() throws InterruptedException { assertThat(row.getBoolean(1)).isTrue(); } } + + @SuppressWarnings("resource") + @Test + public void transactionManagerReturnsCommitStats() throws InterruptedException { + try (TransactionManager manager = client.transactionManager().withCommitStats()) { + TransactionContext txn = manager.begin(); + while (true) { + txn.buffer( + Mutation.newInsertBuilder("T") + .set("K") + .to("KeyCommitStats") + .set("BoolValue") + .to(true) + .build()); + try { + manager.commit(); + assertThat(manager.getCommitStats()).isNotNull(); + assertThat(manager.getCommitStats().getMutationCount()).isEqualTo(2L); + break; + } catch (AbortedException e) { + Thread.sleep(e.getRetryDelayInMillis() / 1000); + txn = manager.resetForRetry(); + } + } + } + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java index ed3450893d..42dc57918d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java @@ -506,4 +506,22 @@ public Void run(TransactionContext transaction) throws SpannerException { } }); } + + @Test + public void transactionRunnerReturnsCommitStats() { + final String key = uniqueKey(); + TransactionRunner runner = client.readWriteTransaction().withCommitStats(); + runner.run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + transaction.buffer( + Mutation.newInsertBuilder("T").set("K").to(key).set("V").to(0).build()); + return null; + } + }); + assertThat(runner.getCommitStats()).isNotNull(); + // MutationCount = 2 (2 columns). + assertThat(runner.getCommitStats().getMutationCount()).isEqualTo(2L); + } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java index 6f11fc6b43..c80056bc2d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java @@ -27,6 +27,7 @@ import com.google.cloud.Timestamp; import com.google.cloud.spanner.Database; import com.google.cloud.spanner.DatabaseClient; +import com.google.cloud.spanner.DatabaseClient.WriteResponse; import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.IntegrationTestEnv; import com.google.cloud.spanner.Key; @@ -157,6 +158,40 @@ public void writeAtLeastOnce() { assertThat(row.getString(0)).isEqualTo("v1"); } + @Test + public void writeReturnsCommitStats() { + WriteResponse response = + client.writeWithCommitStats( + Arrays.asList( + Mutation.newInsertOrUpdateBuilder("T") + .set("K") + .to(lastKey = uniqueString()) + .set("StringValue") + .to("v1") + .build())); + assertThat(response).isNotNull(); + assertThat(response.getCommitTimestamp()).isNotNull(); + assertThat(response.getCommitStats()).isNotNull(); + assertThat(response.getCommitStats().getMutationCount()).isEqualTo(2L); + } + + @Test + public void writeAtLeastOnceReturnsCommitStats() { + WriteResponse response = + client.writeAtLeastOnceWithCommitStats( + Arrays.asList( + Mutation.newInsertOrUpdateBuilder("T") + .set("K") + .to(lastKey = uniqueString()) + .set("StringValue") + .to("v1") + .build())); + assertThat(response).isNotNull(); + assertThat(response.getCommitTimestamp()).isNotNull(); + assertThat(response.getCommitStats()).isNotNull(); + assertThat(response.getCommitStats().getMutationCount()).isEqualTo(2L); + } + @Test public void writeAlreadyExists() { client.write( diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java index f322ec04c0..1ba7b235bb 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequest.java @@ -111,6 +111,11 @@ private CommitRequest( input.readMessage(com.google.spanner.v1.Mutation.parser(), extensionRegistry)); break; } + case 40: + { + returnCommitStats_ = input.readBool(); + break; + } default: { if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { @@ -422,6 +427,26 @@ public com.google.spanner.v1.MutationOrBuilder getMutationsOrBuilder(int index) return mutations_.get(index); } + public static final int RETURN_COMMIT_STATS_FIELD_NUMBER = 5; + private boolean returnCommitStats_; + /** + * + * + *

+   * If `true`, then statistics related to the transaction will be included in
+   * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+   * `false`.
+   * 
+ * + * bool return_commit_stats = 5; + * + * @return The returnCommitStats. + */ + @java.lang.Override + public boolean getReturnCommitStats() { + return returnCommitStats_; + } + private byte memoizedIsInitialized = -1; @java.lang.Override @@ -448,6 +473,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io for (int i = 0; i < mutations_.size(); i++) { output.writeMessage(4, mutations_.get(i)); } + if (returnCommitStats_ != false) { + output.writeBool(5, returnCommitStats_); + } unknownFields.writeTo(output); } @@ -473,6 +501,9 @@ public int getSerializedSize() { for (int i = 0; i < mutations_.size(); i++) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(4, mutations_.get(i)); } + if (returnCommitStats_ != false) { + size += com.google.protobuf.CodedOutputStream.computeBoolSize(5, returnCommitStats_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -490,6 +521,7 @@ public boolean equals(final java.lang.Object obj) { if (!getSession().equals(other.getSession())) return false; if (!getMutationsList().equals(other.getMutationsList())) return false; + if (getReturnCommitStats() != other.getReturnCommitStats()) return false; if (!getTransactionCase().equals(other.getTransactionCase())) return false; switch (transactionCase_) { case 2: @@ -518,6 +550,8 @@ public int hashCode() { hash = (37 * hash) + MUTATIONS_FIELD_NUMBER; hash = (53 * hash) + getMutationsList().hashCode(); } + hash = (37 * hash) + RETURN_COMMIT_STATS_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean(getReturnCommitStats()); switch (transactionCase_) { case 2: hash = (37 * hash) + TRANSACTION_ID_FIELD_NUMBER; @@ -684,6 +718,8 @@ public Builder clear() { } else { mutationsBuilder_.clear(); } + returnCommitStats_ = false; + transactionCase_ = 0; transaction_ = null; return this; @@ -733,6 +769,7 @@ public com.google.spanner.v1.CommitRequest buildPartial() { } else { result.mutations_ = mutationsBuilder_.build(); } + result.returnCommitStats_ = returnCommitStats_; result.transactionCase_ = transactionCase_; onBuilt(); return result; @@ -814,6 +851,9 @@ public Builder mergeFrom(com.google.spanner.v1.CommitRequest other) { } } } + if (other.getReturnCommitStats() != false) { + setReturnCommitStats(other.getReturnCommitStats()); + } switch (other.getTransactionCase()) { case TRANSACTION_ID: { @@ -1712,6 +1752,64 @@ public java.util.List getMutationsBuilde return mutationsBuilder_; } + private boolean returnCommitStats_; + /** + * + * + *
+     * If `true`, then statistics related to the transaction will be included in
+     * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+     * `false`.
+     * 
+ * + * bool return_commit_stats = 5; + * + * @return The returnCommitStats. + */ + @java.lang.Override + public boolean getReturnCommitStats() { + return returnCommitStats_; + } + /** + * + * + *
+     * If `true`, then statistics related to the transaction will be included in
+     * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+     * `false`.
+     * 
+ * + * bool return_commit_stats = 5; + * + * @param value The returnCommitStats to set. + * @return This builder for chaining. + */ + public Builder setReturnCommitStats(boolean value) { + + returnCommitStats_ = value; + onChanged(); + return this; + } + /** + * + * + *
+     * If `true`, then statistics related to the transaction will be included in
+     * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+     * `false`.
+     * 
+ * + * bool return_commit_stats = 5; + * + * @return This builder for chaining. + */ + public Builder clearReturnCommitStats() { + + returnCommitStats_ = false; + onChanged(); + return this; + } + @java.lang.Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java index bb0f6c6acd..a32fa4b439 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitRequestOrBuilder.java @@ -185,5 +185,20 @@ public interface CommitRequestOrBuilder */ com.google.spanner.v1.MutationOrBuilder getMutationsOrBuilder(int index); + /** + * + * + *
+   * If `true`, then statistics related to the transaction will be included in
+   * the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats]. Default value is
+   * `false`.
+   * 
+ * + * bool return_commit_stats = 5; + * + * @return The returnCommitStats. + */ + boolean getReturnCommitStats(); + public com.google.spanner.v1.CommitRequest.TransactionCase getTransactionCase(); } diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java index 2877285845..53f457c9ef 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponse.java @@ -81,6 +81,22 @@ private CommitResponse( commitTimestamp_ = subBuilder.buildPartial(); } + break; + } + case 18: + { + com.google.spanner.v1.CommitResponse.CommitStats.Builder subBuilder = null; + if (commitStats_ != null) { + subBuilder = commitStats_.toBuilder(); + } + commitStats_ = + input.readMessage( + com.google.spanner.v1.CommitResponse.CommitStats.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(commitStats_); + commitStats_ = subBuilder.buildPartial(); + } + break; } default: @@ -117,6 +133,867 @@ public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { com.google.spanner.v1.CommitResponse.Builder.class); } + public interface CommitStatsOrBuilder + extends + // @@protoc_insertion_point(interface_extends:google.spanner.v1.CommitResponse.CommitStats) + com.google.protobuf.MessageOrBuilder { + + /** + * + * + *
+     * The total number of the mutations for the transaction.
+     * 
+ * + * int64 mutation_count = 1; + * + * @return The mutationCount. + */ + long getMutationCount(); + + /** + * + * + *
+     * Length of time the commit was delayed due to overloaded servers.
+     * 
+ * + * .google.protobuf.Duration overload_delay = 2; + * + * @return Whether the overloadDelay field is set. + */ + boolean hasOverloadDelay(); + /** + * + * + *
+     * Length of time the commit was delayed due to overloaded servers.
+     * 
+ * + * .google.protobuf.Duration overload_delay = 2; + * + * @return The overloadDelay. + */ + com.google.protobuf.Duration getOverloadDelay(); + /** + * + * + *
+     * Length of time the commit was delayed due to overloaded servers.
+     * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + com.google.protobuf.DurationOrBuilder getOverloadDelayOrBuilder(); + } + /** + * + * + *
+   * Additional statistics about a commit.
+   * 
+ * + * Protobuf type {@code google.spanner.v1.CommitResponse.CommitStats} + */ + public static final class CommitStats extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:google.spanner.v1.CommitResponse.CommitStats) + CommitStatsOrBuilder { + private static final long serialVersionUID = 0L; + // Use CommitStats.newBuilder() to construct. + private CommitStats(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private CommitStats() {} + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new CommitStats(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } + + private CommitStats( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 8: + { + mutationCount_ = input.readInt64(); + break; + } + case 18: + { + com.google.protobuf.Duration.Builder subBuilder = null; + if (overloadDelay_ != null) { + subBuilder = overloadDelay_.toBuilder(); + } + overloadDelay_ = + input.readMessage(com.google.protobuf.Duration.parser(), extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(overloadDelay_); + overloadDelay_ = subBuilder.buildPartial(); + } + + break; + } + default: + { + if (!parseUnknownField(input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.v1.CommitResponse.CommitStats.class, + com.google.spanner.v1.CommitResponse.CommitStats.Builder.class); + } + + public static final int MUTATION_COUNT_FIELD_NUMBER = 1; + private long mutationCount_; + /** + * + * + *
+     * The total number of the mutations for the transaction.
+     * 
+ * + * int64 mutation_count = 1; + * + * @return The mutationCount. + */ + @java.lang.Override + public long getMutationCount() { + return mutationCount_; + } + + public static final int OVERLOAD_DELAY_FIELD_NUMBER = 2; + private com.google.protobuf.Duration overloadDelay_; + /** + * + * + *
+     * Length of time the commit was delayed due to overloaded servers.
+     * 
+ * + * .google.protobuf.Duration overload_delay = 2; + * + * @return Whether the overloadDelay field is set. + */ + @java.lang.Override + public boolean hasOverloadDelay() { + return overloadDelay_ != null; + } + /** + * + * + *
+     * Length of time the commit was delayed due to overloaded servers.
+     * 
+ * + * .google.protobuf.Duration overload_delay = 2; + * + * @return The overloadDelay. + */ + @java.lang.Override + public com.google.protobuf.Duration getOverloadDelay() { + return overloadDelay_ == null + ? com.google.protobuf.Duration.getDefaultInstance() + : overloadDelay_; + } + /** + * + * + *
+     * Length of time the commit was delayed due to overloaded servers.
+     * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + @java.lang.Override + public com.google.protobuf.DurationOrBuilder getOverloadDelayOrBuilder() { + return getOverloadDelay(); + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (mutationCount_ != 0L) { + output.writeInt64(1, mutationCount_); + } + if (overloadDelay_ != null) { + output.writeMessage(2, getOverloadDelay()); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (mutationCount_ != 0L) { + size += com.google.protobuf.CodedOutputStream.computeInt64Size(1, mutationCount_); + } + if (overloadDelay_ != null) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(2, getOverloadDelay()); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof com.google.spanner.v1.CommitResponse.CommitStats)) { + return super.equals(obj); + } + com.google.spanner.v1.CommitResponse.CommitStats other = + (com.google.spanner.v1.CommitResponse.CommitStats) obj; + + if (getMutationCount() != other.getMutationCount()) return false; + if (hasOverloadDelay() != other.hasOverloadDelay()) return false; + if (hasOverloadDelay()) { + if (!getOverloadDelay().equals(other.getOverloadDelay())) return false; + } + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + MUTATION_COUNT_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashLong(getMutationCount()); + if (hasOverloadDelay()) { + hash = (37 * hash) + OVERLOAD_DELAY_FIELD_NUMBER; + hash = (53 * hash) + getOverloadDelay().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(com.google.spanner.v1.CommitResponse.CommitStats prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+     * Additional statistics about a commit.
+     * 
+ * + * Protobuf type {@code google.spanner.v1.CommitResponse.CommitStats} + */ + public static final class Builder + extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:google.spanner.v1.CommitResponse.CommitStats) + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_fieldAccessorTable + .ensureFieldAccessorsInitialized( + com.google.spanner.v1.CommitResponse.CommitStats.class, + com.google.spanner.v1.CommitResponse.CommitStats.Builder.class); + } + + // Construct using com.google.spanner.v1.CommitResponse.CommitStats.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) {} + } + + @java.lang.Override + public Builder clear() { + super.clear(); + mutationCount_ = 0L; + + if (overloadDelayBuilder_ == null) { + overloadDelay_ = null; + } else { + overloadDelay_ = null; + overloadDelayBuilder_ = null; + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return com.google.spanner.v1.SpannerProto + .internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats getDefaultInstanceForType() { + return com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance(); + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats build() { + com.google.spanner.v1.CommitResponse.CommitStats result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats buildPartial() { + com.google.spanner.v1.CommitResponse.CommitStats result = + new com.google.spanner.v1.CommitResponse.CommitStats(this); + result.mutationCount_ = mutationCount_; + if (overloadDelayBuilder_ == null) { + result.overloadDelay_ = overloadDelay_; + } else { + result.overloadDelay_ = overloadDelayBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, + java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.google.spanner.v1.CommitResponse.CommitStats) { + return mergeFrom((com.google.spanner.v1.CommitResponse.CommitStats) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.google.spanner.v1.CommitResponse.CommitStats other) { + if (other == com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance()) + return this; + if (other.getMutationCount() != 0L) { + setMutationCount(other.getMutationCount()); + } + if (other.hasOverloadDelay()) { + mergeOverloadDelay(other.getOverloadDelay()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.spanner.v1.CommitResponse.CommitStats parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = + (com.google.spanner.v1.CommitResponse.CommitStats) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private long mutationCount_; + /** + * + * + *
+       * The total number of the mutations for the transaction.
+       * 
+ * + * int64 mutation_count = 1; + * + * @return The mutationCount. + */ + @java.lang.Override + public long getMutationCount() { + return mutationCount_; + } + /** + * + * + *
+       * The total number of the mutations for the transaction.
+       * 
+ * + * int64 mutation_count = 1; + * + * @param value The mutationCount to set. + * @return This builder for chaining. + */ + public Builder setMutationCount(long value) { + + mutationCount_ = value; + onChanged(); + return this; + } + /** + * + * + *
+       * The total number of the mutations for the transaction.
+       * 
+ * + * int64 mutation_count = 1; + * + * @return This builder for chaining. + */ + public Builder clearMutationCount() { + + mutationCount_ = 0L; + onChanged(); + return this; + } + + private com.google.protobuf.Duration overloadDelay_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Duration, + com.google.protobuf.Duration.Builder, + com.google.protobuf.DurationOrBuilder> + overloadDelayBuilder_; + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + * + * @return Whether the overloadDelay field is set. + */ + public boolean hasOverloadDelay() { + return overloadDelayBuilder_ != null || overloadDelay_ != null; + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + * + * @return The overloadDelay. + */ + public com.google.protobuf.Duration getOverloadDelay() { + if (overloadDelayBuilder_ == null) { + return overloadDelay_ == null + ? com.google.protobuf.Duration.getDefaultInstance() + : overloadDelay_; + } else { + return overloadDelayBuilder_.getMessage(); + } + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + public Builder setOverloadDelay(com.google.protobuf.Duration value) { + if (overloadDelayBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + overloadDelay_ = value; + onChanged(); + } else { + overloadDelayBuilder_.setMessage(value); + } + + return this; + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + public Builder setOverloadDelay(com.google.protobuf.Duration.Builder builderForValue) { + if (overloadDelayBuilder_ == null) { + overloadDelay_ = builderForValue.build(); + onChanged(); + } else { + overloadDelayBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + public Builder mergeOverloadDelay(com.google.protobuf.Duration value) { + if (overloadDelayBuilder_ == null) { + if (overloadDelay_ != null) { + overloadDelay_ = + com.google.protobuf.Duration.newBuilder(overloadDelay_) + .mergeFrom(value) + .buildPartial(); + } else { + overloadDelay_ = value; + } + onChanged(); + } else { + overloadDelayBuilder_.mergeFrom(value); + } + + return this; + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + public Builder clearOverloadDelay() { + if (overloadDelayBuilder_ == null) { + overloadDelay_ = null; + onChanged(); + } else { + overloadDelay_ = null; + overloadDelayBuilder_ = null; + } + + return this; + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + public com.google.protobuf.Duration.Builder getOverloadDelayBuilder() { + + onChanged(); + return getOverloadDelayFieldBuilder().getBuilder(); + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + public com.google.protobuf.DurationOrBuilder getOverloadDelayOrBuilder() { + if (overloadDelayBuilder_ != null) { + return overloadDelayBuilder_.getMessageOrBuilder(); + } else { + return overloadDelay_ == null + ? com.google.protobuf.Duration.getDefaultInstance() + : overloadDelay_; + } + } + /** + * + * + *
+       * Length of time the commit was delayed due to overloaded servers.
+       * 
+ * + * .google.protobuf.Duration overload_delay = 2; + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Duration, + com.google.protobuf.Duration.Builder, + com.google.protobuf.DurationOrBuilder> + getOverloadDelayFieldBuilder() { + if (overloadDelayBuilder_ == null) { + overloadDelayBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.protobuf.Duration, + com.google.protobuf.Duration.Builder, + com.google.protobuf.DurationOrBuilder>( + getOverloadDelay(), getParentForChildren(), isClean()); + overloadDelay_ = null; + } + return overloadDelayBuilder_; + } + + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:google.spanner.v1.CommitResponse.CommitStats) + } + + // @@protoc_insertion_point(class_scope:google.spanner.v1.CommitResponse.CommitStats) + private static final com.google.spanner.v1.CommitResponse.CommitStats DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new com.google.spanner.v1.CommitResponse.CommitStats(); + } + + public static com.google.spanner.v1.CommitResponse.CommitStats getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public CommitStats parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new CommitStats(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + } + public static final int COMMIT_TIMESTAMP_FIELD_NUMBER = 1; private com.google.protobuf.Timestamp commitTimestamp_; /** @@ -165,6 +1042,60 @@ public com.google.protobuf.TimestampOrBuilder getCommitTimestampOrBuilder() { return getCommitTimestamp(); } + public static final int COMMIT_STATS_FIELD_NUMBER = 2; + private com.google.spanner.v1.CommitResponse.CommitStats commitStats_; + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return Whether the commitStats field is set. + */ + @java.lang.Override + public boolean hasCommitStats() { + return commitStats_ != null; + } + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return The commitStats. + */ + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStats getCommitStats() { + return commitStats_ == null + ? com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance() + : commitStats_; + } + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + @java.lang.Override + public com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder getCommitStatsOrBuilder() { + return getCommitStats(); + } + private byte memoizedIsInitialized = -1; @java.lang.Override @@ -182,6 +1113,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io if (commitTimestamp_ != null) { output.writeMessage(1, getCommitTimestamp()); } + if (commitStats_ != null) { + output.writeMessage(2, getCommitStats()); + } unknownFields.writeTo(output); } @@ -194,6 +1128,9 @@ public int getSerializedSize() { if (commitTimestamp_ != null) { size += com.google.protobuf.CodedOutputStream.computeMessageSize(1, getCommitTimestamp()); } + if (commitStats_ != null) { + size += com.google.protobuf.CodedOutputStream.computeMessageSize(2, getCommitStats()); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -213,6 +1150,10 @@ public boolean equals(final java.lang.Object obj) { if (hasCommitTimestamp()) { if (!getCommitTimestamp().equals(other.getCommitTimestamp())) return false; } + if (hasCommitStats() != other.hasCommitStats()) return false; + if (hasCommitStats()) { + if (!getCommitStats().equals(other.getCommitStats())) return false; + } if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -228,6 +1169,10 @@ public int hashCode() { hash = (37 * hash) + COMMIT_TIMESTAMP_FIELD_NUMBER; hash = (53 * hash) + getCommitTimestamp().hashCode(); } + if (hasCommitStats()) { + hash = (37 * hash) + COMMIT_STATS_FIELD_NUMBER; + hash = (53 * hash) + getCommitStats().hashCode(); + } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -378,6 +1323,12 @@ public Builder clear() { commitTimestamp_ = null; commitTimestampBuilder_ = null; } + if (commitStatsBuilder_ == null) { + commitStats_ = null; + } else { + commitStats_ = null; + commitStatsBuilder_ = null; + } return this; } @@ -409,6 +1360,11 @@ public com.google.spanner.v1.CommitResponse buildPartial() { } else { result.commitTimestamp_ = commitTimestampBuilder_.build(); } + if (commitStatsBuilder_ == null) { + result.commitStats_ = commitStats_; + } else { + result.commitStats_ = commitStatsBuilder_.build(); + } onBuilt(); return result; } @@ -461,6 +1417,9 @@ public Builder mergeFrom(com.google.spanner.v1.CommitResponse other) { if (other.hasCommitTimestamp()) { mergeCommitTimestamp(other.getCommitTimestamp()); } + if (other.hasCommitStats()) { + mergeCommitStats(other.getCommitStats()); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -675,6 +1634,210 @@ public com.google.protobuf.TimestampOrBuilder getCommitTimestampOrBuilder() { return commitTimestampBuilder_; } + private com.google.spanner.v1.CommitResponse.CommitStats commitStats_; + private com.google.protobuf.SingleFieldBuilderV3< + com.google.spanner.v1.CommitResponse.CommitStats, + com.google.spanner.v1.CommitResponse.CommitStats.Builder, + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder> + commitStatsBuilder_; + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return Whether the commitStats field is set. + */ + public boolean hasCommitStats() { + return commitStatsBuilder_ != null || commitStats_ != null; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return The commitStats. + */ + public com.google.spanner.v1.CommitResponse.CommitStats getCommitStats() { + if (commitStatsBuilder_ == null) { + return commitStats_ == null + ? com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance() + : commitStats_; + } else { + return commitStatsBuilder_.getMessage(); + } + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder setCommitStats(com.google.spanner.v1.CommitResponse.CommitStats value) { + if (commitStatsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + commitStats_ = value; + onChanged(); + } else { + commitStatsBuilder_.setMessage(value); + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder setCommitStats( + com.google.spanner.v1.CommitResponse.CommitStats.Builder builderForValue) { + if (commitStatsBuilder_ == null) { + commitStats_ = builderForValue.build(); + onChanged(); + } else { + commitStatsBuilder_.setMessage(builderForValue.build()); + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder mergeCommitStats(com.google.spanner.v1.CommitResponse.CommitStats value) { + if (commitStatsBuilder_ == null) { + if (commitStats_ != null) { + commitStats_ = + com.google.spanner.v1.CommitResponse.CommitStats.newBuilder(commitStats_) + .mergeFrom(value) + .buildPartial(); + } else { + commitStats_ = value; + } + onChanged(); + } else { + commitStatsBuilder_.mergeFrom(value); + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public Builder clearCommitStats() { + if (commitStatsBuilder_ == null) { + commitStats_ = null; + onChanged(); + } else { + commitStats_ = null; + commitStatsBuilder_ = null; + } + + return this; + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public com.google.spanner.v1.CommitResponse.CommitStats.Builder getCommitStatsBuilder() { + + onChanged(); + return getCommitStatsFieldBuilder().getBuilder(); + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + public com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder getCommitStatsOrBuilder() { + if (commitStatsBuilder_ != null) { + return commitStatsBuilder_.getMessageOrBuilder(); + } else { + return commitStats_ == null + ? com.google.spanner.v1.CommitResponse.CommitStats.getDefaultInstance() + : commitStats_; + } + } + /** + * + * + *
+     * The statistics about this Commit. Not returned by default.
+     * For more information, see
+     * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+     * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + private com.google.protobuf.SingleFieldBuilderV3< + com.google.spanner.v1.CommitResponse.CommitStats, + com.google.spanner.v1.CommitResponse.CommitStats.Builder, + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder> + getCommitStatsFieldBuilder() { + if (commitStatsBuilder_ == null) { + commitStatsBuilder_ = + new com.google.protobuf.SingleFieldBuilderV3< + com.google.spanner.v1.CommitResponse.CommitStats, + com.google.spanner.v1.CommitResponse.CommitStats.Builder, + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder>( + getCommitStats(), getParentForChildren(), isClean()); + commitStats_ = null; + } + return commitStatsBuilder_; + } + @java.lang.Override public final Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) { return super.setUnknownFields(unknownFields); diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java index 90c6c6e672..9ff076ff84 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/CommitResponseOrBuilder.java @@ -57,4 +57,45 @@ public interface CommitResponseOrBuilder * .google.protobuf.Timestamp commit_timestamp = 1; */ com.google.protobuf.TimestampOrBuilder getCommitTimestampOrBuilder(); + + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return Whether the commitStats field is set. + */ + boolean hasCommitStats(); + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + * + * @return The commitStats. + */ + com.google.spanner.v1.CommitResponse.CommitStats getCommitStats(); + /** + * + * + *
+   * The statistics about this Commit. Not returned by default.
+   * For more information, see
+   * [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats].
+   * 
+ * + * .google.spanner.v1.CommitResponse.CommitStats commit_stats = 2; + */ + com.google.spanner.v1.CommitResponse.CommitStatsOrBuilder getCommitStatsOrBuilder(); } diff --git a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java index 0932ab5d52..3c5da06295 100644 --- a/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java +++ b/proto-google-cloud-spanner-v1/src/main/java/com/google/spanner/v1/SpannerProto.java @@ -131,6 +131,10 @@ public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry r internal_static_google_spanner_v1_CommitResponse_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_google_spanner_v1_CommitResponse_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_google_spanner_v1_CommitResponse_CommitStats_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_google_spanner_v1_CommitResponse_CommitStats_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_google_spanner_v1_RollbackRequest_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable @@ -148,198 +152,203 @@ public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + "e.spanner.v1\032\034google/api/annotations.pro" + "to\032\027google/api/client.proto\032\037google/api/" + "field_behavior.proto\032\031google/api/resourc" - + "e.proto\032\033google/protobuf/empty.proto\032\034go" - + "ogle/protobuf/struct.proto\032\037google/proto" - + "buf/timestamp.proto\032\027google/rpc/status.p" - + "roto\032\034google/spanner/v1/keys.proto\032 goog" - + "le/spanner/v1/mutation.proto\032\"google/spa" - + "nner/v1/result_set.proto\032#google/spanner" - + "/v1/transaction.proto\032\034google/spanner/v1" - + "/type.proto\"~\n\024CreateSessionRequest\0229\n\010d" - + "atabase\030\001 \001(\tB\'\340A\002\372A!\n\037spanner.googleapi" - + "s.com/Database\022+\n\007session\030\002 \001(\0132\032.google" - + ".spanner.v1.Session\"\251\001\n\032BatchCreateSessi" - + "onsRequest\0229\n\010database\030\001 \001(\tB\'\340A\002\372A!\n\037sp" - + "anner.googleapis.com/Database\0224\n\020session" - + "_template\030\002 \001(\0132\032.google.spanner.v1.Sess" - + "ion\022\032\n\rsession_count\030\003 \001(\005B\003\340A\002\"J\n\033Batch" - + "CreateSessionsResponse\022+\n\007session\030\001 \003(\0132" - + "\032.google.spanner.v1.Session\"\344\002\n\007Session\022" - + "\014\n\004name\030\001 \001(\t\0226\n\006labels\030\002 \003(\0132&.google.s" - + "panner.v1.Session.LabelsEntry\022/\n\013create_" - + "time\030\003 \001(\0132\032.google.protobuf.Timestamp\022=" - + "\n\031approximate_last_use_time\030\004 \001(\0132\032.goog" - + "le.protobuf.Timestamp\032-\n\013LabelsEntry\022\013\n\003" - + "key\030\001 \001(\t\022\r\n\005value\030\002 \001(\t:\0028\001:t\352Aq\n\036spann" - + "er.googleapis.com/Session\022Oprojects/{pro" - + "ject}/instances/{instance}/databases/{da" - + "tabase}/sessions/{session}\"I\n\021GetSession" - + "Request\0224\n\004name\030\001 \001(\tB&\340A\002\372A \n\036spanner.g" - + "oogleapis.com/Session\"\207\001\n\023ListSessionsRe" - + "quest\0229\n\010database\030\001 \001(\tB\'\340A\002\372A!\n\037spanner" - + ".googleapis.com/Database\022\021\n\tpage_size\030\002 " - + "\001(\005\022\022\n\npage_token\030\003 \001(\t\022\016\n\006filter\030\004 \001(\t\"" - + "]\n\024ListSessionsResponse\022,\n\010sessions\030\001 \003(" - + "\0132\032.google.spanner.v1.Session\022\027\n\017next_pa" - + "ge_token\030\002 \001(\t\"L\n\024DeleteSessionRequest\0224" - + "\n\004name\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis" - + ".com/Session\"\202\005\n\021ExecuteSqlRequest\0227\n\007se" - + "ssion\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis." - + "com/Session\022;\n\013transaction\030\002 \001(\0132&.googl" - + "e.spanner.v1.TransactionSelector\022\020\n\003sql\030" - + "\003 \001(\tB\003\340A\002\022\'\n\006params\030\004 \001(\0132\027.google.prot" - + "obuf.Struct\022I\n\013param_types\030\005 \003(\01324.googl" - + "e.spanner.v1.ExecuteSqlRequest.ParamType" - + "sEntry\022\024\n\014resume_token\030\006 \001(\014\022B\n\nquery_mo" - + "de\030\007 \001(\0162..google.spanner.v1.ExecuteSqlR" - + "equest.QueryMode\022\027\n\017partition_token\030\010 \001(" - + "\014\022\r\n\005seqno\030\t \001(\003\022H\n\rquery_options\030\n \001(\0132" - + "1.google.spanner.v1.ExecuteSqlRequest.Qu" - + "eryOptions\032)\n\014QueryOptions\022\031\n\021optimizer_" - + "version\030\001 \001(\t\032J\n\017ParamTypesEntry\022\013\n\003key\030" - + "\001 \001(\t\022&\n\005value\030\002 \001(\0132\027.google.spanner.v1" - + ".Type:\0028\001\".\n\tQueryMode\022\n\n\006NORMAL\020\000\022\010\n\004PL" - + "AN\020\001\022\013\n\007PROFILE\020\002\"\337\003\n\026ExecuteBatchDmlReq" - + "uest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner.g" - + "oogleapis.com/Session\022@\n\013transaction\030\002 \001" - + "(\0132&.google.spanner.v1.TransactionSelect" - + "orB\003\340A\002\022L\n\nstatements\030\003 \003(\01323.google.spa" - + "nner.v1.ExecuteBatchDmlRequest.Statement" - + "B\003\340A\002\022\022\n\005seqno\030\004 \001(\003B\003\340A\002\032\347\001\n\tStatement\022" - + "\013\n\003sql\030\001 \001(\t\022\'\n\006params\030\002 \001(\0132\027.google.pr" - + "otobuf.Struct\022X\n\013param_types\030\003 \003(\0132C.goo" - + "gle.spanner.v1.ExecuteBatchDmlRequest.St" - + "atement.ParamTypesEntry\032J\n\017ParamTypesEnt" - + "ry\022\013\n\003key\030\001 \001(\t\022&\n\005value\030\002 \001(\0132\027.google." - + "spanner.v1.Type:\0028\001\"p\n\027ExecuteBatchDmlRe" - + "sponse\0221\n\013result_sets\030\001 \003(\0132\034.google.spa" - + "nner.v1.ResultSet\022\"\n\006status\030\002 \001(\0132\022.goog" - + "le.rpc.Status\"H\n\020PartitionOptions\022\034\n\024par" - + "tition_size_bytes\030\001 \001(\003\022\026\n\016max_partition" - + "s\030\002 \001(\003\"\243\003\n\025PartitionQueryRequest\0227\n\007ses" - + "sion\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis.c" - + "om/Session\022;\n\013transaction\030\002 \001(\0132&.google" - + ".spanner.v1.TransactionSelector\022\020\n\003sql\030\003" - + " \001(\tB\003\340A\002\022\'\n\006params\030\004 \001(\0132\027.google.proto" - + "buf.Struct\022M\n\013param_types\030\005 \003(\01328.google" - + ".spanner.v1.PartitionQueryRequest.ParamT" - + "ypesEntry\022>\n\021partition_options\030\006 \001(\0132#.g" - + "oogle.spanner.v1.PartitionOptions\032J\n\017Par" - + "amTypesEntry\022\013\n\003key\030\001 \001(\t\022&\n\005value\030\002 \001(\013" - + "2\027.google.spanner.v1.Type:\0028\001\"\261\002\n\024Partit" - + "ionReadRequest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n" - + "\036spanner.googleapis.com/Session\022;\n\013trans" - + "action\030\002 \001(\0132&.google.spanner.v1.Transac" - + "tionSelector\022\022\n\005table\030\003 \001(\tB\003\340A\002\022\r\n\005inde" - + "x\030\004 \001(\t\022\017\n\007columns\030\005 \003(\t\022/\n\007key_set\030\006 \001(" - + "\0132\031.google.spanner.v1.KeySetB\003\340A\002\022>\n\021par" - + "tition_options\030\t \001(\0132#.google.spanner.v1" - + ".PartitionOptions\"$\n\tPartition\022\027\n\017partit" - + "ion_token\030\001 \001(\014\"z\n\021PartitionResponse\0220\n\n" - + "partitions\030\001 \003(\0132\034.google.spanner.v1.Par" - + "tition\0223\n\013transaction\030\002 \001(\0132\036.google.spa" - + "nner.v1.Transaction\"\253\002\n\013ReadRequest\0227\n\007s" - + "ession\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis" - + ".com/Session\022;\n\013transaction\030\002 \001(\0132&.goog" - + "le.spanner.v1.TransactionSelector\022\022\n\005tab" - + "le\030\003 \001(\tB\003\340A\002\022\r\n\005index\030\004 \001(\t\022\024\n\007columns\030" - + "\005 \003(\tB\003\340A\002\022/\n\007key_set\030\006 \001(\0132\031.google.spa" - + "nner.v1.KeySetB\003\340A\002\022\r\n\005limit\030\010 \001(\003\022\024\n\014re" - + "sume_token\030\t \001(\014\022\027\n\017partition_token\030\n \001(" - + "\014\"\217\001\n\027BeginTransactionRequest\0227\n\007session" - + "\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis.com/S" - + "ession\022;\n\007options\030\002 \001(\0132%.google.spanner" - + ".v1.TransactionOptionsB\003\340A\002\"\352\001\n\rCommitRe" - + "quest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner." - + "googleapis.com/Session\022\030\n\016transaction_id" - + "\030\002 \001(\014H\000\022G\n\026single_use_transaction\030\003 \001(\013" - + "2%.google.spanner.v1.TransactionOptionsH" - + "\000\022.\n\tmutations\030\004 \003(\0132\033.google.spanner.v1" - + ".MutationB\r\n\013transaction\"F\n\016CommitRespon" - + "se\0224\n\020commit_timestamp\030\001 \001(\0132\032.google.pr" - + "otobuf.Timestamp\"g\n\017RollbackRequest\0227\n\007s" - + "ession\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapis" - + ".com/Session\022\033\n\016transaction_id\030\002 \001(\014B\003\340A" - + "\0022\300\026\n\007Spanner\022\246\001\n\rCreateSession\022\'.google" - + ".spanner.v1.CreateSessionRequest\032\032.googl" - + "e.spanner.v1.Session\"P\202\323\344\223\002?\":/v1/{datab" - + "ase=projects/*/instances/*/databases/*}/" - + "sessions:\001*\332A\010database\022\340\001\n\023BatchCreateSe" - + "ssions\022-.google.spanner.v1.BatchCreateSe" - + "ssionsRequest\032..google.spanner.v1.BatchC" - + "reateSessionsResponse\"j\202\323\344\223\002K\"F/v1/{data" - + "base=projects/*/instances/*/databases/*}" - + "/sessions:batchCreate:\001*\332A\026database,sess" - + "ion_count\022\227\001\n\nGetSession\022$.google.spanne" - + "r.v1.GetSessionRequest\032\032.google.spanner." - + "v1.Session\"G\202\323\344\223\002:\0228/v1/{name=projects/*" - + "/instances/*/databases/*/sessions/*}\332A\004n" - + "ame\022\256\001\n\014ListSessions\022&.google.spanner.v1" - + ".ListSessionsRequest\032\'.google.spanner.v1" - + ".ListSessionsResponse\"M\202\323\344\223\002<\022:/v1/{data" - + "base=projects/*/instances/*/databases/*}" - + "/sessions\332A\010database\022\231\001\n\rDeleteSession\022\'" - + ".google.spanner.v1.DeleteSessionRequest\032" - + "\026.google.protobuf.Empty\"G\202\323\344\223\002:*8/v1/{na" - + "me=projects/*/instances/*/databases/*/se" - + "ssions/*}\332A\004name\022\243\001\n\nExecuteSql\022$.google" - + ".spanner.v1.ExecuteSqlRequest\032\034.google.s" - + "panner.v1.ResultSet\"Q\202\323\344\223\002K\"F/v1/{sessio" - + "n=projects/*/instances/*/databases/*/ses" - + "sions/*}:executeSql:\001*\022\276\001\n\023ExecuteStream" - + "ingSql\022$.google.spanner.v1.ExecuteSqlReq" - + "uest\032#.google.spanner.v1.PartialResultSe" - + "t\"Z\202\323\344\223\002T\"O/v1/{session=projects/*/insta" - + "nces/*/databases/*/sessions/*}:executeSt" - + "reamingSql:\001*0\001\022\300\001\n\017ExecuteBatchDml\022).go" - + "ogle.spanner.v1.ExecuteBatchDmlRequest\032*" - + ".google.spanner.v1.ExecuteBatchDmlRespon" - + "se\"V\202\323\344\223\002P\"K/v1/{session=projects/*/inst" - + "ances/*/databases/*/sessions/*}:executeB" - + "atchDml:\001*\022\221\001\n\004Read\022\036.google.spanner.v1." - + "ReadRequest\032\034.google.spanner.v1.ResultSe" - + "t\"K\202\323\344\223\002E\"@/v1/{session=projects/*/insta" - + "nces/*/databases/*/sessions/*}:read:\001*\022\254" - + "\001\n\rStreamingRead\022\036.google.spanner.v1.Rea" - + "dRequest\032#.google.spanner.v1.PartialResu" - + "ltSet\"T\202\323\344\223\002N\"I/v1/{session=projects/*/i" - + "nstances/*/databases/*/sessions/*}:strea" - + "mingRead:\001*0\001\022\311\001\n\020BeginTransaction\022*.goo" - + "gle.spanner.v1.BeginTransactionRequest\032\036" - + ".google.spanner.v1.Transaction\"i\202\323\344\223\002Q\"L" - + "/v1/{session=projects/*/instances/*/data" - + "bases/*/sessions/*}:beginTransaction:\001*\332" - + "A\017session,options\022\353\001\n\006Commit\022 .google.sp" - + "anner.v1.CommitRequest\032!.google.spanner." - + "v1.CommitResponse\"\233\001\202\323\344\223\002G\"B/v1/{session" + + "e.proto\032\036google/protobuf/duration.proto\032" + + "\033google/protobuf/empty.proto\032\034google/pro" + + "tobuf/struct.proto\032\037google/protobuf/time" + + "stamp.proto\032\027google/rpc/status.proto\032\034go" + + "ogle/spanner/v1/keys.proto\032 google/spann" + + "er/v1/mutation.proto\032\"google/spanner/v1/" + + "result_set.proto\032#google/spanner/v1/tran" + + "saction.proto\032\034google/spanner/v1/type.pr" + + "oto\"~\n\024CreateSessionRequest\0229\n\010database\030" + + "\001 \001(\tB\'\340A\002\372A!\n\037spanner.googleapis.com/Da" + + "tabase\022+\n\007session\030\002 \001(\0132\032.google.spanner" + + ".v1.Session\"\251\001\n\032BatchCreateSessionsReque" + + "st\0229\n\010database\030\001 \001(\tB\'\340A\002\372A!\n\037spanner.go" + + "ogleapis.com/Database\0224\n\020session_templat" + + "e\030\002 \001(\0132\032.google.spanner.v1.Session\022\032\n\rs" + + "ession_count\030\003 \001(\005B\003\340A\002\"J\n\033BatchCreateSe" + + "ssionsResponse\022+\n\007session\030\001 \003(\0132\032.google" + + ".spanner.v1.Session\"\344\002\n\007Session\022\014\n\004name\030" + + "\001 \001(\t\0226\n\006labels\030\002 \003(\0132&.google.spanner.v" + + "1.Session.LabelsEntry\022/\n\013create_time\030\003 \001" + + "(\0132\032.google.protobuf.Timestamp\022=\n\031approx" + + "imate_last_use_time\030\004 \001(\0132\032.google.proto" + + "buf.Timestamp\032-\n\013LabelsEntry\022\013\n\003key\030\001 \001(" + + "\t\022\r\n\005value\030\002 \001(\t:\0028\001:t\352Aq\n\036spanner.googl" + + "eapis.com/Session\022Oprojects/{project}/in" + + "stances/{instance}/databases/{database}/" + + "sessions/{session}\"I\n\021GetSessionRequest\022" + + "4\n\004name\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapi" + + "s.com/Session\"\207\001\n\023ListSessionsRequest\0229\n" + + "\010database\030\001 \001(\tB\'\340A\002\372A!\n\037spanner.googlea" + + "pis.com/Database\022\021\n\tpage_size\030\002 \001(\005\022\022\n\np" + + "age_token\030\003 \001(\t\022\016\n\006filter\030\004 \001(\t\"]\n\024ListS" + + "essionsResponse\022,\n\010sessions\030\001 \003(\0132\032.goog" + + "le.spanner.v1.Session\022\027\n\017next_page_token" + + "\030\002 \001(\t\"L\n\024DeleteSessionRequest\0224\n\004name\030\001" + + " \001(\tB&\340A\002\372A \n\036spanner.googleapis.com/Ses" + + "sion\"\202\005\n\021ExecuteSqlRequest\0227\n\007session\030\001 " + + "\001(\tB&\340A\002\372A \n\036spanner.googleapis.com/Sess" + + "ion\022;\n\013transaction\030\002 \001(\0132&.google.spanne" + + "r.v1.TransactionSelector\022\020\n\003sql\030\003 \001(\tB\003\340" + + "A\002\022\'\n\006params\030\004 \001(\0132\027.google.protobuf.Str" + + "uct\022I\n\013param_types\030\005 \003(\01324.google.spanne" + + "r.v1.ExecuteSqlRequest.ParamTypesEntry\022\024" + + "\n\014resume_token\030\006 \001(\014\022B\n\nquery_mode\030\007 \001(\016" + + "2..google.spanner.v1.ExecuteSqlRequest.Q" + + "ueryMode\022\027\n\017partition_token\030\010 \001(\014\022\r\n\005seq" + + "no\030\t \001(\003\022H\n\rquery_options\030\n \001(\01321.google" + + ".spanner.v1.ExecuteSqlRequest.QueryOptio" + + "ns\032)\n\014QueryOptions\022\031\n\021optimizer_version\030" + + "\001 \001(\t\032J\n\017ParamTypesEntry\022\013\n\003key\030\001 \001(\t\022&\n" + + "\005value\030\002 \001(\0132\027.google.spanner.v1.Type:\0028" + + "\001\".\n\tQueryMode\022\n\n\006NORMAL\020\000\022\010\n\004PLAN\020\001\022\013\n\007" + + "PROFILE\020\002\"\337\003\n\026ExecuteBatchDmlRequest\0227\n\007" + + "session\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleapi" + + "s.com/Session\022@\n\013transaction\030\002 \001(\0132&.goo" + + "gle.spanner.v1.TransactionSelectorB\003\340A\002\022" + + "L\n\nstatements\030\003 \003(\01323.google.spanner.v1." + + "ExecuteBatchDmlRequest.StatementB\003\340A\002\022\022\n" + + "\005seqno\030\004 \001(\003B\003\340A\002\032\347\001\n\tStatement\022\013\n\003sql\030\001" + + " \001(\t\022\'\n\006params\030\002 \001(\0132\027.google.protobuf.S" + + "truct\022X\n\013param_types\030\003 \003(\0132C.google.span" + + "ner.v1.ExecuteBatchDmlRequest.Statement." + + "ParamTypesEntry\032J\n\017ParamTypesEntry\022\013\n\003ke" + + "y\030\001 \001(\t\022&\n\005value\030\002 \001(\0132\027.google.spanner." + + "v1.Type:\0028\001\"p\n\027ExecuteBatchDmlResponse\0221" + + "\n\013result_sets\030\001 \003(\0132\034.google.spanner.v1." + + "ResultSet\022\"\n\006status\030\002 \001(\0132\022.google.rpc.S" + + "tatus\"H\n\020PartitionOptions\022\034\n\024partition_s" + + "ize_bytes\030\001 \001(\003\022\026\n\016max_partitions\030\002 \001(\003\"" + + "\243\003\n\025PartitionQueryRequest\0227\n\007session\030\001 \001" + + "(\tB&\340A\002\372A \n\036spanner.googleapis.com/Sessi" + + "on\022;\n\013transaction\030\002 \001(\0132&.google.spanner" + + ".v1.TransactionSelector\022\020\n\003sql\030\003 \001(\tB\003\340A" + + "\002\022\'\n\006params\030\004 \001(\0132\027.google.protobuf.Stru" + + "ct\022M\n\013param_types\030\005 \003(\01328.google.spanner" + + ".v1.PartitionQueryRequest.ParamTypesEntr" + + "y\022>\n\021partition_options\030\006 \001(\0132#.google.sp" + + "anner.v1.PartitionOptions\032J\n\017ParamTypesE" + + "ntry\022\013\n\003key\030\001 \001(\t\022&\n\005value\030\002 \001(\0132\027.googl" + + "e.spanner.v1.Type:\0028\001\"\261\002\n\024PartitionReadR" + + "equest\0227\n\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner" + + ".googleapis.com/Session\022;\n\013transaction\030\002" + + " \001(\0132&.google.spanner.v1.TransactionSele" + + "ctor\022\022\n\005table\030\003 \001(\tB\003\340A\002\022\r\n\005index\030\004 \001(\t\022" + + "\017\n\007columns\030\005 \003(\t\022/\n\007key_set\030\006 \001(\0132\031.goog" + + "le.spanner.v1.KeySetB\003\340A\002\022>\n\021partition_o" + + "ptions\030\t \001(\0132#.google.spanner.v1.Partiti" + + "onOptions\"$\n\tPartition\022\027\n\017partition_toke" + + "n\030\001 \001(\014\"z\n\021PartitionResponse\0220\n\npartitio" + + "ns\030\001 \003(\0132\034.google.spanner.v1.Partition\0223" + + "\n\013transaction\030\002 \001(\0132\036.google.spanner.v1." + + "Transaction\"\253\002\n\013ReadRequest\0227\n\007session\030\001" + + " \001(\tB&\340A\002\372A \n\036spanner.googleapis.com/Ses" + + "sion\022;\n\013transaction\030\002 \001(\0132&.google.spann" + + "er.v1.TransactionSelector\022\022\n\005table\030\003 \001(\t" + + "B\003\340A\002\022\r\n\005index\030\004 \001(\t\022\024\n\007columns\030\005 \003(\tB\003\340" + + "A\002\022/\n\007key_set\030\006 \001(\0132\031.google.spanner.v1." + + "KeySetB\003\340A\002\022\r\n\005limit\030\010 \001(\003\022\024\n\014resume_tok" + + "en\030\t \001(\014\022\027\n\017partition_token\030\n \001(\014\"\217\001\n\027Be" + + "ginTransactionRequest\0227\n\007session\030\001 \001(\tB&" + + "\340A\002\372A \n\036spanner.googleapis.com/Session\022;" + + "\n\007options\030\002 \001(\0132%.google.spanner.v1.Tran" + + "sactionOptionsB\003\340A\002\"\207\002\n\rCommitRequest\0227\n" + + "\007session\030\001 \001(\tB&\340A\002\372A \n\036spanner.googleap" + + "is.com/Session\022\030\n\016transaction_id\030\002 \001(\014H\000" + + "\022G\n\026single_use_transaction\030\003 \001(\0132%.googl" + + "e.spanner.v1.TransactionOptionsH\000\022.\n\tmut" + + "ations\030\004 \003(\0132\033.google.spanner.v1.Mutatio" + + "n\022\033\n\023return_commit_stats\030\005 \001(\010B\r\n\013transa" + + "ction\"\345\001\n\016CommitResponse\0224\n\020commit_times" + + "tamp\030\001 \001(\0132\032.google.protobuf.Timestamp\022C" + + "\n\014commit_stats\030\002 \001(\0132-.google.spanner.v1" + + ".CommitResponse.CommitStats\032X\n\013CommitSta" + + "ts\022\026\n\016mutation_count\030\001 \001(\003\0221\n\016overload_d" + + "elay\030\002 \001(\0132\031.google.protobuf.Duration\"g\n" + + "\017RollbackRequest\0227\n\007session\030\001 \001(\tB&\340A\002\372A" + + " \n\036spanner.googleapis.com/Session\022\033\n\016tra" + + "nsaction_id\030\002 \001(\014B\003\340A\0022\300\026\n\007Spanner\022\246\001\n\rC" + + "reateSession\022\'.google.spanner.v1.CreateS" + + "essionRequest\032\032.google.spanner.v1.Sessio" + + "n\"P\202\323\344\223\002?\":/v1/{database=projects/*/inst" + + "ances/*/databases/*}/sessions:\001*\332A\010datab" + + "ase\022\340\001\n\023BatchCreateSessions\022-.google.spa" + + "nner.v1.BatchCreateSessionsRequest\032..goo" + + "gle.spanner.v1.BatchCreateSessionsRespon" + + "se\"j\202\323\344\223\002K\"F/v1/{database=projects/*/ins" + + "tances/*/databases/*}/sessions:batchCrea" + + "te:\001*\332A\026database,session_count\022\227\001\n\nGetSe" + + "ssion\022$.google.spanner.v1.GetSessionRequ" + + "est\032\032.google.spanner.v1.Session\"G\202\323\344\223\002:\022" + + "8/v1/{name=projects/*/instances/*/databa" + + "ses/*/sessions/*}\332A\004name\022\256\001\n\014ListSession" + + "s\022&.google.spanner.v1.ListSessionsReques" + + "t\032\'.google.spanner.v1.ListSessionsRespon" + + "se\"M\202\323\344\223\002<\022:/v1/{database=projects/*/ins" + + "tances/*/databases/*}/sessions\332A\010databas" + + "e\022\231\001\n\rDeleteSession\022\'.google.spanner.v1." + + "DeleteSessionRequest\032\026.google.protobuf.E" + + "mpty\"G\202\323\344\223\002:*8/v1/{name=projects/*/insta" + + "nces/*/databases/*/sessions/*}\332A\004name\022\243\001" + + "\n\nExecuteSql\022$.google.spanner.v1.Execute" + + "SqlRequest\032\034.google.spanner.v1.ResultSet" + + "\"Q\202\323\344\223\002K\"F/v1/{session=projects/*/instan" + + "ces/*/databases/*/sessions/*}:executeSql" + + ":\001*\022\276\001\n\023ExecuteStreamingSql\022$.google.spa" + + "nner.v1.ExecuteSqlRequest\032#.google.spann" + + "er.v1.PartialResultSet\"Z\202\323\344\223\002T\"O/v1/{ses" + + "sion=projects/*/instances/*/databases/*/" + + "sessions/*}:executeStreamingSql:\001*0\001\022\300\001\n" + + "\017ExecuteBatchDml\022).google.spanner.v1.Exe" + + "cuteBatchDmlRequest\032*.google.spanner.v1." + + "ExecuteBatchDmlResponse\"V\202\323\344\223\002P\"K/v1/{se" + + "ssion=projects/*/instances/*/databases/*" + + "/sessions/*}:executeBatchDml:\001*\022\221\001\n\004Read" + + "\022\036.google.spanner.v1.ReadRequest\032\034.googl" + + "e.spanner.v1.ResultSet\"K\202\323\344\223\002E\"@/v1/{ses" + + "sion=projects/*/instances/*/databases/*/" + + "sessions/*}:read:\001*\022\254\001\n\rStreamingRead\022\036." + + "google.spanner.v1.ReadRequest\032#.google.s" + + "panner.v1.PartialResultSet\"T\202\323\344\223\002N\"I/v1/" + + "{session=projects/*/instances/*/database" + + "s/*/sessions/*}:streamingRead:\001*0\001\022\311\001\n\020B" + + "eginTransaction\022*.google.spanner.v1.Begi" + + "nTransactionRequest\032\036.google.spanner.v1." + + "Transaction\"i\202\323\344\223\002Q\"L/v1/{session=projec" + + "ts/*/instances/*/databases/*/sessions/*}" + + ":beginTransaction:\001*\332A\017session,options\022\353" + + "\001\n\006Commit\022 .google.spanner.v1.CommitRequ" + + "est\032!.google.spanner.v1.CommitResponse\"\233" + + "\001\202\323\344\223\002G\"B/v1/{session=projects/*/instanc" + + "es/*/databases/*/sessions/*}:commit:\001*\332A" + + " session,transaction_id,mutations\332A(sess" + + "ion,single_use_transaction,mutations\022\260\001\n" + + "\010Rollback\022\".google.spanner.v1.RollbackRe" + + "quest\032\026.google.protobuf.Empty\"h\202\323\344\223\002I\"D/" + + "v1/{session=projects/*/instances/*/datab" + + "ases/*/sessions/*}:rollback:\001*\332A\026session" + + ",transaction_id\022\267\001\n\016PartitionQuery\022(.goo" + + "gle.spanner.v1.PartitionQueryRequest\032$.g" + + "oogle.spanner.v1.PartitionResponse\"U\202\323\344\223" + + "\002O\"J/v1/{session=projects/*/instances/*/" + + "databases/*/sessions/*}:partitionQuery:\001" + + "*\022\264\001\n\rPartitionRead\022\'.google.spanner.v1." + + "PartitionReadRequest\032$.google.spanner.v1" + + ".PartitionResponse\"T\202\323\344\223\002N\"I/v1/{session" + "=projects/*/instances/*/databases/*/sess" - + "ions/*}:commit:\001*\332A session,transaction_" - + "id,mutations\332A(session,single_use_transa" - + "ction,mutations\022\260\001\n\010Rollback\022\".google.sp" - + "anner.v1.RollbackRequest\032\026.google.protob" - + "uf.Empty\"h\202\323\344\223\002I\"D/v1/{session=projects/" - + "*/instances/*/databases/*/sessions/*}:ro" - + "llback:\001*\332A\026session,transaction_id\022\267\001\n\016P" - + "artitionQuery\022(.google.spanner.v1.Partit" - + "ionQueryRequest\032$.google.spanner.v1.Part" - + "itionResponse\"U\202\323\344\223\002O\"J/v1/{session=proj" - + "ects/*/instances/*/databases/*/sessions/" - + "*}:partitionQuery:\001*\022\264\001\n\rPartitionRead\022\'" - + ".google.spanner.v1.PartitionReadRequest\032" - + "$.google.spanner.v1.PartitionResponse\"T\202" - + "\323\344\223\002N\"I/v1/{session=projects/*/instances" - + "/*/databases/*/sessions/*}:partitionRead" - + ":\001*\032w\312A\026spanner.googleapis.com\322A[https:/" - + "/www.googleapis.com/auth/cloud-platform," - + "https://www.googleapis.com/auth/spanner." - + "dataB\224\002\n\025com.google.spanner.v1B\014SpannerP" - + "rotoP\001Z8google.golang.org/genproto/googl" - + "eapis/spanner/v1;spanner\252\002\027Google.Cloud." - + "Spanner.V1\312\002\027Google\\Cloud\\Spanner\\V1\352\002\032G" - + "oogle::Cloud::Spanner::V1\352A_\n\037spanner.go" - + "ogleapis.com/Database\022 Date: Sun, 18 Oct 2020 17:53:59 +0200 Subject: [PATCH 2/7] fix: add missing methods to clirr-ignored-diff --- .../clirr-ignored-differences.xml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index a594ffe51b..1484d2076c 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -399,5 +399,35 @@ com/google/cloud/spanner/TransactionRunner com.google.cloud.spanner.CommitStats getCommitStats() + + 7012 + com/google/cloud/spanner/AsyncTransactionManager + com.google.cloud.spanner.AsyncTransactionManager withCommitStats() + + + 7012 + com/google/cloud/spanner/AsyncTransactionManager + com.google.api.core.ApiFuture getCommitStats() + + + 7012 + com/google/cloud/spanner/AsyncRunner + com.google.cloud.spanner.AsyncRunner withCommitStats() + + + 7012 + com/google/cloud/spanner/AsyncRunner + com.google.api.core.ApiFuture getCommitStats() + + + 7012 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.spanner.DatabaseClient$WriteResponse writeWithCommitStats(java.lang.Iterable) + + + 7012 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.spanner.DatabaseClient$WriteResponse writeAtLeastOnceWithCommitStats(java.lang.Iterable) + From f7d7ad5823180ed8ddbcbb76a8886a14cc3a55b8 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Sun, 18 Oct 2020 18:49:31 +0200 Subject: [PATCH 3/7] fix: make test cases independent of each other --- .../cloud/spanner/it/ITAsyncAPITest.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java index f5436efb32..c894f0fbb3 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java @@ -52,11 +52,13 @@ import com.google.common.util.concurrent.SettableFuture; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -92,6 +94,17 @@ public static void setUpDatabase() { "CREATE INDEX TestTableByValue ON TestTable(StringValue)", "CREATE INDEX TestTableByValueDesc ON TestTable(StringValue DESC)"); client = env.getTestHelper().getDatabaseClient(db); + executor = Executors.newSingleThreadExecutor(); + } + + @AfterClass + public static void cleanup() { + executor.shutdown(); + } + + @Before + public void setupData() { + client.write(Collections.singleton(Mutation.delete(TABLE_NAME, KeySet.all()))); // Includes k0..k14. Note that strings k{10,14} sort between k1 and k2. List mutations = new ArrayList<>(); @@ -105,12 +118,6 @@ public static void setUpDatabase() { .build()); } client.write(mutations); - executor = Executors.newSingleThreadExecutor(); - } - - @AfterClass - public static void cleanup() { - executor.shutdown(); } @Test From f0eab921e7793df899e584ce2c49b7848b892644 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Sun, 18 Oct 2020 20:16:49 +0200 Subject: [PATCH 4/7] docs: add comments to CommitStats --- .../com/google/cloud/spanner/CommitStats.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java index f07108f88b..41f6ffc38e 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/CommitStats.java @@ -23,7 +23,16 @@ /** * Commit statistics are returned by a read/write transaction if specifically requested. * - *

Usage: {@link TransactionRunner#withCommitStats()} + *

Use one of the following options to run a transaction that returns {@link CommitStats}: + * + *

    + *
  • {@link TransactionRunner#withCommitStats()} + *
  • {@link TransactionManager#withCommitStats()} + *
  • {@link AsyncRunner#withCommitStats()} + *
  • {@link AsyncTransactionManager#withCommitStats()} + *
  • {@link DatabaseClient#writeWithCommitStats(Iterable)} + *
  • {@link DatabaseClient#writeAtLeastOnceWithCommitStats(Iterable)} + *
*/ public class CommitStats { private final long mutationCount; @@ -42,10 +51,25 @@ static CommitStats fromProto(com.google.spanner.v1.CommitResponse.CommitStats pr .plusNanos(proto.getOverloadDelay().getNanos())); } + /** + * The number of mutations that were executed by the transaction. Insert and update operations + * count with the multiplicity of the number of columns they affect. For example, inserting a new + * record may count as five mutations, if values are inserted into five columns. Delete and delete + * range operations count as one mutation regardless of the number of columns affected. Deleting a + * row from a parent table that has the ON DELETE CASCADE annotation is also counted as one + * mutation regardless of the number of interleaved child rows present. The exception to this is + * if there are secondary indexes defined on rows being deleted, then the changes to the secondary + * indexes will be counted individually. For example, if a table has 2 secondary indexes, deleting + * a range of rows in the table will count as 1 mutation for the table, plus 2 mutations for each + * row that is deleted because the rows in the secondary index might be scattered over the + * key-space, making it impossible for Cloud Spanner to call a single delete range operation on + * the secondary indexes. Secondary indexes include the foreign keys backing indexes. + */ public long getMutationCount() { return mutationCount; } + /** The duration that the commit was delayed due to overloaded servers. */ public Duration getOverloadDelay() { return overloadDelay; } From 1fdf282b633eb324e60cf8696ca8d916a2489886 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Sun, 18 Oct 2020 20:28:34 +0200 Subject: [PATCH 5/7] docs: add clearifying comment --- .../cloud/spanner/SessionPoolAsyncTransactionManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java index 0019c1df7a..ceeea512b7 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java @@ -247,7 +247,8 @@ public AsyncTransactionManager withCommitStats() { new ApiFutureCallback() { @Override public void onFailure(Throwable t) { - // Ignore. + // Ignore this error as there is no underlying AsyncTransactionManager to instruct that + // it should return CommitStats. } @Override From c9e707b01cd9f774ee3b4fb35759537c9c0732d9 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Mon, 19 Oct 2020 17:24:38 +0200 Subject: [PATCH 6/7] feat: change response type of write instead of overload --- .../com/google/cloud/spanner/AsyncRunner.java | 11 +- .../google/cloud/spanner/AsyncRunnerImpl.java | 20 +-- .../spanner/AsyncTransactionManager.java | 11 +- .../spanner/AsyncTransactionManagerImpl.java | 21 +-- .../google/cloud/spanner/DatabaseClient.java | 66 ++++--- .../cloud/spanner/DatabaseClientImpl.java | 66 ++----- .../com/google/cloud/spanner/Options.java | 42 ++++- .../com/google/cloud/spanner/SessionImpl.java | 75 ++++---- .../com/google/cloud/spanner/SessionPool.java | 170 ++++++------------ .../SessionPoolAsyncTransactionManager.java | 29 +-- .../cloud/spanner/TransactionManager.java | 8 +- .../cloud/spanner/TransactionManagerImpl.java | 17 +- .../cloud/spanner/TransactionRunner.java | 11 +- .../cloud/spanner/TransactionRunnerImpl.java | 17 +- .../AsyncTransactionManagerImplTest.java | 20 ++- .../spanner/AsyncTransactionManagerTest.java | 3 +- .../cloud/spanner/DatabaseClientImplTest.java | 16 +- .../RetryOnInvalidatedSessionTest.java | 12 +- .../google/cloud/spanner/SessionImplTest.java | 6 +- .../google/cloud/spanner/SessionPoolTest.java | 13 +- .../spanner/TransactionManagerImplTest.java | 19 +- .../spanner/TransactionRunnerImplTest.java | 24 +-- .../connection/ConnectionImplTest.java | 10 -- .../connection/ReadWriteTransactionTest.java | 5 - .../connection/SingleUseTransactionTest.java | 10 -- .../cloud/spanner/it/ITAsyncAPITest.java | 5 +- .../google/cloud/spanner/it/ITBackupTest.java | 18 +- .../spanner/it/ITCommitTimestampTest.java | 14 +- .../cloud/spanner/it/ITReadOnlyTxnTest.java | 2 +- .../spanner/it/ITTransactionManagerTest.java | 3 +- .../cloud/spanner/it/ITTransactionTest.java | 3 +- .../google/cloud/spanner/it/ITWriteTest.java | 13 +- 32 files changed, 323 insertions(+), 437 deletions(-) diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java index 32be0411ed..21c9f60492 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunner.java @@ -57,17 +57,10 @@ interface AsyncWork { */ ApiFuture getCommitTimestamp(); - /** - * Indicates that the {@link AsyncRunner} should request the backend to return {@link - * CommitStats}. The {@link CommitStats} can be retrieved by calling {@link #getCommitStats()} - * after the transaction has successfully committed. - */ - AsyncRunner withCommitStats(); - /** * Returns the {@link CommitStats} of this transaction. This method may only be called after the - * transaction has successfully committed, and only if {@link #withCommitStats()} was called - * before executing the transaction. + * transaction has successfully committed, and only if {@link Options#commitStats()} was specified + * for the transaction. */ ApiFuture getCommitStats(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java index 06383dc0dc..5b11c53820 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncRunnerImpl.java @@ -26,12 +26,13 @@ class AsyncRunnerImpl implements AsyncRunner { private final TransactionRunnerImpl delegate; + private final Options options; private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); private final SettableApiFuture commitStats = SettableApiFuture.create(); - private boolean returnCommitStats; - AsyncRunnerImpl(TransactionRunnerImpl delegate) { - this.delegate = delegate; + AsyncRunnerImpl(TransactionRunnerImpl delegate, Options options) { + this.delegate = Preconditions.checkNotNull(delegate); + this.options = Preconditions.checkNotNull(options); } @Override @@ -75,7 +76,7 @@ private void setCommitTimestampAndStats() { } catch (Throwable t) { commitTimestamp.setException(t); } - if (returnCommitStats) { + if (options.withCommitStats()) { try { commitStats.set(delegate.getCommitStats()); } catch (Throwable t) { @@ -89,18 +90,11 @@ public ApiFuture getCommitTimestamp() { return commitTimestamp; } - @Override - public AsyncRunner withCommitStats() { - delegate.withCommitStats(); - returnCommitStats = true; - return this; - } - @Override public ApiFuture getCommitStats() { Preconditions.checkState( - returnCommitStats, - "getCommitStats may only be invoked if withCommitStats has been invoked before executing the transaction"); + options.withCommitStats(), + "getCommitStats may only be invoked if Options.commitStats() was specified for the transaction"); return commitStats; } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java index 8ceb486b9e..7b55eb1048 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManager.java @@ -191,17 +191,10 @@ public interface AsyncTransactionFunction { /** Returns the state of the transaction. */ TransactionState getState(); - /** - * Indicates that the {@link AsyncTransactionManager} should request the backend to return {@link - * CommitStats}. The {@link CommitStats} can be retrieved by calling {@link #getCommitStats()} - * after the transaction has successfully committed. - */ - AsyncTransactionManager withCommitStats(); - /** * Returns the {@link CommitStats} of this transaction. This method may only be called after the - * transaction has successfully committed, and only if {@link #withCommitStats()} was called - * before committing the transaction. + * transaction has successfully committed, and only if {@link Options#commitStats()} was specified + * for the transaction. */ ApiFuture getCommitStats(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java index 192990a888..1da763a6fb 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/AsyncTransactionManagerImpl.java @@ -37,16 +37,19 @@ final class AsyncTransactionManagerImpl private static final Tracer tracer = Tracing.getTracer(); private final SessionImpl session; + private final Options options; private Span span; - private boolean returnCommitStats; private TransactionRunnerImpl.TransactionContextImpl txn; private TransactionState txnState; private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); private SettableApiFuture commitStats; - AsyncTransactionManagerImpl(SessionImpl session, Span span) { + AsyncTransactionManagerImpl(SessionImpl session, Span span, Options options) { + Preconditions.checkNotNull(session); + Preconditions.checkNotNull(options); this.session = session; + this.options = options; this.span = span; } @@ -55,20 +58,14 @@ public void setSpan(Span span) { this.span = span; } - @Override - public AsyncTransactionManager withCommitStats() { - this.returnCommitStats = true; - return this; - } - @Override public ApiFuture getCommitStats() { Preconditions.checkState( txnState == TransactionState.COMMITTED, "getCommitStats can only be invoked if the transaction committed successfully"); Preconditions.checkState( - returnCommitStats, - "getCommitStats can only be invoked if withCommitStats() was invoked before committing the transaction"); + options.withCommitStats(), + "getCommitStats can only be invoked if Options.commitStats() was specified for the transaction"); return commitStats; } @@ -138,8 +135,8 @@ public ApiFuture commitAsync() { SpannerExceptionFactory.newSpannerException( ErrorCode.ABORTED, "Transaction already aborted")); } - ApiFuture res = txn.commitAsync(returnCommitStats); - if (returnCommitStats) { + ApiFuture res = txn.commitAsync(options.withCommitStats()); + if (options.withCommitStats()) { commitStats = SettableApiFuture.create(); } txnState = TransactionState.COMMITTED; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java index 290265da83..c8f350634a 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClient.java @@ -17,6 +17,7 @@ package com.google.cloud.spanner; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Options.TransactionOption; /** * Interface for all the APIs that are used to read/write data into a Cloud Spanner database. An @@ -24,6 +25,21 @@ */ public interface DatabaseClient { + /** + * Response for write methods that return both a commit {@link Timestamp} and {@link CommitStats}. + */ + interface WriteResponse { + /** The commit timestamp of the transaction. */ + Timestamp getCommitTimestamp(); + + /** + * The commit statistics of the transaction (if requested). + * + * @throws IllegalStateException if no {@link CommitStats} were requested for the transaction. + */ + CommitStats getCommitStats(); + } + /** * Writes the given mutations atomically to the database. * @@ -33,7 +49,7 @@ public interface DatabaseClient { * is not possible to know whether the mutations were applied without performing a subsequent * database operation, but the mutations will have been applied at most once. * - *

Example of blind write. + *

Example of blind write that returns {@link CommitStats}. * *

{@code
    * long singerId = my_singer_id;
@@ -45,12 +61,14 @@ public interface DatabaseClient {
    *         .set("LastName")
    *         .to("Joel")
    *         .build();
-   * dbClient.write(Collections.singletonList(mutation));
+   * dbClient.write(Collections.singletonList(mutation), Options.commitStats());
    * }
* - * @return the timestamp at which the write was committed + * @return the timestamp at which the write was committed and the {@link CommitStats} if those + * were requested. */ - Timestamp write(Iterable mutations) throws SpannerException; + WriteResponse write(Iterable mutations, TransactionOption... options) + throws SpannerException; /** * Writes the given mutations atomically to the database without replay protection. @@ -64,7 +82,7 @@ public interface DatabaseClient { * requires two RPCs (one of which may be performed in advance), and so this method may be * appropriate for latency sensitive and/or high throughput blind writing. * - *

Example of unprotected blind write. + *

Example of unprotected blind write that returns {@link CommitStats}. * *

{@code
    * long singerId = my_singer_id;
@@ -76,35 +94,13 @@ public interface DatabaseClient {
    *         .set("LastName")
    *         .to("Joel")
    *         .build();
-   * dbClient.writeAtLeastOnce(Collections.singletonList(mutation));
+   * dbClient.writeAtLeastOnce(Collections.singletonList(mutation), Options.commitStats());
    * }
* - * @return the timestamp at which the write was committed - */ - Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException; - - /** - * Response for write methods that return both a commit {@link Timestamp} and {@link CommitStats}. - */ - interface WriteResponse { - /** The commit timestamp of the transaction. */ - Timestamp getCommitTimestamp(); - - /** The commit statistics of the transaction. */ - CommitStats getCommitStats(); - } - - /** - * Same as {@link #write(Iterable)}, but requests the backend to return both a commit timestamp - * and {@link CommitStats}. - */ - WriteResponse writeWithCommitStats(Iterable mutations) throws SpannerException; - - /** - * Same as {@link #writeAtLeastOnce(Iterable)}, but requests the backend to return both a commit - * timestamp and {@link CommitStats}. + * @return the timestamp at which the write was committed and the {@link CommitStats} if those + * were requested. */ - WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) + WriteResponse writeAtLeastOnce(Iterable mutations, TransactionOption... options) throws SpannerException; /** @@ -270,7 +266,7 @@ WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) * }); * */ - TransactionRunner readWriteTransaction(); + TransactionRunner readWriteTransaction(TransactionOption... options); /** * Returns a transaction manager which allows manual management of transaction lifecycle. This API @@ -300,7 +296,7 @@ WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) * } * } */ - TransactionManager transactionManager(); + TransactionManager transactionManager(TransactionOption... options); /** * Returns an asynchronous transaction runner for executing a single logical transaction with @@ -333,7 +329,7 @@ WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) * executor); * */ - AsyncRunner runAsync(); + AsyncRunner runAsync(TransactionOption... options); /** * Returns an asynchronous transaction manager which allows manual management of transaction @@ -421,7 +417,7 @@ WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) * } * } */ - AsyncTransactionManager transactionManagerAsync(); + AsyncTransactionManager transactionManagerAsync(TransactionOption... options); /** * Returns the lower bound of rows modified by this DML statement. diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java index 458368ec24..3793ed1156 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/DatabaseClientImpl.java @@ -16,7 +16,7 @@ package com.google.cloud.spanner; -import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Options.TransactionOption; import com.google.cloud.spanner.SessionPool.PooledSessionFuture; import com.google.cloud.spanner.SpannerImpl.ClosedException; import com.google.common.annotations.VisibleForTesting; @@ -62,26 +62,7 @@ PooledSessionFuture getReadWriteSession() { } @Override - public Timestamp write(final Iterable mutations) throws SpannerException { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { - return runWithSessionRetry( - SessionMode.READ_WRITE, - new Function() { - @Override - public Timestamp apply(Session session) { - return session.write(mutations); - } - }); - } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); - throw e; - } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); - } - } - - public WriteResponse writeWithCommitStats(final Iterable mutations) + public WriteResponse write(final Iterable mutations, final TransactionOption... options) throws SpannerException { Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); try (Scope s = tracer.withSpan(span)) { @@ -90,27 +71,7 @@ public WriteResponse writeWithCommitStats(final Iterable mutations) new Function() { @Override public WriteResponse apply(Session session) { - return session.writeWithCommitStats(mutations); - } - }); - } catch (RuntimeException e) { - TraceUtil.setWithFailure(span, e); - throw e; - } finally { - span.end(TraceUtil.END_SPAN_OPTIONS); - } - } - - @Override - public Timestamp writeAtLeastOnce(final Iterable mutations) throws SpannerException { - Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); - try (Scope s = tracer.withSpan(span)) { - return runWithSessionRetry( - SessionMode.READ_WRITE, - new Function() { - @Override - public Timestamp apply(Session session) { - return session.writeAtLeastOnce(mutations); + return session.write(mutations, options); } }); } catch (RuntimeException e) { @@ -122,7 +83,8 @@ public Timestamp apply(Session session) { } @Override - public WriteResponse writeAtLeastOnceWithCommitStats(final Iterable mutations) + public WriteResponse writeAtLeastOnce( + final Iterable mutations, final TransactionOption... options) throws SpannerException { Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); try (Scope s = tracer.withSpan(span)) { @@ -131,7 +93,7 @@ public WriteResponse writeAtLeastOnceWithCommitStats(final Iterable mu new Function() { @Override public WriteResponse apply(Session session) { - return session.writeAtLeastOnceWithCommitStats(mutations); + return session.writeAtLeastOnce(mutations, options); } }); } catch (RuntimeException e) { @@ -209,10 +171,10 @@ public ReadOnlyTransaction readOnlyTransaction(TimestampBound bound) { } @Override - public TransactionRunner readWriteTransaction() { + public TransactionRunner readWriteTransaction(TransactionOption... options) { Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); try (Scope s = tracer.withSpan(span)) { - return getReadWriteSession().readWriteTransaction(); + return getReadWriteSession().readWriteTransaction(options); } catch (RuntimeException e) { TraceUtil.setWithFailure(span, e); throw e; @@ -222,10 +184,10 @@ public TransactionRunner readWriteTransaction() { } @Override - public TransactionManager transactionManager() { + public TransactionManager transactionManager(TransactionOption... options) { Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); try (Scope s = tracer.withSpan(span)) { - return getReadWriteSession().transactionManager(); + return getReadWriteSession().transactionManager(options); } catch (RuntimeException e) { TraceUtil.endSpanWithFailure(span, e); throw e; @@ -233,10 +195,10 @@ public TransactionManager transactionManager() { } @Override - public AsyncRunner runAsync() { + public AsyncRunner runAsync(TransactionOption... options) { Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); try (Scope s = tracer.withSpan(span)) { - return getReadWriteSession().runAsync(); + return getReadWriteSession().runAsync(options); } catch (RuntimeException e) { TraceUtil.endSpanWithFailure(span, e); throw e; @@ -244,10 +206,10 @@ public AsyncRunner runAsync() { } @Override - public AsyncTransactionManager transactionManagerAsync() { + public AsyncTransactionManager transactionManagerAsync(TransactionOption... options) { Span span = tracer.spanBuilder(READ_WRITE_TRANSACTION).startSpan(); try (Scope s = tracer.withSpan(span)) { - return getReadWriteSession().transactionManagerAsync(); + return getReadWriteSession().transactionManagerAsync(options); } catch (RuntimeException e) { TraceUtil.endSpanWithFailure(span, e); throw e; diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java index 879b632d17..323ee6cfc9 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/Options.java @@ -24,6 +24,9 @@ public final class Options implements Serializable { private static final long serialVersionUID = 8067099123096783941L; + /** Marker interface to mark options applicable to read/write transactions. */ + public interface TransactionOption {} + /** Marker interface to mark options applicable to both Read and Query operations */ public interface ReadAndQueryOption extends ReadOption, QueryOption {} @@ -36,6 +39,11 @@ public interface QueryOption {} /** Marker interface to mark options applicable to list operations in admin API. */ public interface ListOption {} + /** Specifying this will cause the transaction to request {@link CommitStats} from the backend. */ + public static TransactionOption commitStats() { + return COMMIT_STATS_OPTION; + } + /** * Specifying this will cause the read to yield at most this many rows. This should be greater * than 0. @@ -106,6 +114,16 @@ public static ListOption filter(String filter) { return new FilterOption(filter); } + /** Option to request {@link CommitStats} for read/write transactions. */ + static final class CommitStatsOption extends InternalOption implements TransactionOption { + @Override + void appendToOptions(Options options) { + options.withCommitStats = true; + } + } + + static final CommitStatsOption COMMIT_STATS_OPTION = new CommitStatsOption(); + /** Option pertaining to flow control. */ static final class FlowControlOption extends InternalOption implements ReadAndQueryOption { final int prefetchChunks; @@ -133,6 +151,7 @@ void appendToOptions(Options options) { } } + private boolean withCommitStats; private Long limit; private Integer prefetchChunks; private Integer bufferRows; @@ -143,6 +162,10 @@ void appendToOptions(Options options) { // Construction is via factory methods below. private Options() {} + boolean withCommitStats() { + return withCommitStats; + } + boolean hasLimit() { return limit != null; } @@ -194,6 +217,9 @@ String filter() { @Override public String toString() { StringBuilder b = new StringBuilder(); + if (withCommitStats) { + b.append("withCommitStats: ").append(withCommitStats).append(' '); + } if (limit != null) { b.append("limit: ").append(limit).append(' '); } @@ -224,7 +250,8 @@ public boolean equals(Object o) { } Options that = (Options) o; - return (!hasLimit() && !that.hasLimit() + return Objects.equals(withCommitStats, that.withCommitStats) + && (!hasLimit() && !that.hasLimit() || hasLimit() && that.hasLimit() && Objects.equals(limit(), that.limit())) && (!hasPrefetchChunks() && !that.hasPrefetchChunks() || hasPrefetchChunks() @@ -243,6 +270,9 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = 31; + if (withCommitStats) { + result = 31 * result + Boolean.valueOf(withCommitStats).hashCode(); + } if (limit != null) { result = 31 * result + limit.hashCode(); } @@ -264,6 +294,16 @@ public int hashCode() { return result; } + static Options fromTransactionOptions(TransactionOption... options) { + Options transactionOptions = new Options(); + for (TransactionOption option : options) { + if (option instanceof InternalOption) { + ((InternalOption) option).appendToOptions(transactionOptions); + } + } + return transactionOptions; + } + static Options fromReadOptions(ReadOption... options) { Options readOptions = new Options(); for (ReadOption option : options) { diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java index 1d99a544a2..121a3c3512 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionImpl.java @@ -25,9 +25,11 @@ import com.google.cloud.spanner.AbstractReadContext.MultiUseReadOnlyTransaction; import com.google.cloud.spanner.AbstractReadContext.SingleReadContext; import com.google.cloud.spanner.AbstractReadContext.SingleUseReadOnlyTransaction; +import com.google.cloud.spanner.Options.TransactionOption; import com.google.cloud.spanner.SessionClient.SessionId; import com.google.cloud.spanner.TransactionRunnerImpl.TransactionContextImpl; import com.google.cloud.spanner.spi.v1.SpannerRpc; +import com.google.common.base.Preconditions; import com.google.common.base.Ticker; import com.google.common.collect.Lists; import com.google.common.util.concurrent.MoreExecutors; @@ -88,14 +90,22 @@ private static class WriteResponseImpl implements WriteResponse { private final Timestamp commitTimestamp; private final CommitStats commitStats; - private WriteResponseImpl(TransactionRunner runner) { + private WriteResponseImpl(TransactionRunner runner, Options options) { this.commitTimestamp = runner.getCommitTimestamp(); - this.commitStats = runner.getCommitStats(); + if (options.withCommitStats()) { + this.commitStats = runner.getCommitStats(); + } else { + this.commitStats = null; + } } private WriteResponseImpl(CommitResponse response) { this.commitTimestamp = Timestamp.fromProto(response.getCommitTimestamp()); - this.commitStats = CommitStats.fromProto(response.getCommitStats()); + if (response.hasCommitStats()) { + this.commitStats = CommitStats.fromProto(response.getCommitStats()); + } else { + this.commitStats = null; + } } @Override @@ -105,6 +115,9 @@ public Timestamp getCommitTimestamp() { @Override public CommitStats getCommitStats() { + Preconditions.checkState( + this.commitStats != null, + "No CommitStats were requested for the transaction. Use Options.commitStats() to request CommitStats for a read/write transaction."); return this.commitStats; } } @@ -147,20 +160,9 @@ public long executePartitionedUpdate(Statement stmt) { } @Override - public Timestamp write(Iterable mutations) throws SpannerException { - return write(mutations, false).getCommitTimestamp(); - } - - @Override - public WriteResponse writeWithCommitStats(Iterable mutations) throws SpannerException { - return new WriteResponseImpl(write(mutations, true)); - } - - private TransactionRunner write(Iterable mutations, boolean withCommitStats) { - TransactionRunner runner = readWriteTransaction(); - if (withCommitStats) { - runner = runner.withCommitStats(); - } + public WriteResponse write(Iterable mutations, TransactionOption... options) + throws SpannerException { + TransactionRunner runner = readWriteTransaction(options); final Collection finalMutations = mutations instanceof java.util.Collection ? (Collection) mutations @@ -173,28 +175,20 @@ public Void run(TransactionContext ctx) { return null; } }); - return runner; + return new WriteResponseImpl(runner, Options.fromTransactionOptions(options)); } @Override - public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException { - return Timestamp.fromProto(writeAtLeastOnce(mutations, false).getCommitTimestamp()); - } - - @Override - public WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) + public WriteResponse writeAtLeastOnce(Iterable mutations, TransactionOption... options) throws SpannerException { - return new WriteResponseImpl(writeAtLeastOnce(mutations, true)); - } - - private CommitResponse writeAtLeastOnce(Iterable mutations, boolean withCommitStats) { setActive(null); + Options opts = Options.fromTransactionOptions(options); List mutationsProto = new ArrayList<>(); Mutation.toProto(mutations, mutationsProto); final CommitRequest request = CommitRequest.newBuilder() .setSession(name) - .setReturnCommitStats(withCommitStats) + .setReturnCommitStats(opts.withCommitStats()) .addAllMutations(mutationsProto) .setSingleUseTransaction( TransactionOptions.newBuilder() @@ -202,7 +196,7 @@ private CommitResponse writeAtLeastOnce(Iterable mutations, boolean wi .build(); Span span = tracer.spanBuilder(SpannerImpl.COMMIT).startSpan(); try (Scope s = tracer.withSpan(span)) { - return spanner.getRpc().commit(request, options); + return new WriteResponseImpl(spanner.getRpc().commit(request, this.options)); } catch (IllegalArgumentException e) { TraceUtil.setWithFailure(span, e); throw newSpannerException(ErrorCode.INTERNAL, "Could not parse commit timestamp", e); @@ -272,26 +266,25 @@ public ReadOnlyTransaction readOnlyTransaction(TimestampBound bound) { } @Override - public TransactionRunner readWriteTransaction() { - return setActive( - new TransactionRunnerImpl(this, spanner.getRpc(), spanner.getDefaultPrefetchChunks())); + public TransactionRunner readWriteTransaction(TransactionOption... options) { + return setActive(new TransactionRunnerImpl(this, Options.fromTransactionOptions(options))); } @Override - public AsyncRunner runAsync() { - return new AsyncRunnerImpl( - setActive( - new TransactionRunnerImpl(this, spanner.getRpc(), spanner.getDefaultPrefetchChunks()))); + public AsyncRunner runAsync(TransactionOption... options) { + Options opts = Options.fromTransactionOptions(options); + return new AsyncRunnerImpl(setActive(new TransactionRunnerImpl(this, opts)), opts); } @Override - public TransactionManager transactionManager() { - return new TransactionManagerImpl(this, currentSpan); + public TransactionManager transactionManager(TransactionOption... options) { + return new TransactionManagerImpl(this, currentSpan, Options.fromTransactionOptions(options)); } @Override - public AsyncTransactionManagerImpl transactionManagerAsync() { - return new AsyncTransactionManagerImpl(this, currentSpan); + public AsyncTransactionManagerImpl transactionManagerAsync(TransactionOption... options) { + return new AsyncTransactionManagerImpl( + this, currentSpan, Options.fromTransactionOptions(options)); } @Override diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java index 0d8f23a3c6..b1a55e89de 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPool.java @@ -47,6 +47,7 @@ import com.google.cloud.grpc.GrpcTransportOptions.ExecutorFactory; import com.google.cloud.spanner.Options.QueryOption; import com.google.cloud.spanner.Options.ReadOption; +import com.google.cloud.spanner.Options.TransactionOption; import com.google.cloud.spanner.SessionClient.SessionConsumer; import com.google.cloud.spanner.SpannerException.ResourceNotFoundException; import com.google.cloud.spanner.SpannerImpl.ClosedException; @@ -720,21 +721,20 @@ public void close() { private TransactionManager delegate; private final SessionPool sessionPool; private PooledSessionFuture session; - private boolean returnCommitStats; + private final TransactionOption[] options; private boolean closed; private boolean restartedAfterSessionNotFound; - AutoClosingTransactionManager(SessionPool sessionPool, PooledSessionFuture session) { - this.sessionPool = sessionPool; - this.session = session; + AutoClosingTransactionManager( + SessionPool sessionPool, PooledSessionFuture session, TransactionOption... options) { + this.sessionPool = Preconditions.checkNotNull(sessionPool); + this.session = Preconditions.checkNotNull(session); + this.options = Preconditions.checkNotNull(options); } @Override public TransactionContext begin() { - this.delegate = session.get().transactionManager(); - if (returnCommitStats) { - this.delegate.withCommitStats(); - } + this.delegate = session.get().transactionManager(options); while (true) { try { return internalBegin(); @@ -747,10 +747,7 @@ public TransactionContext begin() { @SuppressWarnings("resource") private void refreshDelegateTransactionManager() { - delegate = session.get().delegate.transactionManager(); - if (returnCommitStats) { - delegate.withCommitStats(); - } + delegate = session.get().delegate.transactionManager(options); } private TransactionContext internalBegin() { @@ -813,12 +810,6 @@ public Timestamp getCommitTimestamp() { return delegate.getCommitTimestamp(); } - @Override - public TransactionManager withCommitStats() { - this.returnCommitStats = true; - return this; - } - @Override public CommitStats getCommitStats() { return delegate.getCommitStats(); @@ -855,17 +846,20 @@ public TransactionState getState() { */ private static final class SessionPoolTransactionRunner implements TransactionRunner { private final SessionPool sessionPool; + private final TransactionOption[] options; private PooledSessionFuture session; private TransactionRunner runner; - private SessionPoolTransactionRunner(SessionPool sessionPool, PooledSessionFuture session) { - this.sessionPool = sessionPool; - this.session = session; + private SessionPoolTransactionRunner( + SessionPool sessionPool, PooledSessionFuture session, TransactionOption... options) { + this.sessionPool = Preconditions.checkNotNull(sessionPool); + this.session = Preconditions.checkNotNull(session); + this.options = Preconditions.checkNotNull(options); } private TransactionRunner getRunner() { if (this.runner == null) { - this.runner = session.get().readWriteTransaction(); + this.runner = session.get().readWriteTransaction(options); } return runner; } @@ -881,7 +875,7 @@ public T run(TransactionCallable callable) { break; } catch (SessionNotFoundException e) { session = sessionPool.replaceReadWriteSession(e, session); - runner = session.get().delegate.readWriteTransaction(); + runner = session.get().delegate.readWriteTransaction(options); } } session.get().markUsed(); @@ -904,12 +898,6 @@ public TransactionRunner allowNestedTransaction() { return this; } - @Override - public TransactionRunner withCommitStats() { - getRunner().withCommitStats(); - return this; - } - @Override public CommitStats getCommitStats() { return getRunner().getCommitStats(); @@ -918,14 +906,21 @@ public CommitStats getCommitStats() { private static class SessionPoolAsyncRunner implements AsyncRunner { private final SessionPool sessionPool; + private final TransactionOption[] options; private volatile PooledSessionFuture session; private final SettableApiFuture commitTimestamp = SettableApiFuture.create(); - private final SettableApiFuture commitStats = SettableApiFuture.create(); - private boolean returnCommitStats; - - private SessionPoolAsyncRunner(SessionPool sessionPool, PooledSessionFuture session) { - this.sessionPool = sessionPool; - this.session = session; + private final SettableApiFuture commitStats; + + private SessionPoolAsyncRunner( + SessionPool sessionPool, PooledSessionFuture session, TransactionOption... options) { + this.sessionPool = Preconditions.checkNotNull(sessionPool); + this.session = Preconditions.checkNotNull(session); + this.options = options; + if (Options.fromTransactionOptions(options).withCommitStats()) { + commitStats = SettableApiFuture.create(); + } else { + commitStats = null; + } } @Override @@ -940,10 +935,7 @@ public void run() { AsyncRunner runner = null; while (true) { try { - runner = session.get().runAsync(); - if (returnCommitStats) { - runner.withCommitStats(); - } + runner = session.get().runAsync(options); r = runner.runAsync(work, MoreExecutors.directExecutor()).get(); break; } catch (ExecutionException e) { @@ -980,7 +972,7 @@ private void setCommitTimestampAndStats(AsyncRunner delegate) { } catch (Throwable t) { commitTimestamp.setException(t); } - if (returnCommitStats) { + if (commitStats != null) { try { commitStats.set(delegate.getCommitStats().get()); } catch (Throwable t) { @@ -994,16 +986,10 @@ public ApiFuture getCommitTimestamp() { return commitTimestamp; } - @Override - public AsyncRunner withCommitStats() { - this.returnCommitStats = true; - return this; - } - @Override public ApiFuture getCommitStats() { Preconditions.checkState( - returnCommitStats, + commitStats != null, "getCommitStats may only be invoked if withCommitStats has been invoked before executing the transaction"); return commitStats; } @@ -1155,38 +1141,20 @@ private void markCheckedOut() { } @Override - public Timestamp write(Iterable mutations) throws SpannerException { - try { - return get().write(mutations); - } finally { - close(); - } - } - - @Override - public WriteResponse writeWithCommitStats(Iterable mutations) + public WriteResponse write(Iterable mutations, TransactionOption... options) throws SpannerException { try { - return get().writeWithCommitStats(mutations); - } finally { - close(); - } - } - - @Override - public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException { - try { - return get().writeAtLeastOnce(mutations); + return get().write(mutations, options); } finally { close(); } } @Override - public WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) - throws SpannerException { + public WriteResponse writeAtLeastOnce( + Iterable mutations, TransactionOption... options) throws SpannerException { try { - return get().writeAtLeastOnceWithCommitStats(mutations); + return get().writeAtLeastOnce(mutations, options); } finally { close(); } @@ -1291,23 +1259,23 @@ private ReadOnlyTransaction internalReadOnlyTransaction( } @Override - public TransactionRunner readWriteTransaction() { - return new SessionPoolTransactionRunner(SessionPool.this, this); + public TransactionRunner readWriteTransaction(TransactionOption... options) { + return new SessionPoolTransactionRunner(SessionPool.this, this, options); } @Override - public TransactionManager transactionManager() { - return new AutoClosingTransactionManager(SessionPool.this, this); + public TransactionManager transactionManager(TransactionOption... options) { + return new AutoClosingTransactionManager(SessionPool.this, this, options); } @Override - public AsyncRunner runAsync() { - return new SessionPoolAsyncRunner(SessionPool.this, this); + public AsyncRunner runAsync(TransactionOption... options) { + return new SessionPoolAsyncRunner(SessionPool.this, this, options); } @Override - public AsyncTransactionManager transactionManagerAsync() { - return new SessionPoolAsyncTransactionManager(this); + public AsyncTransactionManager transactionManagerAsync(TransactionOption... options) { + return new SessionPoolAsyncTransactionManager(this, options); } @Override @@ -1418,42 +1386,22 @@ void setAllowReplacing(boolean allowReplacing) { } @Override - public Timestamp write(Iterable mutations) throws SpannerException { - try { - markUsed(); - return delegate.write(mutations); - } catch (SpannerException e) { - throw lastException = e; - } - } - - @Override - public WriteResponse writeWithCommitStats(Iterable mutations) + public WriteResponse write(Iterable mutations, TransactionOption... options) throws SpannerException { try { markUsed(); - return delegate.writeWithCommitStats(mutations); - } catch (SpannerException e) { - throw lastException = e; - } - } - - @Override - public Timestamp writeAtLeastOnce(Iterable mutations) throws SpannerException { - try { - markUsed(); - return delegate.writeAtLeastOnce(mutations); + return delegate.write(mutations, options); } catch (SpannerException e) { throw lastException = e; } } @Override - public WriteResponse writeAtLeastOnceWithCommitStats(Iterable mutations) - throws SpannerException { + public WriteResponse writeAtLeastOnce( + Iterable mutations, TransactionOption... options) throws SpannerException { try { markUsed(); - return delegate.writeAtLeastOnceWithCommitStats(mutations); + return delegate.writeAtLeastOnce(mutations, options); } catch (SpannerException e) { throw lastException = e; } @@ -1500,18 +1448,18 @@ public ReadOnlyTransaction readOnlyTransaction(TimestampBound bound) { } @Override - public TransactionRunner readWriteTransaction() { - return delegate.readWriteTransaction(); + public TransactionRunner readWriteTransaction(TransactionOption... options) { + return delegate.readWriteTransaction(options); } @Override - public AsyncRunner runAsync() { - return delegate.runAsync(); + public AsyncRunner runAsync(TransactionOption... options) { + return delegate.runAsync(options); } @Override - public AsyncTransactionManagerImpl transactionManagerAsync() { - return delegate.transactionManagerAsync(); + public AsyncTransactionManagerImpl transactionManagerAsync(TransactionOption... options) { + return delegate.transactionManagerAsync(options); } @Override @@ -1582,8 +1530,8 @@ void markUsed() { } @Override - public TransactionManager transactionManager() { - return delegate.transactionManager(); + public TransactionManager transactionManager(TransactionOption... options) { + return delegate.transactionManager(options); } } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java index ceeea512b7..09594a7c0f 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SessionPoolAsyncTransactionManager.java @@ -22,6 +22,7 @@ import com.google.api.core.ApiFutures; import com.google.api.core.SettableApiFuture; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.Options.TransactionOption; import com.google.cloud.spanner.SessionPool.PooledSessionFuture; import com.google.cloud.spanner.TransactionContextFutureImpl.CommittableAsyncTransactionManager; import com.google.cloud.spanner.TransactionManager.TransactionState; @@ -39,7 +40,8 @@ class SessionPoolAsyncTransactionManager implements CommittableAsyncTransactionM private final SettableApiFuture delegate = SettableApiFuture.create(); - SessionPoolAsyncTransactionManager(PooledSessionFuture session) { + SessionPoolAsyncTransactionManager( + PooledSessionFuture session, final TransactionOption... options) { this.session = session; this.session.addListener( new Runnable() { @@ -47,7 +49,10 @@ class SessionPoolAsyncTransactionManager implements CommittableAsyncTransactionM public void run() { try { delegate.set( - SessionPoolAsyncTransactionManager.this.session.get().transactionManagerAsync()); + SessionPoolAsyncTransactionManager.this + .session + .get() + .transactionManagerAsync(options)); } catch (Throwable t) { delegate.setException(t); } @@ -240,26 +245,6 @@ public TransactionState getState() { } } - @Override - public AsyncTransactionManager withCommitStats() { - ApiFutures.addCallback( - delegate, - new ApiFutureCallback() { - @Override - public void onFailure(Throwable t) { - // Ignore this error as there is no underlying AsyncTransactionManager to instruct that - // it should return CommitStats. - } - - @Override - public void onSuccess(AsyncTransactionManagerImpl result) { - result.withCommitStats(); - } - }, - MoreExecutors.directExecutor()); - return this; - } - @Override public ApiFuture getCommitStats() { return ApiFutures.transformAsync( diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java index e2e6ba7471..2bd7b67c53 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManager.java @@ -90,13 +90,9 @@ public enum TransactionState { /** Returns the state of the transaction. */ TransactionState getState(); - /** Instructs the {@link TransactionManager} to request {@link CommitStats} from the backend. */ - TransactionManager withCommitStats(); - /** - * Returns the {@link CommitStats} if {@link #withCommitStats()} was called before the transaction - * was committed and the transaction committed successfully. Otherwise it will throw {@code - * IllegalStateException}. + * Returns the {@link CommitStats} if {@link Options#commitStats()} was specified for the + * transaction. Otherwise it will throw {@code IllegalStateException}. */ CommitStats getCommitStats(); diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java index c7875c8314..547828f7e7 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionManagerImpl.java @@ -29,27 +29,22 @@ final class TransactionManagerImpl implements TransactionManager, SessionTransac private static final Tracer tracer = Tracing.getTracer(); private final SessionImpl session; + private final Options options; private Span span; - private boolean returnCommitStats; private TransactionRunnerImpl.TransactionContextImpl txn; private TransactionState txnState; - TransactionManagerImpl(SessionImpl session, Span span) { + TransactionManagerImpl(SessionImpl session, Span span, Options options) { this.session = session; this.span = span; + this.options = options; } Span getSpan() { return span; } - @Override - public TransactionManager withCommitStats() { - this.returnCommitStats = true; - return this; - } - @Override public void setSpan(Span span) { this.span = span; @@ -78,7 +73,7 @@ public void commit() { ErrorCode.ABORTED, "Transaction already aborted"); } try { - txn.commit(returnCommitStats); + txn.commit(options.withCommitStats()); txnState = TransactionState.COMMITTED; } catch (AbortedException e1) { txnState = TransactionState.ABORTED; @@ -129,8 +124,8 @@ public CommitStats getCommitStats() { txnState == TransactionState.COMMITTED, "getCommitStats can only be invoked if the transaction committed successfully"); Preconditions.checkState( - returnCommitStats, - "getCommitStats can only be invoked if withCommitStats() was invoked before committing the transaction"); + options.withCommitStats(), + "getCommitStats can only be invoked if Options.commitStats() was specified for the transaction"); return txn.commitStats(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java index 928079cdba..6ba3deaff2 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunner.java @@ -92,17 +92,10 @@ interface TransactionCallable { */ TransactionRunner allowNestedTransaction(); - /** - * Indicates that the {@link TransactionRunner} should request the backend to return {@link - * CommitStats}. The {@link CommitStats} can be retrieved by calling {@link #getCommitStats()} - * after the transaction has successfully committed. - */ - TransactionRunner withCommitStats(); - /** * Returns the {@link CommitStats} of this transaction. This method may only be called after the - * transaction has successfully committed, and only if {@link #withCommitStats()} was called - * before executing the transaction. + * transaction has successfully committed, and only if {@link Options#commitStats()} was specified + * for the transaction. */ CommitStats getCommitStats(); } diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java index 2d0a731d2f..3aafc251dc 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionRunnerImpl.java @@ -30,8 +30,8 @@ import com.google.cloud.spanner.Options.QueryOption; import com.google.cloud.spanner.Options.ReadOption; import com.google.cloud.spanner.SessionImpl.SessionTransaction; -import com.google.cloud.spanner.spi.v1.SpannerRpc; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; @@ -635,8 +635,8 @@ public ListenableAsyncResultSet executeQueryAsync( } private boolean blockNestedTxn = true; - private boolean returnCommitStats = false; private final SessionImpl session; + private final Options options; private Span span; private TransactionContextImpl txn; private volatile boolean isValid = true; @@ -647,14 +647,9 @@ public TransactionRunner allowNestedTransaction() { return this; } - @Override - public TransactionRunner withCommitStats() { - returnCommitStats = true; - return this; - } - - TransactionRunnerImpl(SessionImpl session, SpannerRpc rpc, int defaultPrefetchChunks) { - this.session = session; + TransactionRunnerImpl(SessionImpl session, Options options) { + this.session = Preconditions.checkNotNull(session); + this.options = Preconditions.checkNotNull(options); this.txn = session.newTransaction(); } @@ -741,7 +736,7 @@ public T call() { } try { - txn.commit(returnCommitStats); + txn.commit(options.withCommitStats()); span.addAnnotation( "Transaction Attempt Succeeded", ImmutableMap.of( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java index 7d3f051053..c7869f31e5 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerImplTest.java @@ -35,22 +35,24 @@ public class AsyncTransactionManagerImplTest { @Mock private SessionImpl session; @Mock TransactionRunnerImpl.TransactionContextImpl txn; - private AsyncTransactionManagerImpl manager; @Before public void setUp() { initMocks(this); - manager = new AsyncTransactionManagerImpl(session, mock(Span.class)); } @Test public void commitReturnsCommitStats() { - when(session.newTransaction()).thenReturn(txn); - when(txn.ensureTxnAsync()).thenReturn(ApiFutures.immediateFuture(null)); - Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); - when(txn.commitAsync(true)).thenReturn(ApiFutures.immediateFuture(commitTimestamp)); - manager.withCommitStats().beginAsync(); - manager.commitAsync(); - verify(txn).commitAsync(true); + try (AsyncTransactionManagerImpl manager = + new AsyncTransactionManagerImpl( + session, mock(Span.class), Options.fromTransactionOptions(Options.commitStats()))) { + when(session.newTransaction()).thenReturn(txn); + when(txn.ensureTxnAsync()).thenReturn(ApiFutures.immediateFuture(null)); + Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); + when(txn.commitAsync(true)).thenReturn(ApiFutures.immediateFuture(commitTimestamp)); + manager.beginAsync(); + manager.commitAsync(); + verify(txn).commitAsync(true); + } } } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java index eba0c55c8f..ae1aad6de7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/AsyncTransactionManagerTest.java @@ -212,7 +212,8 @@ public boolean apply(AbstractMessage input) { @Test public void asyncTransactionManager_returnsCommitStats() throws Exception { - try (AsyncTransactionManager manager = client().transactionManagerAsync().withCommitStats()) { + try (AsyncTransactionManager manager = + client().transactionManagerAsync(Options.commitStats())) { TransactionContextFuture txn = manager.beginAsync(); while (true) { try { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java index 975058a524..91706009bc 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/DatabaseClientImplTest.java @@ -163,9 +163,10 @@ public void writeWithCommitStats() { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); WriteResponse response = - client.writeWithCommitStats( + client.write( Arrays.asList( - Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build()), + Options.commitStats()); assertThat(response).isNotNull(); assertThat(response.getCommitTimestamp()).isNotNull(); assertThat(response.getCommitStats()).isNotNull(); @@ -185,9 +186,10 @@ public void writeAtLeastOnceWithCommitStats() { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); WriteResponse response = - client.writeAtLeastOnceWithCommitStats( + client.writeAtLeastOnce( Arrays.asList( - Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build())); + Mutation.newInsertBuilder("FOO").set("ID").to(1L).set("NAME").to("Bar").build()), + Options.commitStats()); assertThat(response).isNotNull(); assertThat(response.getCommitTimestamp()).isNotNull(); assertThat(response.getCommitStats()).isNotNull(); @@ -465,7 +467,7 @@ public Void run(TransactionContext transaction) throws Exception { public void readWriteTransaction_returnsCommitStats() { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); - TransactionRunner runner = client.readWriteTransaction().withCommitStats(); + TransactionRunner runner = client.readWriteTransaction(Options.commitStats()); runner.run( new TransactionCallable() { @Override @@ -522,7 +524,7 @@ public void runAsync_returnsCommitStats() throws Exception { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); ExecutorService executor = Executors.newSingleThreadExecutor(); - AsyncRunner runner = client.runAsync().withCommitStats(); + AsyncRunner runner = client.runAsync(Options.commitStats()); ApiFuture fut = runner.runAsync( new AsyncWork() { @@ -610,7 +612,7 @@ public void transactionManager() throws Exception { public void transactionManager_returnsCommitStats() throws Exception { DatabaseClient client = spanner.getDatabaseClient(DatabaseId.of(TEST_PROJECT, TEST_INSTANCE, TEST_DATABASE)); - try (TransactionManager txManager = client.transactionManager().withCommitStats()) { + try (TransactionManager txManager = client.transactionManager(Options.commitStats())) { while (true) { TransactionContext tx = txManager.begin(); try { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java index fcf1c6e35b..489fe43187 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/RetryOnInvalidatedSessionTest.java @@ -23,7 +23,7 @@ import com.google.api.gax.core.NoCredentialsProvider; import com.google.api.gax.grpc.testing.LocalChannelProvider; import com.google.cloud.NoCredentials; -import com.google.cloud.Timestamp; +import com.google.cloud.spanner.DatabaseClient.WriteResponse; import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; import com.google.cloud.spanner.TransactionRunner.TransactionCallable; import com.google.cloud.spanner.v1.SpannerClient; @@ -1432,8 +1432,9 @@ public void write() throws InterruptedException { initReadWriteSessionPool(); invalidateSessionPool(); try { - Timestamp timestamp = client.write(Arrays.asList(Mutation.delete("FOO", KeySet.all()))); - assertThat(timestamp).isNotNull(); + WriteResponse response = client.write(Arrays.asList(Mutation.delete("FOO", KeySet.all()))); + assertThat(response).isNotNull(); + assertThat(response.getCommitTimestamp()).isNotNull(); assertThat(failOnInvalidatedSession).isFalse(); } catch (SessionNotFoundException e) { assertThat(failOnInvalidatedSession).isTrue(); @@ -1445,9 +1446,10 @@ public void writeAtLeastOnce() throws InterruptedException { initReadWriteSessionPool(); invalidateSessionPool(); try { - Timestamp timestamp = + WriteResponse response = client.writeAtLeastOnce(Arrays.asList(Mutation.delete("FOO", KeySet.all()))); - assertThat(timestamp).isNotNull(); + assertThat(response).isNotNull(); + assertThat(response.getCommitTimestamp()).isNotNull(); assertThat(failOnInvalidatedSession).isFalse(); } catch (SessionNotFoundException e) { assertThat(failOnInvalidatedSession).isTrue(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java index c756a7898a..04496d88c0 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionImplTest.java @@ -213,8 +213,10 @@ public void writeAtLeastOnce() throws ParseException { Mockito.when(rpc.commit(commit.capture(), Mockito.eq(options))).thenReturn(response); Timestamp timestamp = - session.writeAtLeastOnce( - Arrays.asList(Mutation.newInsertBuilder("T").set("C").to("x").build())); + session + .writeAtLeastOnce( + Arrays.asList(Mutation.newInsertBuilder("T").set("C").to("x").build())) + .getCommitTimestamp(); assertThat(timestamp.getSeconds()) .isEqualTo(utcTimeSeconds(2015, Calendar.OCTOBER, 1, 10, 54, 20)); assertThat(timestamp.getNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(21)); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java index d5ea648bbd..d09bfac23e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/SessionPoolTest.java @@ -40,6 +40,7 @@ import com.google.api.core.ApiFutures; import com.google.cloud.Timestamp; +import com.google.cloud.spanner.DatabaseClient.WriteResponse; import com.google.cloud.spanner.MetricRegistryTestUtils.FakeMetricRegistry; import com.google.cloud.spanner.MetricRegistryTestUtils.MetricsRecord; import com.google.cloud.spanner.MetricRegistryTestUtils.PointWithFunction; @@ -1360,7 +1361,7 @@ public void testSessionNotFoundReadWriteTransaction() { when(closedSession.newTransaction()).thenReturn(closedTransactionContext); when(closedSession.beginTransactionAsync()).thenThrow(sessionNotFound); TransactionRunnerImpl closedTransactionRunner = - new TransactionRunnerImpl(closedSession, rpc, 10); + new TransactionRunnerImpl(closedSession, Options.fromTransactionOptions()); closedTransactionRunner.setSpan(mock(Span.class)); when(closedSession.readWriteTransaction()).thenReturn(closedTransactionRunner); @@ -1374,7 +1375,7 @@ public void testSessionNotFoundReadWriteTransaction() { when(openSession.beginTransactionAsync()) .thenReturn(ApiFutures.immediateFuture(ByteString.copyFromUtf8("open-txn"))); TransactionRunnerImpl openTransactionRunner = - new TransactionRunnerImpl(openSession, mock(SpannerRpc.class), 10); + new TransactionRunnerImpl(openSession, Options.fromTransactionOptions()); openTransactionRunner.setSpan(mock(Span.class)); when(openSession.readWriteTransaction()).thenReturn(openTransactionRunner); @@ -1578,7 +1579,9 @@ public void testSessionNotFoundWrite() { when(closedSession.write(mutations)).thenThrow(sessionNotFound); final SessionImpl openSession = mockSession(); - when(openSession.write(mutations)).thenReturn(Timestamp.now()); + WriteResponse response = mock(WriteResponse.class); + when(response.getCommitTimestamp()).thenReturn(Timestamp.now()); + when(openSession.write(mutations)).thenReturn(response); doAnswer( new Answer() { @Override @@ -1630,7 +1633,9 @@ public void testSessionNotFoundWriteAtLeastOnce() { when(closedSession.writeAtLeastOnce(mutations)).thenThrow(sessionNotFound); final SessionImpl openSession = mockSession(); - when(openSession.writeAtLeastOnce(mutations)).thenReturn(Timestamp.now()); + WriteResponse response = mock(WriteResponse.class); + when(response.getCommitTimestamp()).thenReturn(Timestamp.now()); + when(openSession.writeAtLeastOnce(mutations)).thenReturn(response); doAnswer( new Answer() { @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java index 1bfea218f6..e67d524b52 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionManagerImplTest.java @@ -78,7 +78,8 @@ public void release(ScheduledExecutorService exec) { @Before public void setUp() { initMocks(this); - manager = new TransactionManagerImpl(session, mock(Span.class)); + manager = + new TransactionManagerImpl(session, mock(Span.class), Options.fromTransactionOptions()); } @Test @@ -146,12 +147,16 @@ public void commitSucceeds() { @Test public void commitReturnsCommitStats() { - when(session.newTransaction()).thenReturn(txn); - Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); - when(txn.commitTimestamp()).thenReturn(commitTimestamp); - manager.withCommitStats().begin(); - manager.commit(); - verify(txn).commit(true); + try (TransactionManager manager = + new TransactionManagerImpl( + session, mock(Span.class), Options.fromTransactionOptions(Options.commitStats()))) { + when(session.newTransaction()).thenReturn(txn); + Timestamp commitTimestamp = Timestamp.ofTimeMicroseconds(1); + when(txn.commitTimestamp()).thenReturn(commitTimestamp); + manager.begin(); + manager.commit(); + verify(txn).commit(true); + } } @Test diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java index 60b2063c95..28202fc398 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/TransactionRunnerImplTest.java @@ -97,7 +97,7 @@ public void setUp() { MockitoAnnotations.initMocks(this); firstRun = true; when(session.newTransaction()).thenReturn(txn); - transactionRunner = new TransactionRunnerImpl(session, rpc, 1); + transactionRunner = new TransactionRunnerImpl(session, Options.fromTransactionOptions()); when(rpc.commitAsync(Mockito.any(CommitRequest.class), Mockito.anyMap())) .thenReturn( ApiFutures.immediateFuture( @@ -195,15 +195,16 @@ public Void run(TransactionContext transaction) { @Test public void testReturnCommitStats() { - transactionRunner - .withCommitStats() - .run( - new TransactionCallable() { - @Override - public Void run(TransactionContext transaction) throws Exception { - return null; - } - }); + TransactionRunnerImpl transactionRunner = + new TransactionRunnerImpl(session, Options.fromTransactionOptions(Options.commitStats())); + transactionRunner.setSpan(mock(Span.class)); + transactionRunner.run( + new TransactionCallable() { + @Override + public Void run(TransactionContext transaction) throws Exception { + return null; + } + }); verify(txn).commit(true); } @@ -303,7 +304,8 @@ private long[] batchDmlException(int status) { .thenReturn( ApiFutures.immediateFuture(ByteString.copyFromUtf8(UUID.randomUUID().toString()))); when(session.getName()).thenReturn(SessionId.of("p", "i", "d", "test").getName()); - TransactionRunnerImpl runner = new TransactionRunnerImpl(session, rpc, 10); + TransactionRunnerImpl runner = + new TransactionRunnerImpl(session, Options.fromTransactionOptions()); runner.setSpan(mock(Span.class)); ExecuteBatchDmlResponse response1 = ExecuteBatchDmlResponse.newBuilder() diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java index 7da737ca3f..15702f6d5f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionImplTest.java @@ -131,11 +131,6 @@ public void close() { } } - @Override - public TransactionManager withCommitStats() { - return this; - } - @Override public CommitStats getCommitStats() { throw new UnsupportedOperationException(); @@ -352,11 +347,6 @@ public TransactionRunner allowNestedTransaction() { return this; } - @Override - public TransactionRunner withCommitStats() { - return this; - } - @Override public CommitStats getCommitStats() { throw new UnsupportedOperationException(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java index 44245fc9e1..355de39f6f 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ReadWriteTransactionTest.java @@ -131,11 +131,6 @@ public void close() { } } - @Override - public TransactionManager withCommitStats() { - return this; - } - @Override public CommitStats getCommitStats() { throw new UnsupportedOperationException(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java index 3024db5924..464596fd68 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/SingleUseTransactionTest.java @@ -159,11 +159,6 @@ public void close() { } } - @Override - public TransactionManager withCommitStats() { - return this; - } - @Override public CommitStats getCommitStats() { throw new UnsupportedOperationException(); @@ -433,11 +428,6 @@ public TransactionRunner allowNestedTransaction() { return this; } - @Override - public TransactionRunner withCommitStats() { - return this; - } - @Override public CommitStats getCommitStats() { throw new UnsupportedOperationException(); diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java index c894f0fbb3..ea818594b8 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITAsyncAPITest.java @@ -41,6 +41,7 @@ import com.google.cloud.spanner.KeyRange; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Statement; import com.google.cloud.spanner.Struct; @@ -318,7 +319,7 @@ public ApiFuture doWorkAsync(TransactionContext txn) { @Test public void asyncRunnerReturnsCommitStats() { - AsyncRunner runner = client.runAsync().withCommitStats(); + AsyncRunner runner = client.runAsync(Options.commitStats()); runner.runAsync( new AsyncWork() { @Override @@ -341,7 +342,7 @@ public ApiFuture doWorkAsync(TransactionContext txn) { @Test public void asyncTransactionManagerReturnsCommitStats() throws InterruptedException { - try (AsyncTransactionManager mgr = client.transactionManagerAsync().withCommitStats()) { + try (AsyncTransactionManager mgr = client.transactionManagerAsync(Options.commitStats())) { TransactionContextFuture ctx = mgr.beginAsync(); while (true) { try { diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java index 0fbd0702ec..a2b7b05864 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java @@ -306,14 +306,16 @@ public void testBackups() throws InterruptedException, ExecutionException { } // Insert some more data into db2 to get a timestamp from the server. Timestamp commitTs = - client.writeAtLeastOnce( - Arrays.asList( - Mutation.newInsertOrUpdateBuilder("BAR") - .set("ID") - .to(2L) - .set("NAME") - .to("TEST2") - .build())); + client + .writeAtLeastOnce( + Arrays.asList( + Mutation.newInsertOrUpdateBuilder("BAR") + .set("ID") + .to(2L) + .set("NAME") + .to("TEST2") + .build())) + .getCommitTimestamp(); // Test listing operations. // List all backups. diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java index 187f1e9f12..2feb2f60a2 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITCommitTimestampTest.java @@ -89,7 +89,7 @@ public void deleteAllTestRecords() { } private Timestamp write(Mutation m) { - return client.write(Arrays.asList(m)); + return client.write(Arrays.asList(m)).getCommitTimestamp(); } private Struct readRow(DatabaseClient client, String table, Key key, String... columns) { @@ -255,11 +255,13 @@ private void alterColumnOption(String databaseId, String table, String opt) thro private void writeAndVerify(DatabaseClient client, Timestamp ts) { Timestamp commitTimestamp = - client.write( - Arrays.asList( - Mutation.newInsertOrUpdateBuilder("T1").set("ts").to(ts).build(), - Mutation.newInsertOrUpdateBuilder("T2").set("ts").to(ts).build(), - Mutation.newInsertOrUpdateBuilder("T3").set("ts").to(ts).build())); + client + .write( + Arrays.asList( + Mutation.newInsertOrUpdateBuilder("T1").set("ts").to(ts).build(), + Mutation.newInsertOrUpdateBuilder("T2").set("ts").to(ts).build(), + Mutation.newInsertOrUpdateBuilder("T3").set("ts").to(ts).build())) + .getCommitTimestamp(); if (ts == Value.COMMIT_TIMESTAMP) { ts = commitTimestamp; } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITReadOnlyTxnTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITReadOnlyTxnTest.java index db68b5ec88..f1c8e5e6b7 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITReadOnlyTxnTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITReadOnlyTxnTest.java @@ -101,7 +101,7 @@ private static void writeNewValue( String value = "v" + i; Mutation m = Mutation.newInsertOrUpdateBuilder(TABLE_NAME).set("StringValue").to(value).build(); long minCommitNanoTime = System.nanoTime(); - Timestamp timestamp = client.writeAtLeastOnce(Arrays.asList(m)); + Timestamp timestamp = client.writeAtLeastOnce(Arrays.asList(m)).getCommitTimestamp(); if (historyBuilder != null) { historyBuilder.add(new History(timestamp, value, minCommitNanoTime)); } diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java index d80589d4b8..91639c94cb 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionManagerTest.java @@ -28,6 +28,7 @@ import com.google.cloud.spanner.IntegrationTestEnv; import com.google.cloud.spanner.Key; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.Struct; @@ -207,7 +208,7 @@ public void abortAndRetry() throws InterruptedException { @SuppressWarnings("resource") @Test public void transactionManagerReturnsCommitStats() throws InterruptedException { - try (TransactionManager manager = client.transactionManager().withCommitStats()) { + try (TransactionManager manager = client.transactionManager(Options.commitStats())) { TransactionContext txn = manager.begin(); while (true) { txn.buffer( diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java index 42dc57918d..3a3e0dc81d 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITTransactionTest.java @@ -34,6 +34,7 @@ import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.PartitionOptions; import com.google.cloud.spanner.ReadContext; @@ -510,7 +511,7 @@ public Void run(TransactionContext transaction) throws SpannerException { @Test public void transactionRunnerReturnsCommitStats() { final String key = uniqueKey(); - TransactionRunner runner = client.readWriteTransaction().withCommitStats(); + TransactionRunner runner = client.readWriteTransaction(Options.commitStats()); runner.run( new TransactionCallable() { @Override diff --git a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java index c80056bc2d..38dcc4693e 100644 --- a/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java +++ b/google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITWriteTest.java @@ -33,6 +33,7 @@ import com.google.cloud.spanner.Key; import com.google.cloud.spanner.KeySet; import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Options; import com.google.cloud.spanner.ParallelIntegrationTest; import com.google.cloud.spanner.ResultSet; import com.google.cloud.spanner.SpannerException; @@ -130,7 +131,7 @@ private static String uniqueString() { private String lastKey; private Timestamp write(Mutation m) { - return client.write(Arrays.asList(m)); + return client.write(Arrays.asList(m)).getCommitTimestamp(); } private Mutation.WriteBuilder baseInsert() { @@ -161,14 +162,15 @@ public void writeAtLeastOnce() { @Test public void writeReturnsCommitStats() { WriteResponse response = - client.writeWithCommitStats( + client.write( Arrays.asList( Mutation.newInsertOrUpdateBuilder("T") .set("K") .to(lastKey = uniqueString()) .set("StringValue") .to("v1") - .build())); + .build()), + Options.commitStats()); assertThat(response).isNotNull(); assertThat(response.getCommitTimestamp()).isNotNull(); assertThat(response.getCommitStats()).isNotNull(); @@ -178,14 +180,15 @@ public void writeReturnsCommitStats() { @Test public void writeAtLeastOnceReturnsCommitStats() { WriteResponse response = - client.writeAtLeastOnceWithCommitStats( + client.writeAtLeastOnce( Arrays.asList( Mutation.newInsertOrUpdateBuilder("T") .set("K") .to(lastKey = uniqueString()) .set("StringValue") .to("v1") - .build())); + .build()), + Options.commitStats()); assertThat(response).isNotNull(); assertThat(response.getCommitTimestamp()).isNotNull(); assertThat(response.getCommitStats()).isNotNull(); From 1a1adc9538a94667fbab246d9a2519fa255563f7 Mon Sep 17 00:00:00 2001 From: Olav Loite Date: Mon, 19 Oct 2020 18:09:08 +0200 Subject: [PATCH 7/7] fix: add clirr differences --- .../clirr-ignored-differences.xml | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/google-cloud-spanner/clirr-ignored-differences.xml b/google-cloud-spanner/clirr-ignored-differences.xml index 1484d2076c..a33f53c027 100644 --- a/google-cloud-spanner/clirr-ignored-differences.xml +++ b/google-cloud-spanner/clirr-ignored-differences.xml @@ -379,21 +379,11 @@ - - 7012 - com/google/cloud/spanner/TransactionManager - com.google.cloud.spanner.TransactionManager withCommitStats() - 7012 com/google/cloud/spanner/TransactionManager com.google.cloud.spanner.CommitStats getCommitStats() - - 7012 - com/google/cloud/spanner/TransactionRunner - com.google.cloud.spanner.TransactionRunner withCommitStats() - 7012 com/google/cloud/spanner/TransactionRunner @@ -402,32 +392,54 @@ 7012 com/google/cloud/spanner/AsyncTransactionManager - com.google.cloud.spanner.AsyncTransactionManager withCommitStats() + com.google.api.core.ApiFuture getCommitStats() 7012 - com/google/cloud/spanner/AsyncTransactionManager + com/google/cloud/spanner/AsyncRunner com.google.api.core.ApiFuture getCommitStats() - 7012 - com/google/cloud/spanner/AsyncRunner - com.google.cloud.spanner.AsyncRunner withCommitStats() + 7004 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.Timestamp write(java.lang.Iterable) - 7012 - com/google/cloud/spanner/AsyncRunner - com.google.api.core.ApiFuture getCommitStats() + 7006 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.Timestamp write(java.lang.Iterable) + com.google.cloud.spanner.DatabaseClient$WriteResponse - 7012 + 7004 com/google/cloud/spanner/DatabaseClient - com.google.cloud.spanner.DatabaseClient$WriteResponse writeWithCommitStats(java.lang.Iterable) + com.google.cloud.Timestamp writeAtLeastOnce(java.lang.Iterable) - 7012 + 7006 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.Timestamp writeAtLeastOnce(java.lang.Iterable) + com.google.cloud.spanner.DatabaseClient$WriteResponse + + + 7004 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.spanner.TransactionRunner readWriteTransaction() + + + 7004 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.spanner.TransactionManager transactionManager() + + + 7004 + com/google/cloud/spanner/DatabaseClient + com.google.cloud.spanner.AsyncRunner runAsync() + + + 7004 com/google/cloud/spanner/DatabaseClient - com.google.cloud.spanner.DatabaseClient$WriteResponse writeAtLeastOnceWithCommitStats(java.lang.Iterable) + com.google.cloud.spanner.AsyncTransactionManager transactionManagerAsync()