Skip to content

Commit 3f8ffaa

Browse files
committed
3.x: fail fast on permanent keyspace setup failures
Keyspace setup during connection borrow used to treat every failed internal USE as a host or connection failure. That is correct for driver-side failures and transient server errors, but permanent validation errors such as a missing keyspace should fail the user request directly. Report keyspace setup write failures through setKeyspaceAsync failed futures consistently, removing stale synchronous throws and caller catches. Reset failed keyspace attempts so later attempts are not blocked by stale in-flight state, and restore pool accounting when borrow-time setup fails. Preserve QueryValidationException from internal USE without defuncting the connection, and have RequestHandler fail fast for those permanent errors. Keep internal driver failures and transient server-side errors on the normal next-host path before the user query is written, logging those pre-write host-attempt failures only at debug level. Preserve the synchronous setKeyspace busy-connection behavior after async failures are reported through futures: BusyConnectionException is still converted to a non-defuncting ConnectionException, while ConnectionException keeps the previous defunct behavior. Add focused coverage for defunct connections, pool accounting, permanent validation errors, transient server errors, retry-policy accounting, single-connection-pool next-host failover, and synchronous busy keyspace setup. Release test ByteBufs used to synthesize server errors.
1 parent 9d45182 commit 3f8ffaa

4 files changed

Lines changed: 463 additions & 23 deletions

File tree

driver-core/src/main/java/com/datastax/driver/core/Connection.java

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.datastax.driver.core.exceptions.DriverInternalError;
3535
import com.datastax.driver.core.exceptions.FrameTooLongException;
3636
import com.datastax.driver.core.exceptions.OperationTimedOutException;
37+
import com.datastax.driver.core.exceptions.QueryValidationException;
3738
import com.datastax.driver.core.exceptions.TransportException;
3839
import com.datastax.driver.core.exceptions.UnsupportedProtocolVersionException;
3940
import com.datastax.driver.core.utils.MoreFutures;
@@ -845,17 +846,13 @@ void setKeyspace(String keyspace) throws ConnectionException {
845846

846847
try {
847848
Uninterruptibles.getUninterruptibly(setKeyspaceAsync(keyspace));
848-
} catch (ConnectionException e) {
849-
throw defunct(e);
850-
} catch (BusyConnectionException e) {
851-
logger.warn(
852-
"Tried to set the keyspace on busy {}. "
853-
+ "This should not happen but is not critical (it will be retried)",
854-
this);
855-
throw new ConnectionException(endPoint, "Tried to set the keyspace on busy connection");
856849
} catch (ExecutionException e) {
857850
Throwable cause = e.getCause();
858-
if (cause instanceof OperationTimedOutException) {
851+
if (cause instanceof BusyConnectionException) {
852+
throw keyspaceBusyException();
853+
} else if (cause instanceof ConnectionException) {
854+
throw defunct((ConnectionException) cause);
855+
} else if (cause instanceof OperationTimedOutException) {
859856
// Rethrow so that the caller doesn't try to use the connection, but do not defunct as we
860857
// don't want to mark down
861858
logger.warn(
@@ -869,8 +866,15 @@ void setKeyspace(String keyspace) throws ConnectionException {
869866
}
870867
}
871868

872-
ListenableFuture<Connection> setKeyspaceAsync(final String keyspace)
873-
throws ConnectionException, BusyConnectionException {
869+
private ConnectionException keyspaceBusyException() {
870+
logger.warn(
871+
"Tried to set the keyspace on busy {}. "
872+
+ "This should not happen but is not critical (it will be retried)",
873+
this);
874+
return new ConnectionException(endPoint, "Tried to set the keyspace on busy connection");
875+
}
876+
877+
ListenableFuture<Connection> setKeyspaceAsync(final String keyspace) {
874878
SetKeyspaceAttempt existingAttempt = targetKeyspace.get();
875879
if (MoreObjects.equal(existingAttempt.keyspace, keyspace)) return existingAttempt.future;
876880

@@ -900,7 +904,17 @@ ListenableFuture<Connection> setKeyspaceAsync(final String keyspace)
900904
logger.debug("{} Setting keyspace {}", this, keyspace);
901905
// Note: we quote the keyspace below, because the name is the one coming from Cassandra, so
902906
// it's in the right case already
903-
Future future = write(new Requests.Query("USE \"" + keyspace + '"'));
907+
Future future;
908+
try {
909+
future = write(new Requests.Query("USE \"" + keyspace + '"'));
910+
} catch (ConnectionException | BusyConnectionException e) {
911+
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
912+
ksFuture.setException(e);
913+
return ksFuture;
914+
} catch (RuntimeException e) {
915+
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
916+
throw e;
917+
}
904918
Futures.addCallback(
905919
future,
906920
new FutureCallback<Message.Response>() {
@@ -915,7 +929,12 @@ public void onSuccess(Message.Response response) {
915929
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
916930
if (response.type == ERROR) {
917931
Responses.Error error = (Responses.Error) response;
918-
ksFuture.setException(defunct(error.asException(endPoint)));
932+
DriverException exception = error.asException(endPoint);
933+
if (exception instanceof QueryValidationException) {
934+
ksFuture.setException(exception);
935+
} else {
936+
ksFuture.setException(defunct(exception));
937+
}
919938
} else {
920939
ksFuture.setException(
921940
defunct(

driver-core/src/main/java/com/datastax/driver/core/HostConnectionPool.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import static com.datastax.driver.core.Connection.State.TRASHED;
2828

2929
import com.datastax.driver.core.exceptions.AuthenticationException;
30+
import com.datastax.driver.core.exceptions.BusyConnectionException;
3031
import com.datastax.driver.core.exceptions.BusyPoolException;
3132
import com.datastax.driver.core.exceptions.ConnectionException;
3233
import com.datastax.driver.core.exceptions.UnsupportedProtocolVersionException;
@@ -611,7 +612,22 @@ ListenableFuture<Connection> borrowConnection(
611612
if (totalInFlightCount > currentCapacity) maybeSpawnNewConnection(shardId);
612613
}
613614

614-
return leastBusy.setKeyspaceAsync(manager.poolsState.keyspace);
615+
final Connection borrowedConnection = leastBusy;
616+
ListenableFuture<Connection> setKeyspaceFuture =
617+
borrowedConnection.setKeyspaceAsync(manager.poolsState.keyspace);
618+
Futures.addCallback(
619+
setKeyspaceFuture,
620+
new FutureCallback<Connection>() {
621+
@Override
622+
public void onSuccess(Connection connection) {}
623+
624+
@Override
625+
public void onFailure(Throwable t) {
626+
borrowedConnection.release(t instanceof BusyConnectionException);
627+
}
628+
},
629+
MoreExecutors.directExecutor());
630+
return setKeyspaceFuture;
615631
}
616632

617633
private ListenableFuture<Connection> enqueue(

driver-core/src/main/java/com/datastax/driver/core/RequestHandler.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424
import com.codahale.metrics.Timer;
2525
import com.datastax.driver.core.exceptions.BootstrappingException;
2626
import com.datastax.driver.core.exceptions.BusyConnectionException;
27-
import com.datastax.driver.core.exceptions.BusyPoolException;
2827
import com.datastax.driver.core.exceptions.ConnectionException;
2928
import com.datastax.driver.core.exceptions.DriverException;
3029
import com.datastax.driver.core.exceptions.DriverInternalError;
3130
import com.datastax.driver.core.exceptions.NoHostAvailableException;
3231
import com.datastax.driver.core.exceptions.OperationTimedOutException;
3332
import com.datastax.driver.core.exceptions.OverloadedException;
33+
import com.datastax.driver.core.exceptions.QueryValidationException;
3434
import com.datastax.driver.core.exceptions.ReadFailureException;
3535
import com.datastax.driver.core.exceptions.ReadTimeoutException;
3636
import com.datastax.driver.core.exceptions.ServerError;
@@ -468,15 +468,11 @@ public void onSuccess(Connection connection) {
468468

469469
@Override
470470
public void onFailure(Throwable t) {
471-
if (t instanceof BusyPoolException) {
472-
logError(host.getEndPoint(), t);
473-
} else {
474-
logger.warn(
475-
"Unexpected error while querying {} - [{}]. Find next host to query.",
476-
host.getEndPoint(),
477-
t.toString());
478-
logError(host.getEndPoint(), t);
471+
if (t instanceof QueryValidationException) {
472+
setFinalException(null, (QueryValidationException) t);
473+
return;
479474
}
475+
logError(host.getEndPoint(), t);
480476
findNextHostAndQuery();
481477
}
482478
},

0 commit comments

Comments
 (0)