File tree

6 files changed

+361
-205
lines changed

6 files changed

+361
-205
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.spanner;
18+
19+
import com.google.cloud.spanner.SpannerException.ResourceNotFoundException;
20+
import com.google.rpc.ResourceInfo;
21+
import javax.annotation.Nullable;
22+
23+
/**
24+
* Exception thrown by Cloud Spanner when an operation detects that the instance that is being used
25+
* no longer exists. This type of error has its own subclass as it is a condition that should cause
26+
* the client library to stop trying to send RPCs to the backend until the user has taken action.
27+
*/
28+
public class InstanceNotFoundException extends ResourceNotFoundException {
29+
private static final long serialVersionUID = 45297002L;
30+
31+
/** Private constructor. Use {@link SpannerExceptionFactory} to create instances. */
32+
InstanceNotFoundException(
33+
DoNotConstructDirectly token,
34+
@Nullable String message,
35+
ResourceInfo resourceInfo,
36+
@Nullable Throwable cause) {
37+
super(token, message, resourceInfo, cause);
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.cloud.spanner.Options.QueryOption;
2727
import com.google.cloud.spanner.Options.ReadOption;
2828
import com.google.cloud.spanner.SessionClient.SessionConsumer;
29+
import com.google.cloud.spanner.SpannerException.ResourceNotFoundException;
2930
import com.google.common.annotations.VisibleForTesting;
3031
import com.google.common.base.Function;
3132
import com.google.common.base.MoreObjects;
@@ -781,13 +782,14 @@ public void close() {
781782
if (lastException != null && isSessionNotFound(lastException)) {
782783
invalidateSession(this);
783784
} else {
784-
if (lastException != null && isDatabaseNotFound(lastException)) {
785+
if (lastException != null && isDatabaseOrInstanceNotFound(lastException)) {
785786
// Mark this session pool as no longer valid and then release the session into the pool as
786787
// there is nothing we can do with it anyways.
787788
synchronized (lock) {
788-
SessionPool.this.databaseNotFound =
789+
SessionPool.this.resourceNotFoundException =
789790
MoreObjects.firstNonNull(
790-
SessionPool.this.databaseNotFound, (DatabaseNotFoundException) lastException);
791+
SessionPool.this.resourceNotFoundException,
792+
(ResourceNotFoundException) lastException);
791793
}
792794
}
793795
lastException = null;
@@ -1075,7 +1077,7 @@ private static enum Position {
10751077
private SettableFuture<Void> closureFuture;
10761078

10771079
@GuardedBy("lock")
1078-
private DatabaseNotFoundException databaseNotFound;
1080+
private ResourceNotFoundException resourceNotFoundException;
10791081

10801082
@GuardedBy("lock")
10811083
private final LinkedList<PooledSession> readSessions = new LinkedList<>();
@@ -1213,8 +1215,8 @@ private boolean isSessionNotFound(SpannerException e) {
12131215
return e.getErrorCode() == ErrorCode.NOT_FOUND && e.getMessage().contains("Session not found");
12141216
}
12151217

1216-
private boolean isDatabaseNotFound(SpannerException e) {
1217-
return e instanceof DatabaseNotFoundException;
1218+
private boolean isDatabaseOrInstanceNotFound(SpannerException e) {
1219+
return e instanceof DatabaseNotFoundException || e instanceof InstanceNotFoundException;
12181220
}
12191221

12201222
private boolean isPermissionDenied(SpannerException e) {
@@ -1249,7 +1251,7 @@ private PooledSession findSessionToKeepAlive(
12491251
/** @return true if this {@link SessionPool} is still valid. */
12501252
boolean isValid() {
12511253
synchronized (lock) {
1252-
return closureFuture == null && databaseNotFound == null;
1254+
return closureFuture == null && resourceNotFoundException == null;
12531255
}
12541256
}
12551257

@@ -1279,14 +1281,14 @@ PooledSession getReadSession() throws SpannerException {
12791281
span.addAnnotation("Pool has been closed");
12801282
throw new IllegalStateException("Pool has been closed");
12811283
}
1282-
if (databaseNotFound != null) {
1284+
if (resourceNotFoundException != null) {
12831285
span.addAnnotation("Database has been deleted");
12841286
throw SpannerExceptionFactory.newSpannerException(
12851287
ErrorCode.NOT_FOUND,
12861288
String.format(
12871289
"The session pool has been invalidated because a previous RPC returned 'Database not found': %s",
1288-
databaseNotFound.getMessage()),
1289-
databaseNotFound);
1290+
resourceNotFoundException.getMessage()),
1291+
resourceNotFoundException);
12901292
}
12911293
sess = readSessions.poll();
12921294
if (sess == null) {
@@ -1344,14 +1346,14 @@ PooledSession getReadWriteSession() {
13441346
span.addAnnotation("Pool has been closed");
13451347
throw new IllegalStateException("Pool has been closed");
13461348
}
1347-
if (databaseNotFound != null) {
1349+
if (resourceNotFoundException != null) {
13481350
span.addAnnotation("Database has been deleted");
13491351
throw SpannerExceptionFactory.newSpannerException(
13501352
ErrorCode.NOT_FOUND,
13511353
String.format(
13521354
"The session pool has been invalidated because a previous RPC returned 'Database not found': %s",
1353-
databaseNotFound.getMessage()),
1354-
databaseNotFound);
1355+
resourceNotFoundException.getMessage()),
1356+
resourceNotFoundException);
13551357
}
13561358
sess = writePreparedSessions.poll();
13571359
if (sess == null) {
@@ -1495,17 +1497,18 @@ private void handleCreateSessionsFailure(SpannerException e, int count) {
14951497
break;
14961498
}
14971499
}
1498-
this.databaseNotFound =
1500+
this.resourceNotFoundException =
14991501
MoreObjects.firstNonNull(
1500-
this.databaseNotFound, isDatabaseNotFound(e) ? (DatabaseNotFoundException) e : null);
1502+
this.resourceNotFoundException,
1503+
isDatabaseOrInstanceNotFound(e) ? (ResourceNotFoundException) e : null);
15011504
}
15021505
}
15031506

15041507
private void handlePrepareSessionFailure(SpannerException e, PooledSession session) {
15051508
synchronized (lock) {
15061509
if (isSessionNotFound(e)) {
15071510
invalidateSession(session);
1508-
} else if (isDatabaseNotFound(e) || isPermissionDenied(e)) {
1511+
} else if (isDatabaseOrInstanceNotFound(e) || isPermissionDenied(e)) {
15091512
// Database has been deleted or the user has no permission to write to this database. We
15101513
// should stop trying to prepare any transactions. Also propagate the error to all waiters,
15111514
// as any further waiting is pointless.
@@ -1520,10 +1523,10 @@ private void handlePrepareSessionFailure(SpannerException e, PooledSession sessi
15201523
if (isClosed()) {
15211524
decrementPendingClosures(1);
15221525
}
1523-
this.databaseNotFound =
1526+
this.resourceNotFoundException =
15241527
MoreObjects.firstNonNull(
1525-
this.databaseNotFound,
1526-
isDatabaseNotFound(e) ? (DatabaseNotFoundException) e : null);
1528+
this.resourceNotFoundException,
1529+
isDatabaseOrInstanceNotFound(e) ? (ResourceNotFoundException) e : null);
15271530
} else if (readWriteWaiters.size() > 0) {
15281531
releaseSession(session, Position.FIRST);
15291532
readWriteWaiters.poll().put(e);
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public final class SpannerExceptionFactory {
4242
static final String SESSION_RESOURCE_TYPE = "type.googleapis.com/google.spanner.v1.Session";
4343
static final String DATABASE_RESOURCE_TYPE =
4444
"type.googleapis.com/google.spanner.admin.database.v1.Database";
45+
static final String INSTANCE_RESOURCE_TYPE =
46+
"type.googleapis.com/google.spanner.admin.instance.v1.Instance";
4547
private static final Metadata.Key<ResourceInfo> KEY_RESOURCE_INFO =
4648
ProtoUtils.keyForProto(ResourceInfo.getDefaultInstance());
4749

@@ -199,6 +201,8 @@ private static SpannerException newSpannerExceptionPreformatted(
199201
return new SessionNotFoundException(token, message, resourceInfo, cause);
200202
} else if (resourceInfo.getResourceType().equals(DATABASE_RESOURCE_TYPE)) {
201203
return new DatabaseNotFoundException(token, message, resourceInfo, cause);
204+
} else if (resourceInfo.getResourceType().equals(INSTANCE_RESOURCE_TYPE)) {
205+
return new InstanceNotFoundException(token, message, resourceInfo, cause);
202206
}
203207
}
204208
// Fall through to the default.

0 commit comments

Comments
 (0)