Skip to content

Commit fa922ae

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. Return synchronous USE write failures as failed futures so pool accounting is restored. 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. Add focused coverage for defunct connections, pool accounting, permanent validation errors, transient server errors, retry-policy accounting, and moving from a single-connection host pool to the next host after internal keyspace setup failure.
1 parent b666e09 commit fa922ae

4 files changed

Lines changed: 421 additions & 5 deletions

File tree

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

Lines changed: 18 additions & 2 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;
@@ -900,7 +901,17 @@ ListenableFuture<Connection> setKeyspaceAsync(final String keyspace)
900901
logger.debug("{} Setting keyspace {}", this, keyspace);
901902
// Note: we quote the keyspace below, because the name is the one coming from Cassandra, so
902903
// it's in the right case already
903-
Future future = write(new Requests.Query("USE \"" + keyspace + '"'));
904+
Future future;
905+
try {
906+
future = write(new Requests.Query("USE \"" + keyspace + '"'));
907+
} catch (ConnectionException | BusyConnectionException e) {
908+
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
909+
ksFuture.setException(e);
910+
return ksFuture;
911+
} catch (RuntimeException e) {
912+
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
913+
throw e;
914+
}
904915
Futures.addCallback(
905916
future,
906917
new FutureCallback<Message.Response>() {
@@ -915,7 +926,12 @@ public void onSuccess(Message.Response response) {
915926
targetKeyspace.compareAndSet(attempt, defaultKeyspaceAttempt);
916927
if (response.type == ERROR) {
917928
Responses.Error error = (Responses.Error) response;
918-
ksFuture.setException(defunct(error.asException(endPoint)));
929+
DriverException exception = error.asException(endPoint);
930+
if (exception instanceof QueryValidationException) {
931+
ksFuture.setException(exception);
932+
} else {
933+
ksFuture.setException(defunct(exception));
934+
}
919935
} else {
920936
ksFuture.setException(
921937
defunct(

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

Lines changed: 25 additions & 3 deletions
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(
@@ -698,8 +714,14 @@ private void dequeue(final Connection connection) {
698714
pendingBorrowCount.decrementAndGet();
699715
// Ensure that the keyspace set on the connection is the one set on the pool state, in the
700716
// general case it will be.
701-
ListenableFuture<Connection> setKeyspaceFuture =
702-
connection.setKeyspaceAsync(manager.poolsState.keyspace);
717+
ListenableFuture<Connection> setKeyspaceFuture;
718+
try {
719+
setKeyspaceFuture = connection.setKeyspaceAsync(manager.poolsState.keyspace);
720+
} catch (ConnectionException | BusyConnectionException e) {
721+
pendingBorrow.setException(e);
722+
connection.inFlight.decrementAndGet();
723+
continue;
724+
}
703725
// Slight optimization, if the keyspace was already correct the future will be complete, so
704726
// simply complete it here.
705727
if (setKeyspaceFuture.isDone()) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.datastax.driver.core.exceptions.NoHostAvailableException;
3232
import com.datastax.driver.core.exceptions.OperationTimedOutException;
3333
import com.datastax.driver.core.exceptions.OverloadedException;
34+
import com.datastax.driver.core.exceptions.QueryValidationException;
3435
import com.datastax.driver.core.exceptions.ReadFailureException;
3536
import com.datastax.driver.core.exceptions.ReadTimeoutException;
3637
import com.datastax.driver.core.exceptions.ServerError;
@@ -468,6 +469,10 @@ public void onSuccess(Connection connection) {
468469

469470
@Override
470471
public void onFailure(Throwable t) {
472+
if (t instanceof QueryValidationException) {
473+
setFinalException(null, (QueryValidationException) t);
474+
return;
475+
}
471476
if (t instanceof BusyPoolException) {
472477
logError(host.getEndPoint(), t);
473478
} else {

0 commit comments

Comments
 (0)