future) throws SpannerException {
}
throw SpannerExceptionFactory.asSpannerException(e.getCause());
} catch (InterruptedException e) {
- throw SpannerExceptionFactory.propagateInterrupt(e, null /*TODO: requestId*/);
+ throw SpannerExceptionFactory.propagateInterrupt(e);
} catch (CancellationException e) {
- throw SpannerExceptionFactory.newSpannerExceptionForCancellation(
- null, e, null /*TODO: requestId*/);
+ throw SpannerExceptionFactory.newSpannerExceptionForCancellation(null, e);
}
}
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java
index 0d841d24463..837a008a833 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerBatchUpdateException.java
@@ -25,9 +25,8 @@ public class SpannerBatchUpdateException extends SpannerException {
ErrorCode code,
String message,
long[] counts,
- Throwable cause,
- XGoogSpannerRequestId reqId) {
- super(token, code, false, message, cause, null, reqId);
+ Throwable cause) {
+ super(token, code, false, message, cause, null);
updateCounts = counts;
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerException.java
index fbe60e2a1d6..0829cc35d62 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerException.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerException.java
@@ -16,11 +16,11 @@
package com.google.cloud.spanner;
-import com.google.api.core.InternalApi;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ErrorDetails;
import com.google.cloud.grpc.BaseGrpcServiceException;
import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
import com.google.protobuf.util.Durations;
import com.google.rpc.ResourceInfo;
import com.google.rpc.RetryInfo;
@@ -41,9 +41,8 @@ public abstract static class ResourceNotFoundException extends SpannerException
@Nullable String message,
ResourceInfo resourceInfo,
@Nullable Throwable cause,
- @Nullable ApiException apiException,
- @Nullable XGoogSpannerRequestId reqId) {
- super(token, ErrorCode.NOT_FOUND, /* retryable */ false, message, cause, apiException, reqId);
+ @Nullable ApiException apiException) {
+ super(token, ErrorCode.NOT_FOUND, /* retryable */ false, message, cause, apiException);
this.resourceInfo = resourceInfo;
}
@@ -59,7 +58,7 @@ public String getResourceName() {
private final ErrorCode code;
private final ApiException apiException;
- private XGoogSpannerRequestId requestId;
+ private final XGoogSpannerRequestId requestId;
private String statement;
/** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */
@@ -80,25 +79,13 @@ public String getResourceName() {
@Nullable String message,
@Nullable Throwable cause,
@Nullable ApiException apiException) {
- this(token, code, retryable, message, cause, apiException, null);
- }
-
- /** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */
- SpannerException(
- DoNotConstructDirectly token,
- ErrorCode code,
- boolean retryable,
- @Nullable String message,
- @Nullable Throwable cause,
- @Nullable ApiException apiException,
- @Nullable XGoogSpannerRequestId requestId) {
super(message, cause, code.getCode(), retryable);
if (token != DoNotConstructDirectly.ALLOWED) {
throw new AssertionError("Do not construct directly: use SpannerExceptionFactory");
}
this.code = Preconditions.checkNotNull(code);
this.apiException = apiException;
- this.requestId = requestId;
+ this.requestId = extractRequestId(cause);
}
@Override
@@ -109,6 +96,14 @@ public String getMessage() {
return String.format("%s - Statement: '%s'", super.getMessage(), this.statement);
}
+ @Override
+ public String toString() {
+ if (this.requestId == null) {
+ return super.toString();
+ }
+ return super.toString() + " - RequestId: " + this.requestId;
+ }
+
/** Returns the error code associated with this exception. */
public ErrorCode getErrorCode() {
return code;
@@ -150,7 +145,7 @@ static long extractRetryDelay(Throwable cause) {
Metadata trailers = Status.trailersFromThrowable(cause);
if (trailers != null && trailers.containsKey(KEY_RETRY_INFO)) {
RetryInfo retryInfo = trailers.get(KEY_RETRY_INFO);
- if (retryInfo.hasRetryDelay()) {
+ if (retryInfo != null && retryInfo.hasRetryDelay()) {
return Durations.toMillis(retryInfo.getRetryDelay());
}
}
@@ -158,6 +153,20 @@ static long extractRetryDelay(Throwable cause) {
return -1L;
}
+ @Nullable
+ static XGoogSpannerRequestId extractRequestId(Throwable cause) {
+ if (cause != null) {
+ Metadata trailers = Status.trailersFromThrowable(cause);
+ if (trailers != null && trailers.containsKey(XGoogSpannerRequestId.REQUEST_ID_HEADER_KEY)) {
+ String requestId = trailers.get(XGoogSpannerRequestId.REQUEST_ID_HEADER_KEY);
+ if (!Strings.isNullOrEmpty(requestId)) {
+ return XGoogSpannerRequestId.of(requestId);
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Checks the underlying reason of the exception and if it's {@link ApiException} then return the
* reason otherwise null.
@@ -224,10 +233,4 @@ public ErrorDetails getErrorDetails() {
void setStatement(String statement) {
this.statement = statement;
}
-
- /** Sets the requestId. */
- @InternalApi
- public void setRequestId(XGoogSpannerRequestId reqId) {
- this.requestId = reqId;
- }
}
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java
index b9cc1cfb8b1..185f98b5433 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java
@@ -58,37 +58,17 @@ public final class SpannerExceptionFactory {
ProtoUtils.keyForProto(ErrorInfo.getDefaultInstance());
public static SpannerException newSpannerException(ErrorCode code, @Nullable String message) {
- return newSpannerException(code, message, (XGoogSpannerRequestId) (null));
- }
-
- public static SpannerException newSpannerException(
- ErrorCode code,
- @Nullable String message,
- @Nullable Throwable cause,
- @Nullable XGoogSpannerRequestId reqId) {
- return newSpannerExceptionPreformatted(
- code, formatMessage(code, message), cause, (ApiException) (null), reqId);
- }
-
- public static SpannerException newSpannerException(
- ErrorCode code, @Nullable String message, @Nullable XGoogSpannerRequestId reqId) {
- return newSpannerException(code, message, (Throwable) (null), reqId);
+ return newSpannerException(code, message, null);
}
public static SpannerException newSpannerException(
ErrorCode code, @Nullable String message, @Nullable Throwable cause) {
- return newSpannerException(code, message, cause, null);
+ return newSpannerExceptionPreformatted(code, formatMessage(code, message), cause, null);
}
public static SpannerException propagateInterrupt(InterruptedException e) {
- return propagateInterrupt(e, null);
- }
-
- public static SpannerException propagateInterrupt(
- InterruptedException e, XGoogSpannerRequestId reqId) {
Thread.currentThread().interrupt();
- return SpannerExceptionFactory.newSpannerException(
- ErrorCode.CANCELLED, "Interrupted", e, reqId);
+ return SpannerExceptionFactory.newSpannerException(ErrorCode.CANCELLED, "Interrupted", e);
}
/**
@@ -132,27 +112,17 @@ public static SpannerException asSpannerException(Throwable t) {
* #newSpannerException(ErrorCode, String)} instead of this method.
*/
public static SpannerException newSpannerException(Throwable cause) {
- return newSpannerException(null, cause, null);
- }
-
- public static SpannerException newSpannerException(Throwable cause, XGoogSpannerRequestId reqId) {
- return newSpannerException(null, cause, reqId);
+ return newSpannerException(null, cause);
}
public static SpannerBatchUpdateException newSpannerBatchUpdateException(
ErrorCode code, String message, long[] updateCounts) {
- return newSpannerBatchUpdateException(code, message, updateCounts, null);
- }
-
- public static SpannerBatchUpdateException newSpannerBatchUpdateException(
- ErrorCode code, String message, long[] updateCounts, @Nullable XGoogSpannerRequestId reqId) {
DoNotConstructDirectly token = DoNotConstructDirectly.ALLOWED;
SpannerException cause = null;
if (isTransactionMutationLimitException(code, message)) {
- cause =
- new TransactionMutationLimitExceededException(token, code, message, null, null, reqId);
+ cause = new TransactionMutationLimitExceededException(token, code, message, null, null);
}
- return new SpannerBatchUpdateException(token, code, message, updateCounts, cause, reqId);
+ return new SpannerBatchUpdateException(token, code, message, updateCounts, cause);
}
/** Constructs a specific error that */
@@ -205,10 +175,6 @@ public static SpannerBatchUpdateException newSpannerBatchUpdateException(
cause);
}
- public static SpannerException newSpannerException(@Nullable Context context, Throwable cause) {
- return newSpannerException(context, cause, null);
- }
-
/**
* Creates a new exception based on {@code cause}. If {@code cause} indicates cancellation, {@code
* context} will be inspected to establish the type of cancellation.
@@ -216,22 +182,21 @@ public static SpannerException newSpannerException(@Nullable Context context, Th
* Intended for internal library use; user code should use {@link
* #newSpannerException(ErrorCode, String)} instead of this method.
*/
- public static SpannerException newSpannerException(
- @Nullable Context context, Throwable cause, @Nullable XGoogSpannerRequestId reqId) {
+ public static SpannerException newSpannerException(@Nullable Context context, Throwable cause) {
if (cause instanceof SpannerException) {
SpannerException e = (SpannerException) cause;
- return newSpannerExceptionPreformatted(e.getErrorCode(), e.getMessage(), e, null, reqId);
+ return newSpannerExceptionPreformatted(e.getErrorCode(), e.getMessage(), e, null);
} else if (cause instanceof CancellationException) {
- return newSpannerExceptionForCancellation(context, cause, reqId);
+ return newSpannerExceptionForCancellation(context, cause);
} else if (cause instanceof ApiException) {
- return fromApiException((ApiException) cause, reqId);
+ return fromApiException((ApiException) cause);
}
// Extract gRPC status. This will produce "UNKNOWN" for non-gRPC exceptions.
Status status = Status.fromThrowable(cause);
if (status.getCode() == Status.Code.CANCELLED) {
- return newSpannerExceptionForCancellation(context, cause, reqId);
+ return newSpannerExceptionForCancellation(context, cause);
}
- return newSpannerException(ErrorCode.fromGrpcStatus(status), cause.getMessage(), cause, reqId);
+ return newSpannerException(ErrorCode.fromGrpcStatus(status), cause.getMessage(), cause);
}
public static RuntimeException causeAsRunTimeException(ExecutionException executionException) {
@@ -256,11 +221,6 @@ static SpannerException newRetryOnDifferentGrpcChannelException(
static SpannerException newSpannerExceptionForCancellation(
@Nullable Context context, @Nullable Throwable cause) {
- return newSpannerExceptionForCancellation(context, cause, null);
- }
-
- static SpannerException newSpannerExceptionForCancellation(
- @Nullable Context context, @Nullable Throwable cause, @Nullable XGoogSpannerRequestId reqId) {
if (context != null && context.isCancelled()) {
Throwable cancellationCause = context.cancellationCause();
Throwable throwable =
@@ -269,14 +229,13 @@ static SpannerException newSpannerExceptionForCancellation(
: MoreObjects.firstNonNull(cause, cancellationCause);
if (cancellationCause instanceof TimeoutException) {
return newSpannerException(
- ErrorCode.DEADLINE_EXCEEDED, "Current context exceeded deadline", throwable, reqId);
+ ErrorCode.DEADLINE_EXCEEDED, "Current context exceeded deadline", throwable);
} else {
- return newSpannerException(
- ErrorCode.CANCELLED, "Current context was cancelled", throwable, reqId);
+ return newSpannerException(ErrorCode.CANCELLED, "Current context was cancelled", throwable);
}
}
return newSpannerException(
- ErrorCode.CANCELLED, cause == null ? "Cancelled" : cause.getMessage(), cause, reqId);
+ ErrorCode.CANCELLED, cause == null ? "Cancelled" : cause.getMessage(), cause);
}
private static String formatMessage(ErrorCode code, @Nullable String message) {
@@ -355,13 +314,12 @@ static SpannerException newSpannerExceptionPreformatted(
ErrorCode code,
@Nullable String message,
@Nullable Throwable cause,
- @Nullable ApiException apiException,
- @Nullable XGoogSpannerRequestId reqId) {
+ @Nullable ApiException apiException) {
// This is the one place in the codebase that is allowed to call constructors directly.
DoNotConstructDirectly token = DoNotConstructDirectly.ALLOWED;
switch (code) {
case ABORTED:
- return new AbortedException(token, message, cause, apiException, reqId);
+ return new AbortedException(token, message, cause, apiException);
case RESOURCE_EXHAUSTED:
ErrorInfo info = extractErrorInfo(cause, apiException);
if (info != null
@@ -370,8 +328,7 @@ static SpannerException newSpannerExceptionPreformatted(
&& AdminRequestsPerMinuteExceededException.ADMIN_REQUESTS_LIMIT_VALUE.equals(
info.getMetadataMap()
.get(AdminRequestsPerMinuteExceededException.ADMIN_REQUESTS_LIMIT_KEY))) {
- return new AdminRequestsPerMinuteExceededException(
- token, message, cause, apiException, reqId);
+ return new AdminRequestsPerMinuteExceededException(token, message, cause, apiException);
}
case NOT_FOUND:
ResourceInfo resourceInfo = extractResourceInfo(cause);
@@ -379,39 +336,36 @@ static SpannerException newSpannerExceptionPreformatted(
switch (resourceInfo.getResourceType()) {
case SESSION_RESOURCE_TYPE:
return new SessionNotFoundException(
- token, message, resourceInfo, cause, apiException, reqId);
+ token, message, resourceInfo, cause, apiException);
case DATABASE_RESOURCE_TYPE:
return new DatabaseNotFoundException(
- token, message, resourceInfo, cause, apiException, reqId);
+ token, message, resourceInfo, cause, apiException);
case INSTANCE_RESOURCE_TYPE:
return new InstanceNotFoundException(
- token, message, resourceInfo, cause, apiException, reqId);
+ token, message, resourceInfo, cause, apiException);
}
}
case INVALID_ARGUMENT:
if (isTransactionMutationLimitException(cause, apiException)) {
return new TransactionMutationLimitExceededException(
- token, code, message, cause, apiException, reqId);
+ token, code, message, cause, apiException);
}
if (isMissingDefaultSequenceKindException(apiException)) {
- return new MissingDefaultSequenceKindException(
- token, code, message, cause, apiException, reqId);
+ return new MissingDefaultSequenceKindException(token, code, message, cause, apiException);
}
// Fall through to the default.
default:
return new SpannerException(
- token, code, isRetryable(code, cause), message, cause, apiException, reqId);
+ token, code, isRetryable(code, cause), message, cause, apiException);
}
}
static SpannerException newSpannerExceptionPreformatted(
ErrorCode code, @Nullable String message, @Nullable Throwable cause) {
- return newSpannerExceptionPreformatted(
- code, message, cause, null, (XGoogSpannerRequestId) (null));
+ return newSpannerExceptionPreformatted(code, message, cause, null);
}
- private static SpannerException fromApiException(
- ApiException exception, @Nullable XGoogSpannerRequestId reqId) {
+ private static SpannerException fromApiException(ApiException exception) {
Status.Code code;
if (exception.getStatusCode() instanceof GrpcStatusCode) {
code = ((GrpcStatusCode) exception.getStatusCode()).getTransportCode();
@@ -426,8 +380,7 @@ private static SpannerException fromApiException(
errorCode,
formatMessage(errorCode, exception.getMessage()),
exception.getCause(),
- exception,
- reqId);
+ exception);
}
private static boolean isRetryable(ErrorCode code, @Nullable Throwable cause) {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java
index 16f36144723..b4eef3a0f51 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerImpl.java
@@ -459,6 +459,14 @@ public boolean isClosed() {
}
}
+ void resetRequestIdCounters() {
+ gapicRpc.getRequestIdCreator().reset();
+ }
+
+ long getRequestIdClientId() {
+ return gapicRpc.getRequestIdCreator().getClientId();
+ }
+
/** Helper class for gRPC calls that can return paginated results. */
abstract static class PageFetcher implements NextPageFetcher {
private String nextPageToken;
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerRetryHelper.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerRetryHelper.java
index 6ca1a4e02e8..0dabcbd0094 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerRetryHelper.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerRetryHelper.java
@@ -116,8 +116,7 @@ public TimedAttemptSettings createNextAttempt(
public boolean shouldRetry(Throwable prevThrowable, T prevResponse)
throws CancellationException {
if (Context.current().isCancelled()) {
- throw SpannerExceptionFactory.newSpannerExceptionForCancellation(
- Context.current(), null, null);
+ throw SpannerExceptionFactory.newSpannerExceptionForCancellation(Context.current(), null);
}
return prevThrowable instanceof AbortedException
|| prevThrowable instanceof com.google.api.gax.rpc.AbortedException;
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionMutationLimitExceededException.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionMutationLimitExceededException.java
index f6b0a4efd22..de215c5caee 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionMutationLimitExceededException.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/TransactionMutationLimitExceededException.java
@@ -37,9 +37,8 @@ public class TransactionMutationLimitExceededException extends SpannerException
ErrorCode errorCode,
String message,
Throwable cause,
- @Nullable ApiException apiException,
- @Nullable XGoogSpannerRequestId reqId) {
- super(token, errorCode, /* retryable= */ false, message, cause, apiException, reqId);
+ @Nullable ApiException apiException) {
+ super(token, errorCode, /* retryable= */ false, message, cause, apiException);
}
static boolean isTransactionMutationLimitException(ErrorCode code, String message) {
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 6ce8cc11aa6..7afccce194c 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
@@ -449,8 +449,6 @@ private final class CommitRunnable implements Runnable {
@Override
public void run() {
- XGoogSpannerRequestId reqId =
- session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
try {
prev.get();
if (transactionId == null && transactionIdFuture == null) {
@@ -493,8 +491,7 @@ public void run() {
final ApiFuture commitFuture;
final ISpan opSpan = tracer.spanBuilderWithExplicitParent(SpannerImpl.COMMIT, span);
try (IScope ignore = tracer.withSpan(opSpan)) {
- commitFuture =
- rpc.commitAsync(commitRequest, reqId.withOptions(getTransactionChannelHint()));
+ commitFuture = rpc.commitAsync(commitRequest, getTransactionChannelHint());
}
session.markUsed(clock.instant());
commitFuture.addListener(
@@ -505,7 +502,7 @@ public void run() {
// future, but we add a result here as well as a safety precaution.
res.setException(
SpannerExceptionFactory.newSpannerException(
- ErrorCode.INTERNAL, "commitFuture is not done", reqId));
+ ErrorCode.INTERNAL, "commitFuture is not done"));
return;
}
com.google.spanner.v1.CommitResponse proto = commitFuture.get();
@@ -535,9 +532,7 @@ public void run() {
}
if (!proto.hasCommitTimestamp()) {
throw newSpannerException(
- ErrorCode.INTERNAL,
- "Missing commitTimestamp:\n" + session.getName(),
- reqId);
+ ErrorCode.INTERNAL, "Missing commitTimestamp:\n" + session.getName());
}
span.addAnnotation("Commit Done");
opSpan.end();
@@ -577,8 +572,7 @@ public void run() {
res.setException(SpannerExceptionFactory.propagateTimeout(e));
} catch (Throwable e) {
res.setException(
- SpannerExceptionFactory.newSpannerException(
- e.getCause() == null ? e : e.getCause(), reqId));
+ SpannerExceptionFactory.newSpannerException(e.getCause() == null ? e : e.getCause()));
}
}
}
@@ -697,8 +691,7 @@ options, getPreviousTransactionId())))
}
throw se;
} catch (InterruptedException e) {
- throw SpannerExceptionFactory.newSpannerExceptionForCancellation(
- null, e, null /*TODO: requestId*/);
+ throw SpannerExceptionFactory.newSpannerExceptionForCancellation(null, e);
}
}
// There is already a transactionId available. Include that id as the transaction to use.
@@ -938,12 +931,9 @@ private ResultSet internalExecuteUpdate(
final ExecuteSqlRequest.Builder builder =
getExecuteSqlRequestBuilder(
statement, queryMode, options, /* withTransactionSelector= */ true);
- XGoogSpannerRequestId reqId =
- session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
try {
com.google.spanner.v1.ResultSet resultSet =
- rpc.executeQuery(
- builder.build(), reqId.withOptions(getTransactionChannelHint()), isRouteToLeader());
+ rpc.executeQuery(builder.build(), getTransactionChannelHint(), isRouteToLeader());
session.markUsed(clock.instant());
if (resultSet.getMetadata().hasTransaction()) {
onTransactionMetadata(
@@ -1076,11 +1066,9 @@ public long[] batchUpdate(Iterable statements, UpdateOption... update
}
final ExecuteBatchDmlRequest.Builder builder =
getExecuteBatchDmlRequestBuilder(statements, options);
- XGoogSpannerRequestId reqId =
- session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
try {
com.google.spanner.v1.ExecuteBatchDmlResponse response =
- rpc.executeBatchDml(builder.build(), reqId.withOptions(getTransactionChannelHint()));
+ rpc.executeBatchDml(builder.build(), getTransactionChannelHint());
session.markUsed(clock.instant());
long[] results = new long[response.getResultSetsCount()];
for (int i = 0; i < response.getResultSetsCount(); ++i) {
@@ -1104,8 +1092,7 @@ public long[] batchUpdate(Iterable statements, UpdateOption... update
throw newSpannerBatchUpdateException(
ErrorCode.fromRpcStatus(response.getStatus()),
response.getStatus().getMessage(),
- results,
- reqId);
+ results);
}
return results;
} catch (Throwable e) {
@@ -1140,15 +1127,11 @@ public ApiFuture batchUpdateAsync(
final ExecuteBatchDmlRequest.Builder builder =
getExecuteBatchDmlRequestBuilder(statements, options);
ApiFuture response;
- XGoogSpannerRequestId reqId =
- session.getRequestIdCreator().nextRequestId(session.getChannel(), 1);
try {
// Register the update as an async operation that must finish before the transaction may
// commit.
increaseAsyncOperations();
- response =
- rpc.executeBatchDmlAsync(
- builder.build(), reqId.withOptions(getTransactionChannelHint()));
+ response = rpc.executeBatchDmlAsync(builder.build(), getTransactionChannelHint());
session.markUsed(clock.instant());
} catch (Throwable t) {
decreaseAsyncOperations();
@@ -1178,8 +1161,7 @@ public ApiFuture batchUpdateAsync(
throw newSpannerBatchUpdateException(
ErrorCode.fromRpcStatus(batchDmlResponse.getStatus()),
batchDmlResponse.getStatus().getMessage(),
- results,
- reqId);
+ results);
}
return results;
},
@@ -1233,7 +1215,7 @@ public ListenableAsyncResultSet executeQueryAsync(
private final SessionImpl session;
private final Options options;
private ISpan span;
- private TraceWrapper tracer;
+ private final TraceWrapper tracer;
private TransactionContextImpl txn;
private volatile boolean isValid = true;
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/XGoogSpannerRequestId.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/XGoogSpannerRequestId.java
index 47ccb05231c..d858fdb9273 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/XGoogSpannerRequestId.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/XGoogSpannerRequestId.java
@@ -17,13 +17,11 @@
package com.google.cloud.spanner;
import com.google.api.core.InternalApi;
-import com.google.cloud.spanner.spi.v1.SpannerRpc;
import com.google.common.annotations.VisibleForTesting;
+import io.grpc.CallOptions;
import io.grpc.Metadata;
import java.math.BigInteger;
import java.security.SecureRandom;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Objects;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
@@ -31,13 +29,15 @@
@InternalApi
public class XGoogSpannerRequestId {
- // 1. Generate the random process Id singleton.
+ // 1. Generate the random process ID singleton.
@VisibleForTesting
static final String RAND_PROCESS_ID = XGoogSpannerRequestId.generateRandProcessId();
- public static String REQUEST_ID = "x-goog-spanner-request-id";
- public static final Metadata.Key REQUEST_HEADER_KEY =
- Metadata.Key.of(REQUEST_ID, Metadata.ASCII_STRING_MARSHALLER);
+ public static String REQUEST_ID_HEADER_NAME = "x-goog-spanner-request-id";
+ public static final Metadata.Key REQUEST_ID_HEADER_KEY =
+ Metadata.Key.of(REQUEST_ID_HEADER_NAME, Metadata.ASCII_STRING_MARSHALLER);
+ public static final CallOptions.Key REQUEST_ID_CALL_OPTIONS_KEY =
+ CallOptions.Key.create("XGoogSpannerRequestId");
@VisibleForTesting
static final long VERSION = 1; // The version of the specification being implemented.
@@ -59,6 +59,20 @@ public static XGoogSpannerRequestId of(
return new XGoogSpannerRequestId(nthClientId, nthChannelId, nthRequest, attempt);
}
+ @VisibleForTesting
+ long getNthClientId() {
+ return nthClientId;
+ }
+
+ @VisibleForTesting
+ long getNthChannelId() {
+ return nthChannelId;
+ }
+
+ boolean hasChannelId() {
+ return nthChannelId > 0;
+ }
+
@VisibleForTesting
long getAttempt() {
return this.attempt;
@@ -95,8 +109,8 @@ private static String generateRandProcessId() {
return String.format("%016x", bigInt);
}
- @Override
- public String toString() {
+ /** Returns the string representation of this RequestId as it should be sent to Spanner. */
+ public String getHeaderValue() {
return String.format(
"%d.%s.%d.%d.%d.%d",
XGoogSpannerRequestId.VERSION,
@@ -107,6 +121,18 @@ public String toString() {
this.attempt);
}
+ @Override
+ public String toString() {
+ return String.format(
+ "%d.%s.%d.%s.%d.%d",
+ XGoogSpannerRequestId.VERSION,
+ XGoogSpannerRequestId.RAND_PROCESS_ID,
+ this.nthClientId,
+ this.nthChannelId < 0 ? "x" : String.valueOf(this.nthChannelId),
+ this.nthRequest,
+ this.attempt);
+ }
+
public String debugToString() {
return String.format(
"%d.%s.nth_client=%d.nth_chan=%d.nth_req=%d.attempt=%d",
@@ -151,31 +177,38 @@ public void incrementAttempt() {
this.attempt++;
}
- Map withOptions(Map options) {
- Map copyOptions = new HashMap<>();
- if (options != null) {
- copyOptions.putAll(options);
- }
- copyOptions.put(SpannerRpc.Option.REQUEST_ID, this);
- return copyOptions;
- }
-
@Override
public int hashCode() {
return Objects.hash(this.nthClientId, this.nthChannelId, this.nthRequest, this.attempt);
}
- interface RequestIdCreator {
- XGoogSpannerRequestId nextRequestId(long channelId, int attempt);
+ @InternalApi
+ public interface RequestIdCreator {
+ long getClientId();
+
+ XGoogSpannerRequestId nextRequestId(long channelId);
+
+ void reset();
}
- static class NoopRequestIdCreator implements RequestIdCreator {
- NoopRequestIdCreator() {}
+ // TODO: Move this class into test code.
+ static final class NoopRequestIdCreator implements RequestIdCreator {
+ static final NoopRequestIdCreator INSTANCE = new NoopRequestIdCreator();
+
+ private NoopRequestIdCreator() {}
@Override
- public XGoogSpannerRequestId nextRequestId(long channelId, int attempt) {
- return XGoogSpannerRequestId.of(1, 1, 1, 0);
+ public long getClientId() {
+ return 1L;
}
+
+ @Override
+ public XGoogSpannerRequestId nextRequestId(long channelId) {
+ return XGoogSpannerRequestId.of(1, channelId, 1, 0);
+ }
+
+ @Override
+ public void reset() {}
}
public void setChannelId(long channelId) {
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java
index c448695afd2..4a8d643b79c 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/DdlBatch.java
@@ -266,7 +266,7 @@ public ApiFuture runBatchAsync(CallType callType) {
} catch (SpannerException e) {
long[] updateCounts = extractUpdateCounts(operationReference.get());
throw SpannerExceptionFactory.newSpannerBatchUpdateException(
- e.getErrorCode(), e.getMessage(), updateCounts, null /* TODO: requestId */);
+ e.getErrorCode(), e.getMessage(), updateCounts);
}
} catch (Throwable t) {
span.setStatus(StatusCode.ERROR);
diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java
index 371d73a0af2..306bd9842ac 100644
--- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java
+++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java
@@ -19,6 +19,7 @@
import static com.google.cloud.spanner.SpannerExceptionFactory.asSpannerException;
import static com.google.cloud.spanner.SpannerExceptionFactory.newSpannerException;
import static com.google.cloud.spanner.ThreadFactoryUtil.tryCreateVirtualThreadPerTaskExecutor;
+import static com.google.cloud.spanner.XGoogSpannerRequestId.REQUEST_ID_CALL_OPTIONS_KEY;
import com.google.api.core.ApiFunction;
import com.google.api.core.ApiFuture;
@@ -73,6 +74,7 @@
import com.google.cloud.spanner.SpannerOptions.CallContextConfigurator;
import com.google.cloud.spanner.SpannerOptions.CallCredentialsProvider;
import com.google.cloud.spanner.XGoogSpannerRequestId;
+import com.google.cloud.spanner.XGoogSpannerRequestId.RequestIdCreator;
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStub;
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStubSettings;
import com.google.cloud.spanner.admin.database.v1.stub.GrpcDatabaseAdminCallableFactory;
@@ -88,7 +90,6 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;
import com.google.common.util.concurrent.RateLimiter;
@@ -241,6 +242,7 @@ public class GapicSpannerRpc implements SpannerRpc {
public static boolean DIRECTPATH_CHANNEL_CREATED = false;
private static final String API_FILE = "grpc-gcp-apiconfig.json";
+ private final RequestIdCreator requestIdCreator = new RequestIdCreatorImpl();
private boolean rpcIsClosed;
private final SpannerStub spannerStub;
private final RetrySettings executeQueryRetrySettings;
@@ -825,7 +827,7 @@ public OperationFuture call() {
isRetry = true;
if (operationName == null) {
- GrpcCallContext context = newCallContext(null, instanceName, initialRequest, method);
+ GrpcCallContext context = newAdminCallContext(instanceName, initialRequest, method);
return operationCallable.futureCall(initialRequest, context);
} else {
return operationCallable.resumeFutureCall(operationName);
@@ -916,8 +918,7 @@ public Paginated listInstanceConfigs(int pageSize, @Nullable Str
ListInstanceConfigsRequest request = requestBuilder.build();
GrpcCallContext context =
- newCallContext(
- null, projectName, request, InstanceAdminGrpc.getListInstanceConfigsMethod());
+ newAdminCallContext(projectName, request, InstanceAdminGrpc.getListInstanceConfigsMethod());
ListInstanceConfigsResponse response =
get(instanceAdminStub.listInstanceConfigsCallable().futureCall(request, context));
return new Paginated<>(response.getInstanceConfigsList(), response.getNextPageToken());
@@ -940,7 +941,7 @@ public OperationFuture createInsta
}
CreateInstanceConfigRequest request = builder.build();
GrpcCallContext context =
- newCallContext(null, parent, request, InstanceAdminGrpc.getCreateInstanceConfigMethod());
+ newAdminCallContext(parent, request, InstanceAdminGrpc.getCreateInstanceConfigMethod());
return instanceAdminStub.createInstanceConfigOperationCallable().futureCall(request, context);
}
@@ -957,11 +958,8 @@ public OperationFuture updateInsta
}
UpdateInstanceConfigRequest request = builder.build();
GrpcCallContext context =
- newCallContext(
- null,
- instanceConfig.getName(),
- request,
- InstanceAdminGrpc.getUpdateInstanceConfigMethod());
+ newAdminCallContext(
+ instanceConfig.getName(), request, InstanceAdminGrpc.getUpdateInstanceConfigMethod());
return instanceAdminStub.updateInstanceConfigOperationCallable().futureCall(request, context);
}
@@ -971,7 +969,7 @@ public InstanceConfig getInstanceConfig(String instanceConfigName) throws Spanne
GetInstanceConfigRequest.newBuilder().setName(instanceConfigName).build();
GrpcCallContext context =
- newCallContext(null, projectName, request, InstanceAdminGrpc.getGetInstanceConfigMethod());
+ newAdminCallContext(projectName, request, InstanceAdminGrpc.getGetInstanceConfigMethod());
return get(instanceAdminStub.getInstanceConfigCallable().futureCall(request, context));
}
@@ -990,8 +988,8 @@ public void deleteInstanceConfig(
}
DeleteInstanceConfigRequest request = requestBuilder.build();
GrpcCallContext context =
- newCallContext(
- null, instanceConfigName, request, InstanceAdminGrpc.getDeleteInstanceConfigMethod());
+ newAdminCallContext(
+ instanceConfigName, request, InstanceAdminGrpc.getDeleteInstanceConfigMethod());
get(instanceAdminStub.deleteInstanceConfigCallable().futureCall(request, context));
}
@@ -1012,8 +1010,8 @@ public Paginated listInstanceConfigOperations(
final ListInstanceConfigOperationsRequest request = requestBuilder.build();
final GrpcCallContext context =
- newCallContext(
- null, projectName, request, InstanceAdminGrpc.getListInstanceConfigOperationsMethod());
+ newAdminCallContext(
+ projectName, request, InstanceAdminGrpc.getListInstanceConfigOperationsMethod());
ListInstanceConfigOperationsResponse response =
runWithRetryOnAdministrativeRequestsExceeded(
() ->
@@ -1038,7 +1036,7 @@ public Paginated listInstances(
ListInstancesRequest request = requestBuilder.build();
GrpcCallContext context =
- newCallContext(null, projectName, request, InstanceAdminGrpc.getListInstancesMethod());
+ newAdminCallContext(projectName, request, InstanceAdminGrpc.getListInstancesMethod());
ListInstancesResponse response =
get(instanceAdminStub.listInstancesCallable().futureCall(request, context));
return new Paginated<>(response.getInstancesList(), response.getNextPageToken());
@@ -1054,7 +1052,7 @@ public OperationFuture createInstance(
.setInstance(instance)
.build();
GrpcCallContext context =
- newCallContext(null, parent, request, InstanceAdminGrpc.getCreateInstanceMethod());
+ newAdminCallContext(parent, request, InstanceAdminGrpc.getCreateInstanceMethod());
return instanceAdminStub.createInstanceOperationCallable().futureCall(request, context);
}
@@ -1064,8 +1062,8 @@ public OperationFuture updateInstance(
UpdateInstanceRequest request =
UpdateInstanceRequest.newBuilder().setInstance(instance).setFieldMask(fieldMask).build();
GrpcCallContext context =
- newCallContext(
- null, instance.getName(), request, InstanceAdminGrpc.getUpdateInstanceMethod());
+ newAdminCallContext(
+ instance.getName(), request, InstanceAdminGrpc.getUpdateInstanceMethod());
return instanceAdminStub.updateInstanceOperationCallable().futureCall(request, context);
}
@@ -1074,7 +1072,7 @@ public Instance getInstance(String instanceName) throws SpannerException {
GetInstanceRequest request = GetInstanceRequest.newBuilder().setName(instanceName).build();
GrpcCallContext context =
- newCallContext(null, instanceName, request, InstanceAdminGrpc.getGetInstanceMethod());
+ newAdminCallContext(instanceName, request, InstanceAdminGrpc.getGetInstanceMethod());
return get(instanceAdminStub.getInstanceCallable().futureCall(request, context));
}
@@ -1084,7 +1082,7 @@ public void deleteInstance(String instanceName) throws SpannerException {
DeleteInstanceRequest.newBuilder().setName(instanceName).build();
GrpcCallContext context =
- newCallContext(null, instanceName, request, InstanceAdminGrpc.getDeleteInstanceMethod());
+ newAdminCallContext(instanceName, request, InstanceAdminGrpc.getDeleteInstanceMethod());
get(instanceAdminStub.deleteInstanceCallable().futureCall(request, context));
}
@@ -1103,8 +1101,8 @@ public Paginated listBackupOperations(
final ListBackupOperationsRequest request = requestBuilder.build();
final GrpcCallContext context =
- newCallContext(
- null, instanceName, request, DatabaseAdminGrpc.getListBackupOperationsMethod());
+ newAdminCallContext(
+ instanceName, request, DatabaseAdminGrpc.getListBackupOperationsMethod());
ListBackupOperationsResponse response =
runWithRetryOnAdministrativeRequestsExceeded(
() ->
@@ -1128,8 +1126,8 @@ public Paginated listDatabaseOperations(
final ListDatabaseOperationsRequest request = requestBuilder.build();
final GrpcCallContext context =
- newCallContext(
- null, instanceName, request, DatabaseAdminGrpc.getListDatabaseOperationsMethod());
+ newAdminCallContext(
+ instanceName, request, DatabaseAdminGrpc.getListDatabaseOperationsMethod());
ListDatabaseOperationsResponse response =
runWithRetryOnAdministrativeRequestsExceeded(
() ->
@@ -1154,7 +1152,7 @@ public Paginated listDatabaseRoles(
final ListDatabaseRolesRequest request = requestBuilder.build();
final GrpcCallContext context =
- newCallContext(null, databaseName, request, DatabaseAdminGrpc.getListDatabaseRolesMethod());
+ newAdminCallContext(databaseName, request, DatabaseAdminGrpc.getListDatabaseRolesMethod());
ListDatabaseRolesResponse response =
runWithRetryOnAdministrativeRequestsExceeded(
() -> get(databaseAdminStub.listDatabaseRolesCallable().futureCall(request, context)));
@@ -1178,7 +1176,7 @@ public Paginated listBackups(
final ListBackupsRequest request = requestBuilder.build();
final GrpcCallContext context =
- newCallContext(null, instanceName, request, DatabaseAdminGrpc.getListBackupsMethod());
+ newAdminCallContext(instanceName, request, DatabaseAdminGrpc.getListBackupsMethod());
ListBackupsResponse response =
runWithRetryOnAdministrativeRequestsExceeded(
() -> get(databaseAdminStub.listBackupsCallable().futureCall(request, context)));
@@ -1198,7 +1196,7 @@ public Paginated listDatabases(
final ListDatabasesRequest request = requestBuilder.build();
final GrpcCallContext context =
- newCallContext(null, instanceName, request, DatabaseAdminGrpc.getListDatabasesMethod());
+ newAdminCallContext(instanceName, request, DatabaseAdminGrpc.getListDatabasesMethod());
ListDatabasesResponse response =
runWithRetryOnAdministrativeRequestsExceeded(
() -> get(databaseAdminStub.listDatabasesCallable().futureCall(request, context)));
@@ -1300,8 +1298,7 @@ public OperationFuture updateDatabaseDdl(
}
final UpdateDatabaseDdlRequest request = requestBuilder.build();
final GrpcCallContext context =
- newCallContext(
- null,
+ newAdminCallContext(
databaseInfo.getId().getName(),
request,
DatabaseAdminGrpc.getUpdateDatabaseDdlMethod());
@@ -1341,7 +1338,7 @@ public void dropDatabase(String databaseName) throws SpannerException {
DropDatabaseRequest.newBuilder().setDatabase(databaseName).build();
final GrpcCallContext context =
- newCallContext(null, databaseName, request, DatabaseAdminGrpc.getDropDatabaseMethod());
+ newAdminCallContext(databaseName, request, DatabaseAdminGrpc.getDropDatabaseMethod());
runWithRetryOnAdministrativeRequestsExceeded(
() -> {
get(databaseAdminStub.dropDatabaseCallable().futureCall(request, context));
@@ -1356,7 +1353,7 @@ public Database getDatabase(String databaseName) throws SpannerException {
GetDatabaseRequest.newBuilder().setName(databaseName).build();
final GrpcCallContext context =
- newCallContext(null, databaseName, request, DatabaseAdminGrpc.getGetDatabaseMethod());
+ newAdminCallContext(databaseName, request, DatabaseAdminGrpc.getGetDatabaseMethod());
return runWithRetryOnAdministrativeRequestsExceeded(
() -> get(databaseAdminStub.getDatabaseCallable().futureCall(request, context)));
}
@@ -1367,8 +1364,8 @@ public OperationFuture updateDatabase(
UpdateDatabaseRequest request =
UpdateDatabaseRequest.newBuilder().setDatabase(database).setUpdateMask(updateMask).build();
GrpcCallContext context =
- newCallContext(
- null, database.getName(), request, DatabaseAdminGrpc.getUpdateDatabaseMethod());
+ newAdminCallContext(
+ database.getName(), request, DatabaseAdminGrpc.getUpdateDatabaseMethod());
return databaseAdminStub.updateDatabaseOperationCallable().futureCall(request, context);
}
@@ -1379,7 +1376,7 @@ public GetDatabaseDdlResponse getDatabaseDdl(String databaseName) throws Spanner
GetDatabaseDdlRequest.newBuilder().setDatabase(databaseName).build();
final GrpcCallContext context =
- newCallContext(null, databaseName, request, DatabaseAdminGrpc.getGetDatabaseDdlMethod());
+ newAdminCallContext(databaseName, request, DatabaseAdminGrpc.getGetDatabaseDdlMethod());
return runWithRetryOnAdministrativeRequestsExceeded(
() -> get(databaseAdminStub.getDatabaseDdlCallable().futureCall(request, context)));
}
@@ -1563,7 +1560,7 @@ public Backup updateBackup(Backup backup, FieldMask updateMask) {
final UpdateBackupRequest request =
UpdateBackupRequest.newBuilder().setBackup(backup).setUpdateMask(updateMask).build();
final GrpcCallContext context =
- newCallContext(null, backup.getName(), request, DatabaseAdminGrpc.getUpdateBackupMethod());
+ newAdminCallContext(backup.getName(), request, DatabaseAdminGrpc.getUpdateBackupMethod());
return runWithRetryOnAdministrativeRequestsExceeded(
() -> databaseAdminStub.updateBackupCallable().call(request, context));
}
@@ -1574,7 +1571,7 @@ public void deleteBackup(String backupName) {
final DeleteBackupRequest request =
DeleteBackupRequest.newBuilder().setName(backupName).build();
final GrpcCallContext context =
- newCallContext(null, backupName, request, DatabaseAdminGrpc.getDeleteBackupMethod());
+ newAdminCallContext(backupName, request, DatabaseAdminGrpc.getDeleteBackupMethod());
runWithRetryOnAdministrativeRequestsExceeded(
() -> {
databaseAdminStub.deleteBackupCallable().call(request, context);
@@ -1587,7 +1584,7 @@ public Backup getBackup(String backupName) throws SpannerException {
acquireAdministrativeRequestsRateLimiter();
final GetBackupRequest request = GetBackupRequest.newBuilder().setName(backupName).build();
final GrpcCallContext context =
- newCallContext(null, backupName, request, DatabaseAdminGrpc.getGetBackupMethod());
+ newAdminCallContext(backupName, request, DatabaseAdminGrpc.getGetBackupMethod());
return runWithRetryOnAdministrativeRequestsExceeded(
() -> get(databaseAdminStub.getBackupCallable().futureCall(request, context)));
}
@@ -1597,7 +1594,7 @@ public Operation getOperation(String name) throws SpannerException {
acquireAdministrativeRequestsRateLimiter();
final GetOperationRequest request = GetOperationRequest.newBuilder().setName(name).build();
final GrpcCallContext context =
- newCallContext(null, name, request, OperationsGrpc.getGetOperationMethod());
+ newAdminCallContext(name, request, OperationsGrpc.getGetOperationMethod());
return runWithRetryOnAdministrativeRequestsExceeded(
() ->
get(
@@ -1613,7 +1610,7 @@ public void cancelOperation(String name) throws SpannerException {
final CancelOperationRequest request =
CancelOperationRequest.newBuilder().setName(name).build();
final GrpcCallContext context =
- newCallContext(null, name, request, OperationsGrpc.getCancelOperationMethod());
+ newAdminCallContext(name, request, OperationsGrpc.getCancelOperationMethod());
runWithRetryOnAdministrativeRequestsExceeded(
() -> {
get(
@@ -1718,10 +1715,16 @@ public StreamingCall read(
ReadRequest request,
ResultStreamConsumer consumer,
@Nullable Map